【计算机系统(2)】3 逆向工程实验

目录

概览

环境:

过程及内容:

第一关

【主要思路】

可通过gdb获得存放在0x401af8处字符串。(图5)

另外我通过IDA软件也查看到相应字符串“1 ”。(图6)

过关成功(图7)

第二关

【主要思路】

第三关

【主要思路】

汇编代码转C语言代码如下(有改动):

第四关

【主要思路】

汇编代码转化为c语言代码如下(有改动)

第五关

【主要思路】

汇编代码转c语言代码如下,部分改动:

第六关

【主要思路】

结论:

心得体会:


概览

  1. 理解程序(控制语句、函数、返回值、堆栈结构)是如何运行的
  2. 掌握GDB调试工具和objdump反汇编工具

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

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

本实验需解决Phase_1(15)、Phase_2(15)、Phase_3(15)、Phase_4(15)、Phase_5(15)、Phase_6(10)。通过截图+文字的形式把实验过程写在实验报告上,最后并撰写实验结论与心得(15分)。

环境:

  1. 计算机(Intel CPU)
  2. Linux64位操作系统(Ubuntu 17)
  3. GDB调试工具
  4. objdump反汇编工具

过程及内容:

输入$ objdump -d bomb_64 > 1.txt,进行反汇编并将结果输出到1.txt。(图1)

图表 1 反汇编

  • 第一关

【主要思路】

  • 在1.txt中找到和第一关相关的汇编代码,发现如果要不引爆(即不运行至400e82 call1 <explode_bomb>),需要执行上一句的跳转指令,而跳转条件是%rax为0,往上查看可以发现%rax存放的是调用<strings_not_equal>函数的返回值,即判断字符串是否不相等函数,推测应该是2个字符串相等时返回0,具体哪两个字符串需要进一步打开函数分析。(图2)

 

图表 2 第一关汇编代码分析

  • <strings_not_equal>函数汇编代码如下(图3),经过分析验证了我们的猜想,同时可知2个字符串分别为输入字符串(图4),内存0x401af8处字符串,所以只需要找到该字符串就是过关答案。

 

图表 3 <strings_not_equal>函数汇编代码分析

图表 4 main.c里知输入为参数1

  • 将上述汇编代码改写为C语言代码如下(有改动)
//传入2字符串,判断是否不相等
int strings_not_equal(char* s1, char* s2) {

    //2字符串长度不相等时返回1
    if (string_length(s1) != string_length(s2)) {
        return 1;
    }

    //逐字符比较
    int i = 0;
    while (s1[i])
    {
        if (s1[i] != s2[i]) {
            return 1;//不等返回1
        }
        i++;
    }
    return 0;//相等返回0
}
//input为输入字符串,存放于%rdi寄存器
void phase_1(char* input) {

    //于内存0x40123d处取字符串"Science isn't about why, it's about why not?"
    //调用strings_not_equal函数判断输入与该字符串是否不相等
    int res=strings_not_equal(input, "Science isn't about why, it's about why not?");
    
    if (!res) {
        explode_bomb();//不等引爆炸弹
    }
    return;
}

可通过gdb获得存放在0x401af8处字符串。(图5)

图表 5 gdb查看内存里字符串

另外我通过IDA软件也查看到相应字符串“1 ”。(图6)

图表 6 IDA查看字符串

过关成功(图7)

 图表 7 第一关成功

  • 第二关

【主要思路】

  • 在1.txt中找到和第一关相关的汇编代码,发现如果要不引爆(即不运行至400e82 call1 <explode_bomb>),需要执行上一句的跳转指令,而跳转条件是%rax为0,往上查看可以发现%rax存放的是调用<strings_not_equal>函数的返回值,即判断字符串是否不相等函数,推测应该是2个字符串相等时返回0,具体哪两个字符串需要进一步打开函数分析。(图2)

图表 2 第一关汇编代码分析

  • <strings_not_equal>函数汇编代码如下(图3),经过分析验证了我们的猜想,同时可知2个字符串分别为输入字符串(图4),内存0x401af8处字符串,所以只需要找到该字符串就是过关答案。

图表 3 <strings_not_equal>函数汇编代码分析

图表 4 main.c里知输入为参数1

  • 将上述汇编代码改写为C语言代码如下(有改动)
void red_six_numbers(char* input,int* num) {
    //调用输入函数,参数包括输入,格式(存于0x401eb2),数组指针
    int n=__isoc99_sscanf(input,"%d %d %d %d %d %d", num, num+1, num+2, num+3, num+4, num+5);

    if (n <= 5) {
        //如果数字小于6个爆炸
        explode_bomb();
    }
    return;
}

void phase_2(char *input) {//参数为数组指针
    int num[6];
read_six_numbers(input,num);//数组输入
    int sum = 0;
    int i = 0;

    do {
        if (num[i] != num[i + 3]) {
            explode_bomb();//相邻3个不同或相邻相同就爆炸
        }

        sum += num[i];
        i++;
    } while (i < 3);

    if (!sum) {
        explode_bomb();//和为0爆炸
    }
    return;
}

图表 11 第二关过关

  • 第三关

【主要思路】

  • 经gdb或者IDA查询内存0x401ebe为”%d %d”,可确定输入为2个整数。(图12)

 

图表 12 gdb和IDA输入格式查询

  • 由跳转表推测处为switch语句,(gdb或IDA)查询该跳转表(首地址为0x401b60)可得各种情况跳转地址。(图13)

 

图表 13  gdb和IDA查询跳转表

  • 得出不同情况的值。(图14)

图表 14 第三关汇编代码分析

  • 需要输入对应情况编号和结果才可以过关,共8组答案。(表15)

case

0

1

2

3

4

5

6

7

res

0x217

0x39e

0xd6

0x153

0x77

0x160

0x397

0x19c

dec

535

926

214

339

119

352

919

412

图表 15 8组答案

图表 16 过关

汇编代码转C语言代码如下(有改动):

void phase_3(char* input) {
    int a, value,res;//待输入的序号和值,对应答案
    //调用输入函数,参数包括输入,格式(存于0x401ebe)为2整数,存于a和value
    int n = __isoc99_sscanf(input, "%d %d", a,value);

    if (n <= 1||a>7) {
        //如果输入小于2个或序号a大于7非法,爆炸
        explode_bomb();
    }

    switch (a)
    {
    case 0:
        res = 0x217;
        break;
    case 1:
        res = 0x39e;
        break;
    case 2:
        res = 0xd6;
        break;
    case 3:
        res = 0x153;
        break;
    case 4:
        res = 0x77;
        break;
    case 5:
        res = 0x160;
        break;
    case 6:
        res = 0x397;
        break;
    case 7:
        res = 0x19c;
        break;
    default:
        explode_bomb();
        return;
    }

    if (res != value) {
        explode_bomb();
    }
    return;
}

  • 第四关

【主要思路】

  • 相关代码如下,需要查看内存0x401ec1处的值确定输入格式,调用了函数且要求返回值为55才可过关。(图17)

 

图表 17 第4关汇编代码分析

  • gdb查看内存0x401ec1处的值为%d确定输入为1个整数。(图18)

图表 18 输入格式

  • 函数代码如下(图19),发现为递归函数,递归停止条件为参数小于2,推出递归式(斐波那契)

func4\left ( x \right )=\left\{\begin{matrix} 1 ,&x\leqslant 1\\ func4\left ( x -1\right )+func4\left ( x-2 \right ), &x> 1 \end{matrix}\right.

图表 19 递归函数代码分析

可推算要让结果为55,则

func4(2)=func4(0)+func4(1)=1+1=2

func4\left(3\right)=func4\left(1\right)+func4\left(2\right)=1+2=3

func4\left(4\right)=func4\left(2\right)+func4\left(3\right)=2+3=5

func4\left(5\right)=func4\left(3\right)+func4\left(4\right)=3+5=8

func4\left(6\right)=func4\left(4\right)+func4\left(5\right)=5+8=13

func4\left(7\right)=func4\left(5\right)+func4\left(6\right)=8+13=21

func4\left(8\right)=func4\left(6\right)+func4\left(7\right)=13+21=33

func4\left(9\right)=func4\left(7\right)+func4\left(8\right)=21+31=55

结果为8+1=9

汇编代码转化为c语言代码如下(有改动)

int func4(int x) {
    int res = 1;//返回值初始为1

    if (x <= 1) {
        return res;//终止条件
    }
    return func4(x - 1) + func(x - 2);
}

void phase_4(char* input) {
    int x;
    //调用输入函数,参数包括输入
    //格式(存于0x401ec1)为整数,存于x
    int n = __isoc99_sscanf(input, "%d",x);

    if (n != 1 || x<= 0) {
        //如果输入不唯一或x<=0非法,爆炸
        explode_bomb();
    }

    if (func4(x) != 55) {
        explode_bomb();//调用递归函数不为55爆炸
    }
    return;
}

图表 20 第四关过关

  • 第五关

【主要思路】

  • 输入为两整数,需要gdb(图21)或IDA(图22)查询数组,得为16个整型数组。

图表 21 gdb查询数组

图表 22 IDA查询数组

  • 代码如下,发现输入为数组下标和跳转的12次的总和,每次的值为下一次的下标,直到找到15停下,并且要求循环次数为12,而输入就是可以时最终12次跳转结果满足条件的首下标和跳转的11个数总和。(图23)

图表 23 第五关代码分析

查询数组为:num[16] = {10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5}

对应下标表格如下:

下标

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

10

2

14

7

8

12

15

11

0

4

1

13

3

9

6

5

图表 24 数组

反向推理如下:15 <- 6 <- 14 <- 2 <- 1 <- 10 <- 0 <- 8 <- 4 <- 9 <- 13 <- 11 <- 7

即7 -> 11 -> 13 -> 9 -> 4 -> 8 -> 0 -> 10 -> 1 -> 2 -> 14 -> 6 -> 15  (图23)

图表 25 反向推理

  • 所以输入7可过关,总和为11 + 13 + 9 + 4 + 8 + 0 + 10 + 1 + 2 + 14 + 6 + 15 = 93,输入7和93过关。(图26)

 

图表 26 第5关过关

汇编代码转c语言代码如下,部分改动:

void phase_5(char* input) {
    int num, sum;//待输入的下标与跳转11个数总和,对应答案
//调用输入函数,参数包括输入,格式(存于0x401ebe)为2整数,存于num和sum
    int n = __isoc99_sscanf(input, "%d %d", num, sum);

    if (n <= 1 || num > 15) {
        //如果输入小于2个或下标num大于15非法,爆炸
        explode_bomb();
    }
    
    int sum0 = 0;//sum0记录跳转11个数的总和,初始为0
    int i = 0;//i记录跳转次数,初始为0
    int num0 = num;//备份初始下标,再循环中不断更新

    //存于内存0x401ba0的16个数数组
    int array[16] = { 10, 2, 14, 7, 8, 12, 15, 11, 0, 4, 1, 13, 3, 9, 6, 5 };

    do {
        i++;//跳转次数加1
        num0 = *(array+num0);//数组取数,更新为下一次跳转下标
        sum0 += num0;
    } while (i != 12);

    if (num0 != 15||sum!=sum0) {
        explode_bomb();//跳转12次后结果不为15或输入的和不对则爆炸
    }
    return;
}

  • 第六关

【主要思路】

  • 找到相关代码如下,结合代码出现的node0头结点和第六关的考察寻址,以及fun6不断地址加8取值再加8取值,初步怀疑fun6函数考察链表相关知识,那么我根据头结点(内存0x602780处)找到相关链表。(图27)

 

图表 27 第六关代码分析

  • 通过gbd和IDA查阅链表如下:

 

图表 28 gdb和IDA查阅链表如下

根据fun6函数,可知骑在降序排序并返回排序好对应的头结点,设置断点(图29)于函数返回处并查看输入300函数返回后的链表,确实降序。结合返回后代码,说明要是输入的数位于数组中第4大数,而原本第3,4大数为600和673,即要输位于[600,679](图30)

 

图表 29 设置断点

图表 30 fun6函数降序排序,输入601六关过关

结论:

    可以通过反汇编指令获得反汇编代码。在本次拆弹实验,对于汇编代码的查看可以先找到炸弹语句,反推出避免爆炸条件。可以根据函数名初步确定函数功能,再到具体代码进行详细分析。根据输入函数的格式可以初步确定思路内容方向。由跳转表语句或者数组语句可确定相关结构方向。又如不断取值再寻址可确定为链表结构。内存数据可以通过gdb查看。Gdb还可查看跳转表,数组,链表等相关结构。可灵活运用gdb的断点调试功能确定猜测以及函数功能。

心得体会:

通过该次关于汇编代码拆弹的趣味实验,我初步了解了汇编代码在实际一个小工程的运行流程。进一步加深了相关知识点的理解。如内存数据的存取,函数的调用,参数寄存器的设置,栈帧中关于寄存器的保护、临时变量、数组创建的实现的理解,跳转表、数组、链表的具体实现有了较为深刻的理解。同时也加深了关于gdb工具的学习使用,受益良多。

猜你喜欢

转载自blog.csdn.net/weixin_51695846/article/details/125353994