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 parolei

EBX 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 XOR

Urmă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 = 0

Observă 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] = 1

Acum, 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ă!