[Assembler] Odczytywanie wartości funkcji

0

Witam! Ostatnimi czasy bawię się trochę w assemblera, bez problemu odnajduję funkcje, którą aktualnie potrzebuję lecz problem jest gdy chcę napisać swoją. Niby wiem jakie argumenty zawiera ale gdy je podstawie po jej wywołaniu albo debuguje program albo po prostu nie działa.

Wyniki OllyDbg

Było by miło jakby mi ktoś wytłumaczył co znaczą te wszystkie PUSH EBP, PUSH DWORD PTS SS: etc

Napisałem już szkielet funkcji do wypowiadania tekstu w grze. Testowany, działa a wygląda tak:

MOV EDX, EDI ;==> czyta wartość adresu edi i podstawia pod edx w tym wypadki format ASCII np "Hello World!"
MOV ECX, ESI ;==> podstawia wartość ESI i dodaje zera w efekcie końcowym mamy 00000001
CALL MODULE.BASE + SEND.FUNC ;==> wysyła do tego adresu funkcji parametry
RETN ;==> kończy funkcjię

Podobnie chciałbym zrobić z funkcja podana wyżej, służy ona do konstrukcji pakietu a następnie wysłanie go do głównej funkcji odpowiedzialnej za jego wysłanie.
Przykładowy pakiet, który powinna zwrócić ta funkcja: \x96\x01\x66\x79\x82

Pozdrawiam, Ascer!

1

o_O
1.

podstawia wartość ESI i dodaje zera

Mam nadzieje że z tymi zerami to żart. Po prostu wpisujesz do rejestru wartość liczbową i tyle. Ollydbg pokazuje ci całą zawartość rejestru jako hexy i dla czytelności podaje też wiodące 0.

Było by miło jakby mi ktoś wytłumaczył co znaczą te wszystkie PUSH EBP, PUSH DWORD PTS SS: etc

Chyba coś słabo się bawisz w asemblera skoro zadajesz takie pytanie. Dość standardowy początek funkcji to

push ebp
mov ebp, esp
sub esp, liczba

I to jest po prostu utworzenie ramki stosu dla funkcji. Na stos wrzucasz starą wartość ebp, do odczytania kiedy będziesz z funkcji wychodził, potem ustawiasz nową wartość ebp na aktualny stack pointer, w ten sposób wiadomo że "nad" ebp będą argumenty funkcji a "pod" będą zmienne lokalne. I jednocześnie skoro pamiętamy tą pozycje to możemy sobie przesuwać stack pointer. Zwykle masz tam też jakieś sub esp, liczba żeby zaalokować dane na stosie na zmienne lokalne danej funkcji.

CALL MODULE.BASE + SEND.FUNC ;==> wysyła do tego adresu funkcji parametry

Nie, nie, nie. call powoduje odłożenie na stos adresu powrotu (adresu następnej intrukcji po call) i skacze pod adres funkcji. Nic więcej. Argumenty są przekazywane za pomocą tych push których nie rozumiesz. Calling convention jest trochę bardziej złożone i nie zawsze parametry lecą przez stos, ale akurat w tym prostym przykładzie tak jest.

RETN ;==> kończy funkcjię

Nie, nie, nie. ret powoduje pobranie ze stosu adresu powrotu (zostawionego przez call) i skacze pod ten adres. retn dodatkowo zdejmuje ze stosu wrzucone tam parametry.

Poza tym nie bardzo rozumiem co ty w ogóle robisz. Patchujesz jakąś istniejącą binarkę? Zalecam na początek jakiś kurs asemblera ;]

0

Dzięki za odpowiedź, przynajmniej mnie nie olałeś jak inni.
Tak btw to z tego co napisałeś nie wiele rozumie ale cóż faktycznie przydała by się ta wiedza z assemblera. Potrzeba mi roztrzaskać tylko 2 funkcje w sumie i tak do idę do AutoIt, zamykam assemblera ;P

Napisałem funkcje do wpisania własnego kodu do klienta gry. Działa na takiej zasadzie:

  1. Znajduje klienta, pobiera $hwnd, $pid, $module_handle
  2. Tworzy nowy region w pamięci gry (Allocate memory)
  3. Tworzy Opcode, wpisuje do wcześniej ulokowanego regionu
  4. Tworzy Remote Thread
  5. Czeka aż utworzony temat zakończy swoja pracę i zamyka Memory.

Rzeczy, które mi zquotowałeś pewnie znaczą coś innego niż myślałem ale działają idealnie tak jak tam napisałem.
Może za bardzo zajechałem z ta funkcja, podam jakąś prostą :D

http://s6.ifotos.pl/img/skieletpn_asxnwwq.png

0

Ale jesteś pewien że to w ogóle działa co robisz? Ten nowy region pamięci jest w ogóle wykonywalny? Bo jak nie jest to od razu wywali sie na jakimś Data Execution Prevention.
A ten shellcode który tam pakujesz to generujesz jak? Bo rozumiesz że to też musi być odpowiednio zrobione? Asembler który tam pokazałeś na oko mógłby zadziałać, ale nadal może być problem odpowiedniego skompilowania go żeby to miało ręce i nogi.

0

Szkielet funkcji self.say działa pięknie dodałem jeszcze tylko odczytywanie z pamięci klienta wartość życia i leczenie bez zakłócania pisania w grze pierwsza klasa.
To samo robię gdy np chcę otworzyć czat do ogłaszania ofert handlowych w grze "Advertising"

00F70000:
mov ecx,00000005
call 004216F0
retn

Jestem pewien, że da się to zrobić z użyciem runy tylko jak?Po podstawieniu argumentów z poprzedniego screena to wywala mi klienta gry "Klient gry przestał działać"
Coś załapuje jak kod wygląda tak:

098F0000:
mov ecx, 00000C7D
mov edx, 00B0E050
call 0041F660
retn

Niestety nie mam efektu tutaj tylko tak jakby chciał użyć runę na nie istniejącą postać. Złe id?
Tutaj jak sprawdzam czy kod działa w Cheat Engine

http://ifotos.pl/z/asxnahw

0

A ten adres z edx to bierzesz skąd? Jesteś pewien że ten adres ma jakiś sens? Bo jeśli to jest jakiś lokalny adres ze stosu to raczej nie zawsze będzie taki sam. No i wychodzi tu też kwestia calling convention -> https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions bo jak widzisz w tych przykładach teraz parametry funkcji przekazujesz rejestrami, a tam wcześniej miałeś przez stos...

0

Adres edx zawsze jest taki sam no chyba że wprowadzą update klienta.

1

No to nie ma innej rady, musisz się tam zapiąć debugerem i zobaczyć co i czemu nie działa ;]

0
Shalom napisał(a):

No to nie ma innej rady, musisz się tam zapiąć debugerem i zobaczyć co i czemu nie działa ;]

Zapięcie debugerem nic nie dało :(
Poczytałem trochę tutaj, dowiedziałem się paru rzeczy i chyba umiem Ci zadać pytanie.
Masz w assemblerze przykładową funkcje:

funkcja Shalom(argument1, argmunet2, argument3, argument4)
<>
-- treść funkcji, która nasz nie interesuje
<>
end funckji

Teraz twoim zadaniem jest wywołać tą funkcję znając jej argumenty oraz adres w assemblerze.
Argument 1 = 0
Argument 2 = 3197
Argument 3 = 0
Argument 4 = 217320506
Adres funkcji = 420D0C

Jak byś to zapisał hmm?
Ja próbowałem tak i nie działa ale też nie wywala klienta gry.

mov ax, 0
mov ax, C7D
mov ax, 0
mov ecx, CF40C3A
call 420D0C
retn

Problem może być bo mamy 4 argumenty a z tego co się orientuje tylko 2 zmienne ax, ecx więc 2 argumenty mogą być pomijane.
Kurde nie wiem co jeszcze dodać czekam na twoja sensowną odpowiedź.

0
Ascer napisał(a):
mov ax, 0
mov ax, C7D
mov ax, 0
mov ecx, CF40C3A
call 420D0C
retn

wtf? Analizowałeś to?
funkcja przyjmuje argumenty przez stos, a ty to do rejestrów wpisałeś?
No chyba, że jest konwencja fastcall to przez rejestry można podawać, w ogóle eax używa się do zwracania wartości, albo wskaźnika this.

W dodatku te pierwsze 3 linie są bez sensu.
1 - ax == 0
2 - ax == 0xc7d
3 - ax == 0
teraz ax == 0, a reszta danych przepadła.

W ogóle argumenty w kompilatorach c są od prawej do lewej wrzucane i potem call.
Musisz też sprawdzić czy funkcja sama czyści stos, czy ręcznie wyczyścić.

0

Argumenty przez stos to coś w tym stylu?

push arg1
push arg2
push arg3
call adres funkcji wykonalnej

Jeżeli tak to wszelkie próby spełzły na niczym bo jedyne funkcje jakie mi działają to napisane w taki spoób

mov ecx, arg1
mov edx, adres argumentu 2
call adres funkcji wykonalniej

Co do wrzucania argumentów to Auto Assembler w Cheat Engine sam konwertuje i wrzuca je od prawej.
Załączam pogląd całej funkcji, która debuguje.

http://ifotos.pl/zobacz/argpng_asxshsw.png

0

@Ascer: Na screenie widać, że idzie rejestrem, ale otwórz sobie IDA i jak sprawdzisz co robi funkcja w debuggerze lub w ida statycznie, to ją podpisz i tak samo parametry jakie przyjmują bez tego nie zrozumiesz za dużo.

0
Trzeźwy Szczur napisał(a):

@Ascer: Na screenie widać, że idzie rejestrem, ale otwórz sobie IDA i jak sprawdzisz co robi funkcja w debuggerze lub w ida statycznie, to ją podpisz i tak samo parametry jakie przyjmują bez tego nie zrozumiesz za dużo.

Debuger zapięty na odnośniku do funkcji wywołującej i tutaj są wyniki

http://ifotos.pl/z/asxsxaw

0

@Ascer oj niewiele z tego czytania zrozumiałeś. Jest tutaj kilka niejasnych kwestii:

  1. W jaki sposób wołasz tą swoją funkcje? W sensie w jaki sposób skaczesz pod ten twój zaalokowany blok pamieci? Bo jeśli robisz to za pomocą skoku typu jmp a nie call to nie możesz tam mieć ret bo się wysypie
  2. Przeczytaj to co wyżej wstawiłem na temat calling convention. Musisz sprawdzić jak oryginalnie przekazywane są parametry do tej funkcji. Czasem są to rejestry, ale różne a nie sam ax. Poza tym to są 32 bity więc eax jeśli już. A czasem jest to stos! Na oko z tego co pokazałeś to tutaj masz przypadek przekazywania parametrów przez stos.
  3. Dalej, jeśli modyfikujesz stos w swojej funkcji to musisz to zrobić z głową a nie "na pałe". Idea jest taka, że argumentami na stosie zajmuje się wołający funkcje, więc jeśli zrobisz jakieś push i wywołasz funkcje to TY musisz potem zrobić odpowiednie pop (albo jakieś add esp, cośtam) żeby wyrównać stos. Inaczej wywali się na ret z twojej funkcji bo jako adres powrotu pobierze sobie ten twój argument funkcji. Więc jeśli robisz
push param1
push param2
call funkcja

To następnie musisz (!) zrobić

pop eax
pop eax

Albo jakiś ekiwalent zdjęcia tych parametrów ze stosu, np. add esp, 8. Dopiero potem możesz dać ret!

Popatrz zresztą na te listingi asemblera które tam masz. Masz tam przed wywołaniem jakieś 4 pushe po 4 bajty każdy, a potem po callu jest add esp, 10h czyli przesuniecie stosu o 10h czyli o 16 bajtów. Nie jest to przypadek ;]

0

@Shalom
Pełen szacunek za poświęcony czas!
Kod działa pięknie po dodaniu add esp, 10
Jesteś mistrzem :)
http://ifotos.pl/z/asxswpr/

1

Podziwiam samozaparcie, bo nie każdemu chciałoby się rozkminiać asemblera i bawić się w injectowanie shellcodu do aplikacji, tylko po to żeby czitować w tibie ;]

0
Shalom napisał(a):

Podziwiam samozaparcie, bo nie każdemu chciałoby się rozkminiać asemblera i bawić się w injectowanie shellcodu do aplikacji, tylko po to żeby czitować w tibie ;]

Tak przy okazji masz pomysł jak zastąpić to:

push dword ptr ds:[adres funkcji]

na coś w stylu

push liczba dword (np 123456789)

EDIT

push 123456789 dziala
0

Wznawiam wątek odnośnie assemblera.
Od jakiegoś czasu stoje martwym punkcie i nic nie wskazuje na poprawę więc przychodzę o pomoc do was.

Chcę odtworzyć funkcje zapisaną w pamięci klienta gry. Jako debuggera używam CheatEngine bo OllyDbg podczas próby debugowania wywala klienta.

  1. Debuguje funkcje aby sprawdzić ile i jakie przyjmuje argumenty:

title

  1. Przechodzę teraz do miejsca w pamięci klienta gdzie została wywołana funkcja:

title

Jak widać w czerwonym prostokącie są tylko 2 pushe natomiast w zielonym jest już 4.
Interesuję mnie jak to jest, że funkcja ma 4 argumenty a są tylko 2 pushe, może powinienem brać pod uwagę zielony kwadrat a nie tylko czerwony?
Czy w momencie kiedy mam np:

push 00000000
call func1
push 00000000
push 00000864
call func2

Gdy wywołam func1 to ten push przepada? Czy może zalicza się jako argument do następnego wywołania func2?

  1. Próba odtworzenia funkcji zakończona crashem klienta:

title

@Shalom

0

Ale skad pomysł ze ta funkcja ma akurat 4 argumenty? Te wcześniejsze pushe wyglądają raczej na takie używane przez funkcje które zaraz po nich są wołane, ale pamiętaj że calling convention nie wymaga pushowania wszystkich argumentów. Dodatkowo pushe mogły być też wykonane dużo wcześniej w kodzie, jeśli kompilator tak sobie wymyślił.
Czemu nie załadujesz tego np. do IDA Pro? IDA dobrze ogarnia ile argumentów ma dana funkcja na przykład.

0

Pobrałem IDA pro, freeware. Po załadowaniu i ustawieniu breakpointa wywala mi klienta gry tak jak było to wo OllyDbg. Gra ma dość dobre zabezpieczenia i charakteryzuje się szyfrowaniem wartości i struktur. Zapisuje wartości w Double, używa metod std::string, std::unordered_map, wartości zamiast w zwykłych tabelach przechowuje w hash table.

Funkcja, którą wywołuje zwraca 0C. Zrobiłem już z 50+ podobnych funkcji i zawsze tak odczytywałem ilość argumentów i działało do tej pory.
Czyli co mogę zrobić coś takiego?

push 00000864
push 002C0030  
mov ecx, obiekt pointer
call func
retn

Jak w takim razie podam dwa argumenty 00000000 (index plecaka i index miejsca w plecaku)?

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