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 的神秘性一点一点被拨开。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/620933.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nrm使用

为了更方便的切换下包的镜像源&#xff0c;我们可以安装 nrm 这个小工具&#xff0c;利用 nrm 提供的终端命令&#xff0c;可以快速查看和切换下 包的镜像源。 //通过 npm 包管理器&#xff0c;将 nrm 安装为全局可用的工具 npm i nrm -g//查看所有可用的镜像源 nrm ls//将下载…

【现代密码学】笔记 补充7-- CCA安全与认证加密《introduction to modern cryphtography》

【现代密码学】笔记7-- CCA安全与认证加密《introduction to modern cryphtography》 写在最前面7 CCA安全与认证加密 写在最前面 主要在 哈工大密码学课程 张宇老师课件 的基础上学习记录笔记。 内容补充&#xff1a;骆婷老师的PPT 《introduction to modern cryphtography》…

mysql数据库被黑恢复—应用层面delete删除---惜分飞

客户的mysql被人从应用层面攻击,并且删除了一些数据,导致业务无法正常使用,通过底层分析binlog确认类似恢复操作 确认这类的业务破坏是通过delete操作实现的,客户那边不太幸,客户找了多人进行恢复,现场严重破坏,老库被删除,并且还原了历史的备份文件(非故障第一现场),通过底层…

Error: error:0308010C:digital envelope routines::unsupported的解决方案

因为最近安装了pnpm对node版本有要求&#xff0c;升级了node版本是18以后&#xff0c;在运行之前的项目&#xff0c;就跑不起来了&#xff0c;报错如下&#xff1a; Error: error:0308010C:digital envelope routines::unsupported解决方案一&#xff1a; node版本切换到16版…

KEI5许可证没到期,编译却出现Error: C9555E: Failed to check out a license.问题解决

一、编译出现如下报错 二、检查一下许可证 三、许可证在许可日期内&#xff0c;故应该不是许可证的问题 四、检查一下编译器&#xff0c;我用的是这个&#xff0c;这几个编译器的区别其实我不太明白&#xff0c;但我把问题解决是选的这个 五、找到编译器的路径&#xff0c;去复…

Dockerfile的COPY --link

文章目录 总结环境概述“ --link” 是什么引入“ --link”使用“COPY --link”示例什么情况不适用总结参考 注&#xff1a;我做了很多测试&#xff0c;发现不管是否使用 --link &#xff0c;结果貌似都一样。我在网上搜了半天&#xff0c;最后发现&#xff0c;该功能貌似目前被…

计算机网络的常用的网络通信命令(Windows)

ping&#xff1a;它是用来检查网络是否通畅或者网络连接速度的命令。ping命令利用的原理是&#xff1a;网络上的机器都有唯一确定的IP地址&#xff0c;我们给目标IP地址发送一个数据包&#xff0c;对方就要返回一个同样大小的数据包&#xff0c;根据返回的数据包我们可以确定目…

父(子)组件获取子(父)组件的方法和数据

1.父组件获取子组件的方法和数据 在父组件中的子组件添加 ref"childName"&#xff0c;childName自定义 <childComponent ref"childName"></childComponent> #获取子组件的方法 method:方法名this.$refs.childName.method()#获取子组件的数…

Linux——安装docker

安装 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun启动docker systemctl start docker常用指令 查找镜像 docker search [name]拉取镜像 docker pull [name]运行容器 docker run [name] 配置项&#xff1a;-i 交互式 -t 终端 -d 后台运行 --name [n…

【力扣每日一题】力扣83删除排序链表中的重复元素

题目来源 力扣83删除排序链表中的重复元素 题目描述 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 思路分析 思路一&#xff1a;使用两个指针&#xff0c;last指针指向上一个元素&#xff0c;…

高中电学实验学习

bilibili上的笔记有价值的链接 1、 自学物理吧 电学实验 https://www.bilibili.com/video/BV1SM411u757/?spm_id_from333.337.search-card.all.click&vd_source91b03ee59c462b7b3cfbd57346cf1001 2、张老师的物理课堂 测电源电动势和内阻

Unity求射线与球体交点(有可能还能优化)

代码如下&#xff1a; bool RayCrossSphere(Ray ray, Sphere sphere, out Vector3[] vs) {Vector3 c2o sphere.center - ray.origin;float sqrtRadius sphere.radius * sphere.radius;Vector3 project Vector3.Project(c2o, ray.direction);Vector3 vPoint ray.origin pr…

【驱动】TI AM437x(内核调试-02):dynamic 动态打印调试

1、配置内核 dynamic 可以根据不同级别的作用域来启用/禁用-每个源文件、函数、模块、格式字符串和行号的打印信息。 内核中,默认没有配置 dynamic ,因为使能 dynamic 会使内核增大2% Symbol: DYNAMIC_DEBUG [=n] Type : boolean …

SpringCloud:Ribbon

文章目录 Ribbon快速入门Ribbon负载均衡算法常见的负载均衡算法更改算法规则修改配置 饥饿加载 Ribbon ribbon是一个客户端负载均衡器&#xff0c;会从注册中心拉取可用服务&#xff0c;当客户端需要获取服务请求时&#xff0c;ribbon能够解析服务地址并实现负载均衡 快速入门 …

理解TCP/IP协议

一、协议 在计算机网络与信息通讯领域里&#xff0c;人们经常提及 “协议” 一词。互联网中常用的协议有HTTP、TCP、IP等。 协议的必要性 简单来说&#xff0c;协议就是计算机与计算机之间通过网络通信时&#xff0c;事先达成的一种 “约定”。这种“约定”使不同厂商的设备…

Linux工具-搭建文件服务器

当我们使用linux系统作为开发环境时&#xff0c;经常需要在Linux系统之间、Linux和Windows之间传输文件。 对少量文件进行传输时&#xff0c;可以使用scp工具在两台主机之间实现文件传输&#xff1a; rootubuntu:~$ ssh --help unknown option -- - usage: ssh [-46AaCfGgKkMN…

EndNote快速上手

前言&#xff1a;用EndNote主要就是为了方便管理文章引用的文献&#xff0c;所以本篇就是针对EndNote在文章中引用文献需要的技巧&#xff0c;然后本文用的是EndNoteX9。 EndNote快速上手 创建文献资料库创建文献分组导入文献手动输入文件导入在线搜索 修改文献信息去重文献删除…

详解Java之Spring框架中事务管理的艺术

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天聊聊Spring框架中的事务管理。不管是开发小型应用还是大型企业级应用&#xff0c;事务管理都是个不可避免的话题。那么&#xff0c;为什么事务管理这么重要呢&#xff1f;假设在银行系统中转账时&#x…

__declspec(dllexport)与__declspec(dllimport) 的区别

__declspec(dllexport) 和 __declspec(dllimport) 是用于在 Windows 平台上定义导出和导入函数或变量的关键字。它们的作用如下&#xff1a; 1. __declspec(dllexport): 当您在定义一个函数或变量时使用__declspec(dllexport)关键字时&#xff0c;它告诉编译器将该函数或变量导…

06-微服务OpenFeigh和Sentinel持久化

一、OpenFeign基础应用 1.1 概念 OpenFeign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用OpenFeign&#xff0c;可以做到使用HTTP请求访问远程服务&#xff0c;就像调用本地方法一样的&#xff0c;开发者完全感知不到这是在调用远程方法&#xff0c;更感知不到在访…