Autor Beitrag
hydemarie
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: So 19.06.16 23:45 
Ich hab' da ein Problem.

Beim Versuch, eine uralte Delphi-6-DLL für mIRC auf Delphi Xirgendwas (momentan: Seattle) zu hieven, gingen mir natürlich die meisten Funktionen kaputt. PChar und AnsiChar ist eben nicht mehr das gleiche. Aktuell nervt mich TRegistry:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
function readreg( mWnd: hWnd; aWnd: hWnd; Data: PAnsiChar; Parms: PChar; Show: Boolean; NoPause: Boolean ): Integer; exportstdcall;
var
  Path, Key, ReadType, HKEY : String;
  reg : TRegistry;
  I : Integer;
begin
  Path := Copy (String (Data),1,Pos ('|',String(Data)) - 1);
  ReadType := Copy (String(Data),Pos('|',String(Data)) + 1,1);
  I := Length (Path);
  Key := '';
  while (Path[I] <> '\'and (I >= 1do
  begin
    Key := Path[I] + Key;
    dec (I);
  end;
  Path := Copy (Path,1,I);
  HKEY := Copy (Path, 1, Pos ('\',Path) - 1);
  Path := Copy (Path, Pos ('\',Path) + 1, Length (Path) - Length (HKEY));
  reg := TRegistry.Create;
  if HKEY = 'HKEY_CLASSES_ROOT' then
    reg.RootKey := HKEY_CLASSES_ROOT
  else if HKEY = 'HKEY_CURRENT_USER' then
    reg.RootKey := HKEY_CURRENT_USER
  else if HKEY = 'HKEY_LOCAL_MACHINE' then
    reg.RootKey := HKEY_LOCAL_MACHINE
  else if HKEY = 'HKEY_USERS' then
    reg.RootKey := HKEY_USERS
  else if HKEY = 'HKEY_CURRENT_CONFIG' then
    reg.RootKey := HKEY_CURRENT_CONFIG
  else
    strcopy (Data, '$null');
  if reg.OpenKey(Path, False) then
  begin
    if ReadType = '0' then
      strcopy (Data, PAnsiChar(reg.ReadString(Key)))
    else if ReadType = '1' then
      strcopy (Data, PAnsiChar(reg.ReadInteger(Key)))
    else
      strcopy (Data, '$null');
  end
  else strcopy (Data, '$null');
  result := 3;
end;


mIRC liefert, wenn ich das richtig sehe, sämtliche Daten immer als Ansi-Strings aus, weshalb Data blöderweise PAnsiChar sein sollte. Das Ziel hinter dieser Funktion: //echo -ag $dll(meinedll,readreg,registrystring) soll in mIRC den jeweiligen String ausgeben; //echo -ag hier $dll(meine.dll,readreg,HKEY_CURRENT_USER\SOFTWARE\Clients\Mail\|0) zum Beispiel "Mozilla Thunderbird". Aktuell tut's das aber nicht - gibt nur "M" zurück.

Ideen?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 20.06.16 13:03 
ReadString liefert einen UnicodeString. Wenn du den auf einen Pointer auf AnsiChars castest, hast du das M und das Nullzeichen (der zweite Teil des Unicodezeichens) beendet den String.

Schlecht an der Funktion ist aber vor allem, dass du in den Pointer Data einfach ohne Längenprüfung Daten schiebst. Wenn der übergebene Puffer nicht groß genug ist, knallt es. An der Stelle macht es Sinn sich an der Windows API zu orientieren. Dort wird die Größe des Puffers mit übergeben und wenn der zu klein ist, ein entsprechendes Ergebnis geliefert.

Funktionieren sollte es so:
ausblenden Delphi-Quelltext
1:
PAnsiChar(AnsiString(reg.ReadInteger(Key)))					

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: Mo 20.06.16 22:31 
Ah. Nullzeichen war mir neu, in anderen Sprachen ist mir das bisher noch nicht um die Ohren geflogen. Mpf. Danke, AnsiString funtkioniert an dieser Stelle. (Ich ging davon aus, dass PAnsiChar nicht bedeutend anders als ein AnsiString funktioniert. Da bin ich wohl C-verwöhnt.)

Unter welchen Umständen könnte der Pufferinhalt hier denn zu groß werden?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 21.06.16 06:27 
user profile iconhydemarie hat folgendes geschrieben Zum zitierten Posting springen:
Ah. Nullzeichen war mir neu, in anderen Sprachen ist mir das bisher noch nicht um die Ohren geflogen. Mpf. Danke, AnsiString funtkioniert an dieser Stelle. (Ich ging davon aus, dass PAnsiChar nicht bedeutend anders als ein AnsiString funktioniert. Da bin ich wohl C-verwöhnt.)
Das Nullzeichen als Stringende stammt aus C. ;-)

In Delphis Strings wird die Länge mit gespeichert, so dass auch Strings mit Nullzeichen funktionieren solange man sie nicht auf PAnsiChar oder PWideChar castet. Diese sind kompatibel zur Windows API und C, wo das Nullzeichen das Stringende markiert und es keine gespeicherte Länge gibt.

Delphis Strings sind kompatibel mit der jeweiligen P*Char Variante, da in Delphi hinter den Zeichen auch ein unsichtbares Nullzeichen folgt. Dadurch kann man einfach den Pointer auf das erste Zeichen nehmen und hat einen gültigen P*Char. Nur muss man bei dem Cast eben die richtige Variante benutzen, damit der Pointer auch auf die korrekte Zeichenart zeigt. Denn durch den Cast wird der Speicherinhalt nicht verändert.
Durch den expliziten Cast auf AnsiString vorher wird eine Kopie des Strings als AnsiString erstellt und darauf funktioniert dann auch der Pointer auf AnsiChar.

user profile iconhydemarie hat folgendes geschrieben Zum zitierten Posting springen:
Unter welchen Umständen könnte der Pufferinhalt hier denn zu groß werden?
Nun ja, du kopierst mit StrCopy ja den String an die Stelle im Speicher, die du im Pointer Data übergeben bekommen hast. Wenn der Aufrufer dort vorher nicht genügend Speicher reserviert hat, überschreibst du irgendwelchen Speicher. Zum Beispiel, wenn nur 255 Bytes reserviert wurden, du aber 300 Zeichen dorthin kopierst.

Das ist dann der berüchtigte Buffer Overflow, der gerne als Sicherheitslücke benutzt wird um Speicherstellen hinter dem Puffer mit Code zu füllen, der dann (unbeabsichtigt durch das Programm) ausgeführt werden könnte.

Für diesen Beitrag haben gedankt: hydemarie
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: Di 21.06.16 07:42 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Durch den expliziten Cast auf AnsiString vorher wird eine Kopie des Strings als AnsiString erstellt und darauf funktioniert dann auch der Pointer auf AnsiChar.


Auweia. :lol:
Ich meinte, in C/C++ kann ich einen char* ohne bedeutenden Aufwand in einen String umwandeln, ohne mir über Nullzeichen Gedanken machen zu müssen. Das funktioniert da einfach. (Oder ich hab' bisher nur zufällig klug programmiert. :lol:) Mir war nur nicht ganz klar, dass ein PAnsiChar und ein AnsiString grundverschiedene Dinge sind. Wie schon in der Shoutbox angemerkt: Ich mach' einfach deutlich zu wenig Delphi. :(

Insofern: Was gelernt. Danke!

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Nun ja, du kopierst mit StrCopy ja den String an die Stelle im Speicher, die du im Pointer Data übergeben bekommen hast. Wenn der Aufrufer dort vorher nicht genügend Speicher reserviert hat, überschreibst du irgendwelchen Speicher.


Wäre das nicht eben die Sache von mIRC an dieser Stelle?
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 21.06.16 08:29 
user profile iconhydemarie hat folgendes geschrieben Zum zitierten Posting springen:
Wäre das nicht eben die Sache von mIRC an dieser Stelle?
Wenn das deine Funktion direkt aufruft, ja. Aber dann steht sicher in der Doku wie groß der übergebene Puffer ist, so dass du die Länge vor dem Kopieren prüfen kannst.
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: Di 21.06.16 09:03 
Zitat:
The routine in the DLL being called must be of the form:

int __stdcall procname(HWND mWnd, HWND aWnd, char *data, char *parms, BOOL show, BOOL nopause)

(...)

data is the information that you wish to send to the DLL. On return, the DLL can fill this variable with the command it wants mIRC to perform if any.


Nö. :nixweiss:
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 21.06.16 09:51 
Eieiei... genau so entstehen Buffer Overflows...
Du kannst so ja gar nicht wissen wie viel du schreiben darfst...
hydemarie Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: Di 21.06.16 11:03 
Bis jetzt hat alles reingepasst. :lol: