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、SHR、RORなどが実行され、
アドレス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以降の処理を丁寧に読んでいくことで解答が得られる