CS APP ex2 bomb——二进制炸弹
CS APP的实验2,拆解bomb
目录- CS APP的实验2,拆解bomb
- 进入gdb调试模式
- 查看主函数
- phase_1函数
- phase_2函数
- phase_3函数
- phase_4函数
- phase_5
- phase_6
这里是在linux环境下通过gdb来进行的调试
进入gdb调试模式
终端中输入$ gdb bomb
查看主函数
输入(gdb) list main
发现其中初始化函数initialize_bomb()
后,有两个print
输出提示,然后紧接着一个input = read_line()
,将我们的输入作为参数调用了phase_1
函数。
所以这个phase_1
应该就是对输入进行检查的函数,查看该函数的汇编。
phase_1函数
- 输入
(gdb) disassemble phase_1
输出如下
Dump of assembler code for function phase_1:
0x08048b80 <+0>: push %ebp
0x08048b81 <+1>: mov %esp,%ebp
0x08048b83 <+3>: sub $0x8,%esp
0x08048b86 <+6>: movl $0x8049928,0x4(%esp)
0x08048b8e <+14>: mov 0x8(%ebp),%eax
0x08048b91 <+17>: mov %eax,(%esp)
0x08048b94 <+20>: call 0x8049067
0x08048b99 <+25>: test %eax,%eax
0x08048b9b <+27>: je 0x8048ba2
0x08048b9d <+29>: call 0x804962e
0x08048ba2 <+34>: leave
0x08048ba3 <+35>: ret
End of assembler dump.
- 前两句其中对当前的ebp寄存器内容进行压栈,并将栈指针寄存器esp和基地址寄存器ebp修改为当前栈顶地址。
当前状态
栈底方向^
...
main
...
参数
旧ebp <- ebp, esp寄存器
- 第三行将栈顶指针移动了8个字节
栈底方向^
...
旧ebp <- ebp
空
空 <- esp寄存器
- 然后两条mov指令,分别将两个值移入了空出来的栈位置,其中一个是
0x08049928
,另一个是基于ebp向栈底方向偏移8个字节的栈中内容,也就是我们的参数input
栈底方向^
...
旧ebp <- ebp
0x8049928
0x8(%ebp) <- esp寄存器
- 接着调用了函数
,所以我们对里的内存0x08049928
内容进行查看,就是我们的拆炸弹需要的字符串。
(gdb) x/s 0x8049928
答案是:Why make trillions when we could make... billions?
phase_2函数
- 输入
(gdb) disassemble phase_1
0x08048ba4 <+0>: push %ebp
0x08048ba5 <+1>: mov %esp,%ebp
0x08048ba7 <+3>: sub $0x28,%esp
0x08048baa <+6>: movl $0x0,-0x4(%ebp)
0x08048bb1 <+13>: lea -0x20(%ebp),%eax
0x08048bb4 <+16>: mov %eax,0x4(%esp)
0x08048bb8 <+20>: mov 0x8(%ebp),%eax
0x08048bbb <+23>: mov %eax,(%esp)
0x08048bbe <+26>: call 0x8048fd4
0x08048bc3 <+31>: movl $0x0,-0x8(%ebp)
0x08048bca <+38>: jmp 0x8048bf3
0x08048bcc <+40>: mov -0x8(%ebp),%eax
0x08048bcf <+43>: mov -0x20(%ebp,%eax,4),%edx
0x08048bd3 <+47>: mov -0x8(%ebp),%eax
0x08048bd6 <+50>: add $0x3,%eax
0x08048bd9 <+53>: mov -0x20(%ebp,%eax,4),%eax
0x08048bdd <+57>: cmp %eax,%edx
0x08048bdf <+59>: je 0x8048be6
0x08048be1 <+61>: call 0x804962e
0x08048be6 <+66>: mov -0x8(%ebp),%eax
0x08048be9 <+69>: mov -0x20(%ebp,%eax,4),%eax
0x08048bed <+73>: add %eax,-0x4(%ebp)
0x08048bf0 <+76>: incl -0x8(%ebp)
0x08048bf3 <+79>: cmpl $0x2,-0x8(%ebp)
0x08048bf7 <+83>: jle 0x8048bcc
0x08048bf9 <+85>: cmpl $0x0,-0x4(%ebp)
0x08048bfd <+89>: jne 0x8048c04
0x08048bff <+91>: call 0x804962e
0x08048c04 <+96>: leave
0x08048c05 <+97>: ret
-
sub $0x28,%esp
首先sp指针向下空出了0x28也就是40B的空间,32位的系统下,共10个单元,为了方便称之为M0~M9
-
movl $0x0,-0x4(%ebp)
然后给第0h号单元置零,也就是
M0=0
-
lea -0x20(%ebp),%eax; mov %eax,0x4(%esp)
将第7号单元的地址赋值给第8号单元。也就是
M8=&M7
可以推断,M8可能是个数组指针,M7是数组的首地址。 -
mov 0x8(%ebp),%eax;mov %eax,(%esp);call 0x8048fd4
将参数放入最后一个单元M9,然后调用函数,读取了6个数字。正好10个单元剩下了6个,组成了一个6个元素的数组,应该是放在了M6M2中,这里我们成为A0A5。
-
$movl 0x0,-0x8(%ebp)
将1号单元置零,M1=0
-
$cmpl 0x2,-0x8(%ebp);jle 0x8048bcc
将M1和2比较,循环条件是小于等于
-
mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%edx
取值ebp-20+4*M1放入edx,其实就是edx=A[M1]
-
-0x8(%ebp),%eax; add $0x3,%eax; mov -0x20(%ebp,%eax,4),%eax
eax=A[M1+3]
-
cmp %eax,%edx; je 0x8048be6
; call 0x804962e 比较eax和edx,如果不相等这调用exploed_bomb
-
mov -0x8(%ebp),%eax; mov -0x20(%ebp,%eax,4),%eax; add %eax,-0x4(%ebp); incl -0x8(%ebp)
将A[M1]写入M0,M1++
-
经推断,原函数大致如下
void phase_2(char* input){
int result = 0;
int a[6];
input >> a;
for(int i=0;i<=2;i++){
if (a[i]!=a[i+3])
bomb();
result += a[i];
}
if(reuslt == 0)
bomb()
}
所以答案是一个6个元素的数组,a[i]要和a[i+3]相同,且sum(a[0],a[1],a[2])!=0即可。如
1 2 3 1 2 3
或1 1 1 1 1 1
phase_3函数
Dump of assembler code for function phase_3:
0x08048c06 <+0>: push %ebp
0x08048c07 <+1>: mov %esp,%ebp
0x08048c09 <+3>: sub $0x28,%esp
0x08048c0c <+6>: movl $0x0,-0x8(%ebp)
0x08048c13 <+13>: movl $0x0,-0x4(%ebp)
0x08048c1a <+20>: lea -0x10(%ebp),%eax
0x08048c1d <+23>: mov %eax,0xc(%esp)
0x08048c21 <+27>: lea -0xc(%ebp),%eax
0x08048c24 <+30>: mov %eax,0x8(%esp)
0x08048c28 <+34>: movl $0x804995b,0x4(%esp)
0x08048c30 <+42>: mov 0x8(%ebp),%eax
0x08048c33 <+45>: mov %eax,(%esp)
0x08048c36 <+48>: call 0x8048868
0x08048c3b <+53>: mov %eax,-0x4(%ebp)
0x08048c3e <+56>: cmpl $0x1,-0x4(%ebp)
0x08048c42 <+60>: jg 0x8048c49
0x08048c44 <+62>: call 0x804962e
0x08048c49 <+67>: mov -0xc(%ebp),%eax
0x08048c4c <+70>: mov %eax,-0x14(%ebp)
0x08048c4f <+73>: cmpl $0x7,-0x14(%ebp)
0x08048c53 <+77>: ja 0x8048c98
0x08048c55 <+79>: mov -0x14(%ebp),%edx
0x08048c58 <+82>: mov 0x8049964(,%edx,4),%eax
0x08048c5f <+89>: jmp *%eax
0x08048c61 <+91>: addl $0x2cc,-0x8(%ebp)
0x08048c68 <+98>: subl $0x3a9,-0x8(%ebp)
0x08048c6f <+105>: addl $0x12f,-0x8(%ebp)
0x08048c76 <+112>: subl $0xcb,-0x8(%ebp)
0x08048c7d <+119>: addl $0x5d,-0x8(%ebp)
0x08048c81 <+123>: subl $0x2f4,-0x8(%ebp)
0x08048c88 <+130>: addl $0x2f4,-0x8(%ebp)
0x08048c8f <+137>: subl $0x207,-0x8(%ebp)
0x08048c96 <+144>: jmp 0x8048c9d
0x08048c98 <+146>: call 0x804962e
0x08048c9d <+151>: mov -0xc(%ebp),%eax
0x08048ca0 <+154>: cmp $0x5,%eax
0x08048ca3 <+157>: jg 0x8048cad
0x08048ca5 <+159>: mov -0x10(%ebp),%eax
0x08048ca8 <+162>: cmp %eax,-0x8(%ebp)
0x08048cab <+165>: je 0x8048cb2
0x08048cad <+167>: call 0x804962e
0x08048cb2 <+172>: leave
0x08048cb3 <+173>: ret
End of assembler dump.
-
0x08048c06 ~ 0x08048c36
进行了如下操作,申请10个字(4B/字)的空间,
M0=0, M1=0, M6=&M3, M7=&M2,M8=0x804995b, M9=%ebp+8
(依然是从ebp-0x4到ebp-0x28,每4B作为一个单位),然后调用函数sscanf
。惯例其中M9是我们的input,然后M8未知,M7,M2作为指针参数来传递值,放入M2,M3中,因为参数的逆序入栈的。
-
我们检查M8(0x804995b)的内容
可以发现,里面存放的是字符串"%d %d"
也就是告诉我们输入两个数字,两个数字正是通过两个指针来实现传递的。
-
0x08048c3b~0x08048c44
对
sscanf
的结果进行检查,查看是否大于1,否则调用bomb() -
0x08048c49~0x08048c53
对num1进行检查,要求不大于7。
-
0x08048c55~0x08048c5f
一条无条件跳转语句,跳转到(num1*4+0x8049964),注意外面的
*
,跳转的目标不是num1*4+0x8049964
,而是该内存中该地址里的值,因为目前num1的范围是不大于7,还有点大 -
0x08048c61~0x08048c96
这部分应该是上一条跳转语句将要跳转的位置,分别是M1加上或减去某个数。
-
0x08048c9d~0x08048ca3
对num1再进行检查,要求不大于5,这里再次缩小了num1的范围
-
0x08048ca5~0x08048cab
对M1和num2进行比较,要求相等
所以num2就应该是M1计算的结果,而num1则是小于等于5的值,我们可以取5,计算得num2位-519
phase_4函数
Dump of assembler code for function phase_4:
0x08048ce3 <+0>: push %ebp
0x08048ce4 <+1>: mov %esp,%ebp
0x08048ce6 <+3>: sub $0x28,%esp
0x08048ce9 <+6>: lea -0xc(%ebp),%eax
0x08048cec <+9>: mov %eax,0x8(%esp)
0x08048cf0 <+13>: movl $0x8049984,0x4(%esp)
0x08048cf8 <+21>: mov 0x8(%ebp),%eax
0x08048cfb <+24>: mov %eax,(%esp)
0x08048cfe <+27>: call 0x8048868
0x08048d03 <+32>: mov %eax,-0x4(%ebp)
0x08048d06 <+35>: cmpl $0x1,-0x4(%ebp)
0x08048d0a <+39>: jne 0x8048d13
0x08048d0c <+41>: mov -0xc(%ebp),%eax
0x08048d0f <+44>: test %eax,%eax
0x08048d11 <+46>: jg 0x8048d18
0x08048d13 <+48>: call 0x804962e
0x08048d18 <+53>: mov -0xc(%ebp),%eax
0x08048d1b <+56>: mov %eax,(%esp)
0x08048d1e <+59>: call 0x8048cb4
0x08048d23 <+64>: mov %eax,-0x8(%ebp)
0x08048d26 <+67>: cmpl $0x78,-0x8(%ebp)
0x08048d2a <+71>: je 0x8048d31
0x08048d2c <+73>: call 0x804962e
0x08048d31 <+78>: leave
0x08048d32 <+79>: ret
End of assembler dump.
-
0x08048ce3~0x08048cfe
进行了如下操作:申请了4B*10的栈空间,将M2(
ebp-0xc
)的地址写入M7(esp+0x8
),将0x8049984
写入M8,将上层传递进来的参数写入M9,然后调用我们对
0x8049984
进行检查,里面是字符串%d
,可见是读取一个整型同指针M7写入M2 -
0x08048d03~0x08048d06
对
sscanf
的返回值进行检查,要求只输入了一个数值 -
0x08048d0c~0x08048d11
对输入的数进行检查,要求大于0
-
0x08048d18~0x08048d1e
将输入作为参数,调用func4
-
检查func4
可以发现,func4是一个求阶乘的递归函数。
-
0x08048d23~0x08048d2a
将func4的返回值放入M1,对M1和0x78进行比较,要求相等,也就是要求M1==120
我们只需找出阶乘结果位120的数,即答案:5
phase_5
(gdb) disassemble phase_5
Dump of assembler code for function phase_5:
0x08048d33 <+0>: push %ebp
0x08048d34 <+1>: mov %esp,%ebp
0x08048d36 <+3>: sub $0x18,%esp
0x08048d39 <+6>: mov 0x8(%ebp),%eax
0x08048d3c <+9>: mov %eax,(%esp)
0x08048d3f <+12>: call 0x804903d
0x08048d44 <+17>: mov %eax,-0x4(%ebp)
0x08048d47 <+20>: cmpl $0x6,-0x4(%ebp)
0x08048d4b <+24>: je 0x8048d52
0x08048d4d <+26>: call 0x804962e
0x08048d52 <+31>: movl $0x0,-0x8(%ebp)
0x08048d59 <+38>: movl $0x0,-0xc(%ebp)
0x08048d60 <+45>: jmp 0x8048d7e
0x08048d62 <+47>: mov -0xc(%ebp),%eax
0x08048d65 <+50>: add 0x8(%ebp),%eax
0x08048d68 <+53>: movzbl (%eax),%eax
0x08048d6b <+56>: movsbl %al,%eax
0x08048d6e <+59>: and $0xf,%eax
0x08048d71 <+62>: mov 0x804a5c0(,%eax,4),%eax
0x08048d78 <+69>: add %eax,-0x8(%ebp)
0x08048d7b <+72>: incl -0xc(%ebp)
0x08048d7e <+75>: cmpl $0x5,-0xc(%ebp)
0x08048d82 <+79>: jle 0x8048d62
0x08048d84 <+81>: cmpl $0x49,-0x8(%ebp)
0x08048d88 <+85>: je 0x8048d8f
0x08048d8a <+87>: call 0x804962e
0x08048d8f <+92>: leave
0x08048d90 <+93>: ret
End of assembler dump.
-
0x08048d33~0x08048d44
申请4B*6的栈空间,将输入作为参数调用函数
,并将结果写入M0,即M0=length(input)
-
0x08048d47~0x08048d4d
对输入长度进行检查,要求长度为6。
-
0x08048d52~0x08048d60
将M1,M2赋值为0,跳转
-
0x08048d62~0x08048d6e
将(input+M2)0扩展后放入eax,再取出eax对应地址里的值,因为input其实是个字符串,也就是相当于
eax=input[M2]
,然后截取eax的后8位,符号位扩展写回,最后取后四位。这里因为输入是个ascii码,范围是0~127所以符号位扩展肯定是0扩展。最终这系列操作就相当于取输入每个字符的ascii码值的后四位。 -
0x08048d71~0x08048d78
取基于
0x804a5c0
偏移的内存中的内容,累加到M1中 -
0x08048d7b~0x08048d82
M2自增,判断是否小于等于5,继续循环
-
0x08048d84
要求累加结果位0x49
-
检查
0x804a5c0
内容对应的数值:2, 10, 6, 1, 12, 16, 9, 3, 4, 7, 14, 5, 11, 8, 15,
我们取其中6个,让和等于0x49
这里我取下标2,5,5,5,5,7的6个数,只要后四位满足条件即可,所以字符串可以取
255557
,这里顺序无关
phase_6
(gdb) disassemble phase_6
Dump of assembler code for function phase_6:
0x08048e25 <+0>: push %ebp
0x08048e26 <+1>: mov %esp,%ebp
0x08048e28 <+3>: sub $0x18,%esp
0x08048e2b <+6>: movl $0x804a660,-0x8(%ebp)
0x08048e32 <+13>: mov 0x8(%ebp),%eax
0x08048e35 <+16>: mov %eax,(%esp)
0x08048e38 <+19>: call 0x8048858
0x08048e3d <+24>: mov %eax,-0xc(%ebp)
0x08048e40 <+27>: mov -0x8(%ebp),%eax
0x08048e43 <+30>: mov %eax,(%esp)
0x08048e46 <+33>: call 0x8048d91
0x08048e4b <+38>: mov %eax,-0x8(%ebp)
0x08048e4e <+41>: mov -0x8(%ebp),%eax
0x08048e51 <+44>: mov %eax,-0x4(%ebp)
0x08048e54 <+47>: movl $0x1,-0x10(%ebp)
0x08048e5b <+54>: jmp 0x8048e69
0x08048e5d <+56>: mov -0x4(%ebp),%eax
0x08048e60 <+59>: mov 0x8(%eax),%eax
0x08048e63 <+62>: mov %eax,-0x4(%ebp)
0x08048e66 <+65>: incl -0x10(%ebp)
0x08048e69 <+68>: cmpl $0x4,-0x10(%ebp)
0x08048e6d <+72>: jne 0x8048e5d
0x08048e6f <+74>: mov -0x4(%ebp),%eax
0x08048e72 <+77>: mov (%eax),%eax
0x08048e74 <+79>: cmp -0xc(%ebp),%eax
0x08048e77 <+82>: je 0x8048e7e
0x08048e79 <+84>: call 0x804962e
0x08048e7e <+89>: leave
0x08048e7f <+90>: ret
-
0x08048e25~0x08048e3d
申请4B*6的栈空间,将
0x804a660
放入M1,将input作为参数调用
,返回结果写入M2 -
0x08048e40~0x08048e4b
将M1作为参数,调用fun6,将返回结果写回M1
-
0x08048e4e~0x08048e51
M0=M1
-
0x08048e54~0x08048e6d
这里是一个循环如下
for(M3 = 1; M3!= 4; M3++) M0 = *(M0)+8
-
0x08048e6f~0x08048e7f
eax = *M0,也就是将M0指向的值赋值给eax
然后将eax和M2比较,要求相等 -
接着我们对fun6进行解析
Dump of assembler code for function fun6:
0x08048d91 <+0>: push %ebp
0x08048d92 <+1>: mov %esp,%ebp
0x08048d94 <+3>: sub $0x10,%esp
0x08048d97 <+6>: mov 0x8(%ebp),%eax
0x08048d9a <+9>: mov %eax,-0x10(%ebp)
0x08048d9d <+12>: mov 0x8(%ebp),%eax
0x08048da0 <+15>: mov %eax,-0x10(%ebp)
0x08048da3 <+18>: mov 0x8(%ebp),%eax
0x08048da6 <+21>: mov 0x8(%eax),%eax
0x08048da9 <+24>: mov %eax,-0xc(%ebp)
0x08048dac <+27>: mov -0x10(%ebp),%eax
0x08048daf <+30>: movl $0x0,0x8(%eax)
0x08048db6 <+37>: jmp 0x8048e1a
0x08048db8 <+39>: mov -0x10(%ebp),%eax
0x08048dbb <+42>: mov %eax,-0x4(%ebp)
0x08048dbe <+45>: mov -0x10(%ebp),%eax
0x08048dc1 <+48>: mov %eax,-0x8(%ebp)
0x08048dc4 <+51>: jmp 0x8048dd5
0x08048dc6 <+53>: mov -0x4(%ebp),%eax
0x08048dc9 <+56>: mov %eax,-0x8(%ebp)
0x08048dcc <+59>: mov -0x4(%ebp),%eax
0x08048dcf <+62>: mov 0x8(%eax),%eax
0x08048dd2 <+65>: mov %eax,-0x4(%ebp)
0x08048dd5 <+68>: cmpl $0x0,-0x4(%ebp)
0x08048dd9 <+72>: je 0x8048de9
0x08048ddb <+74>: mov -0x4(%ebp),%eax
0x08048dde <+77>: mov (%eax),%edx
0x08048de0 <+79>: mov -0xc(%ebp),%eax
0x08048de3 <+82>: mov (%eax),%eax
0x08048de5 <+84>: cmp %eax,%edx
0x08048de7 <+86>: jg 0x8048dc6
0x08048de9 <+88>: mov -0x8(%ebp),%eax
0x08048dec <+91>: cmp -0x4(%ebp),%eax
0x08048def <+94>: je 0x8048dfc
0x08048df1 <+96>: mov -0x8(%ebp),%edx
0x08048df4 <+99>: mov -0xc(%ebp),%eax
0x08048df7 <+102>: mov %eax,0x8(%edx)
0x08048dfa <+105>: jmp 0x8048e02
0x08048dfc <+107>: mov -0xc(%ebp),%eax
0x08048dff <+110>: mov %eax,-0x10(%ebp)
0x08048e02 <+113>: mov -0xc(%ebp),%eax
0x08048e05 <+116>: mov 0x8(%eax),%eax
0x08048e08 <+119>: mov %eax,-0x8(%ebp)
0x08048e0b <+122>: mov -0xc(%ebp),%edx
0x08048e0e <+125>: mov -0x4(%ebp),%eax
0x08048e11 <+128>: mov %eax,0x8(%edx)
0x08048e14 <+131>: mov -0x8(%ebp),%eax
0x08048e17 <+134>: mov %eax,-0xc(%ebp)
0x08048e1a <+137>: cmpl $0x0,-0xc(%ebp)
0x08048e1e <+141>: jne 0x8048db8
0x08048e20 <+143>: mov -0x10(%ebp),%eax
0x08048e23 <+146>: leave
0x08048e24 <+147>: ret
-
0x08048d91~0x08048da9
这里fun6申请了
4B*4
个栈空间,我们分别叫做N0-N3就是-4(%ebp)~-10(%ebp),接下来,将参数的值写入N3,将参数指向的内存的值写入N2。即,N3 = x, N2 = *(x+8)
-
0x08048dac~0x08048daf
置零操作*(N3+8) = 0,结合上一步,也就是先将内存中x+8存的数据拷贝到N2,然后将x+8的值改为0,且可以观察出x+8是一个32bit的数据。
-
那我们可以现尝试将x里的内容打印观察
x是参数,也就是phase_6中的M1,phase_6一开始有个操作是将
0x804a660
赋值给M1。发现有个
的提示,然后多打印几个字节看看,我们猜想可能是个链表。 发现第三个数值比较奇怪,有点像地址,再打印这个地址的内容看看
可以确定是个链表,结点结构大概是
struct node{ type data; int index; struct* next; }
-
所以前两步操作可以进一步解释
因为其中一个单元占4B,地址+8正好是next域。
N3=head; N2=head->next; head->next=null;
-
0x08048db6和0x08048e1a~0x08048e1e
jmp和一个条件判断,这可以理解成是一个while循环,循环的条件是
N2!=0
,也就是N2结点是否为空。我们最外面的大框架有了,
while(N2!=nullptr){...}
-
我们跳回
也就是0x08048db8~0x08048dc4
这里将N3赋值给N0和N1,也就是
N0=N3,N1=N3
,显然是个循环内的赋初值操作,然后跳转到
。 -
0x08048dd5~0x08048dd9
紧接着又是一个判断和跳转,可以发现这里并没有往回跳,也就是一个if语句,有两个分支。
那么函数框架大致如下:
N3=head, N2=head->next; head->next=nullptr; while(N2!=nullptr){ N0=N1=N3; if(N0 != nullptr)
else } -
我们先看非空部分
0x08048ddb~0x08048de7 先是
edx=N0->data; eax=N2->data;
然后对两者进行比较,结合上面的循环外的赋值操作,可以看出其实是
N0->data
和N0->next->data
进行比较。然后如果
N0->data
大于N0->next->data
,跳转到否则继续执行。
-
0x08048dc6~0x08048dd9 这里的操作先是,将N0赋给N1,然后将*(N0+8)赋值给N0,也就是N0=N0->next
N1=N0; N0=N0->next;
然后进行一个N0的非空判断,如果非空,那就继续上一步比较N0和N0->next
此外这里发现前面一层对
0x08048dec
的判断错了,这里并不是if语句,而是一个循环N3=ptr, N2=ptr->next; while(N2!=nullptr){ N0=N1=N3; while(N0 != nullptr){ if(N0->data > N0->next->data){ N1=N0,N0=N0->next; continue; } else
} else } -
0x08048de9~0x08048def if(N0==N1)
else -
0x08048df1~0x08048dfa 如果
N0!=N1,N1->next = N2;
,然后跳转 -
N1=N2->next; N2->next = N0; N2=N1;
接着是判断N2是否为空,又回到了最外层的循环。整理一下现有的部分
N3=head; N2=head->next; N3->next=nullptr; while(N2!=nullptr){ N0=N3;N1=N3; while(N0!=nullptr){ if(N0->data > N2->data){ N1=N0; N0=N0->next; } } if(N1==N0){ N3=N2; }else{ N1->next=N2; } N1=N2->next; N2->next = N0; N2=N1; } return N3;
-
所以这个函数是一个将链表从小到大排序的一个函数
-
结合我们对phase_6的解析,我们就是要先对链表进行排序,然后输入一个acsii码和链表第4个值相等的字符
所以答案是451