Root Me – Xor Madness – Ghid de rezolvare
- 3 minute de citit
- 6 exemple de cod
Disponibil și în English
Salut,
În acest articol voi descrie cum am rezolvat provocarea PE x86 – Xor Madness de pe Root-Me
Provocarea îți cere o parolă, iar parola este folosită și pentru a valida flag-ul. Ce face provocarea interesantă este că folosește doar xor, sub, call și ret.
Iată cum am abordat provocarea:
- Deoarece binarul avea câteva funcții și unele șiruri de caractere erau în text clar, am încercat să înțeleg imaginea de ansamblu și am etichetat toate funcțiile corespunzător:
win,lose,checkși așa mai departe. - Am descoperit că există 6 etape, fiecare etapă încearcă să proceseze o parte din parolă. Funcția de start este și ea o etapă, iar celelalte etape sunt asemănătoare.
- Apoi am petrecut câteva ore depanând provocarea și documentând tot ce face, pentru a înțelege ce vrea.
Pentru a rezolva această provocare trebuie să știi cum funcționează operația XOR; te rog verifică că o cunoști înainte de a merge mai departe.
Comportamentul provocării este simplu: calculează o adresă, să numim această adresă SOLADDR, și o face să indice valoarea 1. [SOLADDR] = 1.
Ea ia un octet din parola pe care ai introdus-o și îl XOR-ează de două ori. Valoarea rezultată va fi o adresă, pe care o putem numi PASSADDR. Trucul este să te uiți la modul în care parola ta este XOR-ată și să o faci egală cu SOLADDR.
Funcția care te lasă să treci la etapa următoare o face doar dacă locația indicată de PASSADDR are valoarea 1 atribuită. Aceasta este stocată în edx. edx = [PASSADDR].
Următoarea funcție te lasă să treci la etapa următoare dacă și numai dacă EDX este 1:
weird_ret_addr_change proc neararg2_next_stage_address= dword ptr 4xor eax, eaxxor eax, [esp+arg2_next_stage_address]sub eax, [esp+0] ; scade adresa de return din EAXmul dl ; înmulțește eax cu DL. Partea inferioară a lui EDXadd eax, [esp+0] ; adaugă la loc adresa de returnxor ecx, ecx ; resetează ecxxor ecx, [esp+0] ; setează ecx la [esp]xor [esp+0], ecx ; resetează [esp]; [esp] = 0xor [esp+0], eax ; setează [esp] la eaxretn 4Știm deja că EDX este setat la [PASSADDR], adică EDX = *PASSADDR. Fiecare etapă efectuează acest pas.
În funcția main, scanf citește maximum 63 de caractere (nu avea încredere în nimic ce citești, verifică!) și le stochează în EDI, apoi le mută în ESI și obține primul octet din password_input în EBX.
call ds:scanf xor esp, espxor esp, edixor esi, esixor esi, espxor edi, edixor edi, esisub esp, 100hxor ebx, ebxxor bl, [esi] ; ebx conține primul octet al paroleiEBX este apoi XOR-at cu o valoare; primii 3 octeți sunt doar de „decor”, ne interesează doar ultimul octet rezultat.
xor ebx, 59307554h ; ebx conține primul octet.text:004010C0 ;.text:004010C0 ; Exemplu:.text:004010C0 ; ; in xor res.text:004010C0 ; 0x30 ^ 0x54 -> 0x64 caz de test.text:004010C0 ; 0x6C ^ 0x54 -> 0x38 încercarea 1.text:004010C0 ; 0x24 ^ 0x54 -> 0x70 încercarea 2 - reușită.text:004010C0 ;.text:004010C0 ; Primul XORUrmătoarele operații încarcă ESP în EBX, îl XOR-ează cu ultimul octet rezultat din operația anterioară și îl salvează undeva.
.text:004010D0 xor eax, esp ; eax = 0x0019FD74 .text:004010D2 xor eax, ebx ; eax este acum XOR-at cu octetul nostru 0x64 (res).text:004010D2 ;.text:004010D2 ; 0x0019FD74 ^ 0x64 = 0x0019FD10.text:004010D2 ; adresa rezultată este folosită la setarea EDX.text:004010D2 ; în acest caz este invalidă, adresa corectă este.text:004010D2 ; === 0x0019FD04.text:004010D4 xor edx, edx .text:004010D6 xor edx, eax ; edx = 0x0019FD10 (adresa rezultată din octetul nostru XOR-at).text:004010D8 xor ecx, ecx ; ecx = 0.text:004010DA xor ecx, [eax] ; încearcă să obțină valoarea de la [eax].text:004010DA .text:004010DC xor [eax], ecx ; [EAX] = 0xFFFFFFFE.text:004010DC ; eax = 0Observă că EDX conține acum adresa pe care o controlăm, 19FD10, pe care o putem numi PASSADDR. Am obținut 19FD10 după rularea programului și dându-i 0 ca input.
Următoarea bucată de cod calculează SOLADDR prin XOR-area lui ESP cu două valori hardcodate și o face să indice valoarea 1.
.text:004010E0 xor eax, esp ; esp = 0x0019FD74.text:004010E2 xor eax, 0F2h ; 0x0019FD74 ^ 0xF2 = 0x0019FD86.text:004010E7 xor eax, 82h ; 0x0019FD86 ^ 0x82 = 0x0019FD04.text:004010EC xor ecx, ecx ; ecx = 0.text:004010EE xor ecx, [eax] ; ecx = *(0x0019FD04) .text:004010F0 xor [eax], ecx ; [eax] = [eax] ; eax = 0.text:004010F2 xor byte ptr [eax], 1 [eax] = 1Acum, codul atribuie valoarea indicată de PASSADDR lui EDX. EDX = [PASSADDR].
.text:004010F5 xor eax, eax ; eax = 0.text:004010F7 xor eax, edx ; eax = 0x0019FD10; edx este setat mai sus; PASSADDR.text:004010F9 xor edx, edx ; edx = 0.text:004010FB xor edx, [eax] ; edx = [0x0019FD10]; edx = [PASSADDR]Aceasta este singura informație de care ai nevoie pentru a rezolva această provocare. Acum poți găsi parola analizând static etapele următoare.
Mulțumesc pentru lectură!