DEFCON CTF 2009 Binary 300

The answer is the 16 byte decryption key used in the file linked here.
Enter in the form: \xaa\xbb\xcc...
http://shallweplayaga.me/binary/4b2c8b145e30208d00d7ddebd7034560

リンクにあるファイルに使われている16バイトの復号鍵(\xaa\xbb\xcc...)を答えよ
という問題文と共にバイナリファイルが渡される

$ file 4b2c8b145e30208d00d7ddebd7034560
4b2c8b145e30208d00d7ddebd7034560: MS-DOS executable PE 
for MS Windows (GUI) Intel 80386 32-bit

PEフォーマット、つまりWindowsの実行ファイル
IDAPro、OllyDbg(WinDbg)を使って解析していく
まず普通に実行するとhelloworld.txtというファイルが作成される
ファイルの中身は"Hello world!"だけ
次にOllyDbg上で実行すると、例外で終了する(helloworld.txtは作成されない)
どうやらアンチデバッグ技術が使われている

.text:0040110A start proc near
.text:0040110A    push    ebp
.text:0040110B    mov     ebp, esp
.text:0040110D    sub     esp, 1Ch        ; dwExitCode
.text:00401110    mov     esi, 28F9h
.text:00401115    call    ds:GetCommandLineA
.text:0040111B    mov     esi, eax
.text:0040111D    sub     ebx, esi
.text:0040111F    add     eax, 54B5h
.text:00401125    lea     edi, byte_4146C9
.text:0040112B    xor     esi, 5317h
.text:00401131    mov     esi, offset sub_4010AC
.text:00401136    call    esi ; sub_4010AC
.text:00401138    call    ds:ExitThread
.text:0040113E    retn

ポイントはExitThreadの直前で呼ばれているsub_4010AC関数

.text:004010AC sub_4010AC proc near
.text:004010AC var_4 = dword ptr -4
.text:004010AC
.text:004010AC    lea     edx, [edx]
.text:004010AE    jmp     short loc_4010B2
.text:004010B0    db 75h, 0Fh
.text:004010B2 loc_4010B2:
.text:004010B2    mov     ecx, 30h
.text:004010B7    jmp     short loc_4010BB
.text:004010B9    jnz     short loc_4010C5
.text:004010BB loc_4010BB:
.text:004010BB    jmp     short loc_4010BF
.text:004010BD    jz      short loc_4010F1
.text:004010BF loc_4010BF:
.text:004010BF    mov     eax, fs:[ecx]
.text:004010C2    add     bh, 0
.text:004010C5 loc_4010C5:
.text:004010C5    mov     bl, al
.text:004010C7    mov     edx, 20h
.text:004010CC    jmp     short loc_4010D0
.text:004010CE    pop     es
.text:004010CF    sahf
.text:004010D0 loc_4010D0:
.text:004010D0    jmp     short loc_4010D4
.text:004010D2    dw 4375h
.text:004010D4 loc_4010D4:
.text:004010D4    add     eax, edx
.text:004010D6    add     bl, cl
.text:004010D8    jmp     short loc_4010DC
.text:004010DA    jnz     short start
.text:004010DC loc_4010DC:
.text:004010DC    push    offset loc_46367F
.text:004010E1    mov     edx, [esp+4+var_4]
.text:004010E4    add     esp, 4
.text:004010E7    jmp     short loc_4010EB
.text:004010E9    jnz     short loc_4010F1
.text:004010EB loc_4010EB:
.text:004010EB    jmp     short loc_4010EF
.text:004010ED    db 0D2h, 84h
.text:004010EF loc_4010EF:
.text:004010EF    mov     ecx, [eax]
.text:004010F1 loc_4010F1:
.text:004010F1    jmp     short loc_4010F4
.text:004010F3    push    edx
.text:004010F4 loc_4010F4:
.text:004010F4    add     bh, bl
.text:004010F6    mov     ds:4EFFEAh, ecx
.text:004010FC    not     ebx
.text:004010FE    mov     [eax], edx
.text:00401100    jmp     short loc_401104
.text:00401102    dw 0E74h
.text:00401104 loc_401104:
.text:00401104    add     bh, bl
.text:00401106    retn

良い感じに難読化されている
jmpを削って難読化を解くと以下になる

.text:004010AC sub_4010AC proc near
.text:004010AC var_4 = dword ptr -4
.text:004010AC
.text:004010AC    lea     edx, [edx]
.text:004010AE
.text:004010B0
.text:004010B2
.text:004010B2    mov     ecx, 30h
.text:004010B7
.text:004010B9
.text:004010BB
.text:004010BB
.text:004010BD
.text:004010BF
.text:004010BF    mov     eax, fs:[ecx]
.text:004010C2    add     bh, 0
.text:004010C5
.text:004010C5    mov     bl, al
.text:004010C7    mov     edx, 20h
.text:004010CC
.text:004010CE
.text:004010CF
.text:004010D0
.text:004010D0
.text:004010D2
.text:004010D4
.text:004010D4    add     eax, edx
.text:004010D6    add     bl, cl
.text:004010D8
.text:004010DA
.text:004010DC
.text:004010DC    push    offset loc_46367F
.text:004010E1    mov     edx, [esp+4+var_4]
.text:004010E4    add     esp, 4
.text:004010E7
.text:004010E9
.text:004010EB
.text:004010EB
.text:004010ED
.text:004010EF
.text:004010EF    mov     ecx, [eax]
.text:004010F1
.text:004010F1
.text:004010F3 
.text:004010F4
.text:004010F4    add     bh, bl
.text:004010F6    mov     ds:4EFFEAh, ecx
.text:004010FC    not     ebx
.text:004010FE    mov     [eax], edx
.text:00401100
.text:00401102
.text:00401104
.text:00401104    add     bh, bl
.text:00401106    retn

さらに読みやすく

.text:004010AC sub_4010AC proc near
.text:004010AC var_4 = dword ptr -4
.text:004010AC
.text:004010AC    lea     edx, [edx]
.text:004010B2    mov     ecx, 30h
.text:004010BF    mov     eax, fs:[ecx]      // eax = fs:[30]
.text:004010C2    add     bh, 0
.text:004010C5    mov     bl, al
.text:004010C7    mov     edx, 20h           // edx = 20h
.text:004010D4    add     eax, edx           // eax += edx(20h)
.text:004010D6    add     bl, cl
.text:004010DC    push    offset loc_46367F  // 
.text:004010E1    mov     edx, [esp+4+var_4] // 
.text:004010E4    add     esp, 4             // edx = loc_46367F
.text:004010EF    mov     ecx, [eax]         // ecx = [eax]
.text:004010F4    add     bh, bl
.text:004010F6    mov     ds:4EFFEAh, ecx    // ds:4EFFEAh = ecx
.text:004010FC    not     ebx                
.text:004010FE    mov     [eax], edx         // [eax] = edx
.text:00401104    add     bh, bl
.text:00401106    retn

途中にebx(bl, bh, etc...)を操作する命令が入っているが、
これらは無意味なコード(難読化の一種?)であるため無視する
結果的にsub_4010ACの処理は以下の2行のみとなる

ds:4EFFEAh = *(fs:[30] + 20h);
(fs:[30] + 20h) = loc_46367F;

つまりfs:[30] + 20hのバックアップをds:4EFFEAhに置き、
fs:[30] + 20hをloc_46367Fで上書きする
OllyDbgにて004010FEにブレイクポイントをセット → 実行
その時点でのレジストリは以下

EAX 7FFDF020 <-
ECX 7C941000 ntdll.RtlEnterCriticalSection
EDX 0046367F <-
EBX 800AFECF
ESP 0006FFA0
EBP 0006FFC0
ESI 004010AC 4b2c8b14.004010AC
EDI 004146C9 4b2c8b14.004146C9
EIP 004010FE 4b2c8b14.004010FE
C 1  ES 0023 32bit 0(FFFFFFFF)
P 0  CS 001B 32bit 0(FFFFFFFF)
A 0  SS 0023 32bit 0(FFFFFFFF)
Z 0  DS 0023 32bit 0(FFFFFFFF)
S 0  FS 003B 32bit 7FFDE000(FFF)
T 0  GS 0000 NULL
D 0
O 0  LastErr ERROR_FILE_NOT_FOUND (00000002)

eaxの指すアドレスを参照する

7FFDF020  00 10 94 7C E0 10 94 7C 01 00 00 00 70 29 CF 77
7FFDF030  00 00 00 00

00 10 94 7C、つまり7C941000となっている
これをedx、つまり0046367Fに変更する
となると問題は、変更前の値(7C941000)と変更後の値(0046367F)
これ以降、00401138でExitThreadが呼ばれることを考えると
これらの値はExitThread内部で使われると考えられる
ちなみに変更前の値(7C941000)は、RtlEnterCriticalSectionのアドレス
つまり、この変更により
ExitThread内部でRtlEnterCriticalSectionが呼ばれるはずのところで
変更後の値(0046367F)が呼ばれることになる

.text:0046367F loc_46367F:
.text:0046367F    add     bh, bl
.text:00463681    or      ecx, ecx
.text:00463683    stc
.text:00463684    pop     eax
.text:00463685
.text:00463685 loc_463685:
.text:00463685    xor     ebx, ebx
.text:00463687    inc     eax
.text:00463688    jmp     short loc_46368B
.text:00463688
.text:0046368A    db 1Dh
.text:0046368B
.text:0046368B loc_46368B:
.text:0046368B    mov     edx, [eax]
.text:0046368D    sub     al, 0
.text:0046368F    and     edx, 0FFFFh
.text:00463695    jmp     short loc_463699
.text:00463695
.text:00463697    db 60h
.text:00463698    db 72h
.text:00463699
.text:00463699 loc_463699:
.text:00463699    xor     edx, 337Fh
.text:0046369F    mov     al, al
.text:004636A1    xor     edx, 0E280h
.text:004636A7    jnz     short loc_463685
.text:004636A9    not     bh
.text:004636AB    sar     edi, 0
.text:004636AE    mov     ecx, offset unk_4636B8
.text:004636B3    call    eax

004636B3にブレイクポイントをセットしてeaxとecxを確認

EAX 7C95310C ntdll.7C95310C    <-
ECX 004636B8 4b2c8b14.004636B8 <-
EDX 00000000
EBX 0000FF00
ESP 0006FE1C
EBP 0006FE58
ESI 7FFDE000
EDI 00000000
EIP 7C95310C ntdll.7C95310C
C 0  ES 0023 32bit 0(FFFFFFFF)
P 1  CS 001B 32bit 0(FFFFFFFF)
A 0  SS 0023 32bit 0(FFFFFFFF)
Z 1  DS 0023 32bit 0(FFFFFFFF)
S 0  FS 003B 32bit 7FFDE000(FFF)
T 0  GS 0000 NULL
D 0
O 0  LastErr ERROR_FILE_NOT_FOUND (00000002

call eaxでジャンプする先の7C95310Cの命令は以下

7C95310C   ? FFD1    CALL ECX 

call ecxなので結果的に004636B8へ進む

.text:004636B8 loc_4636B8:
.text:004636B8    pop     ebp
.text:004636B9    mov     esp, esp
.text:004636BB    xchg    eax, ebx
.text:004636BC    xchg    eax, ebx
.text:004636BD    mov     eax, large fs:30h
.text:004636C3    add     eax, 20h
.text:004636C6    mov     ebx, ds:4EFFEAh
.text:004636CC    mov     [eax], ebx
###########################################################
################## *(fs:[30] + 20h) = ds:4EFFEAh
###########################################################
.text:004636CE    jmp     short loc_4636D2
.text:004636CE
.text:004636D0    db 74h, 32h
.text:004636D2
.text:004636D2 loc_4636D2:
.text:004636D2    push    4EFFCAh
.text:004636D7    mov     eax, eax
.text:004636D9    push    40h
.text:004636DE    lea     ecx, [ecx]
.text:004636E0    push    776h
.text:004636E5    not     bh
.text:004636E7    push    463631h
.text:004636EC    jmp     short loc_4636F0
.text:004636EC
.text:004636EE    dw 10EFh
.text:004636F0
.text:004636F0 loc_4636F0:
.text:004636F0    xor     eax, eax
.text:004636F2    mov     edx, offset VirtualProtect
.text:004636F7    add     eax, edx
.text:004636F9    add     ebx, ecx
.text:004636FB    call    dword ptr [eax]
###########################################################
################## VirtualProtect(463631h, 776h, 40h, 4EFFCAh)
###########################################################
.text:004636FD    mov     bl, al
.text:004636FF    mov     al, 6Dh
.text:00463701    mov     al, bl
.text:00463703    mov     edx, 125h
.text:00463708    jmp     short loc_46370B
.text:00463708
.text:0046370A    db 15h
.text:0046370B
.text:0046370B loc_46370B:
.text:0046370B    mov     ecx, ds:4EFFCAh
.text:00463711    mov     eax, 46372Ah
.text:00463716
.text:00463716 loc_463716:
.text:00463716    jmp     short loc_46371A
.text:00463716
.text:00463718    db 74h, 12h
.text:0046371A
.text:0046371A loc_46371A:
.text:0046371A    xor     [eax+edx], cl
.text:0046371D    not     cl
.text:0046371F    not     cl
.text:00463721    add     byte ptr [eax+edx], 97h
.text:00463725    sar     ebx, 77h
.text:00463728    dec     edx
.text:00463729
.text:00463729 loc_463729:
.text:00463729    jnz     short loc_463716
###########################################################
################## for(edx=125h; edx != 0; edx--){
##################     46372Ah[edx] ^= cl
##################     46372Ah[edx] += 97h
################## }
###########################################################
.text:0046372B    jz      short loc_463778

fs:[30] + 20hの値を元に戻し、VirtualProtectでメモリアクセス制限を解除
そして0046372B以降のコードを自己書き換え

0046372B     EB 02                  JMP SHORT 4b2c8b14.0046372F
0046372D     75 34                  JNZ SHORT 4b2c8b14.00463763
0046372F     09C0                   OR EAX,EAX
00463731     8D09                   LEA ECX,DWORD PTR DS:[ECX]
00463733     C1FF 00                SAR EDI,0
00463736     68 52374600            PUSH 4b2c8b14.00463752
0046373B     33C0                   XOR EAX,EAX
0046373D     64:FF30                PUSH DWORD PTR FS:[EAX]
00463740     64:8920                MOV DWORD PTR FS:[EAX],ESP
00463743     EB 01                  JMP SHORT 4b2c8b14.00463746
00463745     90                     NOP
00463746     FECB                   DEC BL
00463748     CD 2D                  INT 2D
0046374A     67:62FA                BOUND EDI,EDX
0046374D     B5 66                  MOV CH,66
0046374F     FA                     CLI
00463750     AB                     STOS DWORD PTR ES:[EDI]
00463751     90                     NOP
00463752     8AD8                   MOV BL,AL
00463754     8AC3                   MOV AL,BL
00463756     8B5424 04              MOV EDX,DWORD PTR SS:[ESP+4]

00463748にてint 2dアンチデバッグテクニックを使っている
int 2dアンチデバッグテクニックのフォーマットは以下

    push    offset _seh
    push    fs:[0]
    mov     fs:[0], esp
    int     2dh
    // debugger detected
_seh:
    // debugger not detected
  • デバッガを検知しなかった時に進むアドレスをスタックへ積む
  • fs:[0]の値をスタックへ積む
  • fs:[0]をesp(スタックトップ)に変更
  • int 2dh呼び出し

検知した場合はint 2dhのすぐ下の命令が実行され、
検知しなかった場合はスタックへ積んだアドレス以降が実行される
この問題の場合は、
デバッガを検知した場合は0046374AのBOUND EDI,EDXという命令が実行され
検知しなかった場合はスタックへ積まれている00463752以降が実行される

00463752     8AD8                   MOV BL,AL
00463754     8AC3                   MOV AL,BL
00463756     8B5424 04              MOV EDX,DWORD PTR SS:[ESP+4]
0046375A     EB 02                  JMP SHORT 4b2c8b14.0046375E
0046375C     90                     NOP
0046375D     90                     NOP
0046375E     8B12                   MOV EDX,DWORD PTR DS:[EDX]
00463760     C1E1 00                SHL ECX,0
00463763     F7D2                   NOT EDX
00463765     21C3                   AND EBX,EAX
00463767     C1EA 16                SHR EDX,16
0046376A     EB 02                  JMP SHORT 4b2c8b14.0046376E
0046376C     90                     NOP
0046376D     90                     NOP
0046376E     C1CA 16                ROR EDX,16
00463771     EB 02                  JMP SHORT 4b2c8b14.00463775
00463773     90                     NOP
00463774     90                     NOP
00463775     F7D2                   NOT EDX
00463777     FECB                   DEC BL
00463779     8BC4                   MOV EAX,ESP
0046377B     EB 02                  JMP SHORT 4b2c8b14.0046377F
0046377D     90                     NOP
0046377E     90                     NOP
0046377F     83C0 18                ADD EAX,18
00463782     BB 6534BEFF            MOV EBX,FFBE3465
00463787     33DA                   XOR EBX,EDX
00463789     8918                   MOV DWORD PTR DS:[EAX],EBX
0046378B     EB 02                  JMP SHORT 4b2c8b14.0046378F
0046378D     90                     NOP
0046378E     90                     NOP
0046378F     8AC0                   MOV AL,AL
00463791     CC                     INT3

今度はint 3hを使ったアンチデバッグ
無駄なコードを削除すると以下のようになる

00463752     8AD8                   MOV BL,AL
00463754     8AC3                   MOV AL,BL
00463756     8B5424 04              MOV EDX,DWORD PTR SS:[ESP+4]
0046375E     8B12                   MOV EDX,DWORD PTR DS:[EDX]
00463763     F7D2                   NOT EDX
00463767     C1EA 16                SHR EDX,16
0046376E     C1CA 16                ROR EDX,16
00463775     F7D2                   NOT EDX
00463779     8BC4                   MOV EAX,ESP
0046377F     83C0 18                ADD EAX,18
00463782     BB 6534BEFF            MOV EBX,FFBE3465
00463787     33DA                   XOR EBX,EDX
00463789     8918                   MOV DWORD PTR DS:[EAX],EBX
00463791     CC                     INT3

00463756に処理が来た時のスタックの状態は以下

0006FE14   0006FE48  Pointer to next SEH record
0006FE18   00463752  SE handler

アドレス00463752にある値は8AD88AC3であるため
edxには8AD88AC3が格納され、NOT、SHRRORなどが実行され、
アドレス00463789の時点でeax=esp+18h、ebx=00420F9Aとなる
つまり[esp+18h]に00420F9Aが格納される
int 3hは例外であるため、fs:[0](seh)に登録されたSE handlerが再び呼ばれる
fs:[0]に変更は無いため、再び00463752へ戻る
今度はスタックの状態が以下のようになっている

0006FA40   7C9432A8  RETURN to ntdll.7C9432A8
0006FA44   0006FB28
0006FA48   0006FE14
0006FA4C   0006FB48
0006FA50   0006FAFC
0006FA54   0006FE14  Pointer to next SEH record
0006FA58   7C9432BC  SE handler
0006FA5C   0006FE14

さらにfs:[0]は0006FA54になっている
つまりesp+18h(0006FA58)に00420F9Aを入れるとint 3h呼び出し時に
00420F9Aへ処理が飛ぶ
SEHに関する詳細はこちらが詳しい
http://programming.jugglershu.net/study/seh.html

0046379A     EB 02                  JMP SHORT 4b2c8b14.0046379E
0046379C     90                     NOP
0046379D     90                     NOP
0046379E     8BC4                   MOV EAX,ESP
004637A0     80F7 E0                XOR BH,0E0
004637A3     83C0 24                ADD EAX,24
004637A6     EB 02                  JMP SHORT 4b2c8b14.004637AA
004637A8     90                     NOP
004637A9     90                     NOP
004637AA     BB C9374600            MOV EBX,4b2c8b14.004637C9
004637AF     8918                   MOV DWORD PTR DS:[EAX],EBX
004637B1     03D8                   ADD EBX,EAX
004637B3     8B4424 0C              MOV EAX,DWORD PTR SS:[ESP+C]
004637B7     8990 B8000000          MOV DWORD PTR DS:[EAX+B8],EDX
004637BD     33C0                   XOR EAX,EAX
004637BF     C3                     RETN

0046379A以降にアンチデバッグ処理は無いため
この状態でF9(実行)するとデバッガ上でもhelloworld.txtが作成される
あとは単純にマシン語を解析していくだけだ
以降の解析は、int 2d以降の数バイトをNOPで埋めた状態で行う

00463748     CD 2D                  INT 2D
00463749     90                     NOP
0046374A     90                     NOP
0046374B     90                     NOP
0046374C     90                     NOP
0046374D     90                     NOP
0046374E     90                     NOP
0046374F     90                     NOP
00463750     90                     NOP
00463751     90                     NOP

いよいよ0046379A以降の解析であり、実際の問題文にある16バイトの鍵を探していく
0046379Aまで処理を進めてから、OllyDbgのメニューからView → Run Traceを選択
実行命令トレースウィンドウを表示
続いてOllyDbgのメニューからDebug → Trace intoを選択
これで0046379A以降の処理命令をすべてログとして保存する(数十分ほどかかる)
ファイルにも出力できるのでお好みでどうぞ
Traceしたデータをファイルへ出力すると500MB弱のテキストファイルが出来上がる

$ ls -l rtrace.txt
-rw-r--r-- 1 ubuntu ubuntu 486942599 2010-05-18 15:19 rtrace.txt
$ cat rtrace.txt | more
Address  Thread   Command                                   ; Registers and comments
0046379E Main     MOV EAX,ESP                               ; EAX=0006F66C
004637A0 Main     XOR BH,0E0                                ; EBX=0000E000
004637A3 Main     ADD EAX,24                                ; EAX=0006F690
004637A6 Main     JMP SHORT 4b2c8b14.004637AA
004637AA Main     MOV EBX,4b2c8b14.004637C9                 ; EBX=004637C9
004637AF Main     MOV DWORD PTR DS:[EAX],EBX
004637B1 Main     ADD EBX,EAX                               ; EBX=004D2E59
004637B3 Main     MOV EAX,DWORD PTR SS:[ESP+C]              ; EAX=0006F774
004637B7 Main     MOV DWORD PTR DS:[EAX+B8],EDX
004637BD Main     XOR EAX,EAX                               ; EAX=00000000
004637BF Main     RETN
7C9432A8 Main     MOV ESP,DWORD PTR FS:[0]
7C9432AF Main     POP DWORD PTR FS:[0]
7C9432B6 Main     MOV ESP,EBP
7C9432B8 Main     POP EBP                                   ; EBP=0006F73C
7C9432B9 Main     RETN 14
    Hardware breakpoint 3 at 4b2c8b14.004637C9
....

ログにはループ処理も回数分だけ含まれるため
実行したアドレス重複を排除する

$ cat del.rb
#/usr/local/bin/ruby
addrs = []
datas = []
fp = open("rtrace.txt")
while line = fp.gets
    flag = 1
    tmp = line.split(' ')[0]
    for n in 0 .. (addrs.length - 1)
        if(addrs[n] == tmp)
            flag = 0
            break
        end
    end
    if(flag == 1)
        addrs.push(line.split(' ')[0])
        datas.push(line)
    end
end
fp.close

print datas.join("\x0a")
$ ruby del.rb > data

この実行履歴の中から16(0x10)という値が使われている箇所を列挙する

$ strings data | grep ",10"
004A91D6 Main     ADD ESP,10
004A966F Main     MOV ECX,10                                ; ECX=00000010
004A959C Main     MOV EBX,1000                              ; EBX=00001000
ZwUnmapViewOfSect>MOV EAX,10B                               ; EAX=0000010B
7C94FBD7 Main     SHL EAX,10
009103E3 Main     CMP EAX,10
7C80E611 Main     TEST BYTE PTR DS:[EAX+9],10
7C954A5C Main     SHL EAX,10                                ; EAX=07D00000
7C955B46 Main     TEST BYTE PTR DS:[EAX+9],10
7C9556A8 Main     IMUL EDX,EDX,1003F
7C956778 Main     TEST BYTE PTR DS:[EAX+37],10
7C955F3F Main     TEST BYTE PTR DS:[ESI+37],10
7C952D2D Main     SHR ECX,10                                ; ECX=000007D0
7C9502E0 Main     CMP EDX,10000000
7C95034A Main     CMP CX,10B
7C8104A7 Main     MOV DWORD PTR DS:[EAX],10007
7C952D8D Main     SHL ECX,10                                ; ECX=000C0000
7C9411CE Main     OR DWORD PTR DS:[EAX+10],10

あとはこれらを順番にOllyDbgで確認する
最初の004A91D6はESPへの加算なのでおそらく関係ないだろう
次の004A966FはECXへの代入なので関係がありそうだ
004A966F辺りの命令は暗号化されており、
実行中に復号されるためソフトウェアブレイクだと上書きされる
よって004A966Fにハードウェアブレイクポイントをセットして実行する

(↓暗号化されているがハードウェアブレイクポイントをセット)
004A966F   F69E B5B4B345    NEG BYTE PTR DS:[ESI+45B3B4B5]
004A9675   FD               STD

004A966Fで処理が止まると以下のように復号されている

004A966F   B9 10000000      MOV ECX,10
004A9674   F3:A4            REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

16バイトのデータをコピーしている
esiの指す値を参照する

EAX 003E0000
ECX 7C809B49 kernel32.7C809B49
EDX 7C94E4F4 ntdll.KiFastSystemCallRet
EBX 7C809B02 kernel32.VirtualAllocEx
ESP 0006F694
EBP 0006F69C
ESI 004A9658 4b2c8b14.004A9658 <- 
EDI 003E000C
EIP 004A966F 4b2c8b14.004A966F

004A9658のメモリ空間を見る

004A9658  87 D3 4E 03 34 EE 56 87 1E 4C 86 67 1B 05 23 15
004A9668  8B 7D FC 83 C7 0C 5E B9 10 00 00 00 F3 A4 6A 04

"87 D3 4E 03 34 EE 56 87 1E 4C 86 67 1B 05 23 15"となっている
問題文にパスワードは\xaa\xbb\xcc...と書かれてあるので
"\x87\xd3\x4e\x03\x34\xee\x56\x87\x1e\x4c\x86\x67\x1b\x05\x23\x15"が答え?
ということで解答フォームに入力してみると、無事正解となった
ちなみに実際に解答のデータ(鍵)を使用して復号しているのは009103E3辺りのコード
0046379A以降の処理を丁寧に読んでいくことで解答が得られる