Dziedziczenie funkcji, deasemblacja

0

Dobry day. Pisze tu na forum ponieważ chciałbym się dowiedzieć jak odziedziczyć pewną funkcję znając jej adres uzyskany poprzez deasemblacje. Chce uzyskac cos na zasadzie działania biblioteki Qt. W qt dziedziczymy klase i gdy wywołujemy funkcje (wlasne instrukcje), nastepuje przed tym wykonywanie instrukcji w klasie rodzica/przodka. Jak coś takiego uzyskać znając tylko adres funkcji w aplikacji EXE? Głównie rozchodzi się o to: gdy funkcja w aplikacji EXE zostanie wywołana, wtedy chcę wywołać własną funkcje. Krótko mowiąc chce wyłapać zdarzenie/wydarzenie/event podczas wywołania tejże funkcji.

Przykładowo:
Adres funkcji exe: 0x0055ABDF

Własna funkcja


void Event() //Wywołanie funkcji 0x0055ABDF wewnatrz jej aplikacji
{
    //wlasne instrukcje
}
0

Uściślijmy - chcesz żeby wywołanie funkcji o adresie 0x0055ABDF powodowało zamiast tego wywołanie twojej funkcji? Możesz pod ten adres wrzucić shellcode skaczący do twojej funkcji (tzw. detour jump albo po prostu inline hook).

Zrobienie tego nie jest technicznie trudne, ale jeśli chcesz sobie uprościć robotę to kill1212 napisał do tego nawet małe API - http://rev3rsed.blogspot.com/2011/05/klasa-do-api-hookingu.html

0

Tak właśnie! O inline hook mi chodziło, dopiero sie dowiedziałem oglądając Gynvaela. Moim celem jest podmienieniem oryginalnej funkcji własną. Dzięki za link. Ale szkoda, że nie znam teorii. Dobra odezwe sie co i jak pozniej.

0

Niestety program się crashuje. W trzecim argumencie nie mam pojecia jaki rozmiar podac (podałem 4), a ostatni argument to chyba ten sam adres podać co w drugim. Gynvael mówił coś o zastosowaniu inline hook za pomocą assemblera. Najpierw trzeba skoczyć do własnej funkcji, a następnie po wykonaniu instrukcji wrócić do funkcji oryginalnej. Z assemblerem to krucho u mnie, pomógłbyś mi w tym?

0

Co do długości instrukcji - chodzi chyba o długość w bajtach instrukcji JMP rel32 czyli 5 bajtów.

długość instrukcji (chodzi o to, by skopiować całą instrukcję, która zostanie zmieniona przez skok do naszego kodu, zazwyczaj jest to 5)

  • możesz spróbować.
    Nie dam głowy jak powinno być, bo nigdy z takich ułatwień nie korzystałem ;).

Ok, ad rem. Spróbuję coś wytłumaczyć na przykładzie kodu napisanego przeze mnie przed wiekami (dokładniej - jakieś 2 lata temu). Zadaniem było przechwytywać komunikaty wypisywane przez puts na konsolę i logować do pliku.

Kod wygląda mniej-więcej tak:

char tramp[] =  { 0xB8, 0xD3, 0xDE, 0xAD, 0xAD, // mov eax addr
                        0xFF, 0xD0 // call eax
                        };

Tworzymy 'trampolinę' (terminologia zerżnięta od Gynvaela którego oglądałeś).Tak naprawdę jest tam mov eax, 0xADADDED3, adres wypełniamy w czasie wykonania.

        DWORD addr_hook_wrapper = (DWORD)wrapper; // adres funkcji mającej się wykonać zamiast
        DWORD addr_puts = (DWORD)GetProcAddress(GetModuleHandle("msvcrt"), "puts"); // adres funkcji zastępowanej

        memcpy(tramp+1, &addr_hook_wrapper, 4);

pobranie adresu wrappera puts do zmiennej i zmienienie trampoliny tak żeby skakała pod ten adres.
Ja użyłem wrappera bo potrzebowałem wywołać oryginalną funkcję po tym jak mój hook zrobi co powinien - jeśli tego nie potrzebujesz możesz sobie darować wrappera a twoje życie będzie dużo łatwiejsze - w takim wypadku po prostu wstawiasz zamiast wrappera, adres twojej funkcji.

        hooker(tramp, 7, addr_puts);

Ostatni krok - funkcja wrzucająca hook - żadnego rocket science tu nie ma

void hooker(char *tramp, int t_size, DWORD addr)
{
    DWORD old;
    VirtualProtect((LPVOID)addr, t_size, PAGE_EXECUTE_READWRITE, &old);

    memcpy((LPVOID)addr, tramp, t_size);

    VirtualProtect((LPVOID)addr, t_size, old, &old);
}

To by było w sumie wszystko, ale nie do końca - potrafimy już sprawić żeby wykonał się wskazany przez nas kod.
Z wrapperem byłoby trochę trudniej bo bez asma by się nie obeszło, ale to temat na inny post chyba skoro i tak tego nie potrzebujesz.

(w miarę) pełen kod:

void hooker(char *tramp, int t_size, DWORD addr)
{
    DWORD old;
    VirtualProtect((LPVOID)addr, t_size, PAGE_EXECUTE_READWRITE, &old);

    memcpy((LPVOID)addr, tramp, t_size);

    VirtualProtect((LPVOID)addr, t_size, old, &old);
}

int main()
{
    char tramp[] =  { 0xB8, 0xD3, 0xDE, 0xAD, 0xAD, // mov eax addr
                    0xFF, 0xD0 // call eax
                    };

    DWORD addr_hook_wrapper = (DWORD)twoja_funkcja_mająca_się_wykonać;
    DWORD addr_puts = (DWORD)GetProcAddress(GetModuleHandle("msvcrt"), "puts");  // adres funkcji zastępowanej

    memcpy(tramp+1, &addr_hook_wrapper, 4);

    hooker(tramp, 7, addr_puts);
}

edit: tak czytam swój post i nie wiem czy cokolwiek chociaż w miarę jasno wytłumaczyłem... Mam nadzieję że tak.

0

Dzieki w ogóle ze poswieciles czas:) Mam pytanko w twoim kodzie co oznacza ten adres (ctrl + v: 0xD3, 0xDE, 0xAD, 0xAD) skoro mam zamiar podać adres wlasnej i oryginalnej funkcji. Czy to coś rodzaju jakiejs procedury czy co? na razie nie moge przetestowac, robilem format i przeskoczylem na winde siódemke. Jutro sciagne visuala, bo juz dzisiaj nie bede tego robil, zmeczony...

0

To 0xD3, 0xDE, 0xAD, 0xAD to miejsce na adres twojej funkcji którą chcesz wstawić 'zamiast' domyślnej funkcji.

Zauważ że dwie linijki poniżej go nadpisujemy:

memcpy(tramp+1, &addr_hook_wrapper, 4);

addr_hook_wrapper to faktycznie trochę myląca nazwa, w oryginale naprawdę była adresem wrappera ale teraz to po prostu adres funkcji - tej w twoim kodzie - do której chcesz skakać.

DWORD addr_puts = (DWORD)GetProcAddress(GetModuleHandle("msvcrt"), "puts");

addr_puts jest jeszcze bardziej myląca - w twoim przypadku to ma być 0x0055ABDF.
Z tym adresem w sumie może być ciężko - musisz być pewien wirtualnego adresu (nie offsetu w pliku), a dodatkowo binarka może mieć włączone ASLR.

0

Przetestowalem i.. zadziałało, program wykonal podmienione instrukcje (funkcję)! No tak, ale po wywołaniu oryginalnej funkcji każda po niej instrukcja jest wskazywana przez debuger jako potencjalna przyczyna crashu programu (bo nastapil crash po jej wywolaniu), chodź tak z pewnością nie jest... Przeskanowałem adresy wszystkich zmiennych, na ktorych aktualnie operuje i żaden nie zgadza sie z adresem wskazywanym przez debuger. Najblizej połozonym adresem wzgledem wskazynanego przez debuger jest adres oryginalnej funkcji, a rozni ich tylko przestrzen 16 bajtow. Nie wiem co tu może być przyczyną.

void Function1()
{
    cout << "Running function1..." << endl;
}

void Podrobka()
{
    cout << "Who the fuck is Justin Bieber?" << endl;
}

void hooker(char *tramp, int t_size, DWORD addr)
{

    DWORD old;
    VirtualProtect((LPVOID)addr, t_size, PAGE_EXECUTE_READWRITE, &old);

    memcpy((LPVOID)addr, tramp, t_size);

    VirtualProtect((LPVOID)addr, t_size, old, &old);

    cout << "addr 1: " << &old << endl;
    cout << "addr 2: " << &addr << endl;
    cout << "addr 3: " << &tramp << endl;
    cout << "addr 4: " << &t_size << endl;
}

int main()
{
    char tramp[] = {0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax addr
                                      0xFF, 0xD0}; // call eax

    DWORD addr_hook_wrapper = (DWORD)Podrobka;
    DWORD addr_puts = (DWORD)Function1;

    memcpy(tramp + 1,&addr_hook_wrapper,4);
    hooker(tramp,7,addr_puts);

    cout << "addr 5: " << Podrobka << endl;
    cout << "addr 6: " << Function1 << endl;
    cout << "addr 7: " << &addr_hook_wrapper << endl;
    cout << "addr 8: " << &addr_puts << endl;
    cout << "addr 9: " << &tramp << endl;

    Function1(); //<- Prawidlowo wykonala sie funkcja Podrobka();
    getchar(); //<- Debuger wskazuje tutaj na potencjalna przyczyne crashu programu
    return 0;
}
0

I jak, da sie cos z tym zrobic? Testowales u siebie? Wyszukalem sporo linkow na goole na ten temat i nie ma wcale konkretow.

0

Sorry za spóźnienie się z odpisaniem, nie zauważyłem że odpisałeś.

Przyczyna błędu jest dość trywialna, po prostu jest call eax a powinien być jmp eax - call działa jak wywołanie funkcji, czyli po wykonaniu funkcji (w typ przypadku siedzącej pod adresem w eax) działanie programu wraca w to samo miejsce co jest oczywiście nie tym czego potrzebujemy. Jmp działa jak goto czyli zmienia się wykonywana instrukcja co jest dokładnie tym czego oczekujemy.

Poprawiona trampolina:

        char tramp[] = {0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax addr
                        0xFF, 0xE0}; // jmp eax

Po tej zmianie kod powinien działać (i u mnie działa).

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0