Hier mal etwas menschenlesbarer
Code:
.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
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).
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:
PUNICODE_STRING(nil)^.Length := Irgendwas;
... das kann man in Delphi, soweit ich mich entsinne, auch exakt so ausdrücken und kompilieren!!!
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:
.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
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).