趁熱打鐵先把phase6的破解過程記錄下來
0x00000000004010f4 <+0>: push %r14
0x00000000004010f6 <+2>: push %r13
0x00000000004010f8 <+4>: push %r12
0x00000000004010fa <+6>: push %rbp
0x00000000004010fb <+7>: push %rbx
0x00000000004010fc <+8>: sub $0x50,%rsp
0x0000000000401100 <+12>: mov %rsp,%r13
0x0000000000401103 <+15>: mov %rsp,%rsi
0x0000000000401106 <+18>: callq 0x40145c <read_six_numbers>
0x000000000040110b <+23>: mov %rsp,%r14
0x000000000040110e <+26>: mov $0x0,%r12d
0x0000000000401114 <+32>: mov %r13,%rbp
0x0000000000401117 <+35>: mov 0x0(%r13),%eax
0x000000000040111b <+39>: sub $0x1,%eax
0x000000000040111e <+42>: cmp $0x5,%eax
0x0000000000401121 <+45>: jbe 0x401128 <phase_6+52>
0x0000000000401123 <+47>: callq 0x40143a <explode_bomb>
0x0000000000401128 <+52>: add $0x1,%r12d
0x000000000040112c <+56>: cmp $0x6,%r12d
0x0000000000401130 <+60>: je 0x401153 <phase_6+95>
0x0000000000401132 <+62>: mov %r12d,%ebx
0x0000000000401135 <+65>: movslq %ebx,%rax
0x0000000000401138 <+68>: mov (%rsp,%rax,4),%eax
0x000000000040113b <+71>: cmp %eax,0x0(%rbp)
0x000000000040113e <+74>: jne 0x401145 <phase_6+81>
0x0000000000401140 <+76>: callq 0x40143a <explode_bomb>
0x0000000000401145 <+81>: add $0x1,%ebx
0x0000000000401148 <+84>: cmp $0x5,%ebx
0x000000000040114b <+87>: jle 0x401135 <phase_6+65>
0x000000000040114d <+89>: add $0x4,%r13
0x0000000000401151 <+93>: jmp 0x401114 <phase_6+32>
0x0000000000401153 <+95>: lea 0x18(%rsp),%rsi
0x0000000000401158 <+100>: mov %r14,%rax
0x000000000040115b <+103>: mov $0x7,%ecx
0x0000000000401160 <+108>: mov %ecx,%edx
0x0000000000401162 <+110>: sub (%rax),%edx
0x0000000000401164 <+112>: mov %edx,(%rax)
0x0000000000401166 <+114>: add $0x4,%rax
0x000000000040116a <+118>: cmp %rsi,%rax
0x000000000040116d <+121>: jne 0x401160 <phase_6+108>
0x000000000040116f <+123>: mov $0x0,%esi
0x0000000000401174 <+128>: jmp 0x401197 <phase_6+163>
0x0000000000401176 <+130>: mov 0x8(%rdx),%rdx
0x000000000040117a <+134>: add $0x1,%eax
0x000000000040117d <+137>: cmp %ecx,%eax
0x000000000040117f <+139>: jne 0x401176 <phase_6+130>
0x0000000000401181 <+141>: jmp 0x401188 <phase_6+148>
0x0000000000401183 <+143>: mov $0x6032d0,%edx
0x0000000000401188 <+148>: mov %rdx,0x20(%rsp,%rsi,2)
0x000000000040118d <+153>: add $0x4,%rsi
0x0000000000401191 <+157>: cmp $0x18,%rsi
0x0000000000401195 <+161>: je 0x4011ab <phase_6+183>
0x0000000000401197 <+163>: mov (%rsp,%rsi,1),%ecx
0x000000000040119a <+166>: cmp $0x1,%ecx
0x000000000040119d <+169>: jle 0x401183 <phase_6+143>
0x000000000040119f <+171>: mov $0x1,%eax
0x00000000004011a4 <+176>: mov $0x6032d0,%edx
0x00000000004011a9 <+181>: jmp 0x401176 <phase_6+130>
0x00000000004011ab <+183>: mov 0x20(%rsp),%rbx
0x00000000004011b0 <+188>: lea 0x28(%rsp),%rax
0x00000000004011b5 <+193>: lea 0x50(%rsp),%rsi
0x00000000004011ba <+198>: mov %rbx,%rcx
0x00000000004011bd <+201>: mov (%rax),%rdx
0x00000000004011c0 <+204>: mov %rdx,0x8(%rcx)
0x00000000004011c4 <+208>: add $0x8,%rax
0x00000000004011c8 <+212>: cmp %rsi,%rax
0x00000000004011cb <+215>: je 0x4011d2 <phase_6+222>
0x00000000004011cd <+217>: mov %rdx,%rcx
0x00000000004011d0 <+220>: jmp 0x4011bd <phase_6+201>
0x00000000004011d2 <+222>: movq $0x0,0x8(%rdx)
0x00000000004011da <+230>: mov $0x5,%ebp
0x00000000004011df <+235>: mov 0x8(%rbx),%rax
0x00000000004011e3 <+239>: mov (%rax),%eax
0x00000000004011e5 <+241>: cmp %eax,(%rbx)
0x00000000004011e7 <+243>: jge 0x4011ee <phase_6+250>
0x00000000004011e9 <+245>: callq 0x40143a <explode_bomb>
0x00000000004011ee <+250>: mov 0x8(%rbx),%rbx
0x00000000004011f2 <+254>: sub $0x1,%ebp
0x00000000004011f5 <+257>: jne 0x4011df <phase_6+235>
0x00000000004011f7 <+259>: add $0x50,%rsp
0x00000000004011fb <+263>: pop %rbx
0x00000000004011fc <+264>: pop %rbp
0x00000000004011fd <+265>: pop %r12
0x00000000004011ff <+267>: pop %r13
0x0000000000401201 <+269>: pop %r14
0x0000000000401203 <+271>: retq
pop和push都是在維護寄存器,不用管
<+18> →輸入的是六個數字
<+39>~<+42>是一個編譯器的套路,把eax先減1,再和5做無符號比較,實際上是在判定1<=eax<=6。→6個數字必須介於1~6之間
<+26>但凡看到把0賦值給寄存器(r12),就要考慮它是不是下標。<+52>~<+60>印證了我們的猜想,此處有一個循環。<+60>指出了循環的結束位置在95行。
<+62> 把代表下標的寄存器(r12)賦值給另一個寄存器(ebx),說明多半ebx也是下標。<+81>~<+84>印證了我們的猜想,這是一個循環中的循環。
閱讀代碼可知,截止到<+92>,代碼要求輸入的六個數介於區間[1, 6], 並且互不相等
同構的僞代碼大概是這樣:
for (int i = 1; i < 6; ++i)
int tmp = num[i-1];
if(!(1 <= tmp && tmp <= 6)) explode_bomb();
for (int j = i; j < 6; ++j)
if(num[j] == tmp) explode_bomb();
下面進入代碼的第二部分,從<+95>開始
<+114>~<+121>注意到add、cmp、jX三人組,說明此處又有循環,<+121>的jne是往回跳,說明<+121>就是這段循環的結束位置。
閱讀代碼可知,<+92>~<+121>部分同構的僞代碼大概是這樣:
for (int i = 0; i < 6; ++i)
num[i] = 7 - num[i];
下面進入代碼的第三部分,從<+123>開始
<+123>賦值0,猜測esi是下標
下面這一段跳來跳去的,我也猜不出原始的程序結構長啥樣了,就模擬一遍運行過程吧
首先跳轉到<+163>,把num[0]保存到了ecx中
<+166>把num[0]與1作比較,如果<=1,就跳轉到<+143>?這挺讓人費解的。我們觀察一下<+143>在什麼情況下會被運行到。注意到<+143>的前一句是jmp,跳轉到<+143>的後一句,因此正常情況下<+143>都不會被運行,除非直接跳轉到<+143>
接下來往哪看呢?我們的輸入是1~6,<=1是特殊情況,因此不管這個jmp,往下看纔是程序的主線
<+171>把1賦值給eax,猜測eax是下標
<+176>出現魔法地址0x6032d0,賦值給了edx,一般魔法地址都是重要線索!
跳回<+130>
<+134>~<+139>又見循環三人組,下標爲eax(代號爲j)
循環體的內容是把rdx偏置8字節地址處存放的8字節值賦值給rdx?這什麼鬼。rdx能這麼搞,他偏置8位地址處存放的8字節值也只能是地址,我們想到了鏈表。
打印一下試試
那這個鏈表節點的前8個字節是存啥的,試着打印一下
恰好是節點的編號!
所以節點的c風格聲明應該長這樣:
struct node {
int32_t still_dont_know;
int id;
node* nxt
};
至此,我們發現了魔法地址的驚天祕密!
再看循環結束條件是j != num[i],也就是說,num[i]是幾,鏈表就往後走幾步,rdx保存了節點的地址
跳轉到<+148>
先看<+153>~<+163>又見循環三人組,下標是rsi(代號爲i),記得我們在<+123>給rsi賦值爲0,說明這一帶是一個i爲下標的大循環
再看回<+148>把rdx的值賦值給p[i]。
因此在<+123>~<+181>這一部分,如果把鏈表中編號爲id的節點地址叫做L[id],那麼這一部分乾的事情就是P[i] = L[num[i]];
同構的僞代碼大概是這樣:
for (int i = 0; i < 6; ++i)
node* p;
if (num[i] <= 1) p = head;
else
p = head;
for (int j = 1; j < num[i]; ++j)
p = p->nxt;
P[i] = p;
下面進入代碼的第四部分,從<+183>開始
這部分指針非常亂,最好自己畫張圖
<+193>注意到0x50這個數,0x50 = 0x20 + 6 * 8,因此代表循環結束位置
<+208>~<+215>又見循環三人組,下標爲rax(代號爲i),由<+215>的跳轉地址得知,循環結束位置是<+222>的前一行
在<+183>~<+222>這一部分,所做的事情是按照L[num[0]]->L[num[1]]->...->L[num[5]]的順序重排鏈表(此前的順序是L[0]->L[1]->...->L[5])
同構的僞代碼:
for (int i = 1; i < 6; ++i)
P[i-1]->nxt = P[i]; // P[i]即L[num[i]]
P[5]->nxt = NULL;
下面進入代碼的第五部分,從<+230>開始
指針同樣非常亂,最好自己畫圖
分析方法沒啥特殊的,發現它訪問了node前4字節的值,就叫它val吧
這一部分,所做的事情是檢查是否滿足P[0]->val > ... > P[5]->val
同構的僞代碼:
for (int i = 0; i < 6; ++i)
if(P[i-1]->val < P[i]->val) explode_bomb();
因此我們打印看一下各個節點的val值:
因此當num[i]值爲3, 4, 5, 6, 1, 2時,相應的P[i]->val是924, 691, 477, 443, 332, 168,只有這樣才滿足要求
別忘了之前num的值反轉過,因此正確輸入爲4, 3, 2, 1, 6, 5
最後,完結撒花!
(當然secret_phase還沒做