HASP i Visual FoxPro 9

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 🙂