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 31 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。

    node

    发现有个的提示,然后多打印几个字节看看,我们猜想可能是个链表。

    node@6

    发现第三个数值比较奇怪,有点像地址,再打印这个地址的内容看看

    linklist

    可以确定是个链表,结点结构大概是

    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->dataN0->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