![]() |
AV bei LookupAccountSidW
Liste der Anhänge anzeigen (Anzahl: 2)
Ich bin am verzweifeln. Folgender Code:
Delphi-Quellcode:
Ich bekomme beim zweiten Aufruf von LookupAccountSidW eine AV. Das verrückte ist, in einem anderen Projekt benutze ich exakt den selben Code und da funktioniert er. :wall:
function StrSIDToName(const StrSID: Widestring; var Name: WideString; var SIDType: DWORD): Boolean;
var SID : PSID; Buffer : PWideChar; NameLen, TempLen : Cardinal; succes : Boolean; begin SID := nil; succes := ConvertStringSIDToSID(PWideChar(StrSID), SID); if succes then begin NameLen := 0; TempLen := 0; LookupAccountSidW(nil, SID, nil, NameLen, nil, TempLen, SIDType); GetMem(Buffer, NameLen * sizeOf(WideChar)); try succes := LookupAccountSidW(nil, SID, Buffer, NameLen, nil, TempLen, SIDType); if succes then SetString(Name, Buffer, Namelen); finally FreeMem(Buffer); end; end; if Assigned(SID) then LocalFree(Cardinal(SID)); result := succes; end; Nehme ich LookupAccountSIDA geht alles. Ich will aber die Unicode Version nutzen. Im Anhang mal beide Projekte. Ich weiß nicht, was ich in dem einen Projekt anders mache. |
Re: AV bei LookupAccountSidW
Moin Michael,
in deinem älteren Thread zu diesem Thema hatten wir doch schon geklärt: In der Unicode-Version darf für ReferencedDomainName kein NIL übergeben werden. Freundliche Grüße |
Re: AV bei LookupAccountSidW
Ja, aber warumn funktioniert es in dem einem Projekt trotzdem? Aber auch wenn ich in dem Projekt mit der AV einen Buffer für einen ReferenceName übergebe, bekomme ich eine AV.
Ich vermute, ich reserviere den Speicher in beiden Projekten nicht richtig und habe in dem einem nur Glück, dass es irgendwie passt. Ihc habe gestern schon mit VirtaulAlloc und HeapAlloc rumgespielt, aber es hat nicht geklappt. Verdammt jetzt gehts, wenn ich einen Buffer für die RefernceDomain angebe. Es ar gestern wohl doch etwas spät. :wall: |
Re: AV bei LookupAccountSidW
Ok nochmal zum mitschreiben.
Delphi-Quellcode:
Der Parameter lpReferencedDomainName darf nicht nil sein.
BOOL WINAPI LookupAccountSid(
LPCTSTR lpSystemName, PSID lpSid, LPTSTR lpName, LPDWORD cchName, LPTSTR lpReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse ); In der MSDN steht nichts davon, dass er NULL sein darf. Normalerweise steht sowas dran, oder? Ok, dann muss man davon ausgehen, wenn nichts davon dransteht, dass darf der Parameter auch nicht nil sein. Verwirrend. Ich verwende übrigens LocalAlloc für den SidName und die -domain. Hatte vorher schon einige Probleme bei Funktionen, die mit GetMem nicht funktioniert haben. Leider geht das LocalAlloc am Speichermanager vorbei (soweit ich weiß). |
Re: AV bei LookupAccountSidW
Zitat:
Btw. bei dem ersten Aufruf sind beide Parameter nil und es geht. |
Re: AV bei LookupAccountSidW
Moin Zusammen,
doch, ReferencedDomainName darf sehr wohl nil sein. Zitat:
Der Zielbuffer wird auf nil gesetzt (und ggf. die Bufferlänge auf 0), damit einem die Funktion die benötigte Grösse zurückliefert. |
Re: AV bei LookupAccountSidW
Also ich habe es mal getestet.
Delphi-Quellcode:
Der Parameter darf wirklich NIL sein, jedoch nur dann, wenn folgende Vorbedingung erfüllt ist :
BOOL WINAPI LookupAccountSid(
LPCTSTR lpSystemName, PSID lpSid, LPTSTR lpName, LPDWORD cchName, LPTSTR lpReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse );
@Luckie: Dein Quellcode wäre demnach semantisch inkorrekt. Warum es mal so funktioniert und mal anders nicht, kann ich dir nicht sagen. Zumindest auf meinem XP SP2 ergibt der obige Quellcode immer eine Exception in "ntdll.dll". Ich habe mal dein "WindowsStations.dpr" Programm ausprobiert und musste feststellen, dass StrSIDToName funktioniert, weil die Variable TempLen immer 0 war. Ich habe garkein SID Name bekommen. Dann isses auch kein Wunder, dass es funktioniert :D Die Ansi Variante funktionier übrigens immer. Vermutlich ein Bug in der W-Variante? Oder Kompatibilitätsgründe? |
Re: AV bei LookupAccountSidW
Hallo Chris,
Zitat:
![]() Die AV könnte eventuell hier herrühren:
Delphi-Quellcode:
Je nachdem wie die Variable vor dem Aufruf initialisiert wird - oder nicht - könnte es zu einer AV kommen.
// ...
if succes then SetString(Name, Buffer, Namelen); // ... Freundliche Grüße |
Re: AV bei LookupAccountSidW
Name wird als var Parameter übergeben.
|
Re: AV bei LookupAccountSidW
Hallo?
hat jemand überhaupt mein Post gelesen?????? |
Re: AV bei LookupAccountSidW
Hallo,
ich habe von deinen Tests gelesen. Ist dabei verwertbarer Code entstanden, den du hier veröffentlichen möchtest? Freundliche Grüße |
Re: AV bei LookupAccountSidW
Zitat:
Ich habe es nicht hinbekommen, dass die Funktion TRUE geliefert hat, wenn der Domainpuffer nil war (und dessen Längen-Begleiter). ODER: Die Funktion scheitert zwar nicht mit einer AV, jedoch gibt sie eben auch kein TRUE zurück.
Delphi-Quellcode:
In meiner Security Library habe ich übrigens diesen Code in etwa auch so drin. Und den verwende ich ständig :D, besonders auch, da SIDs eigentlich überall gebraucht werden.uses JclDebug, jwaWindows, jwaSddl, Windows; function StrSIDToName(const StrSID: widestring; var Name: wideString; var SIDType: DWORD): Boolean; var SID : PSID; Buffer,B2 : PwideChar; NameLen, TempLen : Cardinal; success : Boolean; TS : TSecurityID; res : Integer; begin SID := nil; success := true; success := ConvertStringSidToSidW(PWideChar(StrSID), jwaWindows.PSID(SID)); if success then begin NameLen := 0; TempLen := 0; SetLastError(0); if not LookupAccountSidW(nil, SID, nil, NameLen, nil, TempLen, SIDType) and (GetLastError() = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(Buffer, NameLen * sizeOf(wideChar)); GetMem(B2, TempLen * sizeOf(wideChar)); if (Buffer <> nil) then try SetLastError(0); success := LookupAccountSidW(nil, SID, Buffer, NameLen, B2, TempLen, SIDType); if not success then RaiseLastOSError; if success then SetString(Name, Buffer, Namelen); finally FreeMem(Buffer); FreeMem(B2); end; end else RaiseLastOSError; end else RaiseLastOSError; LocalFree(HLOCAL(SID)); result := success; end; |
Re: AV bei LookupAccountSidW
Hallo,
ich kann leider momentan nicht testen, aber nach Aktenlage würde die Funktion bei mir etwa so aussehen:
Delphi-Quellcode:
GetLastError() würde ich außerhalb verwenden, sonst macht Result keinen Sinn mehr und man könnte gleich eine Prozedur verwenden.
function StrSIDToName(const StrSID: WideString; var Name: WideString; var SIDType: DWORD): Boolean;
var SID: PSID; bufName, bufDomain: PWideChar; sizeName, sizeDomain: Cardinal; begin SID := nil; if ConvertStringSidToSidW(PWideChar(StrSID), SID) then begin sizeName := 0; sizeDomain := 0; SetLastError(0); Result := LookupAccountSidW(nil, SID, nil, sizeName, nil, sizeDomain, SIDType); if not Result and (GetLastError() = ERROR_INSUFFICIENT_BUFFER) then begin GetMem(bufName, sizeName * SizeOf(WideChar)); GetMem(bufDomain, sizeDomain * SizeOf(WideChar)); SetLastError(0); Result := LookupAccountSidW(nil, SID, bufName, sizeName, bufDomain, sizeDomain, SIDType); if Result then Name := WideString(bufName) else Name := ''; FreeMem(bufName); FreeMem(bufDomain); end; end; LocalFree(HLOCAL(SID)); Result := True; end; Freundliche Grüße |
Re: AV bei LookupAccountSidW
Zitat:
|
Re: AV bei LookupAccountSidW
Moin Zusammen,
ich hab' mir das Ganze gerade mal im CPU-Fenster angesehen. Einmal mit LookupAccountSidA, einmal mit LookupAccountSidW. In beiden Fällen war die Länge für ReferencedDomainName ermittelt, als Buffer aber nil übergeben worden. (Also der zweite Aufruf) Der Witz ist: Nachdem die Funktionen den Stack mit Parametern gefüllt haben, laufen sie an der gleichen Stelle weiter (so etwas passiert ja öfter, bei Funktionen, die es in beiden Varianten gibt). Beim Aufruf von RtlCopyUnicodeString schlägt dann die Unicode-Version fehl, die andere läuft durch. :gruebel: Ich hatte jetzt allerdings nicht den Nerv mir herauszusuchen, wie nil für ReferencedDomainName nach Unicode umgewandelt wird, so dass der Aufruf nicht fehlschlägt, obwohl kein Buffer angegeben wird. |
Re: AV bei LookupAccountSidW
Mann. Das ist ja ein Krampf mit der Funktion. Wer hat denn da jetzt Mist gebaut? Borland oder Microsoft?
|
Re: AV bei LookupAccountSidW
Ich tippe auf MS. Das scheint entweder nicht dokumentiert zu sein - keiner hat die KB durchgesucht danach - oder es weiß einfach keiner :D
Wenn ich ne LiveID hätte, dann würde ich nen Kommentar zum MSDN Eintrag schreiben und warnen :D |
Re: AV bei LookupAccountSidW
Moin Dezipaitor,
Zitat:
Ich habe das Ganze mal im Visual Studio 2005 Express nachgebaut: LookupAccountSidW mit dem Parameter Domain auf nil (bzw. NULL) schlägt, wie gehabt, fehl (AV: Zugriff verweigert beim Zugriff auf Adresse 0), wohingegen LookupAccountSidA klaglos ausgeführt wird. Das ist dann auch daran zu sehen, dass der Name korrekt gefüllt ist. |
Re: AV bei LookupAccountSidW
Liste der Anhänge anzeigen (Anzahl: 1)
Hu, gewonnen!
Man daran hab ich garnet gedacht. Also das Zeugs in C++ zu machen :D - oder ich war einfach zu faul dafür. --- Ich mach mal ne Konklusion/Schlussfolgerung: Verwende niemals nil in der Unicodevariante von LookupAccountSid (=LookupAccountSidW) für den Parameter lpReferencedDomainName alleine. Entweder du setzt den Parameter lpName UND lpReferencedDomainName auf nil und übergibst über eine Variable 0 an cchReferencedDomainName und cchName, oder du erstellst einen Puffer (mit GetMem) mit der korrekten Größe für beide Parameter. Ein funktionierendes Beispiel findest du entweder von ![]() ![]() Alles andere ist schlecht :D Ich würde ja einen Kommentar in die MSDN für LookupAccountSid schreiben, wenn ich eine LiveID hätte oder mich einschreiben könnte. Da jedoch derzeit jedesmal "We're unable to complete your request Windows Live ID is experiencing technical difficulties. Please try again later." geht es nicht. Also freiwillige vor? P.S. Hat das jemand mal unter Vista probiert? Anbei ein Testprogramm nebst Quelltext zum Testen. ---
Delphi-Quellcode:
Vor der Exception wird versucht an Speicherposition [EDI] zu schreiben. Da dies jedoch 0 ist, schlägt der Zugriff fehl. Ich hab das Register mal umgebogen auf ne andere Adresse und schon hatte ich die Exception mit genau der Adresse von EDI - also liegt es daran.
zweiter Aufruf von RtlCopyUnicodeString
-------------------------------- movzx eax, word ptr [eax] cmp ax, cx push edi mov edi, [edx+$04] mov [ebp+$08], edi jnbe +$0000FC37 mov ecx, eax mov ebx, ecx shr ecx, $02 mov [edx], ax rep movsd ; hier EXCEPTION mov ecx, ebx and ecx, +$03 rep movsb mov cx, word ptr [edx] cmp cx, word ptr [edx+$02] pop edi pop esi pop ebx Hier das Problem mit RtlCopyUnicodeString
Delphi-Quellcode:
var us,u2 : TUnicodeString;
begin us.Length := 4; //Pufferlänge! nicht Stringlänge us.MaximumLength := 4; us.Buffer := WideString('a1'#0#0); u2.Length := 0; u2.MaximumLength := 2; GetMem(u2.Buffer,4*sizeof(WideChar)); RtlCopyUnicodeString(@u2,@us); //ok p := nil; try RtlCopyUnicodeString(p,@us); // Exception except on E : EAccessViolation do Writeln(E.Message); end; |
Re: AV bei LookupAccountSidW
*push*
|
Re: AV bei LookupAccountSidW
Hier mal etwas menschenlesbarer
Code:
An 7C924D1A wird EDI gesetzt also der Zielpuffer. Die Stackposition des übergebenen Parameters wird dazu temporär "mißbraucht", nicht wundern, das ist normal (macht der Compiler).
.text:7C924CE1 ; int __stdcall RtlCopyUnicodeString(PUNICODE_STRING DestinationString,PUNICODE_STRING SourceString)
.text:7C924CE1 public _RtlCopyUnicodeString@8 .text:7C924CE1 _RtlCopyUnicodeString@8 proc near ; CODE XREF: RtlConvertSidToUnicodeString(x,x,x)+158p .text:7C924CE1 ; LdrLoadAlternateResourceModule(x,x)+34A22p ... .text:7C924CE1 .text:7C924CE1 DestinationString= dword ptr 8 .text:7C924CE1 SourceString = dword ptr 0Ch .text:7C924CE1 .text:7C924CE1 ; FUNCTION CHUNK AT .text:7C934947 SIZE 00000014 BYTES .text:7C924CE1 .text:7C924CE1 mov edi, edi .text:7C924CE3 push ebp .text:7C924CE4 mov ebp, esp .text:7C924CE6 mov eax, [ebp+SourceString] .text:7C924CE9 test eax, eax ; Test pointer .text:7C924CEB jz BailOut01 .text:7C924CF1 mov edx, [ebp+DestinationString] .text:7C924CF4 ; CX = DestinationString.MaximumLength [color=blue].text:7C924CF4 mov cx, [edx+UNICODE_STRING.MaximumLength][/color] .text:7C924CF8 push ebx .text:7C924CF9 push esi .text:7C924CFA ; ESI = SourceString.Buffer .text:7C924CFA mov esi, [eax+UNICODE_STRING.Buffer] .text:7C924CFD ; EAX = SourceString.Length .text:7C924CFD movzx eax, [eax+UNICODE_STRING.Length] .text:7C924D00 ; if(AX > CX) ... .text:7C924D00 cmp ax, cx ; Does it fit? .text:7C924D03 push edi [color=red].text:7C924D04 mov edi, [edx+UNICODE_STRING.Buffer][/color] .text:7C924D07 mov [ebp+DestinationString], edi ; "Misuse" the stack location ;) .text:7C924D0A ja TruncateToMaxLength .text:7C924D10 .text:7C924D10 BackFromTruncate: ; CODE XREF: RtlCopyUnicodeString(x,x)+FC69j .text:7C924D10 mov ecx, eax ; EAX contains number of bytes .text:7C924D12 mov ebx, ecx ; Save for later (ECX gets decremented) .text:7C924D14 shr ecx, 2 ; Divide by sizeof(DWORD) .text:7C924D17 ; Now store either the Length of SourceString or MaxLength .text:7C924D17 ; of DestinationString into DestinationString.Length [color=darkgreen].text:7C924D17 mov [edx+UNICODE_STRING.Length], ax[/color] [color=red].text:7C924D1A rep movsd ; Copy DWORD-wise into buffer @EDI[/color] .text:7C924D1C mov ecx, ebx ; Restore size in Bytes into ECX .text:7C924D1E and ecx, 3 ; Remainder from SHR .text:7C924D21 rep movsb ; Copy remaining Bytes (byte-wise) .text:7C924D23 mov cx, [edx+UNICODE_STRING.Length] .text:7C924D26 cmp cx, [edx+UNICODE_STRING.MaximumLength] .text:7C924D2A pop edi ; Restore from 7C924D03 .text:7C924D2B pop esi ; Restore from 7C924CF9 .text:7C924D2C pop ebx ; Restore from 7C924CF8 .text:7C924D2D jnb short ExitThis .text:7C924D2F ; Zero-terminate DestinationString only if we have .text:7C924D2F ; enough space. Otherwise get out. .text:7C924D2F mov ecx, [ebp+DestinationString] .text:7C924D32 shr eax, 1 ; Divide by 2 (== sizeof WCHAR) .text:7C924D34 and word ptr [ecx+eax*2], 0 ; Zero-terminate .text:7C924D39 .text:7C924D39 ExitThis: ; CODE XREF: RtlCopyUnicodeString(x,x)+4Cj .text:7C924D39 ; RtlCopyUnicodeString(x,x)+FC75j .text:7C924D39 pop ebp .text:7C924D3A retn 8 .text:7C924D3A _RtlCopyUnicodeString@8 endp Ich bitte festzuhalten, daß in der blau markierten Zeile bei dem 2ten Aufruf von Dezi's Testprogramm bereits einmal von Adresse 2 (nil+2) gelesen wird (formal PUNICODE_STRING(p)^.MaximumLength, also 2 Bytes/1 Word). In der ersten roten Zeile wird von Adresse 4 (nil+4) gelesen (formal PUNICODE_STRING(p)^.Buffer, also 4 Bytes/1 DWORD). Und jetzt kommt der wunde Punkt. Wieso knallt's in der zweiten roten Zeile und nicht bereits in der Zeile davor (grün), wo ja bereits an 0 (nil) geschrieben wird (formal PUNICODE_STRING(p)^.Length, also 2 Bytes)? Könnte was mit Speicheralignment zu tun haben, vielleicht ist aber einfach die Beobachtung von Dezi falsch oder falsch interpretiert?! Da (p == nil), folgt:
Delphi-Quellcode:
... das kann man in Delphi, soweit ich mich entsinne, auch exakt so ausdrücken und kompilieren!!!
PUNICODE_STRING(nil)^.Length := Irgendwas;
Das würde in jeder Hinsicht knallen. Am Anfang der Funktion wird auch nur SourceString überprüft, jedoch nicht DestinationString - was theoretisch bereits in Bailout01 schiefgehen sollte!
Code:
Eine Übergabe von nil für beide Parameter sollte also genauso zum Absturz führen, da hier wiederum an Adresse 0 (nil) geschrieben wird (rote Zeile, zwei Bytes, entsrpricht der grünen Zeile von oben).
.text:7C93494F BailOut01: ; CODE XREF: RtlCopyUnicodeString(x,x)+Aj
.text:7C93494F mov eax, [ebp+DestinationString] [color=red].text:7C934952 and [eax+UNICODE_STRING.Length], 0[/color] .text:7C934956 jmp ExitThis .text:7C934956 ; END OF FUNCTION CHUNK FOR _RtlCopyUnicodeString@8 |
Re: AV bei LookupAccountSidW
Aber hätte das Microsoft nicht bei der Qualitätskontrolle auffallen müssen? ;)
|
Re: AV bei LookupAccountSidW
Welche QM ? :D
|
Re: AV bei LookupAccountSidW
Moin Zusammen,
also ich sehe es jetzt nicht gerade als ein Problem an, dass die W-Funktion nicht sauber durchläuft, wenn sie mit ungültigen Parametern aufgerufen wird, sondern dass die A-Variante es tut. Die W-Variante müsste natürlich, sinnvoller Weise, 87 zurückmelden, statt auf eine AV aufzulaufen, aber den eigentlichen Fehler sehe ich in der anderen Version, da die Parameter, zumindest gemäss Doku, so nicht nicht zulässig sind. |
Re: AV bei LookupAccountSidW
Christian, das sehe ich ganz ähnlich. Zumal es sinnlos ist RtlCopyUnicodeString mit nur einem (gültigen) Parameter aufzurufen. Die einzige Sache welche mich daran stört ist, daß der eine Parameter (nämlich SourceString) durchaus überprüft wird (siehe 7C924CE9), so daß es irgendwie auch wieder inkonsequent ist den zweiten Parameter nicht auch zu überprüfen.
Auf der anderen Seite muß man es abwägen, da zuviel Fehlerüberprüfung Performance kostet. Formal müßte man zum komplettieren ja auch überprüfen ob der Pointer gültig ist (nicht nur ob er ungleich nil ist) und ob man mindestens sizeof(UNICODE_STRING) lesen/schreiben kann und ob das auch für den Pointer in us.Buffer gilt. |
Re: AV bei LookupAccountSidW
Moin Olli,
Zitat:
Da ist mir die Vorabprüfung aber lieber ;-) |
Re: AV bei LookupAccountSidW
wobei man hätte die Überprüfung auch bei
LookupAccountSidW einbauen können. |
Re: AV bei LookupAccountSidW
Frage noch immer offen?
|
Re: AV bei LookupAccountSidW
Die Frage ist offener als ein Scheunentor groß sein kann.
Ich darf mal das Szenario nachstellen : Microsoft macht in etwa das:
Code:
Es sieht so aus, als ob die Speicherstelle bei [ecx+$0c] garnicht mein GetData ist.
CSidData::InternalLookUpSids(..., ISecurityInformation2 pS2, ...);
{ .. .. LPDataObject lpDO = NULL; ... HRESULT hr = pS2->LookUpSids(..., lpDO); //mein Aufruf OK if (hr <> S_OK) FAIL hr = lpDO->GetData(....); //exception hier ( call dword ptr [ecx+$0c] ) if (hr <> S_OK) FAIL ... } Der Vergleich von @GetData mit der Stelle ergibt über $300-$1000 Bytes Unterschied. Ich bin echt ratlos, besonders da eine reine C++ Implementierung einwandfrei funktioniert. Auch eine komplette neue Implementation des SecurityDialogs unter Delphi mit sowenig Code wie möglich, läuft auf dasselbe hinaus. Es scheint als hätte Borland einen totalen Sc*****d**** gemacht - oder es liegt an was anderes. Auf jeden Fall bin ich verzweifelt - soetwas hab ich noch nie erlebt. --- EDIT: Fehler im Code korrigiert. |
Re: AV bei LookupAccountSidW
Moin Dezipaitor,
hast Du mal in Erwägung gezogen, die benötigten Funktionen (und ggf. Strukturen) selber zu deklarieren? Im Gegensatz zu Borland, importiere ich die Funktionen immer so, dass ich alle Parameter als const deklariere, und dann, bei den var Parametern gezielt Pointer übergebe, da diese Vorgehensweise eher dem entspricht, was man dann auch in Beispielen findet. Ausserdem kann man bei einem var-Parameter auch nicht nil übergeben, was ja bei vielen Funktionen durchaus zulässig ist. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:15 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz