Autor |
Beitrag |
hydemarie
Beiträge: 481
Erhaltene Danke: 51
|
Verfasst: 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:
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; export; stdcall; 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 >= 1) do 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
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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:
Delphi-Quelltext 1:
| PAnsiChar(AnsiString(reg.ReadInteger(Key))) |
Für diesen Beitrag haben gedankt: hydemarie
|
|
hydemarie
Beiträge: 481
Erhaltene Danke: 51
|
Verfasst: 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
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 21.06.16 06:27
hydemarie hat folgendes geschrieben : | 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.
hydemarie hat folgendes geschrieben : | 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
Beiträge: 481
Erhaltene Danke: 51
|
Verfasst: Di 21.06.16 07:42
jaenicke hat folgendes geschrieben : | 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.
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. ) 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!
jaenicke hat folgendes geschrieben : |
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
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 21.06.16 08:29
hydemarie hat folgendes geschrieben : | 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
Beiträge: 481
Erhaltene Danke: 51
|
Verfasst: 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ö.
|
|
jaenicke
Beiträge: 19284
Erhaltene Danke: 1742
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
Beiträge: 481
Erhaltene Danke: 51
|
Verfasst: Di 21.06.16 11:03
Bis jetzt hat alles reingepasst.
|
|
|