Ostatnio natrafiłem na aplikację stworzoną w środowisku Visual FoxPro 9, zabezpieczona była kluczem sprzętowym HASP Hardlock, a sam plik wykonywalny aplikacji był zaszyfrowany (tzw. envelope).
Struktura aplikacji Visual FoxPro
Z nielicznych kontaktów z oprogramowaniem Visual FoxPro wiedziałem, że każda taka aplikacja składa się z prostego loadera (który uruchamia cały engine VFP znajdujący się w dodatkowych plikach DLL) oraz właściwej aplikacji, która znajduje się na końcu pliku wykonywalnego (zapisana jako overlay), całość jest więc w jednym pliku wykonywalnym. Tak jest w przypadku programów niezabezpieczonych:
W przypadku zabezpieczenia HASP, na końcu zabezpieczonego pliku faktycznie znajdują się jakieś dane, ale nie są to dane aplikacji VFP, gdyż nie można jej zdekompilować oprogramowaniem ReFox.
Zabezpieczenia dla aplikacji z overlayami
Dane overlay (tak zapisane dane są w aplikacjach Flash, Director, Shockwave etc.) nie są mapowane w pamięci procesu i muszą być odczytane bezpośrednio z dysku. Popularną metodą ochrony takich aplikacji jest szyfrowanie tych danych i hookowanie funkcji systemu plików (np. CreateFile, ReadFile etc.) w samej aplikacji, poprzez podstawienie adresów funkcji zastępczych w tabeli importów aplikacji. Jeśli aplikacja otworzy swój własny plik i będzie probowała czytać dane overlaya, nie zostanie odczytane to co jest na dysku, ale to co zostało wczesniej zaszyfrowane.
Takie rozwiązanie przez hookowanie tabeli importów działa jedynie w przypadku samodzielnych plików (bez dodatkowych bibliotek), w przypadku aplikacji VFP, dane z pliku wykonywalnego czytane są przez dodatkowe biblioteki, więc wymagany byłby system wide hook.
Envelope HASP dla aplikacji VFP
System zabezpieczeń HASP dla takich plików, działa w ten sposób, że sterownik systemowy HASP-a podpina się pod funkcje systemowe Windows odpowiedzialne za czytanie plików i obecność oryginalnego overlaya jest emulowana tylko dla zabezpieczonej aplikacji i jej bibliotek. Wygląda to mniej więcej tak:
Odczytanie overlaya
Jak zatem uzyskać obraz całego overlaya? Z przeprowadzonych eksperymentów wynika, że dane nie są nigdzie czytane w całości, więc nie ma takiego punktu w programie (a grzebanie w engine VFP nie należy do najprzyjemniejszych), gdzie by można było zrzucić pamięć całego overlaya.
Jednak jeśli czytanie danych overlaya jest emulowane dla samej zabezpieczonej aplikacji, można wstrzyknąć kod czytający zawartość całego pliku do zabezpieczonej aplikacji i dzięki temu uzyskać dane odszyfrowanego overlaya.
Jest tutaj kilka problemów. Po pierwsze, aby wstrzyknąc kod należy dojść do OEP zabezpieczonej aplikacji, co może sprawić trochę problemów, ja skorzystałem z OllyDbg i pluginu Phant0m, ponieważ envelope HASP-a wykrywa popularne narzędzia do debuggowania, co można zobaczyć na zrzuconym obrazie zabepieczonego pliku:
Znalezienie OEP jest relatywnie proste, wersja dla leniwych — zrzucić działający program z pamięci i wyszukać standardowego entrypoint-a dla aplikacji skompilowanych pod Visual C++ (szukać funkcji GetStartupInfoA). Potem wgrać na nowo plik i ustawić hardware breakpoint na tym adresie.
Będąc na adresie OEP można teraz wstrzyknąć kod odpowiedzialny za czytanie pliku. Aby zaoszczedzić trochę kodu, ja wykorzystałem fakt, że plik aplikacji jest przez nią otwierany w pewnym momencie, dzięki czemu nie trzeba pobierać jego ścieżki i go otwierać, a od razu mamy jego uchwyt, który można wykorzystać do odczytania overlaya:
Następnie skorzystałem ze skryptu ODBScript, żeby pod adresem 4015EE załadować kod, który odczyta zawartość pliku i zapisze go na dysku:
; usun wszystkie breakpointy
bphwcall
bc
; ustaw hardware bp na OEP
bphws 401873,"x"
run
; ustaw hardware bp na kod po _lopen
bphws 4015F4,"x"
run
bphwcall
; kompiluj kod z pliku pod adres EIP
asmtxt eip, "C:\fox_dumper.asm"
; pomocniczy bp
bphws 401644,"x"
run
; zrzuc pamiec pod adresem ESI o rozmiarze EBX
dm esi, ebx, "c:\dump.bin"
log "VFP dumper zrobione."
pause
Kod dumpera ładowany jest z zewnętrznego pliku, a nie jest wykorzystywana funkcja ODBScript wykonująca kod assemblera w przestrzeni adresowej aplikacji, ponieważ to po prostu nie działało (jakby ktoś pytał):
; w EAX jest uchwyt pliku
mov edi,eax ; hFile
; nawiguj na koniec pliku (korzystajac z funkcji
; z tabeli importow aplikacji)
push 2 ; FILE_END
push 0 ; offset
push edi ; hFile
call [40203C] ;_llseek
mov ebx,eax ; eax = current offset = size
; alokuj pamiec na rozmiar overlaya
lea eax,[ebx-4] ; na koncu overala zapisany jest DWORD
; ktory wskazuje offset poczatku overlaya
; w pliku
push 0 ; FILE_BEGIN
push eax ; offset
push edi ; hFile
call [40203C] ;_llseek
; odczytaj DWORD, ktory okresja polozenie overalaya w pliku
push 0
mov eax,esp
push 4 ; size
push eax ; &memory
push edi ; hFile
call [402040] ;_lread
pop eax ; esi - raw offset overlaya
push 0 ; FILE_BEGIN
push 0 ; offset
push edi ; hFile
call [40203C] ;_llseek
; alokuj pamiec na calego overlaya
push ebx
push 40 ; GMEM_FIXED or GMEM_ZEROINIT
call GlobalAlloc
mov esi,eax
; odczytaj calego overlaya do nowo zaalokowanej pamieci
push ebx ; size
push esi ; &memory
push edi ; hFile
call [402040] ;_lread
; zamknij oryginalny plik
push edi
call [402038] ;_lclose
; utworz nowy plik
sub esp,512
mov eax,esp
; "C:\x"
mov edx,785C3A43
mov [eax],edx
xor ecx,ecx
mov [eax+4],ecx
; esi - ptr
; ebx - size
push 0
call ExitProcess
Po wykonaniu skryptu, w pliku C:\x otrzymamy zrzut całego pliku (nie tylko overlaya).
Odbudowa oryginalnego pliku wykonywalnego
Posiadając zrzucony overlay (rozpoznamy jego bajty po sygnaturze FE F2 EE), należy go dokleić do odbudowanego loadera VFP. Sam loader po zabezpieczeniu i zrzuceniu wymaga odbudowy importów, tutaj można trochę oszukać, wszystkie loadery VFP (danej wersji) są takie same, różnią się jedynie wersją językową, ikoną i zasobami z nazwą i numerem wersji aplikacji. Jeśli posiadacie środowisko Visual FoxPro 9, można stworzyć projekt w stylu Hello World, skompilować do pliku wykonywalnego, usunąć oryginalny overlay i wkleić nasz, a aplikacja będzie działała poprawnie (można potem w edytorze zasobów podmienić ikonę i wersję aplikacji).
Wnioski
Było trochę problemów z samą zabezpieczoną aplikacją i jej działaniem w OllyDbg z włączonym pluginem Phant0m (dużo restartów, nie dało się normalnie zamknąć aplikacji), jednak sama metoda jest dosyć szybka i uniwersalna, z późniejszych poszukiwań udało mi się jeszcze znaleźć doskonały artykuł prezentujący szereg innych zabezpieczeń stosowanych w aplikacjach VFP, polecam na koniec:
Security in FoxPro — www.foxpert.com/docs/security.en.htm
PS. Pozdro dla wszystkich burtoniarzy 🙂
Świetny wpis 🙂
Będziesz może na SeConference w kwietniu?
Dzięki. A co tam fajnego będzie?