著名的CSAPP实验:二进制炸弹

就是通过gdb和反汇编猜测程序意图,共有6关和一个隐藏关卡

只有输入正确的字符串才能过关,否则会程序会bomb终止运行

隐藏关卡需要输入特定字符串方会开启

实验材料下载地址:http://csapp.cs.cmu.edu/2e/labs.html

下面通关解法:

反汇编:

objdump -d bomb > bomb_assembly_32.S

Phase 1:

打开bomb_assembly_32.S,定位到<phase_1>函数,可以看到以下代码:

     8048b26:    8b                   mov    0x8(%ebp),%eax

     8048b29:     c4 f8                 add    $0xfffffff8,%esp

     8048b2c:     c0              push   $0x80497c0

     8048b31:                           push   %eax

     8048b32:    e8 f9              call    <strings_not_equal>

     8048b37:     c4                  add    $0x10,%esp

     8048b3a:     c0                    test   %eax,%eax

     8048b3c:                         je     8048b43 <phase_1+0x23>

     8048b3e:    e8 b9              call   80494fc <explode_bomb>

可以看出,用户输入字串指针保存在0x8(%ebp), <phase_1>把此指针放入eax,

然后把$0x80497c0压栈,再把eax也就是用户字串指针压栈,

然后调用<strings_not_equal>

待<strings_not_equal>返回后,测试返回值,

若equal则进入下一phase,否则<explode_bomb>

从<strings_not_equal>可知该函数用于比较两函数的值,因此需要两个字串作为输入,

上面代码中,push %eax用于传递用户字串指针,

则push $0x80497c0自然是传递比较字串的指针了.

打开gdb,x/s 0x80497c0, 可以直接查看到该指针指向的子符串:

Public speaking is very easy.

Phase 2:

打开bomb_assembly_32.S,定位到<phase_2>函数,留意以下几行:

     8048b50:    8b                   mov    0x8(%ebp),%edx

     8048b53:     c4 f8                 add    $0xfffffff8,%esp

     8048b56:    8d  e8                 lea    -0x18(%ebp),%eax

     8048b59:                           push   %eax

     8048b5a:                           push   %edx

     8048b5b:    e8               call   8048fd8 <read_six_numbers>

mov 0x8(%ebp),%edx 将用户字串指针存入edx,

lea -0x18(%ebp),%eax 把ebp-0x18这个地址存入eax,

则最后三句

push %eax

push %edx

call 8048fd8 <read_six_numbers>

相当于read_six_numbers( 用户字串指针地址, ebp-0x18 )

现在我们切换到<read_six_numbers>,看看这个函数是干什么的:

先来看下面2行:

     8048fde:    8b 4d                  mov    0x8(%ebp),%ecx

     8048fe1:    8b  0c                 mov    0xc(%ebp),%edx

把用户字串指针存入ecx, ebp-0x18存入edx

往下看:

     8048fe4:    8d                   lea    0x14(%edx),%eax

eax存入了 edx+0x14 这个值

再往下:

     8048fe7:                           push   %eax

     8048fe8:    8d                   lea    0x10(%edx),%eax

     8048feb:                           push   %eax

     8048fec:    8d  0c                 lea    0xc(%edx),%eax

     8048fef:                           push   %eax

     8048ff0:    8d                   lea    0x8(%edx),%eax

     8048ff3:                           push   %eax

     8048ff4:    8d                   lea    0x4(%edx),%eax

     8048ff7:                           push   %eax

     8048ff8:                           push   %edx

上面几行依次把 edx+0x14, edx+0x10, edx+0xc, edx+0x8, edx+4, edx 这6个地址值压栈

注意edx是<phase_2>的stack frame的 ebp-0x18 这个地址值

     8048ff9:     1b 9b             push   $0x8049b1b

     8048ffe:                           push   %ecx

     8048fff:    e8 5c f8 ff ff           call    <sscanf@plt>

前2行把 $0x8049b1b 和 ecx(用户字串指针) 压栈, 然后调用sscanf

sscanf的原型是int sscanf(const char *str, const char *format, ...);

按format的格式解释str,然后把得到的值放入后面省略号所代表的变量中

因此, 按刚才压栈的顺序, str是用户输入字串, $0x8049b1b 是format的地址,

edx, edx+4,...,edx+0x14是对应的变量.

先用gdb查看format, x/s $0x8049b1b, "%d %d %d %d %d %d".

可知,需要从用户字串中提取6个整数,存入(edx)--(edx+0x14)中.

综上, <read_six_numbers> 作用就是从用户字串中提取6个数字, 存入<phase_2>stack frame中的(ebp-0x18)中

回到<phase_2>接着看:

     8048b63:     7d e8               cmpl   $0x1,-0x18(%ebp)

     8048b67:                         je     8048b6e <phase_2+0x26>

     8048b69:    e8 8e              call   80494fc <explode_bomb>

测试(ebp-0x18)是否等于1, 不等则bomb, 因此用户输入的第一个数字应为1.

     8048b6e:    bb               mov    $0x1,%ebx

     8048b73:    8d  e8                 lea    -0x18(%ebp),%esi

令ebx=1, esi = ebp-18

     8048b76:    8d                   lea    0x1(%ebx),%eax

     8048b79:    0f af  9e fc           imul   -0x4(%esi,%ebx,),%eax

     8048b7e:      9e                 cmp    %eax,(%esi,%ebx,)

     8048b81:                         je     8048b88 <phase_2+0x40>

     8048b83:    e8               call   80494fc <explode_bomb>

     8048b88:                           inc    %ebx

     8048b89:     fb                  cmp    $0x5,%ebx

     8048b8c:    7e e8                    jle    8048b76 <phase_2+0x2e>

注意, esi是存放6个数字中第1数字的地址,

因此 -0x4(%esi,%ebx,4) 表示第ebx个数字,

(%esi,ebx,4)表示第ebx+1个数字

因此上面第3-6行代码检查 a[ebx]*(ebx+1) == a[ebx+1], 其中a[n]表示第n个数字

若不等则bomb,否则ebx增1并循环

因此<phase_2>需要输入一个数列, a[1]=1, a[n+1] = a[n]*(n+1), n<=6

1, 2, 6, 24, 120, 720

Phase 3:

打开bomb_assembly_32.S,定位到<phase_3>函数,可以看到以下代码:

     ;; edx stores pointer of user input

     8048b9f:    8b                   mov    0x8(%ebp),%edx

     8048ba2:     c4 f4                 add    $0xfffffff4,%esp

     ;; push ebp-4 onto stack

     8048ba5:    8d  fc                 lea    -0x4(%ebp),%eax

     8048ba8:                           push   %eax

     ;; push ebp-5 onto stack

     8048ba9:    8d  fb                 lea    -0x5(%ebp),%eax

     8048bac:                           push   %eax

     ;; push ebp-12 onto stack

     8048bad:    8d  f4                 lea    -0xc(%ebp),%eax

     8048bb0:                           push   %eax

     ;; push $0x80497de onto stack

     ;; gdb x/s 0x80497de: "%d %c %d"

     8048bb1:     de              push   $0x80497de

     ;; push pointer of user input onto stack

     8048bb6:                           push   %edx

     8048bb7:    e8 a4 fc ff ff           call    <sscanf@plt>

具体代码请看注释,一开始主要是sscanf(用户字串指针, "%d %c %d", ebp-12, ebp-5, ebp-4)

继续看下去:

     ;; (ebp-12) stores the first int, compare to 7

     ;; cmpl takes (ebp-12) as unsigned int

     8048bc9:     7d f4               cmpl   $0x7,-0xc(%ebp)

     ;; (unsigned)(ebp-12) > 7, jump to 0x8048c88, which will bomb

     8048bcd:    0f  b5           ja     8048c88 <phase_3+0xf0>

     ;; jump to *( 0x80497e8 + 4*(the first int) )

     8048bd3:    8b  f4                 mov    -0xc(%ebp),%eax

     8048bd6:    ff   e8        jmp    *0x80497e8(,%eax,)

关键在于最后的跳转,根据输入的第一个整数确定跳转地址,

地址存储在(0x80497e8 + 4*(the first int)).

容易联想到(0x80497e8)存储着一个跳转表,用gdb查看之,x/10wx 0x80497e8:

    0x80497e8:    0x08048be0    0x08048c00    0x08048c16    0x08048c28

    0x80497f8:    0x08048c40    0x08048c52    0x08048c64    0x08048c76

    0x8049808:    0x67006425    0x746e6169

可以看到表中有很多个地址,先来看第一个地址指向的语句(对应的输入整数为0):

     ;; bl = 0x71

     8048be0:    b3                     mov    $0x71,%bl

     ;; if 0x309==777==the last int,

     ;; jump to 0x8048c8f, which will compare the char

     8048be2:     7d fc         cmpl   $0x309,-0x4(%ebp)

     8048be9:    0f  a0           je     8048c8f <phase_3+0xf7>

     8048bef:    e8               call   80494fc <explode_bomb>

可以看出,先把0x71存入bl,

然后若输入的最后一个整数==777的话,则跳转到0x8048c8f

     ;; after compare the last int, jump here

     ;; bl = 0x71 = 'q', compare to the char

     ;; if ==, jump to 0x8048c99, and leave this function

     8048c8f:    3a 5d fb                 cmp    -0x5(%ebp),%bl

     8048c92:                         je     8048c99 <phase_3+0x101>

     8048c94:    e8               call   80494fc <explode_bomb>

比较输入的字符是否等于'q',若等于则defuse成功

因此,输入应为: "0 q 777"

当然此题应该有不止一个答案,选择跳转表中不同的地址会导致不同的输入.

Phase 4:

打开bomb_assembly_32.S,定位到<phase_4>函数,可以看到以下代码:

     ;; edx = pointer of input string

     8048ce6:    8b                   mov    0x8(%ebp),%edx

     8048ce9:     c4 fc                 add    $0xfffffffc,%esp

     ;; eax = ebp-4

     8048cec:    8d  fc                 lea    -0x4(%ebp),%eax

     ;; push ebp-4

     8048cef:                           push   %eax

     ;; push $0x8049808

     ;; x/s 0x804980: "%d"

     8048cf0:                   push   $0x8049808

     ;; push pointer of input string

     8048cf5:                           push   %edx

     8048cf6:    e8  fb ff ff           call    <sscanf@plt>

就是读入一个整数,存入ebp-4

  ;; func4( input_number )

     8048d11:    8b  fc                 mov    -0x4(%ebp),%eax

     8048d14:                           push   %eax

     8048d15:    e8  ff ff ff           call   8048ca0 <func4>

     8048d1a:     c4                  add    $0x10,%esp

     ;; eax should contain the return value of <func4>

     ;; if eax == 0x37 == 55, defused

     8048d1d:     f8                  cmp    $0x37,%eax

     8048d20:                         je     8048d27 <phase_4+0x47>

     8048d22:    e8 d5              call   80494fc <explode_bomb>

然后比较 func4( input_number )==55, 若等于则成功defuse.

接下来看看<func4>:

     ;; ebx = input_number

     8048ca8:    8b 5d                  mov    0x8(%ebp),%ebx

     ;; if input_number<=1, <func4> return 1

     8048cab:     fb                  cmp    $0x1,%ebx

     8048cae:    7e                     jle    8048cd0 <func4+0x30>

     8048cb0:     c4 f4                 add    $0xfffffff4,%esp

     ;; esi == func4( input_number-1 )

     8048cb3:    8d  ff                 lea    -0x1(%ebx),%eax

     8048cb6:                           push   %eax

     8048cb7:    e8 e4 ff ff ff           call   8048ca0 <func4>

     8048cbc:     c6                    mov    %eax,%esi

     8048cbe:     c4 f4                 add    $0xfffffff4,%esp

     ;; esi += func4( input_number-2 )

     8048cc1:    8d  fe                 lea    -0x2(%ebx),%eax

     8048cc4:                           push   %eax

     8048cc5:    e8 d6 ff ff ff           call   8048ca0 <func4>

     8048cca:     f0                    add    %esi,%eax

很明显是Fibonacci数列,  func4(n) = func4(n-1) + func4(n-2)

注意f(0)=f(1)=1, 通过简单计算知f(9)=55

因此输入应为55

Phase 5:

打开bomb_assembly_32.S,定位到<phase_5>函数,可以看到以下代码:

     ;; ebx = pointer of input

     ;; push ebx onto stack

     ;; call string_length

     8048d34:    8b 5d                  mov    0x8(%ebp),%ebx

     8048d37:     c4 f4                 add    $0xfffffff4,%esp

     8048d3a:                           push   %ebx

     8048d3b:    e8 d8              call    <string_length>

     8048d40:     c4                  add    $0x10,%esp

     ;; eax stores the return value of string_length

     ;; if eax == 6, jump to 0x8048d4d 

     8048d43:     f8                  cmp    $0x6,%eax

     8048d46:                         je     8048d4d <phase_5+0x21>

     8048d48:    e8 af              call   80494fc <explode_bomb>

从上面代码可知,输入需要6个字符.

     ;; edx = 0

     8048d4d:     d2                    xor    %edx,%edx

     ;; ecx = ebp-8

     8048d4f:    8d 4d f8                 lea    -0x8(%ebp),%ecx

     ;; esi = 0x804b220

     8048d52:    be  b2             mov    $0x804b220,%esi

     ;; edx is a counter from 0 to 5

     ;; al = (edx + ebx), then al reads a char each time

     8048d57:    8a  1a                 mov    (%edx,%ebx,),%al

     ;; extract the low 4 bit of al

     8048d5a:     0f                    and    $0xf,%al

     ;; sign-extend al to eax

     8048d5c:    0f be c0                 movsbl %al,%eax

     ;; al = ( eax + 0x804b220 )

     ;; x/16c 0x804b220:

     ;; 0x804b220:    105 'i'    115 's'    114 'r'    118 'v'    101 'e'    97 'a'    119 'w'    104 'h'

     ;; 0x804b228:    111 'o'    98 'b'    112 'p'    110 'n'    117 'u'    116 't'    102 'f'    103 'g'

     8048d5f:    8a                   mov    (%eax,%esi,),%al

     ;; edx + ecx = al,

     ;; notice that, ecx = ebp-8

     ;; and edx is a counter from 0 to 5

     8048d62:      0a                 mov    %al,(%edx,%ecx,)

     8048d65:                           inc    %edx

     ;; loop

     8048d66:     fa                  cmp    $0x5,%edx

     8048d69:    7e ec                    jle    8048d57 <phase_5+0x2b>

     ;; ebp-2 = 0, a terminal of string started from ebp-8

     8048d6b:    c6  fe               movb   $0x0,-0x2(%ebp)

     8048d6f:     c4 f8                 add    $0xfffffff8,%esp

上面代码的作用是循环读取6个输入字符中的每一字符input[k],

提取input[k]的低四位,把这四位构成的整数index当作索引,

查找0x804b220开始16个字节中存储的字符.

用gdb查看, x/16c 0x804b220:

     0x804b220:     'i'     's'     'r'     'v'     'e'     'a'     'w'     'h'

     0x804b228:     'o'     'b'     'p'     'n'     'u'     't'     'f'     'g'

获取0x804b220[ input[k] & 0xf ]后,将之copy至 (ebp-8)[k]

继续看:

     ;; x/s 0x804980b: "giants"

     ;; push "giants"

     8048d72:     0b              push   $0x804980b

     ;; push ebp-8

     8048d77:    8d  f8                 lea    -0x8(%ebp),%eax

     8048d7a:                           push   %eax

     ;; compare "giants" and the string started from ebp-8

     8048d7b:    e8 b0              call    <strings_not_equal>

     8048d80:     c4                  add    $0x10,%esp

     8048d83:     c0                    test   %eax,%eax

     ;; if two strings equal to each other, defused

     8048d85:                         je     8048d8c <phase_5+0x60>

     8048d87:    e8               call   80494fc <explode_bomb>

上面代码便是将ebp-18开始的字串和"giants"比较,若相等,则defused.

注意到 (ebp-18)[k] = 0x804b220[ input[k] & 0xf ]

     0x804b220:     'i'     's'     'r'     'v'     'e'     'a'     'w'     'h'

     0x804b228:     'o'     'b'     'p'     'n'     'u'     't'     'f'     'g'

因此,

     input[]&0xf = 0xf, input[]&0xf = 0x0,

     input[]&0xf = 0x5, input[]&0xf = 0xb,

     input[]&0xf = 0xd, input[]&0xf = 0x1,

只要输入的各个字符的低四位符合上面就好,我个人选取了"opekma"

Phase 6:

写得太复杂了,各种内外循环,各种跳转,看得头晕,日后有闲再看.

现在先把能看懂的部份写出来:

     ;; edx = pointer of input

     8048da1:    8b                   mov    0x8(%ebp),%edx

     ;; (ebp-0x34) = $0x804b26c

     8048da4:    c7  cc 6c b2       movl   $0x804b26c,-0x34(%ebp)

     8048dab:     c4 f8                 add    $0xfffffff8,%esp

     ;; read six numbers from input,

     ;; and storse in the area started from ebp-18

     8048dae:    8d  e8                 lea    -0x18(%ebp),%eax

     8048db1:                           push   %eax

     8048db2:                           push   %edx

     8048db3:    e8               call   8048fd8 <read_six_numbers>

上面代码就是从输入读入6个整数,存入ebp-0x18,

初步怀疑0x804b26c地址存放着一个链表.

     ;; edi = 0

     8048db8:     ff                    xor    %edi,%edi

     8048dba:     c4                  add    $0x10,%esp

     8048dbd:    8d                   lea    0x0(%esi),%esi

     ;; eax = (ebp-0x18 + 4*edi) = six-number[edi]

     ;; ebp-0x18 = the beginning address of the six numbers

     ;; edi is a counter from 0 to 5

     8048dc0:    8d  e8                 lea    -0x18(%ebp),%eax

     8048dc3:    8b  b8                 mov    (%eax,%edi,),%eax

     ;; eax = six-number[edi]-1

     8048dc6:                           dec    %eax

     ;; if eax <= 5 , continue

     8048dc7:     f8                  cmp    $0x5,%eax

     8048dca:                         jbe    8048dd1 <phase_6+0x39>

     8048dcc:    e8 2b              call   80494fc <explode_bomb>

     ;; if edi+1 > 5, finish edi loop

     8048dd1:    8d 5f                  lea    0x1(%edi),%ebx

     8048dd4:     fb                  cmp    $0x5,%ebx

     8048dd7:    7f                     jg     8048dfc <phase_6+0x64>

     ;; (ebp-0x38) = edi*4

     8048dd9:    8d  bd         lea    0x0(,%edi,),%eax

     8048de0:      c8                 mov    %eax,-0x38(%ebp)

     ;; esi = ebp-18 = the beginning address of the six numbers

     8048de3:    8d  e8                 lea    -0x18(%ebp),%esi

     ;; edx = (ebp-0x38) = edi*4

     ;; inner loops,

     ;; ebx is the counter from edi+1 to 5

     8048de6:    8b  c8                 mov    -0x38(%ebp),%edx

     ;; eax = edx + esi = six-number[edi]

     8048de9:    8b                   mov    (%edx,%esi,),%eax

     ;; compare six-number[edi] and six-number[edi+ebx]

     8048dec:    3b  9e                 cmp    (%esi,%ebx,),%eax

     ;; if six-number[edi] != six-number[edi+1], continue

     8048def:                         jne    8048df6 <phase_6+0x5e>

     8048df1:    e8               call   80494fc <explode_bomb>

     ;; ebx++

     ;; if ebx<=5, jump to 0x8048de6, ebx loops

     ;; else , finish ebx loop

     8048df6:                           inc    %ebx

     8048df7:     fb                  cmp    $0x5,%ebx

     8048dfa:    7e ea                    jle    8048de6 <phase_6+0x4e>

内外两层循环,外层用edi计数,确保输入的6个整数不大于6,

内层用ebx计数,保证所有数字两两不相等.

再往后的代码异常混乱,各种链表离历,没空看....

先从网上获得答案:4 2 6 3 1 5

Secret Phase:

首先要找到<secret_phase>的入口,经搜索发现入口是在<phase_defused>里面.

先来看看<phase_defused>:

     ;; every time call read_line, ( 0x804b480 )++

     ;; only with 6 correct answer given ,will the secret phase appear

     :     3d  b4        cmpl   $0x6,0x804b480

     804953a:                         jne    804959f <phase_defused+0x73>

(0x804b480)是一个计数器,每当调用一次<read_line>每自增1,因此只有6关全通才能打开隐藏关卡.

     ;; push ebp-0x50

     804953c:    8d 5d b0                 lea    -0x50(%ebp),%ebx

     804953f:                           push   %ebx

     ;; push ebp-0x54

     :    8d  ac                 lea    -0x54(%ebp),%eax

     :                           push   %eax

     ;; (gdb) x/s 0x8049d03

     ;; 0x8049d03:    "%d %s"

     :      9d             push   $0x8049d03

     ;; push the string stores in 0x804b770

     ;; the address of input of phase 4

     :      b7             push   $0x804b770

     804954e:    e8 0d f3 ff ff           call    <sscanf@plt>

     ....

     ;; (gdb) x/s 0x8049d09

     ;; 0x8049d09:    "austinpowers"

     804955e:      9d             push   $0x8049d09

     ;; push the %s

     :                           push   %ebx

     :    e8 c7 fa ff ff           call    <strings_not_equal>

省略号上方的代码调用sscanf( (char *)0x804b770, "%d %s", (int *)(ebp-0x54), (char *)ebp-0x50 )

即从0x804b770读入一个整数和字串.

再看省略号下方的代码,比较读入的字串和"austinpowers", 若相等,则打开<secret_phase>

好了,现在问题是,如何把一个整数和"austinpowers"写入地址0x804b770?

回想前几关,写入字串都是通过read_line,所以猜想可能是在某一关的输入中多输入些内容以写入地址0x804b770.

用gdb查看前几关输入字串的指针,发现第4关的输入刚好是在地址0x804b770,而Phase 4只需输入一个数字,因此只需

在第4关的输入中多输入一个"austinpowers"即可进入<secret_phase>.

现在看看<secret_phase>:

     8048eef:    e8               call   80491fc <read_line>

     8048ef4:    6a                     push   $0x0

     ;; strtol( user input string, 0, 10)

     ;; long int strtol(const char *nptr, char **endptr, int base);

     ;; converts the initial part of the string in nptr to a long integer value according to the given base

     8048ef6:    6a 0a                    push   $0xa

     8048ef8:    6a                     push   $0x0

     8048efa:                           push   %eax

     8048efb:    e8 f0 f8 ff ff           call   80487f0 <__strtol_internal@plt>

首先,读入一个字串,并用strtol将之转换为long int.

     ;; if fun7( 0x804b320, the input long int )

     ;; x/d 0x804b320: (0x804b320) = 36

     8048f17:                           push   %ebx

     8048f18:      b3             push   $0x804b320

     8048f1d:    e8  ff ff ff           call   8048e94 <fun7>

     8048f22:     c4                  add    $0x10,%esp

     ;; if fun7(0x804b320, the input long int) == 7, defused

     8048f25:     f8                  cmp    $0x7,%eax

     8048f28:                         je     8048f2f <secret_phase+0x47>

     8048f2a:    e8 cd              call   80494fc <explode_bomb>

代码很简单,调用fun7( (void *)0x804b320, 输入的整数 ),若返回值==7, 则成功defused.

现在看看<fun7>:

     ;; edx = the first parameter, an address

     8048e9a:    8b                   mov    0x8(%ebp),%edx

     ;; eax = the input long int

     8048e9d:    8b  0c                 mov    0xc(%ebp),%eax

     ;; if edx != 0

     8048ea0:     d2                    test   %edx,%edx

     8048ea2:     0c                    jne    8048eb0 <fun7+0x1c>

     8048ea4:    b8 ff ff ff ff           mov    $0xffffffff,%eax

     8048ea9:    eb                     jmp    8048ee2 <fun7+0x4e>

     8048eab:                           nop

     8048eac:    8d                 lea    0x0(%esi,%eiz,),%esi

     ;; if the input number >= (edx), jump to 0x8048ec5 

     8048eb0:    3b                     cmp    (%edx),%eax

     8048eb2:    7d                     jge    8048ec5 <fun7+0x31>

     ;; eax > (edx)

     8048eb4:     c4 f8                 add    $0xfffffff8,%esp

     ;; <func7>( (edx+4) ,the input long int )

     8048eb7:                           push   %eax

     8048eb8:    8b                   mov    0x4(%edx),%eax

     8048ebb:                           push   %eax

     8048ebc:    e8 d3 ff ff ff           call   8048e94 <fun7>

     ;; return eax *= 2, exit

     8048ec1:     c0                    add    %eax,%eax

     8048ec3:    eb 1d                    jmp    8048ee2 <fun7+0x4e>

     ;; the input number >= (edx)

     ;; if eax == (edx), return eax=0

     8048ec5:    3b                     cmp    (%edx),%eax

     8048ec7:                         je     8048ee0 <fun7+0x4c>

     ;; the input number > (edx)

     8048ec9:     c4 f8                 add    $0xfffffff8,%esp

     ;; <fun7>( (edx+8) ,the input long int )

     8048ecc:                           push   %eax

     8048ecd:    8b                   mov    0x8(%edx),%eax

     8048ed0:                           push   %eax

     8048ed1:    e8 be ff ff ff           call   8048e94 <fun7>

     ;; fun7 return 2*eax + 1

     8048ed6:     c0                    add    %eax,%eax

     8048ed8:                           inc    %eax

     8048ed9:    eb                     jmp    8048ee2 <fun7+0x4e>

     8048edb:                           nop

     8048edc:    8d                 lea    0x0(%esi,%eiz,),%esi

     8048ee0:     c0                    xor    %eax,%eax

从上面代码可看出函数原型是:fun7( void *address, long int number ).

    当 number == *(int*)address, fun7( address, number) = 

    当 number > *(int*)address, fun7( address, number) = *fun7( address+, number ) + 

    当 number < *(int*)address, fun7( address, number) = *fun7( address+, number )

从上面可以看出, 上面的address表示的是棵二叉树(左子树的值<父节点的值, 右子树的值>父节点的值):

     struct BST

     {

         int num;

         struct BST *left;

         struct BST *right;

     } *bst;

则上面的递推式可表示为:

    当 number == bst->num, fun7( bst, number ) = ;

    当 number > bst->num, fun7( bst, number ) = *fun7( bst->right, number ) + ;

    当 number < bst->num, fun7( bst, number ) = *fun7( bst->left, number );

鉴于<secret_phase>需要fun7( (struct BST *)0x804b320, number )返回7,一个奇数,所以第一步应该执行第二钟情况,

又经观察发现以下递推规律:

        fun7( (struct BST *)0x804b320, number )

    =    * fun7( (struct BST *)0x804b320->right, number ) + 

    =    * ( * fun7( (struct BST *)0x804b320->right->right, number ) + ) + 

    =    * fun7( (struct BST *)0x804b320->right->right, number ) + 

    =    * ( * fun7( (struct BST *)0x804b320->right->right->right, number ) + ) + 

    =    * fun7( (struct BST *)0x804b320->right->right->right, number ) + 

因此当 number == (struct BST *)0x804b320->right->right->right->num, fun7便可返回7

用gdb查看,

    x/wx 0x804b320+  ==>  0x0804b308 

    x/wx 0x804b308+  ==>  0x0804b2d8

    x/wx 0x804b2d8+  ==>  0x0804b278

    x/d  0x0804b278   ==>  

因此应输入1001

CSAPP Lab2: Binary Bomb的更多相关文章

  1. csapp lab2 bomb 二进制炸弹《深入理解计算机系统》

    bomb炸弹实验 首先对bomb这个文件进行反汇编,得到一个1000+的汇编程序,看的头大. phase_1: 0000000000400ef0 <phase_1>: 400ef0: 48 ...

  2. CSAPP lab2 二进制拆弹 binary bombs phase_6

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.htmlphase_2  https://www.cnblogs. ...

  3. CSAPP lab2 二进制拆弹 binary bombs phase_5

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.htmlphase_2  https://www.cnblogs. ...

  4. CSAPP lab2 二进制拆弹 binary bombs phase_4

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.htmlphase_2  https://www.cnblogs. ...

  5. CSAPP lab2 二进制拆弹 binary bombs phase_3

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.htmlphase_2  https://www.cnblogs. ...

  6. CSAPP lab2 二进制拆弹 binary bombs phase_2

    给出对应于7个阶段的7篇博客 phase_1  https://www.cnblogs.com/wkfvawl/p/10632044.htmlphase_2  https://www.cnblogs. ...

  7. CSAPP lab2 二进制拆弹 binary bombs phase_1

    这个实验从开始到完成大概花了三天的时间,由于我们还没有学习编译原理.汇编语言等课程,为了完成这个实验我投机取巧了太多,看了网上很多的解题方法,为了更加深入学习编译反编译,觉得需要从头开始好好梳理一下. ...

  8. CSAPP:Binary Bomb

    本篇文章参考了:http://www.cnblogs.com/remlostime/archive/2011/05/21/2052708.html大神的文章,有时候没思路了会来看一下,但是保证本文的每 ...

  9. IDA Pro 在CSAPP lab2中的使用

    在做lab2的时候,偶然的情况下,发现了IDA pro这样一个反汇编工具,总的来说对于lab2这样的小实验读懂代码的大体功能是有作用的,但对于想要具体明白某一条指令的执行过程,又显得不足,到最后还是需 ...

随机推荐

  1. js函数参数设置默认值

    php有个很方便的用法是在定义函数时可以直接给参数设默认值,如: function simue ($a=1,$b=2){  return $a+$b;}echo simue(); //输出3echo ...

  2. php 获取某个月的周次信息

    在做统计的时候如果按照周统计 ,需要对某个月的周次信息进行计算,如果本月头一天不是星期一,则向上一个月取周一,本月最后的几天如果不能正好是一周,则忽略. 例如 2019-09月计算出来的结果 2016 ...

  3. 如何在google test中指定只运行一部分测试

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:如何在google test中指定只运行一部分测试.

  4. ASP.NET MVC提交到服务器的几种方法

    多年不搞WEB开发了,最近有个小活干干,记录一下学习的心得. 以下为几种脚本向服务器提交的方法: 1. $.ajax({ type: "GET", url: "/Test ...

  5. unity3d中namespace的使用注意问题

    最近在重构游戏中的老一套逻辑,同时要加入新的功能逻辑,因为各种命名问题,就使用namespace进行控制.但是namespace在unity3d引擎中的使用与一般不同,需要注意一点:要作为Compon ...

  6. 【转载】NativeSQL实例

    转自:http://blog.sina.com.cn/s/blog_3f2c03e301017fqz.html     ---------------------------------------- ...

  7. Windows下安装Eric5时出现的“Sorry, please install QtHelp.”问题解决办法

    解决Windows下安装Eric5时出现的“Sorry, please install QtHelp.”问题   PyQt4在Windows中使用了DirectX作为加速,不过,PyQt4没有使用最新 ...

  8. DTrace to Troubleshoot Java Native Memory Problems

    How to Use DTrace to Troubleshoot Java Native Memory Problems on Oracle Solaris 11 Hands-On Labs of ...

  9. WCF - 学习总目录

    WCF - 基础 WCF - 地址 WCF - 绑定 WCF - 绑定后续之自定义绑定 WCF - 契约 WCF - 序列化 WCF - 消息 WCF - 实例与会话 WCF - REST服务

  10. HDU2029JAVA

    Palindromes _easy version Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Jav ...