http://www.javashuo.com/article/p-hdtlqhrz-nz.htmlhtml
首先感受應該是個遞歸問題node
/* Round and 'round in memory we go, where we stop, the bomb blows! */ input = read_line(); phase_5(input); phase_defused(); printf("Good work! On to the next...\n");
0000000000401062 <phase_5>: 401062: 53 push %rbx 401063: 48 83 ec 20 sub $0x20,%rsp 401067: 48 89 fb mov %rdi,%rbx 40106a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 401071: 00 00 401073: 48 89 44 24 18 mov %rax,0x18(%rsp) 401078: 31 c0 xor %eax,%eax 40107a: e8 9c 02 00 00 callq 40131b <string_length> 40107f: 83 f8 06 cmp $0x6,%eax 401082: 74 4e je 4010d2 <phase_5+0x70> 401084: e8 b1 03 00 00 callq 40143a <explode_bomb>
剛開始就開了個金絲雀這個題應該不太對勁。。rsp+0x18
這個位置存儲了咱們金絲雀的值這是爲了防止緩衝區溢出隨後調用string_length
函數來判斷輸入的字符串長度。能夠發現這裏規定來咱們輸入的字符串長度必須是6不然直接爆炸。知足要求後跳轉到<phase_5+0x70>
c++
4010d2: b8 00 00 00 00 mov $0x0,%eax 4010d7: eb b2 jmp 40108b <phase_5+0x29> 把rax=0而後跳轉到40108b %rbx=%rdi part1 --------------------------------------------------------- 40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx // 40108f: 88 0c 24 mov %cl,(%rsp) 401092: 48 8b 14 24 mov (%rsp),%rdx 401096: 83 e2 0f and $0xf,%edx 401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx 4010a0: 88 54 04 10 mov %dl,0x10(%rsp,%rax,1) 4010a4: 48 83 c0 01 add $0x1,%rax 4010a8: 48 83 f8 06 cmp $0x6,%rax 4010ac: 75 dd jne 40108b <phase_5+0x29> 4010ae: c6 44 24 16 00 movb $0x0,0x16(%rsp) part2 --------------------------------------------------------- 4010b3: be 5e 24 40 00 mov $0x40245e,%esi 4010b8: 48 8d 7c 24 10 lea 0x10(%rsp),%rdi 4010bd: e8 76 02 00 00 callq 401338 <strings_not_equal> 4010c2: 85 c0 test %eax,%eax 4010c4: 74 13 je 4010d9 <phase_5+0x77> 4010c6: e8 6f 03 00 00 callq 40143a <explode_bomb> 4010cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 4010d0: eb 07 jmp 4010d9 <phase_5+0x77> 4010d2: b8 00 00 00 00 mov $0x0,%eax 4010d7: eb b2 jmp 40108b <phase_5+0x29> part3 --------------------------------------------------------- 4010d9: 48 8b 44 24 18 mov 0x18(%rsp),%rax 4010de: 64 48 33 04 25 28 00 xor %fs:0x28,%rax 4010e5: 00 00 4010e7: 74 05 je 4010ee <phase_5+0x8c> 4010e9: e8 42 fa ff ff callq 400b30 <__stack_chk_fail@plt> 4010ee: 48 83 c4 20 add $0x20,%rsp 4010f2: 5b pop %rbx 4010f3: c3 retq
首先咱們能夠發現part2部分是咱們把rsp+0x10位置處的值和0x40245e位置處的值進行比較若是不想等則直接爆炸。所以rsp+0x10
位置存儲的值必須和0x40245e
位置處的值同樣。check一下數組
(gdb) x/s 0x40245e 0x40245e: "flyers"
能夠發現rsp+0x10位置處也必須爲"flyers"而後咱們比較一下金絲雀若是沒有緩衝區溢出的話則返回。
接下來咱們看part1裏面到底發生了什麼這裏寫一個僞代碼會更好理解函數
func (char *c ,int rax, int 1){ //初始rax=0 long a=c[rax*1] //這裏會把a的高32位置0 char tmp=byte(a)[0:8]//這裏把a的2進製表示的低八位給tmp //注意(rsp)=tmp tmp就是咱們輸入的第一個字符 long rdx=tmp; edx=edx&0xf //也就是咱們只保存後4位 edx=m[0x4024b0+rdx] //這裏的rdx裏保存的就是咱們輸入的第一個字符 (rsp+10+rax)=edx //低8位 func(c,rax+1,1); //而後循環調用 }
咱們設咱們輸入的六個字符分別爲a1,a2,a3,a4,a5,a6 這裏能夠發現咱們的棧幀處其實存儲的是m[0x4024b0+rdx]
的值首先咱們看一下0x4024b0
中到底存儲了哪些東西ui
(gdb) print (char*)0x4024b0 $5 = 0x4024b0 <array> "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
emm這是一個字符串數組。嗷這裏其實咱們傳入的就是索引值而後利用索引值來拿到咱們須要的「flyers」
f-9 l-15 y-14 e-5 r-6 s-7
注意這裏咱們輸入的是字符串所以要把他們的ASCLL 值看成索引
由於咱們只取了輸入的每個字符的後4位ASCLl碼看成索引值。也就是說全部後四位知足上面要求的字符均可。這裏咱們隨便取一組。咱們能夠對上面的值。都加上64,這樣不會改變後4位的位模式。而且還能獲得簡單的結果
73=I 79=O 78=N 69=E 70=F 77=G
結果是IONEFG 能夠成功過掉this
IONEFG Good work! On to the next..
好了終於來到了最後一關是否是頗有成就感。看上去很是難的樣子設計
/* This phase will never be used, since no one will get past the * earlier ones. But just in case, make this one extra hard. */ input = read_line(); phase_6(input); phase_defused();
這個的彙編有億點點長。下面先放一個整體邏輯的僞代碼來幫助你們理解3d
cin>>a[6];//輸入六個數 if(a[i]=a[i+1]||a[i]>6)bomb! i=1~6全部元素都不能相同都不能大於6 //這裏咱們有一個單鏈表 node1->node2->node3->node4->node5->node6 /因爲最後咱們的鏈表要知足單調遞減 / 按照值進行排序以下。因此咱們從新排序的鏈表順序必須以下 node3>node4>node5>node6>node1>node2 List *L=new(List(-1));/新建一個鏈表 for(int i=1;i<=6;i++){ if(a[i]==6)L[i]=node1; /L[i]表示咱們新鏈表的第i個結點。 else{ int b=7-a[i],p=node1; while(b--){ p=p->next; } L[i]=p; /這裏就是7-a[i]爲多少就把node[7-a[i]]賦給L[i] } }
下面在開始慢慢讀彙編語言指針
00000000004010f4 <phase_6>: 4010f4: 41 56 push %r14 4010f6: 41 55 push %r13 4010f8: 41 54 push %r12 4010fa: 55 push %rbp 4010fb: 53 push %rbx //以上均爲對調用者保存寄存器的保存過程 4010fc: 48 83 ec 50 sub $0x50,%rsp 401100: 49 89 e5 mov %rsp,%r13 401103: 48 89 e6 mov %rsp,%rsi 401106: e8 51 03 00 00 callq 40145c <read_six_numbers> //這裏的rsi就是咱們的棧幀指針而後調用 read_six_numbers
這裏的分析和第二關有點像簡略分析過程。獲得咱們六個參數的分佈
第一個參數 m[%rsp] 設爲a1 第二個參數 m[%4+rsp] 設爲a2 第三個參數 m[%8+rsp] 設爲a3 第四個參數 m[%c+rsp] 設爲a4 第五個參數 m[%10+rsp] 設爲a5 第六個參數 m[%14+rsp] 設爲a6
這個彙編特別長因此咱們的大部分分析。都放在了代碼的註釋上
40110b: 49 89 e6 mov %rsp,%r14 //%r14=%rsp 40110e: 41 bc 00 00 00 00 mov $0x0,%r12d //%r12d=0x0 401114: 4c 89 ed mov %r13,%rbp //%rbp=%rsp 401117: 41 8b 45 00 mov 0x0(%r13),%eax //eax= a1 40111b: 83 e8 01 sub $0x1,%eax //eax=a1-1 40111e: 83 f8 05 cmp $0x5,%eax // a1-1-5=a1-6 401121: 76 05 jbe 401128 <phase_6+0x34> //a1-6<=0 /* 這裏咱們發現若是輸入的參數>6則會直接爆炸 */ -------------------------------------------------------------------- 401123: e8 12 03 00 00 callq 40143a <explode_bomb> 401128: 41 83 c4 01 add $0x1,%r12d //%r12d+=1 =1 40112c: 41 83 fc 06 cmp $0x6,%r12d // r12d -6 401130: 74 21 je 401153 <phase_6+0x5f> // r12d=6 則跳轉 401132: 44 89 e3 mov %r12d,%ebx // ebx=%r12d =1 401135: 48 63 c3 movslq %ebx,%rax // rax=ebx=1 401138: 8b 04 84 mov (%rsp,%rax,4),%eax //eax=m[rsp+4*rax] =m[rsp+4]=a2 40113b: 39 45 00 cmp %eax,0x0(%rbp) // a1-a2 40113e: 75 05 jne 401145 <phase_6+0x51> // 若是a1 和a2 相同則直接爆炸 401140: e8 f5 02 00 00 callq 40143a <explode_bomb> 401145: 83 c3 01 add $0x1,%ebx //ebx+=1 =2 401148: 83 fb 05 cmp $0x5,%ebx // ebx-5 40114b: 7e e8 jle 401135 <phase_6+0x41> //ebx-5<=0 ebx<=5 40114d: 49 83 c5 04 add $0x4,%r13 //%r13+=4 %r13=%rsp+4 401151: eb c1 jmp 401114 <phase_6+0x20> // 這裏有遞歸關係注意
對上面的代碼寫一個簡單的c語言僞代碼以下
int r12d=0 func (int a[6],int i=0 ){ //裏面保存了咱們的6個參數 if(a[i]>6) bomb! r12d+=1; if (r12d=6){call 0x401153} int tmp=r12d; while(tmp<=5) { int c=tmp; int res=a[c]; if(res==a[i]) bomb!; else{ tmp+=1; } } func(a[6],i++); }
上面就是說咱們的參數都不能同樣。而且每個都不能大於6而後一直要到r12d=6才能繼續
而後繼續往下執行
401153: 48 8d 74 24 18 lea 0x18(%rsp),%rsi //%rsi=%rsp+0x18 401158: 4c 89 f0 mov %r14,%rax //%rax=%r14=%rsp 40115b: b9 07 00 00 00 mov $0x7,%ecx // %ecx=7 401160: 89 ca mov %ecx,%edx //%edx=7 401162: 2b 10 sub (%rax),%edx // %edx=7-a1 401164: 89 10 mov %edx,(%rax) /a1=7-a1 401166: 48 83 c0 04 add $0x4,%rax // %rax=%rsp+4 40116a: 48 39 f0 cmp %rsi,%rax // 這裏實際上是一個判斷 由於咱們的棧幀就到%rsp+14 40116d: 75 f1 jne 401160 <phase_6+0x6c>
上面又造成一個遞歸這裏在寫一個c語言的僞代碼
int rsi=6; func(int i=0){ a[i]=7-a[i]; if(i!=6)func(i++); }
上面至關於讓ai=7-ai(i=1,2,3,4,5,6)
下面的邏輯很是複雜。。。這裏要很認真的看下面說的ai都是咱們一開始輸入的ai。
40116f: be 00 00 00 00 mov $0x0,%esi 401174: eb 21 jmp 401197 <phase_6+0xa3> //將%esi置0以後跳轉到401197 { 401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx //ecx=m[rsp+rsi]= a1 40119a: 83 f9 01 cmp $0x1,%ecx // 這裏若是7-a1<=1 a1>=6 a1=6 則直接401183 40119d: 7e e4 jle 401183 <phase_6+0x8f> } // ai <6 則會走下面 40119f: b8 01 00 00 00 mov $0x1,%eax 4011a4: ba d0 32 60 00 mov $0x6032d0,%edx 4011a9: eb cb jmp 401176 <phase_6+0x82> 401176: 48 8b 52 08 mov 0x8(%rdx),%rdx //rdx= m[6032d8] 40117a: 83 c0 01 add $0x1,%eax //eax=2 40117d: 39 c8 cmp %ecx,%eax 40117f: 75 f5 jne 401176 <phase_6+0x82> 401181: eb 05 jmp 401188 <phase_6+0x94>
這裏咱們須要一個簡單的c語言代碼來看一下到底發生了什麼
首先咱們這裏讀取了m[6032d8]的值咱們須要看一下這裏面有什麼
(gdb) x 0x6032d8 0x6032d8 <node1+8>: 0x006032e0
這裏的node1就頗有靈性。咱們在這多看幾個值
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000 0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000 0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000 0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000 0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000 0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000
這裏咱們能夠發現這實際上是一個單鏈表。上面的操做就是從開始一直日後移動。移動的步數等於7-a[i]
while(7-a[i]--){ P=P-next ;//p就表示咱們鏈表的起點0x6032d0; } //獲得這個p就是咱們的第i個節點 //若是ai=6 則直接到這裏。不然通過上面的處理以後還會到這裏 401183: ba d0 32 60 00 mov $0x6032d0,%edx //%edx=0x6032d0 401188: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2) //m[%rsp+20]=rdx 40118d: 48 83 c6 04 add $0x4,%rsi //%rsi=4 401191: 48 83 fe 18 cmp $0x18,%rsi //%rsi -0x18 401195: 74 14 je 4011ab <phase_6+0xb7> 401197: 8b 0c 34 mov (%rsp,%rsi,1),%ecx //ecx=m[rsp+4]= 7-a2 40119a: 83 f9 01 cmp $0x1,%ecx // 這裏若是7-a2<=1 a2>=6 a2=6 則直接401183 40119d: 7e e4 jle 401183 <phase_6+0x8f> 40119f: b8 01 00 00 00 mov $0x1,%eax 4011a4: ba d0 32 60 00 mov $0x6032d0,%edx 4011a9: eb cb jmp 401176 <phase_6+0x82>
這裏其實又是一個大的循環。看到這裏慢慢好像看懂了。這裏設a[1]-a[j]!=6 pi就是咱們通過上面操做獲得的第p個結點。則通過上面的彙編代碼就會出現下面的結果。若是a[i]=6的話則r[rsp+x]=0x6032d0
也就是物理意義上的node1
r[rsp+20]=p1; r[rsp+28]=p2; ............ r[rsp+20+2*rsi]=pj r[rsp+20+2*(rsi+1)]=0x6032d0 //a[j+1]=6
....剩下的節點不可能會是6所以會把咱們其餘的結點放到這裏。因爲每個數字都不相同因此從r[rsp+20]~r[rsp+50]就是咱們從新排列以後的6個節點。
下面的pi均爲從新排列以後的pi
4011ab: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx // rbx=p1; 4011b0: 48 8d 44 24 28 lea 0x28(%rsp),%rax //rax= rsp+0x28 4011b5: 48 8d 74 24 50 lea 0x50(%rsp),%rsi //rsi=rsp+0x50 4011ba: 48 89 d9 mov %rbx,%rcx //rcx=p1 4011bd: 48 8b 10 mov (%rax),%rdx //rdx=p2 4011c0: 48 89 51 08 mov %rdx,0x8(%rcx)// 4011c4: 48 83 c0 08 add $0x8,%rax //rax=rsp+0x30 4011c8: 48 39 f0 cmp %rsi,%rax // 這裏表示咱們的6個結點是否遍歷完 4011cb: 74 05 je 4011d2 <phase_6+0xde> 4011cd: 48 89 d1 mov %rdx,%rcx 4011d0: eb eb jmp 4011bd <phase_6+0xc9> ``` 上面的功能就是把咱們從新排列以後的鏈表串聯起來。 ```c++ 4011d2: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx) 4011da: bd 05 00 00 00 mov $0x5,%ebp //控制循環 4011df: 48 8b 43 08 mov 0x8(%rbx),%rax //rax=p2 4011e3: 8b 00 mov (%rax),%eax 4011e5: 39 03 cmp %eax,(%rbx) //p2->val<=p1->val 4011e7: 7d 05 jge 4011ee <phase_6+0xfa> 4011e9: e8 4c 02 00 00 callq 40143a <explode_bomb> 4011ee: 48 8b 5b 08 mov 0x8(%rbx),%rbx 4011f2: 83 ed 01 sub $0x1,%ebp 4011f5: 75 e8 jne 4011df <phase_6+0xeb>
上面的式子告訴咱們咱們從新排列完以後的節點必須按照遞減的順序不然就會直接爆炸。那咱們先按照以前的結點把結點大小排序一下。
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000 0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000 0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000 0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000 0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000 0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000
node3>node4>node5>node6>node1>node2
經過上面的分析咱們能夠很容易的總結出答案。用一個簡單的僞代碼來模擬一下上面的全部過程
cin>>a[6];//輸入六個數 if(a[i]=a[i+1]||a[i]>6)bomb!//i=1~6全部元素都不能相同都不能大於6 //這裏咱們有一個單鏈表 node1->node2->node3->node4->node5->node6 // 因爲最後咱們的鏈表要知足單調遞減 // 按照值進行排序以下。因此咱們從新排序的鏈表順序必須以下 node3>node4>node5>node6>node1>node2 List *L=new(List(-1));//新建一個鏈表 for(int i=1;i<=6;i++){ if(a[i]==6)L[i]=node1; //L[i]表示咱們新鏈表的第i個結點。 else{ int b=7-a[i],p=node1; while(b--){ p=p->next; } L[i]=p; //這裏就是7-a[i]爲多少就把node[7-a[i]]賦給L[i] } }
結論能夠很容易獲得
因爲L[5]=node1 因此咱們輸入的第五個數爲6
其餘輸入經過公式L[i]=node[7-a[i]]
L[1]=node3=node[7-a[1]] a[1]=4; L[2]=node4=node[7-a[2]] a[2]=3; L[3]=node5=node[7-a[3]] a[3]=2; L[4]=node6=node[7-a[4]] a[4]=1; L[6]=node2=node[7-a[6]] a[1]=5;
全部最後的輸入爲4 3 2 1 6 5
這裏顯示咱們經過了全部的實驗。是否是超爽的。可是先別急這個實驗還有彩蛋下面讓咱們去找一下彩蛋。
首先是很是皮的一段話
/* Wow, they got it! But isn't something... missing? Perhaps * something they overlooked? Mua ha ha ha ha! */
其實以前讀彙編的代碼的時候就有發現secret_phase的存在感受彩蛋應該就在這裏了吧
咱們發如今phase_defused裏面調用了咱們的隱藏關卡。首先咱們解決如何進入彩蛋關的問題。
00000000004015c4 <phase_defused>: 4015c4: sub $0x78,%rsp 4015c8: mov %fs:0x28,%rax 4015cf: 4015d1: mov %rax,0x68(%rsp) 4015d6: xor %eax,%eax 4015d8: cmpl $0x6,0x202181(%rip) // 603760 <num_input_strings> 4015df: jne 40163f <phase_defused+0x7b> 4015e1: lea 0x10(%rsp),%r8 4015e6: lea 0xc(%rsp),%rcx 4015eb: lea 0x8(%rsp),%rdx 4015f0: mov $0x402619,%esi // 有奇怪的地址,check一下,發現是 "%d %d %s" 4015f5: mov $0x603870,%edi // 這裏是 "" 4015fa: callq 400bf0 <__isoc99_sscanf@plt> //調用sscanf 4015ff: cmp $0x3,%eax
上面check sscanf的返回值表示輸入的參數個數,若是是3個,就到401604行
401602: jne 401635 <phase_defused+0x71> 401604: mov $0x402622,%esi //這裏又有奇怪的地址 check一下 "DrEvil" 401609: lea 0x10(%rsp),%rdi //%rdi=0x10(%rsp) 40160e: callq 401338 <strings_not_equal> 401613: test %eax,%eax 401615: jne 401635 <phase_defused+0x71> 401617: mov $0x4024f8,%edi //check 0x4024f8 Curses, "you've found the secret phase!" 40161c: callq 400b10 <puts@plt> 401621: mov $0x402520,%edi // check 0x402520 "But finding it and solving it are quite different..." 401626: callq 400b10 <puts@plt> 40162b: mov $0x0,%eax 401630: callq 401242 <secret_phase> # 調用彩蛋關 401635: mov $0x402558,%edi // "Congratulations! You've defused the bomb!" 40163a: callq 400b10 <puts@plt> 40163f: mov 0x68(%rsp),%rax 401644: xor %fs:0x28,%rax
經過上面的代碼能夠發現咱們在輸入三個參數%d %d %s
的時候最後輸入DrEvil便可開啓隱藏關。對於第三關和第四關的結果一樣適用。這裏咱們須要找出在哪一關的時候輸入才能開啓隱藏關。
以前咱們發現0x603870做爲sscanf函數的第一個參數它不該該爲空的下面給phase_defused加一個斷點來分析。能夠發現最後的時候裏面的值居然第四關的密碼。能夠確定咱們是在第四關的時候輸入DrEvil進入隱藏關。
(gdb) b *0x4015fa Breakpoint 4 at 0x4015fa (gdb) info break Num Type Disp Enb Address What 4 breakpoint keep y 0x00000000004015fa <phase_defused+54> (gdb) r Border relations with Canada have never been better. Phase 1 defused. How about the next one? 1 2 4 8 16 32 That's number 2. Keep going! 0 207 Halfway there! 7 0 So you got that one. Try this one. IONEFG Good work! On to the next... 4 3 2 1 6 5 Breakpoint 4, 0x00000000004015fa in phase_defused () (gdb) p (char*) 0x603870 $13 = 0x603870 <input_strings+240> "7 0"
接下來的關鍵就是secret_phase和fun7
0000000000401242 <secret_phase>: 401242: 53 push %rbx 401243: e8 56 02 00 00 callq 40149e <read_line> 401248: ba 0a 00 00 00 mov $0xa,%edx 40124d: be 00 00 00 00 mov $0x0,%esi 401252: 48 89 c7 mov %rax,%rdi 401255: e8 76 f9 ff ff callq 400bd0 <strtol@plt> //string to long 40125a: 48 89 c3 mov %rax,%rbx 40125d: 8d 40 ff lea -0x1(%rax),%eax 401260: 3d e8 03 00 00 cmp $0x3e8,%eax 401265: 76 05 jbe 40126c <secret_phase+0x2a> 401267: e8 ce 01 00 00 callq 40143a <explode_bomb> 40126c: 89 de mov %ebx,%esi 40126e: bf f0 30 60 00 mov $0x6030f0,%edi 401273: e8 8c ff ff ff callq 401204 <fun7>
這裏把咱們輸入的值和0x6030f0傳遞給fun7
401278: 83 f8 02 cmp $0x2,%eax 40127b: 74 05 je 401282 <secret_phase+0x40> 40127d: e8 b8 01 00 00 callq 40143a <explode_bomb> 401282: bf 38 24 40 00 mov $0x402438,%edi //check "Wow! You've defused the secret stage!" 401287: e8 84 f8 ff ff callq 400b10 <puts@plt> 40128c: e8 33 03 00 00 callq 4015c4 <phase_defused> 401291: 5b pop %rbx 401292: c3 retq
經過上面咱們發現若是fun7可以返回2的話咱們就完成了彩蛋關那麼關鍵就在於fuc7
0000000000401204 <fun7>: 401204: 48 83 ec 08 sub $0x8,%rsp 401208: 48 85 ff test %rdi,%rdi 40120b: 74 2b je 401238 <fun7+0x34> 40120d: 8b 17 mov (%rdi),%edx 40120f: 39 f2 cmp %esi,%edx 401211: 7e 0d jle 401220 <fun7+0x1c>
上面咱們取了m[rdi]=m[0x6030f0]
的值而後和esi也就是咱們輸入的值進行比較。不如先看看0x6030f0裏放了些什麼 這裏咱們把本題要用到的所有取出來。感受上應該是一個樹結構。由於每個結點都有兩個指針域和一個值域。
(gdb) x/120 0x6030f0 0x6030f0 <n1>: 0x00000024 0x00000000 0x00603110 0x00000000 0x603100 <n1+16>: 0x00603130 0x00000000 0x00000000 0x00000000 0x603110 <n21>: 0x00000008 0x00000000 0x00603190 0x00000000 0x603120 <n21+16>: 0x00603150 0x00000000 0x00000000 0x00000000 0x603130 <n22>: 0x00000032 0x00000000 0x00603170 0x00000000 0x603140 <n22+16>: 0x006031b0 0x00000000 0x00000000 0x00000000 0x603150 <n32>: 0x00000016 0x00000000 0x00603270 0x00000000 0x603160 <n32+16>: 0x00603230 0x00000000 0x00000000 0x00000000 0x603170 <n33>: 0x0000002d 0x00000000 0x006031d0 0x00000000 0x603180 <n33+16>: 0x00603290 0x00000000 0x00000000 0x00000000 0x603190 <n31>: 0x00000006 0x00000000 0x006031f0 0x00000000 0x6031a0 <n31+16>: 0x00603250 0x00000000 0x00000000 0x00000000 0x6031b0 <n34>: 0x0000006b 0x00000000 0x00603210 0x00000000 0x6031c0 <n34+16>: 0x006032b0 0x00000000 0x00000000 0x00000000 0x6031d0 <n45>: 0x00000028 0x00000000 0x00000000 0x00000000 0x6031e0 <n45+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x6031f0 <n41>: 0x00000001 0x00000000 0x00000000 0x00000000 0x603200 <n41+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x603210 <n47>: 0x00000063 0x00000000 0x00000000 0x00000000 0x603220 <n47+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x603230 <n44>: 0x00000023 0x00000000 0x00000000 0x00000000 0x603240 <n44+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x603250 <n42>: 0x00000007 0x00000000 0x00000000 0x00000000 0x603260 <n42+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x603270 <n43>: 0x00000014 0x00000000 0x00000000 0x00000000 0x603280 <n43+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x603290 <n46>: 0x0000002f 0x00000000 0x00000000 0x00000000 0x6032a0 <n46+16>: 0x00000000 0x00000000 0x00000000 0x00000000 0x6032b0 <n48>: 0x000003e9 0x00000000 0x00000000 0x00000000 0x6032c0 <n48+16>: 0x00000000 0x00000000 0x00000000 0x00000000
根據上圖能夠畫出這棵樹
能夠發現這是一顆二分查找樹。這下就簡單多了。
if root->val <=input jmp 0x401220 else 0x401213 401213: 48 8b 7f 08 mov 0x8(%rdi),%rdi 401217: e8 e8 ff ff ff callq 401204 <fun7> //每次都向左走找到符合的值 40121c: 01 c0 add %eax,%eax 40121e: eb 1d jmp 40123d <fun7+0x39> 401220: b8 00 00 00 00 mov $0x0,%eax //%rax=0 401225: 39 f2 cmp %esi,%edx //r->val - input 401227: 74 14 je 40123d <fun7+0x39> //=0 return 401229: 48 8b 7f 10 mov 0x10(%rdi),%rdi // 若是r->val小 去右邊 40122d: e8 d2 ff ff ff callq 401204 <fun7> 401232: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax 401236: eb 05 jmp 40123d <fun7+0x39> 401238: b8 ff ff ff ff mov $0xffffffff,%eax //爲空來這裏 40123d: 48 83 c4 08 add $0x8,%rsp 401241: c3 retq
用僞代碼來解釋上面的過程
int res=0; func(Bitree *r ,long input){ if(!r)return 0xffffffff if(r->val<=input){ res=0; if(r->val <input){ func(r->right,input); res=res*2+1; } else return res; }else{ func(r->left,input); res*=2; } return res; }
能夠發現當輸入爲22的時候能夠正好獲得res=2
Curses, you've found the secret phase! But finding it and solving it are quite different... 22 Wow! You've defused the secret stage! Congratulations! You've defused the bomb! [Inferior 1 (process 120) exited normally]
都寫完的時候仍是頗有成就感的,並且的確頗有趣。不知道何時國內能設計出這麼有意思的實驗cmu賽高。 彩蛋關的時候其實如何找到彩蛋有點參考了別人的教程。當時確實沒想到是這樣找的。以及第六關這個鏈表結構也是獲得了一點提示。這樣可能下降難度了把。 寫的有點匆忙可能會有一些問題。歡迎你們積極指出。我先準備考試啦後面的實驗考完試在更新