Wywoływanie funkcji DLL'ki

0

Witam

Czy jest możliwość zrobienia czegoś takiego:

  • program gra.exe na przykład jakaś gra
  • biblioteka przykladowa.dll która ma w sobie funkcję, która wywołuje funkcje z pliku gra.exe.
  • launcher na przykład launcher.exe, który po odpaleniu injectuje przykladowa.dll do odpalonego gra.exe, ale dopiero po kliknięciu jakiegoś przycisku w tym launcherze wykona funkcję w DLL'ce na przykład ustawiającą jakiś pojazd w programie gra.exe.
    Chciałbym zrobić coś takiego, że tym launcherem był by na przykład klient multi-player i sterował by programem gra.exe przez tą DLL'kę. Da się coś takiego zrobić? Jak się to fachowo nazywa? :)

@refresh

0
bajos napisał(a):

Da się coś takiego zrobić?

Chcesz wywoływać funkcje z dllki z innego procesu - ok, da się. Ale chyba kombinujesz coś za bardzo z z tym zewnętrzym ładowaniem dllki do innego procesu, dlaczego gra.exe nie może załadować sobie tej dllki sama? Sądzę, że Twój rzeczywisty problem może być rozwiązany prościej.

1

Pytanie: w jakim stopniu znasz assemblera, i w jakim stopniu sobie poradzisz z deasemblacją? Jeśli to nie jest dla ciebie problemem to łatwo to zrobisz w sumie.
Musisz zrobić kilka hooków na pamięć gry, i kiedy jest naciskany button, to coś się dzieje, i ty musisz wiedzieć co.
Wtedy w disassemblerze patrzysz sobie adres tam gdzie to jest wywoływane, robisz hooka, w którym calluje się zupełnie inna funkcja z argumentami również.
Jeśli dalej nie będziesz wiedział jak się za to zabrać, to napisz, to ci wyjaśnię na prostym przykładzie jak to zrobić.

0

Dopiero się uczę NASM, ale z tym poradnikiem zrobiłem .dll do Sapera http://www.p-programowanie.pl/cpp/dll-injection-wywolywanie-funkcji-z-parametrami/ i chciałbym jej funkcję funkcja(); wywoływać z np. programu launcher.exe do programu gra.exe (Saper). @several gra.exe nie może sama załadować, ponieważ to "obcy" program, którego nie pisałem ja.

2

Dobrze, więc mamy taki oto przykład gry:
http://pastebin.com/SgYWfMaQ
Proste, mamy dwa przyciski, jeden wywołuje jedną funkcje, drugi drugą. Druga funkcja ma argument który jest wyświetlany w MessageBoxie.
Naszym celem jest, aby klikając pierwszy przycisk, wykonywała się druga funkcja z naszym parametrem, ale i wykonywało swoją pierwszą funkcję później.
Żeby się do tego zabrać oczywiście potrzebujemy jakiegoś disassemblera, jedyne co mogę polecić to jest IDA, która jest darmowa, ale ja ci radzę zaopatrzyć się w IDĘ która wyciekła z ESETu, która posiada plugin pseudocodeu w C.
W tym kroku trzeba zaznaczyć, że posiadamy pdb, czyli taki plik odpadowy, w którym kompilator zapisuje różne rzeczy. I nie są to byle jakie śmieci, bo jest tam w zasadzie wszystko aby przeprowadzić pomyślną dekompilację programu. Ale nie łudźmy się że w jakiejkolwiek grze znajdziemy pdb. W 99.99999999% przypadkach jest tylko exe.
Więc kiedy ładujemy exeka do IDA, to IDA nas pyta czy załadować pdb, i my wybieramy NIE.
Kiedy już nam się do IDA wczyta nasza binarka, no to działamy.
Trzeba nam adres gdzie się wykonuje function1, lub sam adres function1, oraz adres function2. Lecz pamiętaj, że w większości przypadków exeki nie mają exportów, przez co nie wiadomo gdzie dana funkcja jest. Tzn. IDA sama rozpoznaje gdzie się funkcje zaczynają, a gdzie kończą po referencjach funkcji.
Stąd niektóre exeki potrafią się do IDA ładować po parenaście minut..
Oczywiście my nie będziemy rozkodowywali całego exeka linijka po linijce, ponieważ to nie ma sensu, poza tym, zajmie nam to na prawdę dużo czasu(mam na myśli dekady).
Na nasze szczęście IDA rozpoznaje stringi na podstawie jakiegoś tam algorytmu. Na podstawie stringów możemy dojść, gdzie ten string jest wykorzystywany, czyli jego referencja.
Udajemy się więc do karty stringów, jeśli jej nie masz to: View -> Open Subviews -> Strings. Teraz szukamy interesującego nas stringu.
Wiemy, że function1 wyświetla string: "Wykonalem function1", więc spróbujmy go wyszukać.

user image
Teraz klikamy na niego dwa razy lub enter, i powinno nas przenieść do karty IDA View:
user image
Po prawej od stringa, widzimy, że już się wyświetlił nam pierwsza referencja, czyli XRef: sub_401200, offset: 30.
Innym sposobem podpatrzenia Xrefów w tym miejscu, jest kliknięcie prawym na "aWykonalemFunct"(po prawej od adresu) i wybranie Jump to Xref to operand, wyświetla nam się lista i jeśli klikniemy na jakiś element dwa razy to nas przeniesie do adresu. I robimy tak.
user image
Teraz prawdopodobnie zastanawiasz się o co tutaj chodzi, dlaczego funkcja w której to jest to jest: int __stdcall sub_401200(HWND hWnd, int, int, int),
otóż, rozświetli ci sytuację pseudocode tej funkcji, który wygląda następująco:
user image
Jak nietrudno zauważyć, jest to nasze WndProc, a po prostu VC++ zrobił z function1 inline.
Często tak się może zdarzyć że VC++ nam zinlinuje funkcje, ale nie ma co się gniewać na niego.
Później natomiast doskonale widzimy, że jest obsługiwany drugi button, i możemy zauważyć, że sub_401349 to jest po prostu rand.
W zasadzie już mamy wszystkie potrzebne adresy:

.text:0040122C push 0 ; uType
.text:0040122E push 0 ; lpCaption
.text:00401230 push offset aWykonalemFunct ; "Wykonalem function1"
.text:00401235 push 0 ; hWnd
.text:00401237 call ds:MessageBoxA

oraz

.text:00401249 call sub_401349
.text:0040124E cdq
.text:0040124F mov ecx, 0Ah
.text:00401254 idiv ecx
.text:00401256 mov ecx, edx
.text:00401258 call sub_401000

Czyli już wiemy że sub_401000 to jest function2.
Przejdź teraz do sub_401000 podwójnie klikając na niego, lub enter i zobacz co tam jest ;-)
Teraz jedynie musimy wykonać jeden hook, będzie to zwykły jump, do funkcji naszej dllki w miejscu wykonania MessageBoxa, a w funkcji naszej dllki wywołać function2 z gry z naszym parametrem.
Jak tego dokonać? To jest proste. Jeśli ładujemy dllkę do pamięci gry to my już siedzimy w procesie. Co sprawia, że możemy kontrolować pamięć, nawet nie tylko tą która jest alokowana. Trzeba pamiętać że do pamięci wczytują się również instrukcje, i to z pamięci są one wykonywane. Więc pozmieniajmy trochę pamięć.
Musisz zrobić w kodzie dllki taką funkcję, która nie ma żadnych śmieci z kompilatora. Jedynie takie instrukcje które sam napiszesz.
Do tego przyda ci się _declspec(naked).
Pamiętaj również, że callując własną funkcję w kodzie gry, musisz uważać aby nie ponaruszać rejestrów, bo jeśli zechcesz aby funkcja poszła dalej, to musisz bardzo na to uważać i korzystać z pushad oraz popad.
Pamiętaj również o tym, że jeśli chcesz wykonać coś na początku danej funkcji, ale później ona ma swobodnie iść do przodu, to musisz nadpisane instrukcje opcode przepisać.
Instrukcja JMP ma 5 bajtów jeśli bierzemy jmp-far. Czyli E9 00 00 00 00, i te zera to adres do funkcji.
Czyli musisz sobie napisać funkcję, która przyjmuje adres hooka, i adres funkcji do której ma skoczyć.
Czyli musisz mieć wolnych 5 bajtów, tzn. je nadpisać, a później jakoś wykonać.
Pamiętaj, że jakiekolwiek zabawy w offsetach nie są za dobrym pomysłem na hook, tzn, np:
.text:00401230 push offset aWykonalemFunct ; "Wykonalem function1"
.text:00401235 push 0 ; hWnd
.text:00401237 call ds:MessageBoxA

Tutaj ciężko ci będzie zrobić hooka, bo będziesz musiał zobaczyć offset tego stringa, póxniej offset MessageBoxa itd. a jest to dość niefajne.
Ale jeśli już na prawdę nie ma wyboru, no to niestety, trzeba to zrobić.
Adres hooka: 00401230. Adres stringa: 0040D518.
Oczywiście musimy także uwzględnić adres powrotny, bo jeśli mamy jmp, to on musi później znowu skoczyć tam gdzie była hookowana funkcja, czyli w naszym wypadku to jest:
00401235.
Należy wspomnieć, że w function2 VC++ zrobił psikusa i dla optymalizacji argument walnął do ecx.
Musimy zrobić sobie reprodukcję funkcji:
unsigned long sub_401000_addr = 0x00401000;
void sub_401000(unsigned long number)
{
_asm
{
mov ecx, number
call sub_401000_addr
}
}

Pamiętaj, że jeśli korzystasz z jmp, to nie odkłada się adres powrotu na stos. I w ten oto sposób, jeśli sub_401000 zechce powrócić, to nie zrobi tego w ten naszej funkcji sub_401000 tylko zrobi to w funkcji w której callowaliśmy naszą reprodukcję! Dlatego w tym przypadku użyłem call, jest lepiej.

To by było na tyle. Pamiętaj, że w jakiejkolwiek grze nie będziesz miał stringów z wykonania funkcji jak to tutaj zrobiliśmy. Tam jest ostre szukanie, ostre ogarnianie czegoś z niczego. Niektóre gry generują RTTI przez co masz bardzo ułatwioną pracę, ponieważ masz nazwy funkcji oraz informacje o klasach.

Cały kod źródłowy Gry, Launchera, i Dllki masz tutaj:
https://mega.co.nz/#!tVIGQbyD!fipaYTmycWbHPnfE9V3S3FkktbNhc_NH3vztfdxH6jY
mirror2:
http://www37.zippyshare.com/v/5616201/file.html

Powodzenia życzę w hackowaniu... yy.. znaczy w hookowaniu! :D

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