ďťż
Nie chcesz mnie, Ben. SkĹadam siÄ z siedmiu warstw popieprzenia okraszonych odrobinÄ
gĂłwnianego szaleĹstwa.
znaleziona_funkcja:
shl
mov
add
mov
eax, 2
ebx, [ebp+offset first_thunk]
eax, ebx
eax, [eax]
; EAX= adres funkcji
Mamy adres funkcji, teraz możemy już w łatwy sposób uzyskać adres KERNEL32.DLL
kernel db "KERNEL32.DLL",0
mov edx, offset kernel
add edx, ebp
push edx
; zachowaj
;GetModuleHandle("KERNEL32.DLL"); ; jeżeli błąd, to funkcja zwraca NULL
mov [ebp+offset adresGMH], eax
cali eax
cmp eax, O
jne znaleziono_kernel
W przypadku, gdy któryś z fragmentów kodu skoczy do etykiety "nie_znalezione", możemy się jeszcze ratować próbą wykorzystania stałego adresu załadowania KERNEL32.DLL, ale uwaga adres ten nie musi być we wszystkich wersjach Windows taki sam. Trzeba uważać ponieważ w przypadku, gdy nie jest to adres jądra, to możemy doprowadzić do zawieszenia się systemu.
nie_znalezione:
mov eax, OBFFTOOOOh
Wcześniej jeżeli wszystko poszło po naszej myśli, to program skoczy do etykiety "znaleziono_kernel" a rejestr EAX będzie wskazywał na moduł jądra w pamięci.
znaleziono_kernel:
mov
mov
cmp
jne
mov
add
cmp
jne
[ebp+offset adres jądra], eax
edi, eax
wordptr [edi]. 'ZM'
błąd
edi, [edi+3Ch]
edi, [ebp+offset adres jądra]
word ptr [edi], 'EP'
błąd
zachowaj adres jądra standardowe sprawdzenia
Wszystko w porządku, mamy zlokalizowane jądro w pamięci. Teraz powinniśmy odszukać funkcję GetProcAddress, która zwraca nam adres funkcji w pamięci. Dzięki temu w przyszłości nie będzie potrzeby stosowania naszego kodu do odnajdywania funkcji, co bardzo nam ułatwi prace, ponieważ kiedy zajdzie potrzeba wywołania dowolnego API, posłużymy się GetModuleHandle (zwróci nam uchwyt do modułu) oraz GetProcAddress (zwróci nam adres funkcji z tego modułu do którego mamy uchwyt). Sprawa wydaje się być prosta i oczywista. Posiadając adres jądra, możemy przystąpić do analizowania jego tabeli eksportów, w celu odnalezienia szukanej, eksportowanej przez ten moduł funkcji GetProcAddress .
32
pushad
mov
add
mov
add
lodsd
mov
lodsd
lodsd
mov
add
lodsd
add
mov
lodsd
add
mov
lodsd
add
mov
mov
lodsd
add
esi, [edi+78h]
esi, [ebp+offset adres jądra] [ebp+offset eksport], esi esi, lOh
[ebp+offset baza_numer], eax
[ebp+offset liczba_nazw], eax eax, [ebp+offset adres jądra]
eax, [ebp+offset adres jądra] [ebp+offset adres_funkcji], eax
eax, [ebp+offset adres jądra] [ebp+offset adres_nazw], eax
eax, [ebp+offset adres jądra] [ebp+offset adres_numerow], eax esi, [ebp+offset adres_funkcji]
eax, [ebp+offset adres jądra]
; przejdź do tabeli eksportu (element O w DataDirectory)
; dodaj aby otrzymać VA z RVA
; zachowaj
; ustaw się. na pole w IMAGE_EXPORT_DIRECTORY
; pobierz nBase
; pobierz NumberOfFunctions ; pobierz NumberOjNames
; pobierz AddressOfFunctions
; pobierz AddressOjNames
; pobierz AddressOjNameOrdinals
Pobraliśmy wszystkie istotne pola z IMAGE_EXPORT_DIRECTORY. funkcji GetProcAddress w tabeli eksportów:
Możemy przystąpić do szukania
mov
mov
mov
add
xor
mov
add
szuka j_dalej: mov
skanuj:
cmpsb
jne
cmp
je
esi, [ebp+offset adres_nazw]
[ebp+offset indeks], esi
edi, [esi]
edi, [ebp+offset adres jądra]
ecx, ecx
ebx, offset strGPA
ebx, ebp
esi, ebx
następny byte ptr [edi], O znaleziono_funkcj e skanuj
; wskaźnik na pierwszą nazwę ; zachowaj indeks do tabeli
;licznik
; ustaw EBX na nazwę funkcji, której szukamy
; ESI = nazwa szukanej funkcji
; porównaj jeden bajt (znak stringa) ; nie ta funkcja? ; koniec?
następny:
inc
cmp
jge
add
mov
mov
add
ex
ex, word ptr [ebp+offset liczba_nazw]
błąd
dword ptr [ebp+offset indeks], 4
esi, [ebp+offset indeks]
edi, [esi]
edi, [ebp+offset adres jądra]
szukaj_dalej
; porównanie licznika z ilością
; importowanych funkcji z KERENL32.DLL
; 4 = DWORD, zwiększ i próbuj dalej
33
,gdzie mamy tak zdefiniowane zmienne:
strGPA db "GetProcAddress",0
adresGPA dd O
Gdy znaleziono string w tabeli wskazywanej przez AddressOfNames, odpowiadający szukanej funkcji, to rejestr CX zawiera indeks do tabeli AddressOfNameOrdinals. Teraz jeżeli chcemy uzyskać RVA szukanej funkcji, to trzeba wykonać (zapis języka C):
NumerFunkcji = *(CX * 2 + AddressOfNameOrdinals ); (mnożymy przez 2 bo elementami w tabeli
AddressOfNameOrdinals są WORD'y)
NumerFunkcji jest indeksem do tabeli adresów funkcji (AddressOfNames), której elementami sąDWORD'y. Zatem posiadając taki indeks, możemy otrzymać adres naszej API GetProcAddress:
AdresFunkcji= *(NumerFunkcji*4 + AddressOfFunctions);
Tak wygląda to w assemblerze:
znaleziono_funkcje:
mov ebx, esi
inc ebx
shl ecx, l ; ECX=ECX*2, bo 2A1=2
mov esi, [ebp+offset adres_numerow] ; AddressOfNameOrdinals
add esi, ecx
xor eax, eax
mov ax, word ptr [esi]
shl eax, 2 ; NumerFunkcji=NumerFunkcji*4, bo 2A2=4
mov esi, [ebp+offset adres_funkcji] ; AddressOfFunctions
add esi, eax
mov edi, dword ptr [esi] ; pobierz RVA
add edi, [ebp+offset adres Jądra] ; i skonwertuj do VA (YirtalAddress)
EDI wskazuje na funkcję GetProcAddress ! Zachowamy to.
mov [ebp+offset adresGPA], edi
popad ; zakończ całą operację, odzyskaj zachowane rejestry
Teraz już możemy spokojnie wywoływać dowolne API, możemy przecież odnaleźć ich adresy:
push offset
|
WÄ
tki
|