逆向工程实验

        又一个计算系统的实验,这次实验的主要目标是熟悉GDB调试工具,以及一些汇编的语法。

  实验环境:32位操作系统(Fedora 13)。

  实验内容:本实验设计为一个黑客拆解二进制炸弹的游戏。我们仅给黑客(同学)提供一个二进制可执行文件bomb和主函数所在的源程序bomb.c,不提供每个关卡的源代码。程序运行中有6个关卡(6phase),每个关卡需要用户输入正确的字符串或数字才能通关,否则会引爆炸弹!

  要求同学运用GDB调试工具和objdump反汇编工具,通过分析汇编代码找到在每个phase程序段中,引导程序跳转到“explode_bomb”程序段的地方,并分析其成功跳转的条件,以此为突破口寻找应该在命令行输入何种字符串来通关。

  接下来是各个关卡的解答

1. phase_1

phase_1的代码如图1。

逆向工程实验_第1张图片

                图1

分析:0x8048b3e是bomb引导函数,其转移所需条件是%eax为0,通过阅读strings_not_equal的代码可以知道,若两个字符串相等,则%eax设置为0,否则设置为1;那么,bomb函数的转移条件就变成两个字符串不相等,而两个字符串分别为0x80497c0和%eax所存储的字符串首地址。%eax是用户输入的字符串,那么答案就是地址0x804907c0所存储的字符串。

逆向工程实验_第2张图片

           图2

输出字符串可知答案为“Public speaking is very easy.”。

2. phase_2

phase_2汇编代码如下图3所示。

逆向工程实验_第3张图片

                  图3

分析:图中红框表示两个炸弹的位置,而黄色方框表示引爆炸弹的转移条件,先分析第一个炸弹,转移条件为M (%ebp-0x18) 的不等于1,那么就需要知道 (%ebp-0x18) 表示什么,第一次出现%ebp-0x18是在地址0x8048b56,此时的%ebp-0x18是作为一个参数传入给函数read_six_numbers,显然,这是一个读取六个数字的函数,那么阅读这个函数的代码,代码如图4所示。从图中可以看出,%ebp的值表示是一个数组的地址,而这个函数就是为这个数组写入值得。从图中可以看到还有一个炸弹,但这个炸弹的意思是如果输入数字不足6个,就引爆炸弹,所以这道题需要输入6个数。而数组的首地址是(%ebp-0x18),那么回到调用函数phase_2,那么调用函数的第一个炸弹的拆除条件就明了了,就是数组的第一个数必须是1。接下来是第二个炸弹,经过阅读和简单的举例,可以得到第二个炸弹之前的那段代码是一个循环,而且是一个迭代,假设数组为a,那么这个循环的意思是a[1]=2*a[0],a[2]=3*a[1],a[3]=4*a[2],以此类推,直到求出a[5]为止,那么这个炸弹也就拆除了。

这道题的答案就是1、2、6、24、120、720

逆向工程实验_第4张图片

                  图4

3. phase_3

phase_3的汇编代码如下:

08048b98 :

 8048b98: 55                    push   %ebp

 8048b99: 89 e5                 mov    %esp,%ebp

 8048b9b: 83 ec 14              sub    $0x14,%esp

 8048b9e: 53                    push   %ebx

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

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

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

 8048ba8:50                   push   %eax

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

 8048bac:50                   push   %eax

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

 8048bb0:50                   push   %eax

 8048bb1:68 de 97 04 08       push   $0x80497de

 8048bb6:52                   push   %edx

 8048bb7:e8 a4 fc ff ff       call   8048860

 8048bbc: 83 c4 20              add    $0x20,%esp

 8048bbf: 83 f8 02              cmp    $0x2,%eax

 8048bc2: 7f 05                 jg     8048bc9

 8048bc4:e8 33 09 00 00       call   80494fc

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

 8048bcd:0f 87 b5 00 00 00    ja     8048c88

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

 8048bd6:ff 24 85 e8 97 04 08 jmp    *0x80497e8(,%eax,4)

 8048bdd: 8d 76 00              lea    0x0(%esi),%esi

 8048be0: b3 71                 mov    $0x71,%bl

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

 8048be9: 0f 84 a0 00 00 00     je     8048c8f

 8048bef:e8 08 09 00 00       call   80494fc

 8048bf4: e9 96 00 00 00        jmp    8048c8f

 8048bf9: 8d b4 26 00 00 00 00 lea    0x0(%esi,%eiz,1),%esi

 8048c00: b3 62                 mov    $0x62,%bl

 8048c02: 81 7d fc d6 00 00 00 cmpl   $0xd6,-0x4(%ebp)

 8048c09: 0f 84 80 00 00 00     je     8048c8f

 8048c0f:e8 e8 08 00 00       call   80494fc

 8048c14: eb 79                 jmp    8048c8f

 8048c16: b3 62                 mov    $0x62,%bl

 8048c18: 81 7d fc f3 02 00 00 cmpl   $0x2f3,-0x4(%ebp)

 8048c1f: 74 6e                 je     8048c8f

 8048c21:e8 d6 08 00 00       call   80494fc

 8048c26: eb 67                 jmp    8048c8f

 8048c28: b3 6b                 mov    $0x6b,%bl

 8048c2a: 81 7d fc fb 00 00 00 cmpl   $0xfb,-0x4(%ebp)

 8048c31: 74 5c                 je     8048c8f

 8048c33:e8 c4 08 00 00       call   80494fc

 8048c38: eb 55                 jmp    8048c8f

 8048c3a: 8d b6 00 00 00 00     lea    0x0(%esi),%esi

 8048c40: b3 6f                 mov    $0x6f,%bl

 8048c42: 81 7d fc a0 00 00 00 cmpl   $0xa0,-0x4(%ebp)

 8048c49: 74 44                 je     8048c8f

 8048c4b:e8 ac 08 00 00       call   80494fc

 8048c50: eb 3d                 jmp    8048c8f

 8048c52: b3 74                 mov    $0x74,%bl

 8048c54: 81 7d fc ca 01 00 00 cmpl   $0x1ca,-0x4(%ebp)

 8048c5b: 74 32                 je     8048c8f

 8048c5d:e8 9a 08 00 00       call   80494fc

 8048c62: eb 2b                 jmp    8048c8f

 8048c64: b3 76                 mov    $0x76,%bl

 8048c66: 81 7d fc 0c 03 00 00 cmpl   $0x30c,-0x4(%ebp)

 8048c6d: 74 20                 je     8048c8f

 8048c6f:e8 88 08 00 00       call   80494fc

 8048c74: eb 19                 jmp    8048c8f

 8048c76: b3 62                 mov    $0x62,%bl

 8048c78: 81 7d fc 0c 02 00 00 cmpl   $0x20c,-0x4(%ebp)

 8048c7f: 74 0e                 je     8048c8f

 8048c81:e8 76 08 00 00       call   80494fc

 8048c86: eb 07                 jmp    8048c8f

 8048c88: b3 78                 mov    $0x78,%bl

 8048c8a:e8 6d 08 00 00       call   80494fc

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

 8048c92: 74 05                 je     8048c99

 8048c94:e8 63 08 00 00       call   80494fc

 8048c99: 8b 5d e8              mov    -0x18(%ebp),%ebx

 8048c9c: 89 ec                 mov    %ebp,%esp

 8048c9e: 5d                    pop    %ebp

 8048c9f: c3                    ret    

简要分析如下:

黄色部分:为输入函数做准备,通过gdb调试分析可知,地址-0x4(%ebp)、-0x5(%ebp)、-0xc(%ebp)为三个要输入的参数,具体调试过程见图5。黄色部分中地址0x80497de存储的是字符串,其值为“%d  %c  %d”,说明中间的数是char,而前后两数是int。

逆向工程实验_第5张图片

              图5

把断点设在0x8048bbf,run程序,在第三关输入5 a 160的测试数据,然后检查寄存器ebp的值,从汇编代码可以知道,ebp存储的是一个地址,而-0x4(%ebp)为输入的第三个数,-0x5(%ebp)为输入的第二个数,-0xc(%ebp)为输入的第一个数。

红色部分:为炸弹的位置,第一个炸弹比较简单,意思是输入数据少于3个就引爆炸弹。后面的炸弹是针对不同的输入做不同的处理。

绿色部分:switch头部的判断,当第一个输入大于7时,跳到倒数第二个炸弹位置,所以倒数第二个炸弹可以拆除。

其它部分:现在任务就简单了,通过都代码就可以得出答案了。最后一个炸弹是判断第二个参数的,所有答案如下表所示。

第一个参数

0

1

2

4

5

6

7

第二个参数

q

b

b

k

o

v

b

第三个参数

777

214

755

251

160

458

524

4. phase_4

phase_4代码如下,这里直接阅读代码即可读懂,所以没有调试。

08048ce0 :

 8048ce0: 55                    push   %ebp

 8048ce1: 89 e5                 mov    %esp,%ebp

 8048ce3: 83 ec 18              sub    $0x18,%esp

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

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

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

 8048cef: 50                    push   %eax

 8048cf0: 68 08 98 04 08        push   $0x8049808

 8048cf5: 52                    push   %edx

 8048cf6: e8 65 fb ff ff        call   8048860

 8048cfb: 83 c4 10              add    $0x10,%esp

 8048cfe: 83 f8 01              cmp    $0x1,%eax

 8048d01: 75 06                 jne    8048d09

 8048d03: 83 7d fc 00           cmpl   $0x0,-0x4(%ebp)

 8048d07: 7f 05                 jg     8048d0e

 8048d09:e8 ee 07 00 00       call   80494fc

 8048d0e: 83 c4 f4              add    $0xfffffff4,%esp

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

 8048d14: 50                    push   %eax

 8048d15: e8 86 ff ff ff        call   8048ca0

 8048d1a: 83 c4 10              add    $0x10,%esp

 8048d1d: 83 f8 37              cmp    $0x37,%eax

 8048d20: 74 05                 je     8048d27

 8048d22:e8 d5 07 00 00       call   80494fc

 8048d27: 89 ec                 mov    %ebp,%esp

 8048d29: 5d                    pop    %ebp

 8048d2a: c3                    ret    

 8048d2b: 90                    nop


具体分析:

红色部分:炸弹位置,按照第三关的分析方法,第一个炸弹作用是判断输入的数据是否大于0,所以输入的数据只有一个,且其值必须大于0。第二个炸弹判断经过函数func4处理后,得到的结果是否等于55。

 

接下来主要是看func4的作用。

08048ca0 :

 8048ca0: 55                    push   %ebp

 8048ca1: 89 e5                 mov    %esp,%ebp

 8048ca3: 83 ec 10              sub    $0x10,%esp

 8048ca6: 56                    push   %esi

 8048ca7: 53                    push   %ebx

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

 8048cab: 83 fb 01              cmp    $0x1,%ebx

 8048cae: 7e 20                 jle    8048cd0

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

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

 8048cb6: 50                    push   %eax

 8048cb7: e8 e4 ff ff ff        call   8048ca0

 8048cbc: 89 c6                 mov    %eax,%esi

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

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

 8048cc4: 50                    push   %eax

 8048cc5: e8 d6 ff ff ff        call   8048ca0

 8048cca: 01 f0                 add    %esi,%eax

 8048ccc: eb 07                 jmp    8048cd5

 8048cce: 89 f6                 mov    %esi,%esi

 8048cd0: b8 01 00 00 00        mov    $0x1,%eax

 8048cd5: 8d 65 e8              lea    -0x18(%ebp),%esp

 8048cd8: 5b                    pop    %ebx

 8048cd9: 5e                    pop    %esi

 8048cda: 89 ec                 mov    %ebp,%esp

 8048cdc: 5d                    pop    %ebp

 8048cdd: c3                    ret    

 8048cde: 89 f6                 mov    %esi,%esi

func4用来处理输入的数,该函数的功能是求斐波那契数。地址8048cab的语句判断当数小于等于1时,返回1。其它情况的处理是func4(n-1)+func4(n-2)。就是这样,没什么好说的。

那么答案就是:9。

5. phase_5

phase_5的代码如下:

08048d2c :

 8048d2c: 55                    push   %ebp

 8048d2d: 89 e5                 mov    %esp,%ebp

 8048d2f: 83 ec 10              sub    $0x10,%esp

 8048d32: 56                    push   %esi

 8048d33: 53                    push   %ebx

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

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

 8048d3a: 53                    push   %ebx

 8048d3b: e8 d8 02 00 00        call   8049018

 8048d40: 83 c4 10              add    $0x10,%esp

 8048d43: 83 f8 06              cmp    $0x6,%eax

 8048d46: 74 05                 je     8048d4d

 8048d48:e8 af 07 00 00       call   80494fc

 8048d4d:31 d2                xor    %edx,%edx

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

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

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

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

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

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

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

 8048d65:42                   inc    %edx

 8048d66:83 fa 05             cmp    $0x5,%edx

 8048d69:7e ec                jle    8048d57

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

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

 8048d72: 68 0b 98 04 08        push   $0x804980b

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

 8048d7a: 50                    push   %eax

 8048d7b: e8 b0 02 00 00        call   8049030

 8048d80: 83 c4 10              add    $0x10,%esp

 8048d83: 85 c0                 test   %eax,%eax

 8048d85: 74 05                 je     8048d8c

 8048d87:e8 70 07 00 00       call   80494fc

 8048d8c: 8d 65 e8              lea    -0x18(%ebp),%esp

 8048d8f: 5b                    pop    %ebx

 8048d90: 5e                    pop    %esi

 8048d91: 89 ec                 mov    %ebp,%esp

 8048d93: 5d                    pop    %ebp

 8048d94: c3                    ret    

 8048d95: 8d 76 00              lea    0x0(%esi),%esi


具体分析:

红色部分:两个炸弹,从第一个炸弹前面调用了函数string_length可以知道,输入数据是一个字符串,且其长度为6。第二个炸弹之前有一个判断字符串是否相等的函数,地址0x8048d6f

的语句是一个字符串常量,通过gdb调试输出其值,为“giants”。那么初步判断输入字符串经过某些处理后和giants进行相等比较,若相等,%eax设置为0,否则,设置为1。看起来和第一道题很相似。那么问题主要在中间的黄色部分呢。

黄色部分:%edx是一个计数器,初始化为0,%ecx是待构造的字符串,%esi是字符串常量,其值如图6所示。%ebx是输入的字符串。


            图6

接下来是一个循环,该循环的作用是依次取出%ebx的字符,截取其后四位得到值x,x再符号扩展为32位为y,然后在%esi存储的字符串常量中找到对应位置y的字符。最后的判断只要构造出的字符串为giants,则可拆除炸弹。那么答案也就显而易见了。这道题答案不定。这里只给出其中一个:opekmq

6. phase_6

08048d98 :

 8048d98: 55                    push   %ebp

 8048d99: 89 e5                 mov    %esp,%ebp

 8048d9b: 83 ec 4c              sub    $0x4c,%esp

 8048d9e: 57                    push   %edi

 8048d9f: 56                    push   %esi

 8048da0: 53                    push   %ebx

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

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

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

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

 8048db1: 50                    push   %eax

 8048db2: 52                    push   %edx

 8048db3: e8 20 02 00 00        call   8048fd8

 8048db8: 31 ff                 xor    %edi,%edi

 8048dba: 83 c4 10              add    $0x10,%esp

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

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

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

 8048dc6:48                   dec    %eax

 8048dc7:83 f8 05             cmp    $0x5,%eax

 8048dca:76 05                jbe    8048dd1

 8048dcc:e8 2b 07 00 00       call   80494fc

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

 8048dd4:83 fb 05             cmp    $0x5,%ebx

 8048dd7:7f 23                jg     8048dfc

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

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

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

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

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

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

 8048def:75 05                jne    8048df6

 8048df1:e8 06 07 00 00       call   80494fc

 8048df6:43                   inc    %ebx

 8048df7:83 fb 05             cmp    $0x5,%ebx

 8048dfa:7e ea                jle    8048de6

 8048dfc:47                   inc    %edi

 8048dfd:83 ff 05             cmp    $0x5,%edi

 8048e00:7e be                jle    8048dc0

 8048e02: 31 ff                 xor    %edi,%edi

 8048e04: 8d 4d e8              lea    -0x18(%ebp),%ecx

 8048e07: 8d 45 d0              lea    -0x30(%ebp),%eax

 8048e0a: 89 45 c4              mov    %eax,-0x3c(%ebp)

 8048e0d: 8d 76 00              lea    0x0(%esi),%esi

 8048e10:8b 75 cc             mov    -0x34(%ebp),%esi

 8048e13:bb 01 00 00 00       mov    $0x1,%ebx

 8048e18:8d 04 bd 00 00 00 00 lea    0x0(,%edi,4),%eax

 8048e1f:89 c2                mov    %eax,%edx

 8048e21:3b 1c 08             cmp    (%eax,%ecx,1),%ebx

 8048e24:7d 12                jge    8048e38

 8048e26:8b 04 0a             mov    (%edx,%ecx,1),%eax

 8048e29:8d b4 26 00 00 00 00 lea    0x0(%esi,%eiz,1),%esi

 8048e30:8b 76 08             mov    0x8(%esi),%esi

 8048e33:43                   inc    %ebx

 8048e34:39 c3                cmp    %eax,%ebx

 8048e36:7c f8                jl     8048e30

 8048e38:8b 55 c4             mov    -0x3c(%ebp),%edx

 8048e3b:89 34 ba             mov    %esi,(%edx,%edi,4)

 8048e3e:47                   inc    %edi

 8048e3f:83 ff 05             cmp    $0x5,%edi

 8048e42:7e cc                jle    8048e10

 8048e44: 8b 75 d0              mov    -0x30(%ebp),%esi

 8048e47: 89 75 cc              mov    %esi,-0x34(%ebp)

 8048e4a: bf 01 00 00 00        mov    $0x1,%edi

 8048e4f: 8d 55 d0              lea    -0x30(%ebp),%edx

 8048e52:8b 04 ba             mov    (%edx,%edi,4),%eax

 8048e55:89 46 08             mov    %eax,0x8(%esi)

 8048e58:89 c6                mov    %eax,%esi

 8048e5a:47                   inc    %edi

 8048e5b:83 ff 05             cmp    $0x5,%edi

 8048e5e:7e f2                jle    8048e52

 8048e60: c7 46 08 00 00 00 00 movl   $0x0,0x8(%esi)

 8048e67: 8b 75 cc              mov    -0x34(%ebp),%esi

 8048e6a: 31 ff                 xor    %edi,%edi

 8048e6c: 8d 74 26 00           lea    0x0(%esi,%eiz,1),%esi

 8048e70:8b 56 08             mov    0x8(%esi),%edx

 8048e73:8b 06                mov    (%esi),%eax

 8048e75:3b 02                cmp    (%edx),%eax

 8048e77:7d 05                jge    8048e7e

 8048e79:e8 7e 06 00 00       call   80494fc

 8048e7e:8b 76 08             mov    0x8(%esi),%esi

 8048e81:47                   inc    %edi

 8048e82:83 ff 04             cmp    $0x4,%edi

 8048e85:7e e9                jle    8048e70

 8048e87: 8d 65 a8              lea    -0x58(%ebp),%esp

 8048e8a: 5b                    pop    %ebx

 8048e8b: 5e                    pop    %esi

 8048e8c: 5f                    pop    %edi

 8048e8d: 89 ec                 mov    %ebp,%esp

 8048e8f: 5d                    pop    %ebp

 8048e90: c3                    ret    

 8048e91: 8d 76 00              lea    0x0(%esi),%esi


具体分析:

这段代码需要输入6个数字。这里以2 1 3 4 5 6为测试数据。

红色部分:炸弹的位置,共有3个。

绿色部分:为一个双层循环。输入数据通过单步运行输出各个寄存器的值可知,这个双层循环的作用为:对输入的6个数字判断是否在1~6之间,且互不相等。若其中有大于6或有两个数相等则引爆炸弹。图7为单步调试过程部分截图。

逆向工程实验_第6张图片

              图7

黄色部分:为第二个循环,这个循环有点奇怪,它的作用是为地址%ebp-0x34之后的连续六个地址空间分配六个奇怪的地址。这个地址分别为0x804b260、0x804b26c、0x804b254、0x804b248、0x804b23c、0x8048230。对应图8。观察可知,这些地址大小都相差0xc,而且这个循环结束之后,0x804b26c这个最大的值位于六个连续地址的第二位,其它都是按递减顺序来的,再次分布调试代码可知,这些值得分配是按输入数据的大小来分的,它们的值对应如下表。(看不懂先看后面)

输入的数据

分配的地址

2

0x804b260

1

0x804b26c

3

0x804b254

4

0x804b248

5

0x804b23c

6

0x8048230


逆向工程实验_第7张图片

            图8  phase_6 部分调试过程

灰色部分:第三个循环,这个循环的作用如下:

地址

对应的值

0xbffff1d4

0x804b260

0x804b274

0x804b254

0x804b268

0x804b26c

0x804b25c

0x804b248

0x804b250

0x804b23c

0x804b244

0x804b230

简单来说,就是在第二个循环那些奇怪的值加上0x8之后对应的地址存接下来的值。可以想到类似于链表之类的东西。(看不懂先看后面)

蓝色部分:第四个循环,这个循环看完之后我瞬间明白之前两个循环的作用了。这个循环分别比较了0x804b260和0x804b26c等地址对所存值的大小,如果后者大于等于前者,就引爆炸弹。所以,这里我把所有奇怪地址对应的值都输出了。如下表。


地址

0x804b26c

253

0x804b260

725

0x804b254

301

0x804b248

997

0x804b23c

212

0x804b230

432

结合前面黄色部分的分析,可以知道这道题其实就是给右表这些值排个序,使他们按降序排列。

那么答案也就显而易见了:4 2 6 3 1 5

7. secrect_phase

一开始看main函数的时候就发现了一个隐藏关卡。不过不知道怎么启动。既然前六关做完离ddl还有很多时间,这里就来试一下。

main函数中每个关卡之后都有一行 call   804952c ,那么接下来就来看一下这个函数。

0804952c :

 804952c: 55                    push   %ebp

 804952d: 89 e5                 mov    %esp,%ebp

 804952f: 83 ec 64              sub    $0x64,%esp

 8049532: 53                    push   %ebx

 8049533: 83 3d 80 b4 04 08 06 cmpl   $0x6,0x804b480    //这个直接数的值是空

 804953a: 75 63                 jne    804959f

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

 804953f: 53                    push   %ebx

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

 8049543: 50                    push   %eax

 8049544: 68 03 9d 04 08        push   $0x8049d03   //%d %s”,输入一个数和一个字符串

 8049549: 68 70 b7 04 08        push   $0x804b770   //空值

 804954e: e8 0d f3 ff ff        call   8048860

 8049553: 83 c4 10              add    $0x10,%esp

 8049556: 83 f8 02              cmp    $0x2,%eax  //输入的数据的个数(一个字符串算一个)

 8049559: 75 37                 jne    8049592  //若不等于两个则结束这个函数

 804955b: 83 c4 f8              add    $0xfffffff8,%esp

 804955e: 68 09 9d 04 08        push   $0x8049d09    //austinpowers

 8049563: 53                    push   %ebx

 8049564: e8 c7 fa ff ff        call   8049030

 8049569: 83 c4 10              add    $0x10,%esp

 804956c: 85 c0                 test   %eax,%eax

 804956e: 75 22                 jne    8049592   //若输入的字符串和“austinpowers”不同,则结束函数

 8049570: 83 c4 f4              add    $0xfffffff4,%esp

 8049573: 68 20 9d 04 08        push   $0x8049d20   //Curses, you've found the secret phase!\n

 8049578: e8 93 f2 ff ff        call   8048810

 804957d: 83 c4 f4              add    $0xfffffff4,%esp

 8049580: 68 60 9d 04 08        push   $0x8049d60   //But finding it and solving it are quite different...\n

 8049585: e8 86 f2 ff ff        call   8048810

 804958a: 83 c4 20              add    $0x20,%esp

 804958d:e8 56 f9 ff ff       call   8048ee8     //引发隐藏关卡

 8049592: 83 c4 f4              add    $0xfffffff4,%esp

 8049595: 68 a0 9d 04 08        push   $0x8049da0      //Congratulations! You've defused the bomb!\n

 804959a: e8 71 f2 ff ff        call   8048810

 804959f: 8b 5d 98              mov    -0x68(%ebp),%ebx

 80495a2: 89 ec                 mov    %ebp,%esp

 80495a4: 5d                    pop    %ebp

 80495a5: c3                    ret    

 80495a6: 90                    nop

 80495a7: 90                    nop

 80495a8: 90                    nop

 80495a9: 90                    nop

 80495aa: 90                    nop

 80495ab: 90                    nop

 80495ac: 90                    nop

 80495ad: 90                    nop

 80495ae: 90                    nop

 80495af: 90                    nop

红色部分为隐藏关卡位置。

具体分析:通过阅读代码可以发现这个函数中有很多的直接数出现,经过前面的几个关卡的经验,这里直接调试输出这些直接数的值。如图9。

逆向工程实验_第8张图片

               图 9

将图中内容对应着代码一起看。就可以读懂这段代码了。(如代码中的注释,读懂后写的),根据注释可以知道,这段代码的引发需要两个数据,一个int和一个string。

看一下前面关卡的输入就可以知道,只有第四关的输入小于2个,其它关卡的输入都大于2。那么在第四关输入9的时候顺便输入“austinpowers”肯定可以引发隐藏关卡。如图10。

逆向工程实验_第9张图片

     图10   与想象中不同,以为第4关就可以引发隐藏关卡

接下来继续看secret_phase的代码(输入第一个测试数据“1234”,引爆第一颗炸弹后再输入第二个测试数据“900”)

08048ee8 :

 8048ee8: 55                    push   %ebp

 8048ee9: 89 e5                 mov    %esp,%ebp

 8048eeb: 83 ec 14              sub    $0x14,%esp   //(%esp-$0x14)存储的是字符串“But finding it and solving it are quite different...\n”的首地址

 8048eee: 53                    push   %ebx     //经过调试,ebx寄存器存储的是“austinpowers”的首地址。

 8048eef: e8 08 03 00 00        call   80491fc

 8048ef4: 6a 00                 push   $0x0

 8048ef6: 6a 0a                 push   $0xa

 8048ef8: 6a 00                 push   $0x0

 8048efa: 50                    push   %eax    //eax寄存器存储的是“1234”的首地址

 8048efb: e8 f0 f8 ff ff        call   80487f0 <__strtol_internal@plt>   //这个函数的作用不太明了,暂时看下面

 8048f00: 83 c4 10              add    $0x10,%esp

 8048f03: 89 c3                 mov    %eax,%ebx    //%eax的值移到%ebx,此时%ebx=”1234”

 8048f05: 8d 43 ff              lea    -0x1(%ebx),%eax   //此时%eax的值为”1233”

 8048f08: 3d e8 03 00 00        cmp    $0x3e8,%eax    //%eax的值和1000比较

 8048f0d: 76 05                 jbe    8048f14

 8048f0f:e8 e8 05 00 00       call   80494fc     //%eax的值大于1000的话就引爆炸弹,那么此时就可以知道函数__strtol_internal的作用了,就是把字符串转化成int型,测试数据“1234”在这里引爆了第一颗炸弹。那么输入另一个测试数据“900”。

 8048f14: 83 c4 f8              add    $0xfffffff8,%esp

 8048f17: 53                    push   %ebx   //%ebx存储的是测试数据“900

 8048f18: 68 20 b3 04 08        push   $0x804b320

 8048f1d: e8 72 ff ff ff        call   8048e94   //这里需要调用fun7fun7的功能在下面分析。

 8048f22: 83 c4 10              add    $0x10,%esp

 8048f25: 83 f8 07              cmp    $0x7,%eax    //%eax存的是fun7的返回值,只要这个值和0x7想等,就可以跳过第二个炸弹

 8048f28: 74 05                 je     8048f2f

 8048f2a:e8 cd 05 00 00       call   80494fc

 8048f2f: 83 c4 f4              add    $0xfffffff4,%esp

 8048f32: 68 20 98 04 08        push   $0x8049820

 8048f37: e8 d4 f8 ff ff        call   8048810

 8048f3c: e8 eb 05 00 00        call   804952c

 8048f41: 8b 5d e8              mov    -0x18(%ebp),%ebx

 8048f44: 89 ec                 mov    %ebp,%esp

 8048f46: 5d                    pop    %ebp

 8048f47: c3                    ret    

 8048f48: 90                    nop

 8048f49: 90                    nop

 8048f4a: 90                    nop

 8048f4b: 90                    nop

 8048f4c: 90                    nop

 8048f4d: 90                    nop

 8048f4e: 90                    nop

 8048f4f: 90                    nop

具体分析:

红色部分:炸弹位置,第一个炸弹的引爆条件是输入的字符串大于1001。第二个炸弹的引爆条件是fun7返回值和0x7不相等。那么这个隐藏关卡的关键就只剩下fun7的作用了。

 

下面是fun7函数的代码

08048e94 :

 8048e94: 55                    push   %ebp

 8048e95: 89 e5                 mov    %esp,%ebp

 8048e97: 83 ec 08              sub    $0x8,%esp

 8048e9a: 8b 55 08              mov    0x8(%ebp),%edx   //存的是36的地址“0x804b320”,这个地址在相同的计算机中是不会变的。

 8048e9d: 8b 45 0c              mov    0xc(%ebp),%eax   //%eax存的是900

 8048ea0: 85 d2                 test   %edx,%edx   //测试edx的值是否为0,为0的时候修改标志位ZF=1,否则ZF=0

 8048ea2: 75 0c                 jne    8048eb0   //当标志位ZF不等于1时,跳到0x8048eb0,否则,忽略这条语句

 8048ea4: b8 ff ff ff ff        mov    $0xffffffff,%eax    //eax赋值为-1

 8048ea9: eb 37                 jmp    8048ee2  //跳到0x8048ee2

 8048eab: 90                    nop

 8048eac: 8d 74 26 00           lea    0x0(%esi,%eiz,1),%esi

 8048eb0: 3b 02                 cmp    (%edx),%eax   //比较%edx存储的地址里面的值和%eax的值

 8048eb2: 7d 11                 jge    8048ec5   //%eax大于等于M(%edx)的值,则跳到0x8048ec5

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

 8048eb7: 50                    push   %eax

 8048eb8: 8b 42 04              mov    0x4(%edx),%eax    //%edx存储的值加4后得到一个地址,取这个地址里面的值x,把x赋给%eax

 8048ebb: 50                    push   %eax

 8048ebc: e8 d3 ff ff ff        call   8048e94

 8048ec1: 01 c0                 add    %eax,%eax    //%eax的值乘2

 8048ec3: eb 1d                 jmp    8048ee2   //跳到0x8048ee2

 8048ec5: 3b 02                 cmp    (%edx),%eax     //比较M(%edx)的值和%eax的值的大小

 8048ec7: 74 17                 je     8048ee0    //M(%edx)%eax的值相等,则跳到0x8048ee0

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

 8048ecc: 50                    push   %eax

 8048ecd: 8b 42 08              mov    0x8(%edx),%eax   //%edx存储的值加8后得到一个地址,取这个地址里面的值x,把x赋给%eax

 8048ed0: 50                    push   %eax

 8048ed1: e8 be ff ff ff        call   8048e94

 8048ed6: 01 c0                 add    %eax,%eax    //%eax的值乘2

 8048ed8: 40                    inc    %eax      //%eax的值加1

 8048ed9: eb 07                 jmp    8048ee2   //跳到0x8048ee2

 8048edb: 90                    nop

 8048edc: 8d 74 26 00           lea    0x0(%esi,%eiz,1),%esi

 8048ee0: 31 c0                 xor    %eax,%eax   //%eax置为0

 8048ee2: 89 ec                 mov    %ebp,%esp

 8048ee4: 5d                    pop    %ebp

 8048ee5: c3                    ret    

 8048ee6: 89 f6                 mov    %esi,%esi

具体分析:这个函数是一个递归函数,通过对代码的阅读,可以发现这个函数的关键点是地址“0x804b320”的值,以及它加n*0x4和n*0x8的值。下表表示的就是这些地址和值的对应关系。

地址%edx

%edx为地址里面的值

0x4(%edx)

0x8(%edx)

0x804b320

36

0x804b314

0x804b308

 

 

 

 

0x804b314

8

0x804b214

0x804b2fc

0x804b308

50

0x804b2f0

0x804b2d8

 

 

 

 

0x804b214

0

0

0

0x804b2fc

22

0x0804b290

0x804b2a8

0x804b2f0

45

0x804b2cc

0x804b284

0x804b2d8

107

0x804b2b4

0x804b278

 

 

 

 

0

0

0

0

0

0

0

0

0x804b290

20

0

0

0x804b2a8

35

0

0

0x804b2cc

40

0

0

0x804b284

47

0

0

0x804b2b4

99

0

0

0x804b278

1001

0

0

通过写这个表的过程,可以发现这其实是一棵二叉树。下面以二叉树的形式来表示这些数据。

逆向工程实验_第10张图片

这颗二叉树我只画出不是0的部分。

结合代码和注释可以得出这个函数的作用:

设输入的数为x,x与36比较,若小于36,则继续与36的左子树比较;若大于36,则与36的右子树比较;若等于36,则%eax的值置为0。若跳到某一个子树为0时,把%eax的值置为-1。当函数返回时,若是从右子树返回,则%eax=2*%eax+1,若从左子树返回,则%eax=2*%eax。

那么为了让%eax返回0x7,那么输入的数x需要满足的首要条件是:x∈{36,8,50,22,45,107,20,35,40,47,99,1001}。因为如果x等于其它数的话,那么fun7返回的值一定为负数。所以,只需简单代入这些值计算一下就可以知道答案了。

答案为1001。

 

继续查看代码发现还有一个奇怪的关卡invalid_phase,但没找到程序入口,而且根据这个关卡名称也可以判断这是个没用的关卡。

最后是一张测试截图

逆向工程实验_第11张图片


总结:这次实验花的时间挺长的(接近一周的时间),主要考察GDB调试和汇编代码阅读能力,在这两方面我都需要加强。

你可能感兴趣的:(深入理解计算机系统)