最近我在面試中被要求破解一個程式,並且成功拿到了工作。
大家好,開了一個新部落格我真是非常激動吶,於是我計劃每週都要更新幾篇文章。看了標題各位應該知道本文的大概內容了,這裡我會講述一個在土耳其的安卡拉麵試的故事。
“軟體安全工程師(Software Security Engineer)”——我申請的是這樣一個職位,面試時候他們問了我一些低階的問題,有些我能答上來,還有些卻不行。
然後他們給我發了一封郵件,其中包含了一個加密的程式(CRACK MEEE!)。回家之後我下載了它,開啟發現需要一個密碼來解鎖,心想原來他們是要我找到這個密碼。:)
乍看之下這是相當困難的一件事,不過下面我會介紹破解過程中一些主要的概念。:)
第一件事就是在終端中執行程式
root@lisa:~# ./CrackTheDoor
*** DOOR CONTROL SYSTEM ***
PASSWORD:
我隨手嘗試了幾個愚蠢的密碼,3次之後,它就退出了。:)
再嘗試用工具去分析它,比如獲取檔案資訊:
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:~#
於是我們就得到了關於這個二進位制包更詳細的資訊:)
接下來:
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命令的結果可以看出它是一個共享庫對吧?那麼你們聽說過Virtual DSO(Virtual Dynamic Shared Object)嗎?
這裡建議閱讀一下關於的linux-gate.so詳細介紹。
libc.so.6是GNU系統上一個通用的C語言庫,這個你們可能都知道。
ld-linux.so是linux的動態載入器。
到目前為止一切都還算順利,那麼使用偵錯程式來執行這個程式試試:
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/CrackTheDoorProgram received signal SIGSEGV, Segmentation fault.
0x080484fb in __do_global_dtors_aux ()
(gdb)
結果程式自己就掛掉了,應該是我們使用偵錯程式造成的,看來有些反除錯的小花招整合在裡面了。那麼……
我們來重新執行它,並且找到程式是從哪開始執行的:
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
…
…
這樣就得到了程式的入口,在這裡設定一個斷點再除錯看看:
按r執行,應該會停在入口的第一行:
=> 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&A,你也可以切換為Intel語法體系。我個人而言,更偏向Intel語法。
在地址0x8047654處,我們首先吧0x55放到AL暫存器,然後將它和0x99異或,得到了0xCC。
0xCC非常重要,因為它會中斷你的程式,正如評論中所說,這在x86架構中是斷點指令(譯註:INT 3)。當偵錯程式想要中斷程式時,會將0xCC這個位元組放到需要斷點的地方。
在0x8047666處有個指令repnz scas,意思是在段暫存器ES和偏移量EDI範圍的記憶體中尋找AL裡的值(0xCC)。
簡單來說,這行就是會搜尋記憶體中的0xCC,如果找到就讓程式掛掉。
不過我不想在這花費太多時間,我們來執行strace命令:
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\3\3\1\300o\1004″…, 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:),畢竟這就是它們的任務嘛。
我們可以想象一下這段程式碼,可能是下面這樣的:
1 2 3 4 5 6 7 8 9 |
int main() { if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { printf("DEBUGGING... Bye\n"); return 1; } printf("Hello\n"); return 0; } |
順便說一句,你只能使用一次ptrace[PTRACE_TRACEME]。因此如果偵錯程式在這之前使用ptrace,那麼我們的呼叫就會返回false,於是就能知道還有其他東西在控制程式了。
現在需要做的是繞過ptrace保護機制,使程式無法檢測到自己執行在偵錯程式中。
所以一個可能的策略是改變這個系統呼叫的返回值。
系統呼叫是使用者態與核心態之間的橋樑,於是可以確定ptrace中一定用到了一些系統呼叫來控制程式。
我們希望檢測到程式使用ptrace,並且設定它的返回值為0:)
我在home目錄下建立了一個檔案叫.gdbinit,這樣一來,當執行gdb的時候,其中的配置資訊就會被自動載入。
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了,或者說是true。
通過這種方式,我們繞過了反除錯機制,那就回到gdb繼續吧:
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/CrackTheDoorCatchpoint 1 (call to syscall ptrace), 0x08047698 in ?? ()
=> 0x08047698: 3d 00 f0 ff ff cmp eax,0xfffff000Catchpoint 1 (returned from syscall ptrace), 0x08047698 in ?? ()
=> 0x08047698: 3d 00 f0 ff ff cmp eax,0xfffff000*** DOOR CONTROL SYSTEM ***
PASSWORD:
好了,至少目前為止我們能夠隨心所欲地來除錯了。:)
我在函式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]
終於到有樂子的步驟了。
我看到有些常量在記憶體中移動,並且和我輸入的值進行了異或運算。
繼續深入研究……
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]
和之前得到的結果類似,現在我在其中增加一些常量:
下面是這個函式後面執行的情況:
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]
你們看出其中執行的模式了嗎?如果沒看出來也沒關係……
在這個函式中,程式同樣將我的輸入和一些常量進行了異或運算。
現在來看看輸入,首先輸入會和這些常量進行異或,然後將結果和另一些常量進行對比。
因此這兩個函式應該是這樣的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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 } |
於是寫了一個簡單的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 ***
看!成功了!
這個公司又給我發來了第二輪的破解題目,也一樣很有趣,下次我會在文章中寫到的。
譯註:原文評論中較多的疑問是如何定位到函式PJeGPC4TIVaKFmmy53DJ,有人給出了合理的解釋,作者也預設了:其實很簡單,不停地嘗試。也許寫下這篇文章之前有很多很多次失敗,但是讀者最感興趣的部分是成功的,因此那些都被省略了。