rev2_ghidra
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| rev2_ghidra [2025/10/30 16:29] – mbunic | rev2_ghidra [2025/12/01 11:40] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ==== Zadatak s Hacknite platforme - rev2 ==== | + | ==== rev2 ==== |
| - | Uz zadatak je dana samo izvršna datoteka " | + | Uz zadatak je dana samo izvršna datoteka " |
| + | Pokretanjem programa, traži se lozinka i nakon unosa, ispisuje se poruka o pogrešnoj lozinki. | ||
| - | Pokretanjem programa, program nas pita za password i ispisuje krivi password, pri unosu netočnog passworda. | + | {{ rev2_ghidra: |
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | + | ||
| - | {{ : | + | |
| Može se pokrenuti naredba file kako bi se saznale osnovne informacije o programu. | Može se pokrenuti naredba file kako bi se saznale osnovne informacije o programu. | ||
| Line 18: | Line 13: | ||
| </ | </ | ||
| - | {{ : | + | {{ rev2_ghidra: |
| - | + | ||
| - | Program je statically liked, znači da se može samostalno pokretati. Također je stripped, što znači da imena varijabli i funkcija neće biti sačuvane, što će malo otežati reverzno inženjerstvo nad programom. | + | |
| + | Program je statically linked, zato se može samostalno pokretati. Također je stripped, zato imena varijabli i funkcija neće biti sačuvane, što će malo otežati reverzno inženjerstvo nad programom. | ||
| - | Nakon toga se može pokrenuti naredba, strings da se vidi ima li zanimljivih stringova u datoteci. | + | Nakon toga se može pokrenuti naredba strings da se vidi ima li zanimljivih stringova u datoteci. |
| < | < | ||
| Line 29: | Line 23: | ||
| </ | </ | ||
| - | No ni jedan vraćeni rezultat ne izgleda kao da previše otkriva. Može se također odmah pretražiti | + | No niti jedan vraćeni rezultat ne izgleda kao da previše otkriva. Može se također odmah pretražiti |
| < | < | ||
| Line 35: | Line 29: | ||
| </ | </ | ||
| - | zastavica " | + | zastavica " |
| - | {{ : | + | {{ rev2_ghidra: |
| - | + | ||
| - | Ovo nam također ništa | + | |
| + | Ovo nam također ništa korisno ne otkriva o programu, osim što prikazuje tekst koji se ispisuje pri unosu passworda i ispisu pri krivom unosu passworda. | ||
| Nakon toga se može probati naredba " | Nakon toga se može probati naredba " | ||
| Line 48: | Line 41: | ||
| </ | </ | ||
| - | {{ : | + | {{ rev2_ghidra: |
| - | Također se ne prvi pogled ne mogu pronaći dodatne korisne informacije o programu u ispisu. | + | Također se na prvi pogled ne mogu pronaći dodatne korisne informacije o programu u ispisu. |
| - | Slijedeći korak je korištenje besplatnog Ghidra | + | Sljedeći korak je korištenje besplatnog Ghidra |
| - | {{ : | + | {{ rev2_ghidra: |
| Dvoklikom na rev2 datoteku u sučelju, otvara se sučelje za pregled dekompajliranog koda programa. | Dvoklikom na rev2 datoteku u sučelju, otvara se sučelje za pregled dekompajliranog koda programa. | ||
| - | {{ : | + | {{ rev2_ghidra: |
| Na slici vidimo entry point, dio koda koji se prvi izvršava pri pokretanju programa, u Decompile sučelju vidi se da entry point funkcija samo poziva funkciju " | Na slici vidimo entry point, dio koda koji se prvi izvršava pri pokretanju programa, u Decompile sučelju vidi se da entry point funkcija samo poziva funkciju " | ||
| - | {{ : | + | {{ rev2_ghidra: |
| Pregledom dekompajliranog koda ove funkcije, može se zaključiti da je ovo main funkcija, vidi se dio koda koji ispisuje "Enter password: ", i dio koda odmah ispod, koji bi mogao biti fgets funkcija za čitanje korisničkog unosa, koji zatim sprema na varijablu pbVar5. | Pregledom dekompajliranog koda ove funkcije, može se zaključiti da je ovo main funkcija, vidi se dio koda koji ispisuje "Enter password: ", i dio koda odmah ispod, koji bi mogao biti fgets funkcija za čitanje korisničkog unosa, koji zatim sprema na varijablu pbVar5. | ||
| Line 72: | Line 65: | ||
| </ | </ | ||
| - | Na početku main funkcije, | + | Na početku main funkcije, |
| < | < | ||
| Line 84: | Line 77: | ||
| </ | </ | ||
| - | također, vidi se da je drugi argument poziva funkcije | + | također, vidi se da je drugi argument poziva funkcije |
| Treći argument je pointer | Treći argument je pointer | ||
| - | {{ : | + | {{ rev2_ghidra: |
| vrijednost je | vrijednost je | ||
| Line 104: | Line 97: | ||
| </ | </ | ||
| - | odgovarala | + | odgovarala |
| < | < | ||
| Line 110: | Line 103: | ||
| </ | </ | ||
| - | Sad se može označiti varijabla local_118, pritisnuti gumb L i preimenovati u input_buffer. | + | Može se označiti varijabla local_118, pritisnuti gumb L i preimenovati u input_buffer. |
| - | {{ : | + | {{ rev2_ghidra: |
| - | Također se varijable | + | Također se varijabla |
| - | FUN_00404ee0 u fgets. Varijablu lVar2 nećemo preimenovati, | + | FUN_00404ee0 u fgets. Varijablu lVar2 nećemo preimenovati, |
| - | Kako bi se olakšalo praćenje važnih varijabli, varijable koje su bitne u izvođenju programa mogu se označiti i pritiskom desnog klika i opcije highlight, mogu se postaviti da budu bolje naznačenje u ostatku programa. | + | Kako bi se olakšalo praćenje važnih varijabli, varijable koje su bitne u izvođenju programa mogu se označiti i pritiskom desnog klika i opcije highlight, mogu se postaviti da budu istaknuto označenu ostatku programa. |
| - | fgets funkcija pri uspješnom izvršavanju, vraća pointer na string buffer, a ako je vraćena vrijednost NULL, dogodila se greška, što odgovara ovom djelu koda | + | fgets funkcija pri uspješnom izvršavanju vraća pointer na string buffer, a ako je vraćena vrijednost NULL, dogodila se greška, što odgovara ovom dijelu |
| < | < | ||
| Line 129: | Line 122: | ||
| </ | </ | ||
| - | Ako je fgets vratio NULL, varijabla uVar3 se postavlja u 1 i skače se na kraj main funkcije, te se varijabla uVar3 vrača kao return vrijednost main funkcije, | + | Ako je fgets vratio NULL, varijabla uVar3 se postavlja u 1 i skače se na kraj main funkcije, te se varijabla uVar3 vraća kao return vrijednost main funkcije, |
| - | Ako se fgets uspješno izvršio, izvršava se else block, koji počinje ovim kodom | + | Ako se fgets uspješno izvršio, izvršava se else blok, koji počinje ovim kodom |
| < | < | ||
| Line 143: | Line 136: | ||
| dvoklikom na adresu DAT_0047f021 na kojoj je vrijednost drugog argumenta proslijeđenog ovoj funkciji, može se vidjeti vrijednost na toj adresi | dvoklikom na adresu DAT_0047f021 na kojoj je vrijednost drugog argumenta proslijeđenog ovoj funkciji, može se vidjeti vrijednost na toj adresi | ||
| - | {{ : | + | {{ rev2_ghidra: |
| Vrijednost je " | Vrijednost je " | ||
| - | Može se zaključiti da funkcija thunk_FUN_00412860 vjerojatno vraća poziciju na kojoj se u korisničkom unosu nalazi CR LF, odnosno kraj unosa. | + | Može se zaključiti da funkcija thunk_FUN_00412860 vjerojatno vraća poziciju |
| < | < | ||
| Line 157: | Line 150: | ||
| U tom slučaju bi varijabla | U tom slučaju bi varijabla | ||
| - | Nakon toga, u if statementu, vrijednost varijable | + | Nakon toga, u if statementu, vrijednost varijable lVar2 se uspoređuje s 0x15, odnosno 21 u dekadskom zapisu, što točno odgovara duljini FLAG formata |
| < | < | ||
| Line 163: | Line 156: | ||
| </ | </ | ||
| - | Ako je duljina korisničkog jednaka 21, ulazi se u do - while block, unutar kojega se nalazi switch sa 7 caseva, | + | Ako je duljina korisničkog |
| - | Pregledom switcha, na kraju switcha se odmah može vidjeti case 0, koji samo ispisuje | + | Pregledom switcha, na kraju switcha se odmah može vidjeti case 0, koji samo ispisuje |
| - | {{ : | + | {{ rev2_ghidra: |
| - | Na početku do blocka, se nalazi ova linija | + | Na početku do bloka, se nalazi ova linija |
| < | < | ||
| Line 175: | Line 168: | ||
| </ | </ | ||
| - | koja postavlja varijablu bVar1 na dereferenciranu vrijednost user_input pointera, koji je pointer na string buffer. | + | koja postavlja varijablu bVar1 na dereferenciranu vrijednost user_input pointera, koji je pointer na string buffer. |
| - | može preimenovati u user_input_char. | + | |
| - | U switchu postoji 7 caseva, a case se određuje prema vrijednosti puVar2. | + | U switchu postoji 7 caseva, a case se određuje prema vrijednosti |
| - | {{ : | + | {{ rev2_ghidra: |
| - | {{ : | + | {{ rev2_ghidra: |
| Vidi se da se u svakom caseu uzima user_input_char, | Vidi se da se u svakom caseu uzima user_input_char, | ||
| - | u case 1 operacija je XOR (^), u case 2 operacije je ADD (+) u case 3 operacije je SUB (-), u case 4 operacije je MUL (*), u case 5 operacija je bitwise SHIFT (<< i >>), u case 6 operacija je bitwise NOT (~) i u case 7 operacije je također ADD (+), kao što je prethodno opisano. | + | u caseu 1 operacija je XOR (^), u caseu 2 operacije je ADD (+) u caseu 3 operacije je SUB (-), u caseu 4 operacije je MUL (*), u caseu 5 operacija je bitwise SHIFT (<< i >>), u caseu 6 operacija je bitwise NOT (~) i u caseu 7 operacije je također ADD (+), kao što je prethodno opisano. |
| - | {{ : | + | odlaskom na adresu gdje je Stack[-0x158] i dvoklikom na XREF[3, |
| + | |||
| + | {{ rev2_ghidra: | ||
| Također možemo vidjeti opis switch caseva, koji je upravo bio opisan. | Također možemo vidjeti opis switch caseva, koji je upravo bio opisan. | ||
| - | {{ : | + | {{ rev2_ghidra: |
| Još je preostalo pronaći što je varijabla koja određuje koji switch case će biti odabran, operandi u switch caseovima i očekivani rezultati operacija, koji su svi određeni u varijabli puVar2. | Još je preostalo pronaći što je varijabla koja određuje koji switch case će biti odabran, operandi u switch caseovima i očekivani rezultati operacija, koji su svi određeni u varijabli puVar2. | ||
| - | Varijabla puVar2 je definirana pri početku programa i pokazuje na adresu varijable local_158, | + | Varijabla puVar2 je definirana pri početku programa i pokazuje na adresu varijable local_158, |
| - | {{ : | + | {{ rev2_ghidra: |
| - | No pošto | + | Budući da je program stripped, moguće je da je Ghidra |
| - | Na samoj kraju do blocka, nakon switcha casea-7, se user_input pointer povećava za 1, znači da se za sljedeću iteraciju do blocka | + | Na samom kraju do bloka, nakon switcha casea-7, se user_input pointer povećava za 1, tako će se za sljedeću iteraciju do bloka koristiti |
| - | početak do blocka | + | početak do bloka |
| < | < | ||
| Line 210: | Line 204: | ||
| </ | </ | ||
| - | a puVar2 se povećava za 3 u svakoj iteraciji, što taman odgovara 3 vrijednosti koje se koriste | + | a puVar2 se povećava za 3 u svakoj iteraciji, što taman odgovara 3 vrijednosti koje se koriste u svakom do bloku, puVar2 + 0 određuje koji switch case će se izvršiti, |
| - | u svakom do blocku, puVar2 + 0 određuje koji switch case će se izvršiti, | + | |
| - | {{ : | + | {{ rev2_ghidra: |
| - | Pošto | + | Budući da će se svaki user input character imati jednu vlastitu iteraciju, a puVar2 se povećava za 3 nakon svake iteracije, |
| Ovo se također može vidjeti na slici ispod, while se izvršava sve dok varijabla pbVar2 koja se povećava za 3 nije jednaka adresi spremljenoj na local_119, koja je RSP uvećan za 0x3f, odnosno 63 u dekadskom zapisu, zato što ima 63 byte vrijednosti spremljenih na stacku. | Ovo se također može vidjeti na slici ispod, while se izvršava sve dok varijabla pbVar2 koja se povećava za 3 nije jednaka adresi spremljenoj na local_119, koja je RSP uvećan za 0x3f, odnosno 63 u dekadskom zapisu, zato što ima 63 byte vrijednosti spremljenih na stacku. | ||
| - | {{ : | + | {{ rev2_ghidra: |
| - | Na slici 15. se vidi da je puVar2 pointer na local_158, sada znamo da local_158 zapravo sadržava 63 byteova, | + | Na slici 16. se vidi da je puVar2 pointer na local_158, sada znamo da local_158 zapravo sadržava 63 bajtova |
| - | {{ : | + | {{ rev2_ghidra: |
| Znamo da sadržava byte vrijednost i duljine je 63, pa type možemo postaviti u | Znamo da sadržava byte vrijednost i duljine je 63, pa type možemo postaviti u | ||
| Line 230: | Line 223: | ||
| </ | </ | ||
| - | {{ : | + | {{ rev2_ghidra: |
| - | {{ : | + | {{ rev2_ghidra: |
| Sada je promijenjen prikaz i može se jasno vidjeti da su ovdje zapravo pohranjene vrijednosti: | Sada je promijenjen prikaz i može se jasno vidjeti da su ovdje zapravo pohranjene vrijednosti: | ||
| - | < | ||
| OPERACIJA | OPERACIJA | ||
| OPERAND | OPERAND | ||
| OČEKIVANI REZULTAT | OČEKIVANI REZULTAT | ||
| - | </ | ||
| Vrijednosti su u trojkama, prva vrijednost je operacija koja će se izvršiti, odnosno vrijednost 0-7 koja određuje koji switch case 0-7 će se izvršiti, druga vrijednost je operand, treća vrijednost je očekivani rezultat. Za svaki od 21 user input charactera, postoji odgovarajuća trojka. | Vrijednosti su u trojkama, prva vrijednost je operacija koja će se izvršiti, odnosno vrijednost 0-7 koja određuje koji switch case 0-7 će se izvršiti, druga vrijednost je operand, treća vrijednost je očekivani rezultat. Za svaki od 21 user input charactera, postoji odgovarajuća trojka. | ||
| - | Pregled prvih triju vrijednosti je prikazan na slici 22. | + | Pregled prvih triju vrijednosti je prikazan na slici ispod. |
| - | {{ : | + | {{ rev2_ghidra: |
| - | Ovdje se može vidjeti da je prva vrijednost 2, što znači da će se izvršiti switch case 2, koji je ADD (+), operand je 0x92, a očekivani rezultat je 0xd5. | + | Ovdje se može vidjeti da je prva vrijednost 2, zbog koje će se izvršiti switch case 2, koji je ADD (+), operand je 0x92, a očekivani rezultat je 0xd5. |
| - | Što znači | + | Odnosno |
| < | < | ||
| Line 256: | Line 247: | ||
| </ | </ | ||
| - | odnosno | + | Iz ovoga se može zaključiti koji je očekivani znak korisničkog unosa |
| < | < | ||
| Line 264: | Line 255: | ||
| 0xd5 - 0x92 je 0x43, odnosno 67 u dekadskom zapisu, što odgovara ASCII vrijednosti " | 0xd5 - 0x92 je 0x43, odnosno 67 u dekadskom zapisu, što odgovara ASCII vrijednosti " | ||
| - | Sada se ili ovako " | + | Sada se ili ovako " |
| - | U python | + | U Python |
| < | < | ||
| Line 272: | Line 263: | ||
| </ | </ | ||
| - | I nakon toga možemo samo prekopirati sve linije koje sadržavaju definirane vrijednosti varijable local_158, kao što su prikazane na slici 20. | + | I nakon toga možemo samo prekopirati sve linije koje sadržavaju definirane vrijednosti varijable local_158, kao što su prikazane na slici 21. |
| - | Ovime već imamo validan | + | Ovime već imamo validan |
| - | {{ : | + | {{ rev2_ghidra: |
| Dodavanjem ovog koda na kraj, možemo dobiti ispis operacija i očekivanih rezultata koji se izvršavaju u programu. | Dodavanjem ovog koda na kraj, možemo dobiti ispis operacija i očekivanih rezultata koji se izvršavaju u programu. | ||
| - | {{ : | + | {{ rev2_ghidra: |
| + | |||
| + | Pokretanjem koda sada dobivamo ispis svih operacija nad korisničkim unosom i očekivane rezultate tih operacija: | ||
| + | |||
| + | {{ rev2_ghidra: | ||
| + | |||
| + | Sada se samo Python programski kod za ispis operacija programa može promijeniti da radi inverz operacija za svih 7 slučaja, te tako nađe korisnički input koji bi riješio postavljene jednadžbe i ispiše krajnji rezultat. | ||
| + | |||
| + | < | ||
| + | local_158 = [0] * 63 | ||
| + | |||
| + | local_158[0x38] = 0xca | ||
| + | local_158[0x39] = 7 | ||
| + | local_158[0x3a] = 0 | ||
| + | local_158[0x3b] = 0xcc | ||
| + | local_158[0] = 2 | ||
| + | local_158[1] = 0x92 | ||
| + | local_158[2] = 0xd5 | ||
| + | local_158[3] = 7 | ||
| + | local_158[4] = 0 | ||
| + | local_158[5] = 0xac | ||
| + | local_158[6] = 7 | ||
| + | local_158[7] = 0 | ||
| + | local_158[8] = 0xba | ||
| + | local_158[9] = 7 | ||
| + | local_158[10] = 0 | ||
| + | local_158[0xb] = 0xce | ||
| + | local_158[0xc] = 1 | ||
| + | local_158[0xd] = 0x42 | ||
| + | local_158[0xe] = 0x72 | ||
| + | local_158[0xf] = 1 | ||
| + | local_158[0x30] = 1 | ||
| + | local_158[0x31] = 0xb3 | ||
| + | local_158[0x32] = 0x87 | ||
| + | local_158[0x33] = 4 | ||
| + | local_158[0x34] = 0x89 | ||
| + | local_158[0x35] = 0xf8 | ||
| + | local_158[0x36] = 6 | ||
| + | local_158[0x37] = 0 | ||
| + | local_158[0x10] = 0x7f | ||
| + | local_158[0x11] = 0x4d | ||
| + | local_158[0x12] = 7 | ||
| + | local_158[0x13] = 0 | ||
| + | local_158[0x14] = 0xcb | ||
| + | local_158[0x15] = 4 | ||
| + | local_158[0x16] = 0xf1 | ||
| + | local_158[0x17] = 0xab | ||
| + | local_158[0x18] = 6 | ||
| + | local_158[0x19] = 0 | ||
| + | local_158[0x1a] = 0xce | ||
| + | local_158[0x1b] = 4 | ||
| + | local_158[0x1c] = 0x6b | ||
| + | local_158[0x1d] = 0xbc | ||
| + | local_158[0x1e] = 1 | ||
| + | local_158[0x1f] = 0x7d | ||
| + | local_158[0x3c] = 2 | ||
| + | local_158[0x3d] = 0x98 | ||
| + | local_158[0x3e] = 0xf5 | ||
| + | local_158[0x20] = 0x44 | ||
| + | local_158[0x21] = 1 | ||
| + | local_158[0x22] = 0xe5 | ||
| + | local_158[0x23] = 0xd4 | ||
| + | local_158[0x24] = 7 | ||
| + | local_158[0x25] = 0 | ||
| + | local_158[0x26] = 200 | ||
| + | local_158[0x27] = 4 | ||
| + | local_158[0x28] = 0xdd | ||
| + | local_158[0x29] = 7 | ||
| + | local_158[0x2a] = 5 | ||
| + | local_158[0x2b] = 7 | ||
| + | local_158[0x2c] = 0x9a | ||
| + | local_158[0x2d] = 7 | ||
| + | local_158[0x2e] = 0 | ||
| + | local_158[0x2f] = 0xcc | ||
| + | |||
| + | def reconstructUserInput(operation, | ||
| + | def ret(b): | ||
| + | return chr(b & 0xFF) | ||
| + | match operation: | ||
| + | case 1: # XOR | ||
| + | return ret(expected_result ^ operand) | ||
| + | case 2: # ADD | ||
| + | return ret((expected_result - operand) & 0xFF) | ||
| + | case 3: # SUB | ||
| + | return ret((expected_result + operand) & 0xFF) | ||
| + | case 4: # MUL | ||
| + | # solve (user * operand) & 0xFF == expected_result | ||
| + | # since all values are printable and operand is invertible modulo 256, use brute force | ||
| + | for x in range(256): | ||
| + | if (x * operand) & 0xFF == expected_result: | ||
| + | return ret(x) | ||
| + | case 5: # SHIFT (rotate-left) | ||
| + | n = operand & 7 | ||
| + | for x in range(256): | ||
| + | if ((x << n) | (x >> (8 - n))) & 0xFF == expected_result: | ||
| + | return ret(x) | ||
| + | case 6: # BITWISE NOT (no operand) | ||
| + | return ret((~expected_result) & 0xFF) | ||
| + | case 7: # ADD (no operand) | ||
| + | return ret((-expected_result) & 0xFF) | ||
| + | |||
| + | i = 0 | ||
| + | user_input_solve = "" | ||
| + | while i < 63: | ||
| + | operation = local_158[i] | ||
| + | operand = local_158[i + 1] | ||
| + | expected_result =local_158[i + 2] | ||
| + | |||
| + | userInput = reconstructUserInput(operation, | ||
| + | |||
| + | print(userInput) | ||
| + | |||
| + | user_input_solve += userInput | ||
| - | Pokretanjem koda sada dobivamo ispis svih operacija nad korsiničkim unosom | + | |
| - | {{ :slika25.png? | + | print(" |
| - | Sada se samo Python programski kod sa slike 22 može promijeniti da radi inverz operacija za svih 7 slučaja, tako nađe korisnički input koji bi riješio postavljenje jednadžbe i ispiše krajnji rezultat. | + | </ |
| - | {{ : | ||
| - | Pokretanjem ovog programa, dobiva se rješenje zadatka | + | Pokretanjem ovog programa dobiva se rješenje zadatka |
| - | {{ :slika27.png? | + | {{ rev2_ghidra:slika26.png? |
| - | Napomena: | + | ==Napomena== |
| - | Preporučuje se da kad radite reversing, probate otvoriti datoteku i s drugim alatima za reverzno inženjerstvo, | + | Preporučuje se da kad radite reversing, probate otvoriti datoteku i drugim alatima za reverzno inženjerstvo, |
| - | {{ :slika28.png? | + | {{ rev2_ghidra:slika27.png? |
| - | Može se vidjeti da je Binary Ninja odmah uspješno rekonstruirala | + | Može se vidjeti da je Binary Ninja odmah uspješno rekonstruirala |
rev2_ghidra.1761841757.txt.gz · Last modified: 2025/12/01 11:40 (external edit)