RE CTFd CMU x86 writeup

に公開

はじめに

以下の常設CTFに掲載されているCMU x86の問題をPhase1~6まで解いたので,未来の自分のためにもwriteupを残しておきます.

  • https://reversing.ctfd.io
  • Cutterを使用.
  • 勉強のためにアセンブラを読むため,デコンパイラ機能は不使用.

Phase 1

Phase 1では,はじめに文字列Public speaking is very easy.が定義される.
次に,後述するstrings_not_equalで,入力された文字列と上述の文字列とが等しくないかを判定している.等しくない場合,戻り値1が返されることで,explode_bombが実行して失敗する.
そのため,そのまま上述の文字列を入力すればクリアになる.
回答は Public speaking is very easy. である.

コード
;-- phase_1:
gcc2_compiled.(int32_t arg_4h);
; arg int32_t arg_4h @ stack + 0x4
0x08048b20      push    ebp
0x08048b21      mov     ebp, esp
0x08048b23      sub     esp, 8
0x08048b26      mov     eax, dword [arg_4h]
0x08048b29      add     esp, 0xfffffff8

0x08048b2c      push    str.Public_speaking_is_very_easy. ; 0x80497c0 ; int32_t arg_8h
0x08048b31      push    eax               ; int32_t arg_4h ; ユーザの入力した文字列
0x08048b32      call    strings_not_equal ; sym.strings_not_equal
0x08048b37      add     esp, 0x10
0x08048b3a      test    eax, eax          ; 文字列が等しい場合,eax=0なのでジャンプ
0x08048b3c      je      0x8048b43

0x08048b3e      call    explode_bomb ; sym.explode_bomb

0x08048b43      mov     esp, ebp
0x08048b45      pop     ebp
0x08048b46      ret
0x08048b47      nop

string_not_equal

本関数は,2つの文字列の先頭ポインタを引数で受け取っている.
後述のstring_lengthで,それぞれの文字列長を求め,ebxeaxに格納し,同じ長さであることを確認している.
次のループでは,文字列の各文字を比較している.

  • 文字を比較する.等しければループを続行し,等しくなければ戻り値1を設定して関数を終了する.
  • 文字列の位置を指すポインタを1増加し,次の文字を参照する.

よって,本関数は与えられた2つの文字列が一致した場合0を,不一致の場合1を返す関数である.

コード
strings_not_equal(int32_t arg_4h, int32_t arg_8h);
; var int32_t var_1ch @ stack - 0x1c
; arg int32_t arg_4h @ stack + 0x4
; arg int32_t arg_8h @ stack + 0x8
0x08049030      push    ebp
0x08049031      mov     ebp, esp
0x08049033      sub     esp, 0xc
0x08049036      push    edi
0x08049037      push    esi
0x08049038      push    ebx
0x08049039      mov     esi, dword [arg_4h] ; 第1引数の文字列の先頭ポインタ
0x0804903c      mov     edi, dword [arg_8h] ; 第2引数の文字列の先頭ポインタ
0x0804903f      add     esp, 0xfffffff4
0x08049042      push    esi           ; int32_t arg_4h
0x08049043      call    string_length ; sym.string_length
0x08049048      mov     ebx, eax      ; 文字列長をebxに格納
0x0804904a      add     esp, 0xfffffff4
0x0804904d      push    edi           ; int32_t arg_4h
0x0804904e      call    string_length ; sym.string_length
0x08049053      cmp     ebx, eax      ; 文字列長を比較
0x08049055      je      0x8049060
0x08049057      mov     eax, 1        ; 文字列長が不一致の場合,戻り値1として関数終了
0x0804905c      jmp     0x804907f
0x0804905e      mov     esi, esi

0x08049060      mov     edx, esi      ; 第1引数の文字列の先頭ポインタ
0x08049062      mov     ecx, edi      ; 第2引数の文字列の先頭ポインタ
0x08049064      cmp     byte [edx], 0 ; NUL文字で終了
0x08049067      je      0x804907d
0x08049069      lea     esi, [esi]
0x08049070      mov     al, byte [edx]
0x08049072      cmp     al, byte [ecx] ; 文字を比較して不等の場合,戻り値1として関数終了
0x08049074      jne     0x8049057
0x08049076      inc     edx ; 第1引数の文字列を指すポインタを1増加
0x08049077      inc     ecx ; 第2引数の文字列を指すポインタを1増加
0x08049078      cmp     byte [edx], 0 ; NUL文字で終了
0x0804907b      jne     0x8049070
0x0804907d      xor     eax, eax      ; 戻り値0

0x0804907f      lea     esp, [var_1ch]
0x08049082      pop     ebx
0x08049083      pop     esi
0x08049084      pop     edi
0x08049085      mov     esp, ebp
0x08049087      pop     ebp
0x08049088      ret
0x08049089      lea     esi, [esi]

string_length

まず,引数(arg_4h)は文字列の先頭ポインタを受け取る.
次に,戻り値eaxを0で初期化する.戻り値eaxは引数の文字列長である.

1文字目がNUL文字の場合は関数の後方までジャンプし,0を返して終了する.
それ以外の場合,ループが実行される.

  • 文字列の位置を指すポインタedxを1増加させる.
  • 戻り値であり,文字列長のeaxを1増加させる.
  • edxのポインタが指す値がNUL文字ならループを終了する

よって,本関数は引数で与えられた文字列の長さを返す関数である.

コード
string_length(int32_t arg_4h);
; arg int32_t arg_4h @ stack + 0x4
0x08049018      push    ebp
0x08049019      mov     ebp, esp
0x0804901b      mov     edx, dword [arg_4h] ; 引数の文字列の先頭ポインタ
0x0804901e      xor     eax, eax            ; 文字列の長さ,初期値0
0x08049020      cmp     byte [edx], 0       ; \0であれば関数終了
0x08049023      je      0x804902c
0x08049025      inc     edx           ; 次の文字を参照
0x08049026      inc     eax           ; 文字列の長さ+1
0x08049027      cmp     byte [edx], 0 ; \0であれば関数終了
0x0804902a      jne     0x8049025
0x0804902c      mov     esp, ebp
0x0804902e      pop     ebp
0x0804902f      ret

Phase 2

はじめにread_six_numbersが実行され,ユーザが入力した6つの整数を配列に格納する.

まず,配列の0番目に格納された値は1でないと,explode_bombが実行される.
次に,ebxをループ変数とするループが実行される.

  • eaxebx+1が代入される.
  • eaxと配列のebx-1番目の値を乗算する.
  • 乗算結果が配列のebx番目と等しいなら,ループする.等しくなければexplode_bombが実行される.

配列[] = {A, B, C, D, E, F};とする.

ebx eax x ebx-1番目の数 = ebx番目の数
1 2 x A = B
2 3 x B = C
3 4 x C = D
4 5 x D = E
5 6 x E = F

配列の0番目Aは1であることが分かっているため,残りのB~Fを求めると以下の通りである.

ebx eax x ebx-1番目の数 = ebx番目の数
1 2 x 1 = 2
2 3 x 2 = 6
3 4 x 6 = 24
4 5 x 24 = 120
5 6 x 120 = 720

よって,回答は1 2 6 24 120 720である.

read_six_numbers

read_six_numbersにはユーザの入力した文字列と,配列の先頭ポインタ(var_8h)が渡される.
次に,sscanfを実行しており,入力された数字を配列の各要素に格納している.
sscanfは戻り値に変換できた数を返すため,6つ変換できたかを確認している.

コード
read_six_numbers(const char *s, int arg_8h);
; arg const char *s @ stack + 0x4
; arg int arg_8h @ stack + 0x8
0x08048fd8      push    ebp
0x08048fd9      mov     ebp, esp
0x08048fdb      sub     esp, 8
0x08048fde      mov     ecx, dword [s]
0x08048fe1      mov     edx, dword [arg_8h]

; read_six_numbers("入力された文字列", numbers);
; sscanf("入力された文字列", "%d %d %d %d %d %d", (省略));
; 左から順にedx, edx+0x4, ..., edx+14
0x08048fe4      lea     eax, [edx + 0x14]
0x08048fe7      push    eax
0x08048fe8      lea     eax, [edx + 0x10]
0x08048feb      push    eax
0x08048fec      lea     eax, [edx + 0xc]
0x08048fef      push    eax
0x08048ff0      lea     eax, [edx + 8]
0x08048ff3      push    eax
0x08048ff4      lea     eax, [edx + 4]
0x08048ff7      push    eax
0x08048ff8      push    edx
0x08048ff9      push    str.d__d__d__d__d__d ; 0x8049b1b ; const char *format
0x08048ffe      push    ecx        ; const char *s
0x08048fff      call    sscanf     ; sym.imp.sscanf ; int sscanf(const char *s, const char *format, va_list args)
0x08049004      add     esp, 0x20
0x08049007      cmp     eax, 5     ; 変換できた数が5つより大きい(6つ読み取れた)場合はbombが爆発しない
0x0804900a      jg      0x8049011
0x0804900c      call    explode_bomb ; sym.explode_bomb
0x08049011      mov     esp, ebp
0x08049013      pop     ebp
0x08049014      ret
0x08049015      lea     esi, [esi]
sscanf("入力された文字列", "%d %d %d %d %d %d", 配列の0番目のアドレス,配列の1番目のアドレス, ..., 配列の5番目のアドレス);

Phase 3

Phase 3ではsscanfで,整数,文字,整数を読み取っている.順に,[var_10h][var_9h][var_8h]に格納される.
次に,1つ目の整数が7より大きい場合は0x0804 8bd6番地にジャンプする.
7以下の場合eax[var_10h]が格納され,jmp dowrd [eax*4 + data.080597e8]でジャンプする.data.080597e8にはアドレス値が連続で格納されており,1つ目の整数によってジャンプ先が変わる仕掛けになっている.これはswitch-case文の仕組みである.

1つ目の整数が0の場合(Cだとcase 0:)の場合である.

  • blに文字qを格納する.
  • 1つ目の整数が777と等しいかチェックする.
    他も同様の事を実施しているので省略する.

最後にblと中央の文字が等しいかをチェックしている.
よって,回答は以下のいずれか各行である.(例えば,0 q 777)

1つ目の整数 中央の文字 2つ目の整数 2つ目の整数(10進数)
0 q 0x309 777
1 b 0xd6 214
2 b 0x2f3 755
3 k 0xfb 251
4 o 0xa0 160
5 t 0x1ca 458
6 v 0x30c 780
7 b 0x20c 524
コード
; var int32_t var_1ch @ stack - 0x1c
; var int var_10h @ stack - 0x10
; var char var_9h @ stack - 0x9
; var uint_fast32_t var_8h @ stack - 0x8
; arg const char *s @ stack + 0x4
0x08048b98      push    ebp
0x08048b99      mov     ebp, esp
0x08048b9b      sub     esp, 0x14
0x08048b9e      push    ebx
0x08048b9f      mov     edx, dword [s]
0x08048ba2      add     esp, 0xfffffff4
0x08048ba5      lea     eax, [var_8h]
0x08048ba8      push    eax
0x08048ba9      lea     eax, [var_9h]
0x08048bac      push    eax
0x08048bad      lea     eax, [var_10h]
0x08048bb0      push    eax
0x08048bb1      push    str.d__c__d ; 0x80497de ; const char *format
0x08048bb6      push    edx        ; const char *s
0x08048bb7      call    sscanf     ; sym.imp.sscanf ; int sscanf(const char *s, const char *format, va_list args)
0x08048bbc      add     esp, 0x20
0x08048bbf      cmp     eax, 2     ; 2
0x08048bc2      jg      0x8048bc9
0x08048bc4      call    explode_bomb ; sym.explode_bomb
0x08048bc9      cmp     dword [var_10h], 7 ; 1つ目の整数が7の場合,default
0x08048bcd      ja      case.default.switch.0x08048bd6
0x08048bd3      mov     eax, dword [var_10h]
;-- switch
0x08048bd6      jmp     dword [eax*4 + data.080497e8] ; 0x80497e8 ; switch table (8 cases) at 0x80497e8
0x08048bdd      lea     esi, [esi]
;-- case 0:                        ; from 0x8048bd6
0x08048be0      mov     bl, 0x71              ; blに文字qを代入
0x08048be2      cmp     dword [var_8h], 0x309 ; 2つ目の整数が777かチェック
0x08048be9      je      0x8048c8f
0x08048bef      call    explode_bomb ; sym.explode_bomb
0x08048bf4      jmp     0x8048c8f  ; sym.phase_3+0xf7
0x08048bf9      lea     esi, [esi]
;-- case 1:                        ; from 0x8048bd6

(省略)

0x08048c8f      cmp     bl, byte [var_9h] ; 中央の文字が各case文でblに格納した文字と等しいかチェック
0x08048c92      je      0x8048c99
0x08048c94      call    explode_bomb ; sym.explode_bomb
0x08048c99      mov     ebx, dword [var_1ch]
0x08048c9c      mov     esp, ebp
0x08048c9e      pop     ebp
0x08048c9f      ret

Phase 4

Phase 4ではssancfで整数を1つ読み込んでいる.整数が2つ以上入力されたり,整数が0以下の場合はexplode_bombが実行されて失敗する.
読み込んだ整数は,func4の引数に渡されている.そして戻り値が55(=0x37)であればクリアである.
func4はフィボナッチ数を求める関数であるため,Phase 4の回答は9である.

func4の動作がメインのためphase_4の内容は省略.

func4

func4は自分自身をcallしており,再帰関数であることがわかる.

  • 引数ebxが1以下の場合,戻り値として1を返す.
  • 引数ebxが1より大きい場合,1引いた値を自分自身に渡している.さらに,2引いた値を自分自身に渡している.
    それぞれの戻り値はesieaxに格納され,最後に加算している.
    従って,func4は以下の通り表せる.
\textrm{func4(ebx)}=\begin{cases} \textrm{func4(ebx-1)+func4(ebx-2)} & \textrm{ebx > 1}\\1 & \textrm{otherwise}\end{cases}

すなわちfunc4はフィボナッチ数を求める関数である.
※ 0, 1, 1, ...ではなく1, 2, 3, ...から始まる.

コード
func4(va_list arg_4h);
; var int32_t var_1ch @ stack - 0x1c
; arg va_list arg_4h @ stack + 0x4
0x08048ca0      push    ebp
0x08048ca1      mov     ebp, esp
0x08048ca3      sub     esp, 0x10
0x08048ca6      push    esi
0x08048ca7      push    ebx
0x08048ca8      mov     ebx, dword [arg_4h]

0x08048cab      cmp     ebx, 1     ; 引数が1以下の時,1を返す
0x08048cae      jle     0x8048cd0
0x08048cb0      add     esp, 0xfffffff4

; esi = func4(引数-1)
0x08048cb3      lea     eax, [ebx - 1]
0x08048cb6      push    eax        ; va_list arg_4h
0x08048cb7      call    func4
0x08048cbc      mov     esi, eax
0x08048cbe      add     esp, 0xfffffff4

; eax = func4(引数-2)
0x08048cc1      lea     eax, [ebx - 2]
0x08048cc4      push    eax        ; va_list arg_4h
0x08048cc5      call    func4

;  eax + esi = func4(引数-2) + func4(引数-1)
0x08048cca      add     eax, esi
0x08048ccc      jmp     0x8048cd5
0x08048cce      mov     esi, esi
0x08048cd0      mov     eax, 1
0x08048cd5      lea     esp, [var_1ch]
0x08048cd8      pop     ebx
0x08048cd9      pop     esi
0x08048cda      mov     esp, ebp
0x08048cdc      pop     ebp
0x08048cdd      ret
0x08048cde      mov     esi, esi

Phase 5

はじめにstring_lengthで入力された文字列長が6と等しいかチェックしている.
続いてisrveawhobpnutfgという文字列が定義される.
さらにedxをループ変数とするループで次の順番に処理を行っている.

  • 入力された文字を取り出してalに格納する.
  • 文字と0xfとのAND演算を行う(al &= 0xf).
  • isrveawhobpnutfgal番目の文字を,var_ch(ecx)のポインタに順番に格納する.

最後に,var_ch(ecx)の指す文字列がgiantsと等しければクリアとなる.

入力値(al) al &= 0xf isrv...のN番目 isrv...のN番目の文字
'0' al &= 0xf 0 i
'1' al &= 0xf 1 s
al &= 0xf 2 r
al &= 0xf 3 v
al &= 0xf 4 e
'5' al &= 0xf 5 a
al &= 0xf 6 w
al &= 0xf 7 h
al &= 0xf 8 o
al &= 0xf 9 b
al &= 0xf 10 p
';' al &= 0xf 11 n
al &= 0xf 12 u
'=' al &= 0xf 13 t
al &= 0xf 14 f
'?' al &= 0xf 15 g

よって,回答を ?05;=1 とすることでisrv...からgiantsを取り出せる.

コード
phase_5(int32_t arg_4h);
; var int32_t var_1ch @ stack - 0x1c
; var int32_t var_ch @ stack - 0xc
; var int32_t var_6h @ stack - 0x6
; arg int32_t arg_4h @ stack + 0x4
0x08048d2c      push    ebp
0x08048d2d      mov     ebp, esp
0x08048d2f      sub     esp, 0x10
0x08048d32      push    esi
0x08048d33      push    ebx
0x08048d34      mov     ebx, dword [arg_4h] ; 入力された文字列の先頭アドレス
0x08048d37      add     esp, 0xfffffff4
0x08048d3a      push    ebx           ; int32_t arg_4h
0x08048d3b      call    string_length ; sym.string_length
0x08048d40      add     esp, 0x10
0x08048d43      cmp     eax, 6        ; 入力された文字列が6文字かチェック
0x08048d46      je      0x8048d4d
0x08048d48      call    explode_bomb  ; sym.explode_bomb

0x08048d4d      xor     edx, edx       ; ループ変数 edx = 0
0x08048d4f      lea     ecx, [var_ch]  ; ecx = var_ch(ある配列の先頭アドレス)
0x08048d52      mov     esi, array.123 ; 文字列"isrveawhobpnutfg"の先頭アドレス

0x08048d57      mov     al, byte [edx + ebx] ; 入力された文字列のedx番目の文字をalに格納
0x08048d5a      and     al, 0xf              ; al &= 0xf
0x08048d5c      movsx   eax, al              ; eax = al
0x08048d5f      mov     al, byte [eax + esi] ; 文字列"isr..."の先頭からal(eax)番目の文字をalに格納
0x08048d62      mov     byte [edx + ecx], al ; ある配列var_chに順番に格納
0x08048d65      inc     edx                  ; edx++
0x08048d66      cmp     edx, 5     ; 5       ; 6回ループ(0から5)
0x08048d69      jle     0x8048d57

; ある配列に格納した文字列が,giantsと等しければクリア
0x08048d6b      mov     byte [var_6h], 0
0x08048d6f      add     esp, 0xfffffff8
0x08048d72      push    str.giants ; 0x804980b ; int32_t arg_8h
0x08048d77      lea     eax, [var_ch]
0x08048d7a      push    eax        ; int32_t arg_4h
0x08048d7b      call    strings_not_equal ; sym.strings_not_equal
0x08048d80      add     esp, 0x10
0x08048d83      test    eax, eax
0x08048d85      je      0x8048d8c
0x08048d87      call    explode_bomb ; sym.explode_bomb
0x08048d8c      lea     esp, [var_1ch]
0x08048d8f      pop     ebx
0x08048d90      pop     esi
0x08048d91      mov     esp, ebp
0x08048d93      pop     ebp
0x08048d94      ret
0x08048d95      lea     esi, [esi]

Phase 6

Phase 6は長いため分割する.

値のチェック

はじめにread_six_numbersで6つの整数を読み取っている.次の二重ループで値の整合性をチェックしている.

  • 外側のループ開始
    • 整数を順に参照し,1減じた値が5以下であることをチェック
    • 6回分ループしたら内側のループをスキップ
    • 内側のループ開始
      • 外側のループで参照している整数が,他の整数と異なることをチェック
    • 内側のループ終了
  • 外側のループ終了

つまり,入力値は6以下かつ他の入力値と異なる必要がある.

コード
phase_6(int32_t arg_4h);
; var int32_t var_5ch @ stack - 0x5c
; var int32_t var_40h @ stack - 0x40
; var int32_t var_3ch @ stack - 0x3c
; var int32_t var_38h @ stack - 0x38
; var int32_t var_34h @ stack - 0x34
; var int32_t var_1ch @ stack - 0x1c
; arg int32_t arg_4h @ stack + 0x4
0x08048d98      push    ebp
0x08048d99      mov     ebp, esp
0x08048d9b      sub     esp, 0x4c
0x08048d9e      push    edi
0x08048d9f      push    esi
0x08048da0      push    ebx
0x08048da1      mov     edx, dword [arg_4h]
0x08048da4      mov     dword [var_38h], 0x804b26c ; obj.node1
0x08048dab      add     esp, 0xfffffff8
0x08048dae      lea     eax, [var_1ch]
0x08048db1      push    eax        ; int arg_8h  ; 6つの整数を格納する配列の先頭アドレス
0x08048db2      push    edx        ; const char *s (ユーザが入力した文字列)
0x08048db3      call    read_six_numbers ; sym.read_six_numbers
; edi = 0;
0x08048db8      xor     edi, edi
0x08048dba      add     esp, 0x10
0x08048dbd      lea     esi, [esi]

; edi = 0; edi <= 5; edi++;
0x08048dc0      lea     eax, [var_1ch]           ; 6つの整数を格納する配列の先頭アドレス
0x08048dc3      mov     eax, dword [eax + edi*4] ; edi番目の整数を順に取り出す
0x08048dc6      dec     eax                      ; 整数を1減ずる
0x08048dc7      cmp     eax, 5     ; 5           ; 5以下であれば爆発しない
0x08048dca      jbe     0x8048dd1
0x08048dcc      call    explode_bomb ; sym.explode_bomb
0x08048dd1      lea     ebx, [edi + 1]           ; 内側のループの初期値
0x08048dd4      cmp     ebx, 5     ; 5
0x08048dd7      jg      0x8048dfc
0x08048dd9      lea     eax, [edi*4]
0x08048de0      mov     dword [var_3ch], eax     ; edi番目を[var_3ch]に格納
0x08048de3      lea     esi, [var_1ch]

; ebx = edi + 1; edi <= 5; edi++;
0x08048de6      mov     edx, dword [var_3ch]     ; edi番目
0x08048de9      mov     eax, dword [edx + esi]   ; edi番目以降の値をループで取り出して
0x08048dec      cmp     eax, dword [esi + ebx*4] ; 等しくないことをチェック
0x08048def      jne     0x8048df6
0x08048df1      call    explode_bomb ; sym.explode_bomb
0x08048df6      inc     ebx
0x08048df7      cmp     ebx, 5     ; 5
0x08048dfa      jle     0x8048de6

0x08048dfc      inc     edi
0x08048dfd      cmp     edi, 5     ; 5
0x08048e00      jle     0x8048dc0 

ポインタ配列

続いて,ポインタ配列の先頭を指すvar_34h[var_40h]に格納する.
最初のループでは,6つの整数を格納する配列を参照する.

  • 外側のループ開始
    • [var_38h]に格納されたアドレスをesiに格納する.
    • 入力値が,1未満であれば内側のループをスキップ
    • 内側のループ開始(入力値-1回ループ)
      • esiのアドレスに8加えたアドレス」に格納された値をesiに格納する.
    • 内側のループ終了
    • [var_40h]に格納したポインタ配列の先頭アドレスvar_34hを参照し,esiに格納したアドレスを格納する.
  • 外側のループ終了

[var_38h]の値は,phase_6の冒頭0x08048da行目にて,0x804b26cの値が格納されている.

...
;-- node2:
0x0804b260      .dword 0x000002d5 ; esi ← esi(0x0804b260) + 8 (入力値が2の時)
0x0804b264      .dword 0x00000002
0x0804b268      .dword 0x0804b254 ; obj.node3
;-- node1:
0x0804b26c      .dword 0x000000fd ; ← [var_38h]
0x0804b270      .dword 0x00000001
0x0804b274      .dword 0x0804b260 ; esi ← [var_38h] + 8 (入力値が1の時)
...

外側のループでは,6つの入力値を順に参照する.内側のループでは(入力値-1)回分だけループを繰り返し,esi+8のアドレスを,以下の順に取り出す.

  • 入力値が1の場合,内側のループは実行されない.
    • esi = 0x0804b26c
  • 入力値が2の場合,内側のループは1回だけ実行される.
    • 0x0804b274 = 0x0804b26c + 8
    • esi = 0x0804b274に格納された値0x0804b260
  • 入力値が3の場合,内側のループは2回だけ実行される.
    • 0x0804b274 = 0x0804b26c + 8
    • esi = 0x0804b274に格納された値0x0804b260
    • 0x0804b268 = 0x0804b260 + 8
    • esi = 0x0804b268に格納された値0x0804b254

最後に,[var_40h]が先頭の配列にesiに格納されたアドレスが指す値が格納される.まとめると次の表になる.

入力値 esi+8の値 esi+8に格納されたアドレス esi+8に格納されたアドレス」のアドレスが指す値
1 - 0x0804b26c 0x000000fd
2 0x0804b268 0x0804b260 0x000002d5
3 0x0804b25c 0x0804b254 0x0000012d
4 0x0804b250 0x0804b248 0x000003e5
5 0x0804b244 0x0804b23c 0x000000d4
6 0x0804b238 0x0804b230 0x000001b0
コード
0x08048e02      xor     edi, edi             ; edi = 0;
0x08048e04      lea     ecx, [var_1ch]       ; 入力配列の先頭アドレス
0x08048e07      lea     eax, [var_34h]       ; ポインタ配列の先頭アドレス
0x08048e0a      mov     dword [var_40h], eax ; [ver_40h]にポインタ配列の先頭アドレスを格納
0x08048e0d      lea     esi, [esi]

; edi = 0; edi <= 5; edi++;
0x08048e10      mov     esi, dword [var_38h] ; esi = [var_38h]
0x08048e13      mov     ebx, 1               ; ebx = 1
0x08048e18      lea     eax, [edi*4]         ; edi番目
0x08048e1f      mov     edx, eax
0x08048e21      cmp     ebx, dword [eax + ecx] ; 「1 >= 入力配列のedi番目の値」ならループの直後にジャンプ
0x08048e24      jge     0x8048e38
0x08048e26      mov     eax, dword [edx + ecx] ; 入力配列のedi番目の値
0x08048e29      lea     esi, [esi]

; ebx = 1; ebx < 配列のedi番目の値; ebx++;
0x08048e30      mov     esi, dword [esi + 8] ;
0x08048e33      inc     ebx
0x08048e34      cmp     ebx, eax
0x08048e36      jl      0x8048e30

0x08048e38      mov     edx, dword [var_40h]     ; アドレスvar_34hを格納
0x08048e3b      mov     dword [edx + edi*4], esi ; var_34hからedi番目に上のループの結果を格納
0x08048e3e      inc     edi
0x08048e3f      cmp     edi, 5     ; 5
0x08048e42      jle     0x8048e10

0x08048e44      mov     esi, dword [var_34h] ; ポインタ配列の先頭に格納されたアドレス
0x08048e47      mov     dword [var_38h], esi

0x08048e4a      mov     edi, 1         ; edi = 1
0x08048e4f      lea     edx, [var_34h] ; ポインタ配列の先頭
; edi = 1; edi <= 5; edi++;
0x08048e52      mov     eax, dword [edx + edi*4] ; ポインタ配列から順にアドレスを取り出す
0x08048e55      mov     dword [esi + 8], eax     ; (アドレス+8)に格納
0x08048e58      mov     esi, eax
0x08048e5a      inc     edi
0x08048e5b      cmp     edi, 5     ; 5
0x08048e5e      jle     0x8048e52  ;
`[var_38h]`のアドレス
;-- node6:
0x0804b230      .dword 0x000001b0
0x0804b234      .dword 0x00000006
0x0804b238      .dword 0x00000000
;-- node5:
0x0804b23c      .dword 0x000000d4
0x0804b240      .dword 0x00000005
0x0804b244      .dword 0x0804b230 ; obj.node6
;-- node4:
0x0804b248      .dword 0x000003e5
0x0804b24c      .dword 0x00000004
0x0804b250      .dword 0x0804b23c ; obj.node5
;-- node3:
0x0804b254      .dword 0x0000012d
0x0804b258      .dword 0x00000003
0x0804b25c      .dword 0x0804b248 ; obj.node4
;-- node2:
0x0804b260      .dword 0x000002d5 
0x0804b264      .dword 0x00000002
0x0804b268      .dword 0x0804b254 ; obj.node3 
;-- node1:
0x0804b26c      .dword 0x000000fd ; ← var_38h
0x0804b270      .dword 0x00000001
0x0804b274      .dword 0x0804b260 ; obj.node2

判定

最後に,[var_38h]の値をループで取り出している.そして,現在参照している値が次の値よりも大きいかを判定している.つまり,6つの整数を入力することで,先ほどの表の最右列の値を[var_38h]の配列に格納し,その値が降順に並んでいることでクリアになる.

入力値 esi+8の値 esi+8に格納されたアドレス esi+8に格納されたアドレス」のアドレスが指す値
1 - 0x0804b26c 0x000000fd
2 0x0804b268 0x0804b260 0x000002d5
3 0x0804b25c 0x0804b254 0x0000012d
4 0x0804b250 0x0804b248 0x000003e5
5 0x0804b244 0x0804b23c 0x000000d4
6 0x0804b238 0x0804b230 0x000001b0

表より,以下の順序で入力すれば,値が降順に並ぶ.

入力配列 4 2 6 3 1 5
ポインタ配列 0x0804b248 0x0804b260 0x0804b230 0x0804b254 0x0804b26c 0x0804b23c
上のアドレスが指す値 0x03e5 0x02d5 0x01b0 0x012d 0x00fd 0x00d4 ← 降順

よって,回答は4 2 6 3 1 5である.

コード
0x08048e60      mov     dword [esi + 8], 0   ; 0
0x08048e67      mov     esi, dword [var_38h] ; esi = dataポインタ
0x08048e6a      xor     edi, edi             ; edi = 0;
0x08048e6c      lea     esi, [esi]

; edi = 0; edi <= 4; edi++;
0x08048e70      mov     edx, dword [esi + 8] ; esi+8番目に格納された値
0x08048e73      mov     eax, dword [esi]     ; esi番目に格納された値
0x08048e75      cmp     eax, dword [edx]     ; esi+8番目のアドレスの値の方が大きいか?
0x08048e77      jge     0x8048e7e
0x08048e79      call    explode_bomb ; sym.explode_bomb
0x08048e7e      mov     esi, dword [esi + 8]
0x08048e81      inc     edi
0x08048e82      cmp     edi, 4     ; 4
0x08048e85      jle     0x8048e70

0x08048e87      lea     esp, [var_5ch] ;
0x08048e8a      pop     ebx
0x08048e8b      pop     esi
0x08048e8c      pop     edi
0x08048e8d      mov     esp, ebp
0x08048e8f      pop     ebp
0x08048e90      ret
0x08048e91      lea     esi, [esi]

Discussion