CSAPP - 反编译 initialize_bomb()

2024-01-14 11:28

本文主要是介绍CSAPP - 反编译 initialize_bomb(),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CSAPP - 保持好奇,反汇编 initialize_bomb()

相比于直接看 bomblab phase_1 的答案,我更想搞懂答案之外涉及的每个函数的反汇编 - 反正是一个实验,代码能复杂到哪里去? 而搞懂这些函数, 无疑对于实际工程中的各种 debug 问题, 能补全基本的 gdb 调试技能。这一篇是分析 initialize_bomb() 函数.

好奇 - Ctrl-C 被接管了?

查看 bomb.c 可以看到,除了读取文件、打印提示信息,在 phase_1() 之前还做了一件事:初始化 bomb:

int main()
{.../* Do all sorts of secret stuff that makes the bomb harder to defuse. */initialize_bomb(); // 好奇这里read_line();phase_1();...
}

如果是第一次运行 bomb, 输入字符串后希望临时退出, 会发现 Ctrl+C 并不能立即退出。原因是 initialize_bomb() 里使用了信号量,捕获了 Ctrl+C。 当然,这是一个猜测,需要从汇编代码验证。 和上一篇的方式一样,先获取汇编代码, 再逐句翻译, 随后整理和简化C代码。

反汇编 initialize_bomb() - 翻译出来了,但是很懵

(gdb) disassemble initialize_bomb
Dump of assembler code for function initialize_bomb:         // void initialize_bomb() {0x00000000004013a2 <+0>:     sub    rsp,0x8               //0x00000000004013a6 <+4>:     mov    esi,0x4012a0          // void* p2 = 0x4012a0;0x00000000004013ab <+9>:     mov    edi,0x2               // int p1 = 2;0x00000000004013b0 <+14>:    call   0x400b90 <signal@plt> // signal(p1, p2);0x00000000004013b5 <+19>:    add    rsp,0x8               //0x00000000004013b9 <+23>:    ret                          // }
End of assembler dump.

于是踉踉跄跄的写出 C 代码:

void initialize_bomb()
{void* p2 = 0x4012a0;signal(2, p2);
}

其中 void* p2 = 0x4012a0 让人费解, 需要结合 signal 的函数原型分析:

man signalNAMEsignal - ANSI C signal handlingSYNOPSIS#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);

可以发现, signal() 第二个参数是一个函数指针, 对应到 initialize_bomb() 函数中, 0x4012a0 是这个函数指针的值, 查看其汇编代码:

神奇的 0x4012a0 - 来反汇编吧!

(gdb) disassemble 0x4012a0
Dump of assembler code for function sig_handler:                    // sig_handler() {0x00000000004012a0 <+0>:     sub    rsp,0x8                      //0x00000000004012a4 <+4>:     mov    edi,0x4024c0                 // const char* p1 = (char*)0x4024c0; , 用 x /s 查看知道是 "So you think you can stop the bomb with ctrl-c, do you?"0x00000000004012a9 <+9>:     call   0x400b10 <puts@plt>          // puts(p1);0x00000000004012ae <+14>:    mov    edi,0x3                      // int p2 = 3;0x00000000004012b3 <+19>:    call   0x400c50 <sleep@plt>         // sleep(p2);0x00000000004012b8 <+24>:    mov    esi,0x402582                 // int p3 = 0x402582;    0x402582 值为 "Well..."0x00000000004012bd <+29>:    mov    edi,0x1                      // int p4 = 1;0x00000000004012c2 <+34>:    mov    eax,0x0                      // int ret = 0;0x00000000004012c7 <+39>:    call   0x400c00 <__printf_chk@plt>  // __printf_chk(p4, p3);  __printf_chk 是 printf 的安全版本,会检查格式字符串有效性0x00000000004012cc <+44>:    mov    rdi,QWORD PTR [rip+0x20246d]        # 0x603740 <stdout@@GLIBC_2.2.5>0x00000000004012d3 <+51>:    call   0x400be0 <fflush@plt>        // fflush(stdou);0x00000000004012d8 <+56>:    mov    edi,0x1                      // int p5 = 1;0x00000000004012dd <+61>:    call   0x400c50 <sleep@plt>         // sleep(p5);0x00000000004012e2 <+66>:    mov    edi,0x40258a                 // const char* p6 = (char*)0x40258a; "OK. :-)"0x00000000004012e7 <+71>:    call   0x400b10 <puts@plt>          // puts(p6);0x00000000004012ec <+76>:    mov    edi,0x10                     // int p7 = 16;0x00000000004012f1 <+81>:    call   0x400c20 <exit@plt>          // exit(p7);   // 返回16
End of assembler dump.

这段代码,相比于 phase_1 本身的代码,有意思的多。

首先我们验证, 当启动 bomb 程序后,输入 Ctrl+C, 并等待5秒左右,是否会退出,返回值是什么:

zz@Legion-R7000P% ./bomb
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
^CSo you think you can stop the bomb with ctrl-c, do you?
Well...OK. :-)
zz@Legion-R7000P% echo $?
16

返回值的确是16,和直接从汇编代码看到的一致。

对应的C代码,整理一下:

void sig_handler(int )
{puts("So you think you can stop the bomb with ctrl-c, do you?");sleep(3);printf("Well...");fflush(stdout);sleep(1);puts("OK. :-)");exit(16);
}void initialize_bomb()
{signal(2, sig_handler);
}

验证 - 捕获 Ctrl-C, 是这么玩的吗?

首先拿出这几次反汇编中,人工写出的C代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <signal.h>int string_length(const char* str)
{for (const char* p = str; ; p++){int ret = p - str;if (*p == 0){return ret;}}
}int strings_not_equal(const char* s1, const char* s2)
{const char* p1 = s1;const char* p2 = s2;int len1 = string_length(s1);int len2 = string_length(s2);if (len1 != len2){return 1;}while (true){char c1 = *p1;if (c1 =='\0'){return 0;}if (c1 != *p2){return 1;}p1++;p2++;}
}void sig_handler(int)
{puts("So you think you can stop the bomb with ctrl-c, do you?");sleep(3);printf("Well...");fflush(stdout);sleep(1);puts("OK. :-)");exit(16);
}void initialize_bomb()
{signal(2, sig_handler);
}

然后添加一小段调用代码, 也就是 main() 函数: 每隔1秒打印一个 hello world N, N 是递增的数字。 而在打印 hello world N 的过程中, 如果按下了 Ctrl+C, 就会捕获到信号,打印出和 bomblab 一样的输出,并最终结束:

int main()
{initialize_bomb();int i = 0;while (true){printf("hello world, %d\n", i);i += 1;sleep(1);}return 0;
}

运行一下, 的确如此, 就是这么玩的:

zz@Legion-R7000P% gcc test.c
zz@Legion-R7000P% ./a.out
hello world, 0
hello world, 1
hello world, 2
^CSo you think you can stop the bomb with ctrl-c, do you?
Well...OK. :-)

总结

  1. 保持好奇, 保持一定可以搞清楚的信念, 汇编之下, 了无秘密。
  2. x /s 0x4024c0 命令再次被使用,使用了好几次, gdb 命令得到了强化
  3. 看到往 rsi 寄存器(函数第二个参数)存入莫名奇妙的数字,不要慌,它就是一个函数的地址, 照常去 disas 它,没有难度, sig_handler() 很简单
  4. printf 和 __printf_chk, 这里确实是可以忽略和猜测的函数
  5. 整理好每一个反汇编出来的函数, 然后按自己想法,添加 main 函数去验证, bomb lab 的神秘性一点一点被拨开。

这篇关于CSAPP - 反编译 initialize_bomb()的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/604994

相关文章

类的load方法和initialize方法对比

1. load方法在main()之前被调用,而initialize方法在main()之后调用 load方法实际是在load_images过程中被调用的。load_images会将当前应用依赖的所有镜像(动态库)加载到内存,在在加载中首先是对镜像进行扫描,将所有包含 load 方法的类加入列表 loadable_classes ,然后从这个列表中逐一调用其所包含的 load 方法。 +[XXCl

apk反编译修改教程系列-----修改apk 解除软件限制功能 实例操作步骤解析_6【二十五】

目前很多软件都需要票票才可以使用完全的功能。纯免费的功能性app已经很少见了。 今天继续以一款app为大家来演示如何去除软件的限制功能。教程的目的主要是学习反编译的基础修改方法,了解app的修改步骤以及基础的入门修改常识。每个使用修改方法不一定适用于所有app。只是给你另外的思路与步骤参考。 反编译工具:MT**绿色版 演示apk;**清单 app

[学习笔记]《CSAPP》深入理解计算机系统 - Chapter 3 程序的机器级表示

总结一些第三章的一些关键信息 Chapter 3 程序的机器级表示结构 updating... Chapter 3 程序的机器级表示 局部变量通常保存在寄存器中,而不是内存中,访问寄存器比内存快的多. 有些时候,局部数据必须存放在内存中, 寄存器不足够存放所有的本地数据对一个局部变量使用地址运算符 &, 因此必须能够为它产生一个地址某些局部变量是数组或结构,因此必须能够通过数组或

[runtime] initialize方法讲解

+ (void)initialize Description(描述)     Initializes the class before it receives its first message. 在这个类第一次接受信息之前初始化这个类. The runtime sends initialize to each class in a program just before the cl

jar反编译成java工具

jar反编译成java工具 在开发过程中,经常使用到jar包。有的jar是在ide不能查看源码的。 这时就可以使用到jar的反编译工具。 传送门: 点击下载工具 免费下载的一个反编译工具,解压之后可以看到 免费下载的一个反编译工具,解压之后可以看到 现在就可以选择是发编译jar还是反编译文件夹了。 1.反编译jar之后会在jar包的同级文件夹中生成相应的文件,里边的文件

android apk反编译 查看源码

首先请下载所需要使用的工具包。 工具包有这三个工具: 工具介绍: apktool 作用:资源文件获取,可以提取出图片文件和布局文件进行使用查看 dex2jar 作用:将apk反编译成java源码(classes.dex转化成jar文件) jd-gui 作用:查看APK中classes.dex转化成出的jar文件,即源码文件 接下来我们直接进入正题。 一、我们解压a

三款常用的Java字节码反编译工具

转载请注明:   http://xuantan.iteye.com/blog/2030651   研究Java字节码测试已有一段时间了,工作中经常会用到字节码反编译工具。 现将三款本人感觉比较好用的反编译工具分享出来:   1、jd-gui:最好用,但有时反编译出来的结果会有遗漏。 https://code.google.com/p/cxldemo/downloads/detail?name=

CSAPP Data Lab

CSAPP 的第一个 Lab,对应知识点为书中的第 2 章(信息的表示与处理),要求使用受限制的运算符和表达式实现一些位操作。主要分为两个部分:整数部分和浮点数部分。其中整数部分限制较多,比较偏重技巧性,部分题个人认为很有难度。而浮点数部分则比较基础,主要考察对 IEEE 754 标准的熟悉程度,代码较长,但思路相对简单。 bitXor 思路 使用德-摩根定律进行推导,推导过程如下: 代

阶段二 - 小程序反编译及调试

在打开小程序后,小程序的apkg会放在以下目录中: 使用解包工具对小程序进行解包: 解包工具:链接: https://pan.baidu.com/s/1KLDbivGPCPhqt1nbLVbhXA?pwd=kckq 提取码: kckq 解包后会生成一个这样的文件: 在这里输入cmd后,输入:node wuWxapkg.js 上述步骤生成的文件路径 执行后,会生成一个文件夹,里面就是小程

Android中的apk通过签名校验防止反编译重打包

Android开发完打包好的apk如果被别人盗取可能会存在被反编译重打包的风险,那么我们如何来保护我们辛苦开发出来的程序不被别人盗取重打包呢,这里我们通过签名文件入手,因为打包时候是需要签名的,别人盗取后如果重打包也是需要签名的,那么如果重打包的签名跟我们原本打包的签名不一样,就可以阻止他使用该apk。 我们要在首页MainActivity里来判断重打包的签名文件是否与原签名文件一致: /**