POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook
Eren Yagdiran

本記事は、原著者の許諾のもとに翻訳・掲載しております。

長文ですが、よかったら読んでください。

就職面接でプログラムの解読を求められました。そして、就職が決まりました。

皆さん、こんにちは。新しいブログを開設したので、私は今とても張り切っています。週に何度か記事を投稿するつもりです。

タイトルを見れば大体の話の内容は分かると思いますが、これから書くのは、トルコのアンカラで受けた就職面接の話です。

私が応募した職は「ソフトウェアセキュリティエンジニア」でした。面接中、面接官たちは非常に専門性が低い質問をしてきましたが、分かることもあれば分からないこともありました。

その後、その企業からメールが届き、保護および暗号化されたバイナリファイルが添付されていました(「解読してみろ」ということでしょう)。

帰宅後にファイルをダウンロードすると、ファイルを開くために聞かれたのはパスワードだけでした。面接官が私に課した課題は、そのパスワードを探すことでした。

初めはとても難しそうに見えますが、私が行った手順の基本的な概念を紹介しようと思います。

まず端末に入力したのは以下の通りです。

root@lisa:~# ./CrackTheDoor 

        *** DOOR CONTROL SYSTEM  ***



PASSWORD:

的外れなキーワードを3回入力すると、プログラムが終了しました。

でも、分析手段はまだありますよ。もう少しファイルについて調べてみましょう。

root@lisa:~# file CrackTheDoor 
CrackTheDoor: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, BuildID[sha1]=0x9927be2fe310bea01d412164103b9c8b2d7567ea, not stripped
root@lisa:~#

よし、これで少しバイナリファイルに関する情報が増えましたね。

これはどうでしょう。

root@lisa:~# ldd CrackTheDoor 
    linux-gate.so.1 =>  (0xf777b000)
    libc.so.6 => /lib32/libc.so.6 (0xf760c000)
    /lib/ld-linux.so.2 (0xf777c000)
root@lisa:~#

なるほど、普通の内容ですね。少し説明します。 linux-gate.so はファイルシステム内に存在しませんが、 ldd コマンドでは共有ライブラリとして表示されていますよね? そうなんです。仮想DSO(仮想動的共有オブジェクト)について聞いたことはありますか?

linux-gate.so について読んでみてください。

恐らくご存じだと思いますが、 libc.so.6 は、GNUシステムの標準Cライブラリです。

ld-linux.so は、Linuxの動的ローダです。

とにかく、ここまでは全て順調です。あとはデバッグ環境でプログラムを実行して、何が起こるか見てみる必要があります。

root@lisa:~# gdb CrackTheDoor 
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/CrackTheDoor...(no debugging symbols found)...done.
(gdb) r
Starting program: /root/CrackTheDoor 

Program received signal SIGSEGV, Segmentation fault.
0x080484fb in __do_global_dtors_aux ()
(gdb)

プログラムがクラッシュしましたね。デバッガを走らせていると分かったのです。ということは、プログラムの中にアンチデバッグ技法が埋め込まれているはずです。

プログラムをリロードしてスタート地点に戻りましょう。

root@lisa:~# gdb CrackTheDoor 
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/CrackTheDoor...(no debugging symbols found)...done.
(gdb) info file
Symbols from "/root/CrackTheDoor".
Local exec file:
    `/root/CrackTheDoor', file type elf32-i386.
    Entry point: 0x804762c
...
...

プログラムのエントリポイントがありました。そこにブレークポイントを入れ、エントリポイントを通じてプログラムのデバッグを開始しましょう。

b * 0x804762c

“r”でプログラムを実行します。するとエントリポイントの1行目で中断するはずです。

gdb) x/30i $pc
=> 0x804762c: pusha  
   0x804762d:   mov    $0xaa,%dl
   0x804762f:   mov    $0x8048480,%edi
   0x8047634:   mov    $0x8048cbc,%ecx
   0x8047639:   mov    %edi,0x80476f3
   0x804763f:   mov    %ecx,0x80476f7
   0x8047645:   sub    %edi,%ecx
   0x8047647:   mov    $0x804762f,%esi
   0x804764c:   push   $0x80476c1
   0x8047651:   pusha  
   0x8047652:   mov    $0x55,%al
   0x8047654:   xor    $0x99,%al
   0x8047656:   mov    $0x8047656,%edi
   0x804765b:   mov    $0x80476e5,%ecx
   0x8047660:   sub    $0x8047656,%ecx
   0x8047666:   repnz scas %es:(%edi),%al
   0x8047668:   je     0x804770a
   0x804766e:   mov    %edi,0x80476eb
   0x8047674:   popa   
   0x8047675:   add    0x80476eb,%edx
   0x804767b:   ret

このように見えるはずです。この構文モードはAT&Tですが、Intelモードにも変更できます。私の考えではIntel構文の方が少し良いです。

アドレス中の 0x8047654 ですが、まずALレジスタに 0x55 を入れ、 0x99 を介して排他的論理和(xor)をとると、 0xCC となります。

0xCC は非常に重要です。なぜなら、それは処理を中断する、もしくはコメントでピーターが言ったように、x86アーキテクチャの中では実行を中断してデバッガを起動することを意味するからです。デバッガがプログラムを中断したい時、その中断したい所でバイトを0xCCにスワップします。

0x8047666 repnz scas 命令がありますね。これは、 es レジスタによって区切られた edi までのメモリリージョンの中で al 内の値( 0xCC )があるかどうかを探します。

ですから、これらの行は基本的にはメモリをスキャンし、 0xCC があるとプログラムをクラッシュさせたりするのです。

ここであまり時間を使いたくありません。 strace を使ってみましょう。

root@lisa:~# strace ./CrackTheDoor 
execve("./CrackTheDoor", ["./CrackTheDoor"], [/* 17 vars */]) = 0
[ Process PID=31085 runs in 32 bit mode. ]
brk(0)                                  = 0x9972000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7715000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=35597, ...}) = 0
mmap2(NULL, 35597, PROT_READ, MAP_PRIVATE, 3, 0) = 0xfffffffff770c000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib32/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300o\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1441884, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff770b000
mmap2(NULL, 1456504, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xfffffffff75a7000
mprotect(0xf7704000, 4096, PROT_NONE)   = 0
mmap2(0xf7705000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15d) = 0xfffffffff7705000
mmap2(0xf7708000, 10616, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfffffffff7708000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xfffffffff75a6000
set_thread_area(0xffe4d864)             = 0
mprotect(0xf7705000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0xf7733000, 4096, PROT_READ)   = 0
munmap(0xf770c000, 35597)               = 0
ptrace(PTRACE_TRACEME, 0, 0x1, 0)       = -1 EPERM (Operation not permitted)
ptrace(PTRACE_TRACEME, 0, 0x1, 0)       = -1 EPERM (Operation not permitted)

最後の方の行を見ると、プログラムがまたクラッシュしています。原因は ptrace システムコールです。

Linuxで ptrace は「Process Trace」の略で、デバッガのように内部の状態を変更しながら別のプロセスを制御することができます。

デバッガは ptrace を非常によく使います。それが仕事ですからね。

コードをイメージすると、こんな感じのはずです。

int main()
{
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
        printf("DEBUGGING... Bye\n");
        return 1;
    }
    printf("Hello\n");
    return 0;
}

ところで、 ptrace[PTRACE_TRACEMe] は1度しか行えません。ですから、過去にデバッガがプログラムで ptrace を実行していたら、プログラム制御をしているものがあることに気付かせるために、その後の呼び出しにはFALSEが返ってくるでしょう。

デバッガの下で実行されているということをプログラムに気付かせないために、この ptrace 保護を回避する必要があります。

そこで、システムコールの結果を書き変えてしまうことにしましょう。

システムコールはユーザ空間からカーネル空間への入り口です。 ptrace もプロセスを操作するのに、何らかのシステムコールを使用しているに違いありません。

プログラムがいつptraceを使用するのかを見破り、その結果を0に設定してしまいましょう。次のようにします。

ホームディレクトリに、新たに .gdbinit ファイルを作成します。これにより、 gdb を実行する度に次のような設定が自動的に読み込まれます。

~/.gdbinit
set disassembly-flavor intel # Intel syntax is better
set disassemble-next-line on
catch syscall ptrace #Catch the syscall.
commands 1
set ($eax) = 0
continue
end

eax はシステムコールの結果を保持します。そしてそれは常に0に、いわば真になるわけです。

このようにして ptrace 保護を回避します。それでは改めて gdb を実行しましょう。

eren@lisa:~$ gdb ./CrackTheDoor 
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Catchpoint 1 (syscall 'ptrace' [26])
Reading symbols from /home/eren/CrackTheDoor...(no debugging symbols found)...done.
(gdb) r
Starting program: /home/eren/CrackTheDoor 

Catchpoint 1 (call to syscall ptrace), 0x08047698 in ?? ()
=> 0x08047698:    3d 00 f0 ff ff   cmp    eax,0xfffff000

Catchpoint 1 (returned from syscall ptrace), 0x08047698 in ?? ()
=> 0x08047698:    3d 00 f0 ff ff   cmp    eax,0xfffff000

        *** DOOR CONTROL SYSTEM  ***



PASSWORD:

これで少なくともデバッガは思い通りに使えるようになりました。

ここで PJeGPC4TIVaKFmmy53DJ に新たにブレークポイントを設定します。

Breakpoint 2, 0x08048534 in PJeGPC4TIVaKFmmy53DJ ()
=> 0x08048534 <PJeGPC4TIVaKFmmy53DJ+0>: 1e  push   ds
(gdb) x/40i $pc
=> 0x8048534 <PJeGPC4TIVaKFmmy53DJ>:    push   ds
   0x8048535 <PJeGPC4TIVaKFmmy53DJ+1>:    mov    ebp,esp
   0x8048537 <PJeGPC4TIVaKFmmy53DJ+3>:    sub    esp,0x20
   0x804853a <PJeGPC4TIVaKFmmy53DJ+6>:    mov    BYTE PTR [ebp-0x1],0xe4
   0x804853e <PJeGPC4TIVaKFmmy53DJ+10>:   mov    BYTE PTR [ebp-0x2],0x87
   0x8048542 <PJeGPC4TIVaKFmmy53DJ+14>:   mov    BYTE PTR [ebp-0x3],0xfb
   0x8048546 <PJeGPC4TIVaKFmmy53DJ+18>:   mov    BYTE PTR [ebp-0x4],0xbe
   0x804854a <PJeGPC4TIVaKFmmy53DJ+22>:   mov    BYTE PTR [ebp-0x5],0xc9
   0x804854e <PJeGPC4TIVaKFmmy53DJ+26>:   mov    BYTE PTR [ebp-0x6],0x93
   0x8048552 <PJeGPC4TIVaKFmmy53DJ+30>:   mov    BYTE PTR [ebp-0x7],0x84
   0x8048556 <PJeGPC4TIVaKFmmy53DJ+34>:   mov    BYTE PTR [ebp-0x8],0xfc
   0x804855a <PJeGPC4TIVaKFmmy53DJ+38>:   mov    BYTE PTR [ebp-0x9],0x8d
   0x804855e <PJeGPC4TIVaKFmmy53DJ+42>:   mov    BYTE PTR [ebp-0xa],0xe5
   0x8048562 <PJeGPC4TIVaKFmmy53DJ+46>:   mov    BYTE PTR [ebp-0xb],0xbf
   0x8048566 <PJeGPC4TIVaKFmmy53DJ+50>:   mov    BYTE PTR [ebp-0xc],0x5c
   0x804856a <PJeGPC4TIVaKFmmy53DJ+54>:   mov    BYTE PTR [ebp-0xd],0xe2
   0x804856e <PJeGPC4TIVaKFmmy53DJ+58>:   mov    BYTE PTR [ebp-0xe],0x76
   0x8048572 <PJeGPC4TIVaKFmmy53DJ+62>:   mov    BYTE PTR [ebp-0xf],0x21
   0x8048576 <PJeGPC4TIVaKFmmy53DJ+66>:   mov    BYTE PTR [ebp-0x10],0xb8
   0x804857a <PJeGPC4TIVaKFmmy53DJ+70>:   mov    DWORD PTR [ebp-0x14],0x0
   0x8048581 <PJeGPC4TIVaKFmmy53DJ+77>:   mov    eax,DWORD PTR [ebp-0x14]
   0x8048584 <PJeGPC4TIVaKFmmy53DJ+80>:   add    eax,DWORD PTR [ebp+0x8]
   0x8048587 <PJeGPC4TIVaKFmmy53DJ+83>:   movzx  eax,BYTE PTR [eax]
   0x804858a <PJeGPC4TIVaKFmmy53DJ+86>:   test   al,al
   0x804858c <PJeGPC4TIVaKFmmy53DJ+88>:   je     0x8048808 <PJeGPC4TIVaKFmmy53DJ+724>
   0x8048592 <PJeGPC4TIVaKFmmy53DJ+94>:   mov    eax,DWORD PTR [ebp-0x14]
   0x8048595 <PJeGPC4TIVaKFmmy53DJ+97>:   add    eax,DWORD PTR [ebp+0x8]
   0x8048598 <PJeGPC4TIVaKFmmy53DJ+100>:  mov    edx,DWORD PTR [ebp-0x14]
   0x804859b <PJeGPC4TIVaKFmmy53DJ+103>:  add    edx,DWORD PTR [ebp+0x8]
   0x804859e <PJeGPC4TIVaKFmmy53DJ+106>:  movzx  edx,BYTE PTR [edx]
   0x80485a1 <PJeGPC4TIVaKFmmy53DJ+109>:  xor    dl,BYTE PTR [ebp-0x1]
   0x80485a4 <PJeGPC4TIVaKFmmy53DJ+112>:  mov    BYTE PTR [eax],dl
   0x80485a6 <PJeGPC4TIVaKFmmy53DJ+114>:  add    DWORD PTR [ebp-0x14],0x1
   0x80485aa <PJeGPC4TIVaKFmmy53DJ+118>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80485ad <PJeGPC4TIVaKFmmy53DJ+121>:  add    eax,DWORD PTR [ebp+0x8]
   0x80485b0 <PJeGPC4TIVaKFmmy53DJ+124>:  movzx  eax,BYTE PTR [eax]
   0x80485b3 <PJeGPC4TIVaKFmmy53DJ+127>:  test   al,al
   0x80485b5 <PJeGPC4TIVaKFmmy53DJ+129>:  je     0x804880b <PJeGPC4TIVaKFmmy53DJ+727>
   0x80485bb <PJeGPC4TIVaKFmmy53DJ+135>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80485be <PJeGPC4TIVaKFmmy53DJ+138>:  add    eax,DWORD PTR [ebp+0x8]
   0x80485c1 <PJeGPC4TIVaKFmmy53DJ+141>:  mov    edx,DWORD PTR [ebp-0x14]
   0x80485c4 <PJeGPC4TIVaKFmmy53DJ+144>:  add    edx,DWORD PTR [ebp+0x8]
   0x80485c7 <PJeGPC4TIVaKFmmy53DJ+147>:  movzx  edx,BYTE PTR [edx]
   0x80485ca <PJeGPC4TIVaKFmmy53DJ+150>:  xor    dl,BYTE PTR [ebp-0x2]

これは面白いですね。

いくつかの定数がどこかに移動していっています。そして私がプログラムに与えた入力とこれらの定数との排他的論理和がとられています。

さらに調べてみます。

(gdb) x/30i X1bdrhN8Yk9NZ59Vb7P2
   0x8048838 <X1bdrhN8Yk9NZ59Vb7P2>:  sbb    ecx,DWORD PTR [ecx+0x20ec83e5]
   0x804883e <X1bdrhN8Yk9NZ59Vb7P2+6>:    mov    DWORD PTR [ebp-0x18],0x0
   0x8048845 <X1bdrhN8Yk9NZ59Vb7P2+13>:   mov    BYTE PTR [ebp-0x1],0xd9
   0x8048849 <X1bdrhN8Yk9NZ59Vb7P2+17>:   mov    BYTE PTR [ebp-0x2],0xcd
   0x804884d <X1bdrhN8Yk9NZ59Vb7P2+21>:   mov    BYTE PTR [ebp-0x3],0xc9
   0x8048851 <X1bdrhN8Yk9NZ59Vb7P2+25>:   mov    BYTE PTR [ebp-0x4],0xe5
   0x8048855 <X1bdrhN8Yk9NZ59Vb7P2+29>:   mov    BYTE PTR [ebp-0x5],0x9e
   0x8048859 <X1bdrhN8Yk9NZ59Vb7P2+33>:   mov    BYTE PTR [ebp-0x6],0xd0
   0x804885d <X1bdrhN8Yk9NZ59Vb7P2+37>:   mov    BYTE PTR [ebp-0x7],0xe8
   0x8048861 <X1bdrhN8Yk9NZ59Vb7P2+41>:   mov    BYTE PTR [ebp-0x8],0xa5
   0x8048865 <X1bdrhN8Yk9NZ59Vb7P2+45>:   mov    BYTE PTR [ebp-0x9],0xaf
   0x8048869 <X1bdrhN8Yk9NZ59Vb7P2+49>:   mov    BYTE PTR [ebp-0xa],0x87
   0x804886d <X1bdrhN8Yk9NZ59Vb7P2+53>:   mov    BYTE PTR [ebp-0xb],0xd2
   0x8048871 <X1bdrhN8Yk9NZ59Vb7P2+57>:   mov    BYTE PTR [ebp-0xc],0x79
   0x8048875 <X1bdrhN8Yk9NZ59Vb7P2+61>:   mov    BYTE PTR [ebp-0xd],0xa9
   0x8048879 <X1bdrhN8Yk9NZ59Vb7P2+65>:   mov    BYTE PTR [ebp-0xe],0x5d
   0x804887d <X1bdrhN8Yk9NZ59Vb7P2+69>:   mov    BYTE PTR [ebp-0xf],0x7
   0x8048881 <X1bdrhN8Yk9NZ59Vb7P2+73>:   mov    BYTE PTR [ebp-0x10],0x81
   0x8048885 <X1bdrhN8Yk9NZ59Vb7P2+77>:   mov    DWORD PTR [ebp-0x14],0x0
   0x804888c <X1bdrhN8Yk9NZ59Vb7P2+84>:   mov    eax,DWORD PTR [ebp-0x14]
   0x804888f <X1bdrhN8Yk9NZ59Vb7P2+87>:   add    eax,DWORD PTR [ebp+0x8]
   0x8048892 <X1bdrhN8Yk9NZ59Vb7P2+90>:   movzx  eax,BYTE PTR [eax]
   0x8048895 <X1bdrhN8Yk9NZ59Vb7P2+93>:   cmp    al,BYTE PTR [ebp-0x1]
   0x8048898 <X1bdrhN8Yk9NZ59Vb7P2+96>:   je     0x80488a2 <X1bdrhN8Yk9NZ59Vb7P2+106>
   0x804889a <X1bdrhN8Yk9NZ59Vb7P2+98>:   mov    eax,DWORD PTR [ebp-0x18]

ここも同じですね。今度は別の定数群を格納しています。

さて、以下が関数の残りの部分です。

0x804889d <X1bdrhN8Yk9NZ59Vb7P2+101>:    jmp    0x8048a20 <X1bdrhN8Yk9NZ59Vb7P2+488>
   0x80488a2 <X1bdrhN8Yk9NZ59Vb7P2+106>:  add    DWORD PTR [ebp-0x14],0x1
   0x80488a6 <X1bdrhN8Yk9NZ59Vb7P2+110>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80488a9 <X1bdrhN8Yk9NZ59Vb7P2+113>:  add    eax,DWORD PTR [ebp+0x8]
   0x80488ac <X1bdrhN8Yk9NZ59Vb7P2+116>:  movzx  eax,BYTE PTR [eax]
   0x80488af <X1bdrhN8Yk9NZ59Vb7P2+119>:  cmp    al,BYTE PTR [ebp-0x2]
   0x80488b2 <X1bdrhN8Yk9NZ59Vb7P2+122>:  je     0x80488bc <X1bdrhN8Yk9NZ59Vb7P2+132>
   0x80488b4 <X1bdrhN8Yk9NZ59Vb7P2+124>:  mov    eax,DWORD PTR [ebp-0x18]
   0x80488b7 <X1bdrhN8Yk9NZ59Vb7P2+127>:  jmp    0x8048a20 <X1bdrhN8Yk9NZ59Vb7P2+488>
   0x80488bc <X1bdrhN8Yk9NZ59Vb7P2+132>:  add    DWORD PTR [ebp-0x14],0x1
   0x80488c0 <X1bdrhN8Yk9NZ59Vb7P2+136>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80488c3 <X1bdrhN8Yk9NZ59Vb7P2+139>:  add    eax,DWORD PTR [ebp+0x8]
   0x80488c6 <X1bdrhN8Yk9NZ59Vb7P2+142>:  movzx  eax,BYTE PTR [eax]
   0x80488c9 <X1bdrhN8Yk9NZ59Vb7P2+145>:  cmp    al,BYTE PTR [ebp-0x3]
   0x80488cc <X1bdrhN8Yk9NZ59Vb7P2+148>:  je     0x80488d6 <X1bdrhN8Yk9NZ59Vb7P2+158>
   0x80488ce <X1bdrhN8Yk9NZ59Vb7P2+150>:  mov    eax,DWORD PTR [ebp-0x18]
   0x80488d1 <X1bdrhN8Yk9NZ59Vb7P2+153>:  jmp    0x8048a20 <X1bdrhN8Yk9NZ59Vb7P2+488>
   0x80488d6 <X1bdrhN8Yk9NZ59Vb7P2+158>:  add    DWORD PTR [ebp-0x14],0x1
   0x80488da <X1bdrhN8Yk9NZ59Vb7P2+162>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80488dd <X1bdrhN8Yk9NZ59Vb7P2+165>:  add    eax,DWORD PTR [ebp+0x8]
---Type <return> to continue, or q <return> to quit---
   0x80488e0 <X1bdrhN8Yk9NZ59Vb7P2+168>:  movzx  eax,BYTE PTR [eax]
   0x80488e3 <X1bdrhN8Yk9NZ59Vb7P2+171>:  cmp    al,BYTE PTR [ebp-0x4]
   0x80488e6 <X1bdrhN8Yk9NZ59Vb7P2+174>:  je     0x80488f0 <X1bdrhN8Yk9NZ59Vb7P2+184>
   0x80488e8 <X1bdrhN8Yk9NZ59Vb7P2+176>:  mov    eax,DWORD PTR [ebp-0x18]
   0x80488eb <X1bdrhN8Yk9NZ59Vb7P2+179>:  jmp    0x8048a20 <X1bdrhN8Yk9NZ59Vb7P2+488>
   0x80488f0 <X1bdrhN8Yk9NZ59Vb7P2+184>:  add    DWORD PTR [ebp-0x14],0x1
   0x80488f4 <X1bdrhN8Yk9NZ59Vb7P2+188>:  mov    eax,DWORD PTR [ebp-0x14]
   0x80488f7 <X1bdrhN8Yk9NZ59Vb7P2+191>:  add    eax,DWORD PTR [ebp+0x8]
   0x80488fa <X1bdrhN8Yk9NZ59Vb7P2+194>:  movzx  eax,BYTE PTR [eax]
   0x80488fd <X1bdrhN8Yk9NZ59Vb7P2+197>:  cmp    al,BYTE PTR [ebp-0x5]
   0x8048900 <X1bdrhN8Yk9NZ59Vb7P2+200>:  je     0x804890a <X1bdrhN8Yk9NZ59Vb7P2+210>
   0x8048902 <X1bdrhN8Yk9NZ59Vb7P2+202>:  mov    eax,DWORD PTR [ebp-0x18]
   0x8048905 <X1bdrhN8Yk9NZ59Vb7P2+205>:  jmp    0x8048a20 <X1bdrhN8Yk9NZ59Vb7P2+488>
   0x804890a <X1bdrhN8Yk9NZ59Vb7P2+210>:  add    DWORD PTR [ebp-0x14],0x1
   0x804890e <X1bdrhN8Yk9NZ59Vb7P2+214>:  mov    eax,DWORD PTR [ebp-0x14]

パターンが分かりましたか? 分からなくても大丈夫です。

ここでは再び、排他的論理和をとった後の私の入力と定数とを、プログラムが比較しています。

ここで、改めて入力に注目してみましょう。最初の入力といくつかの定数との排他的論理和がとられ、その結果が他の定数と比較されています。

これら2つの関数はおそらく以下のようなものでしょう。

void PJeGPC4TIVaKFmmy53DJ (int * p)
{
  int array[] = {0xe4,0x87,0xfb,0xbe,0xc9,0x93,0x84,0xfc,0x8d,0xe5,0xbf,0x5c,0xe2,0x76,0x21,0xb8}
  for(i=0;i<16;i++)
 {
    p[i] = p[i] ^ array[i]
 }
}
int X1bdrhN8Yk9NZ59Vb7P2(int * p)
{
   int array = {0xd9,0xcd,0xc9,0xe5,0x9e,0xd0,0xe8,0xa5,0xaf,0x87,0xd2,0x79,0xa9,0x5d,0x7,0x81}
   for(i=0;i<16;i++)
 {
    if(p[i] != array[i])
         return false; // fail..
 }
  return true 
}

パスワードを見つけるために、これらの2つの定数の排他的論理和をとる簡単なPythonのスクリプトを書いてみましょう。

#!/usr/bin/python
firstConst = [0xe4,0x87,0xfb,0xbe,0xc9,0x93,0x84,0xfc,0x8d,0xe5,0xbf,0x5c,0xe2,0x76,0x21,0xb8]
secondConst = [0xd9,0xcd,0xc9,0xe5,0x9e,0xd0,0xe8,0xa5,0xaf,0x87,0xd2,0x79,0xa9,0x5d,0x7,0x81]
ret =""
for x in range(16):
        ret+=chr(firstConst[x] ^ secondConst[x])
print ret
eren@lisa:~$ ./CrackTheDoor 

        *** DOOR CONTROL SYSTEM  ***



PASSWORD: =J2[WClY"bm%K+&9

        ***  ACCESS GRANTED  ***

        ***  THE DOOR OPENED ***

やりました!

次は、パート2についても書こうと思っています。

会社がラウンド2として、新たな問題を送ってきたのです。そちらも面白いですよ。

(ちなみに、採用は決まりました)

自分も挑戦してみたいという方がいらっしゃれば、バイナリファイルをお送りしますので、私にメールをください。

(タイポの指摘でもいいですけどね)

監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。