CTF之逆向分析——没有源代码一样能干

36 阅读8分钟

一、什么是逆向分析

在常见的系统环境中,开发者或开发组织通常向用户提供的是编译好的程序,而非原始的代码文件,这种形式便于机器执行而并不便于人类阅读,为了分析这些程序的运行逻辑,达到去除恶意代码以及为老旧程序提供支持,部分开发者开始尝试逆向这些文件,以获取厂商所提供的软件的代码逻辑,为自己所用,部分安全厂商也在逆向恶意的木马、病毒等,以协助用户清理病毒的感染痕迹。

二、汇编基础

参考之前的文章,以及下面的内容:汇编指令 · 语雀

在常见的逆向中,跳转指令是程序逻辑的关键所在,汇编中的跳转指令往往对应着C语言中的if判断语句、while循环语句、for循环语句、switch判断语句等,跳转指令通过各种判断来实现高级语言的各种结构,下面是一些汇编的跳转指令:

指令描述备注
JMP无条件跳转-
JCXZ / JECXZCX / ECX 寄存器为 0 则跳转计数器专用
JE / JZ等于 / 为零则跳转标志位 ZF=1
JNE / JNZ不等于 / 不为零则跳转标志位 ZF=0
JA / JNBE无符号大于 / 不小于等于Above
JAE / JNB / JNC无符号大于等于 / 不低于 / 无进位Above or Equal
JB / JNAE / JC无符号小于 / 不大于等于 / 进位Below
JBE / JNA无符号小于等于 / 不大于Below or Equal
JG / JNLE有符号大于 / 不小于等于Greater
JGE / JNL有符号大于等于 / 不小于Greater or Equal
JL / JNGE有符号小于 / 不大于等于Less
JLE / JNG有符号小于等于 / 不大于Less or Equal
JS / JNS结果为负 / 不为负符号标志 SF
JO / JNO溢出 / 不溢出溢出标志 OF
JP / JPE奇偶位为偶奇偶标志 PF
JNP / JPO奇偶位为奇奇偶标志 PF

三、调试基础

(一)gdb调试

协助工具安装

  1. pwndbg

打开Releases · pwndbg/pwndbg · GitHub,选择对应架构的portable版本下载,下载后解压到你想要放置的目录下面,进入解压目录的bin文件夹,右键打开终端,输入下面的命令即可,无需另外安装gdb:

./pwndbg 目标调试文件
  1. gef

打开 hugsy/gef,使用下面命令克隆到本地:

git clone https://github.com/hugsy/gef.git

然后进入scripts目录,运行gef.sh安装即可,也可以将项目目录下的gef.py拷贝出来,在~/.gdbinit文件中添加下面的一行即可:

source ~/目标路径/gef.py

运行gdb,提示符已经变成gef>,说明安装成功。

调试指令

内存操作

用于查看和修改程序运行时的内存数据。

命令格式说明参数详解
x/nfu addr查看内存通用命令n:个数(如20);f:格式(x/d/u/c/s…);u:单位(b/h/w/g)
x/s addr查看字符串以字符串格式显示内存内容
显示格式(f)含义单位大小(u)
x十六进制b(1字节)
d十进制有符号h(2字节)
u十进制无符号w(4字节)
c字符g(8字节)
t二进制-
s字符串-
断点管理

控制程序在特定位置暂停执行。

命令说明
b function / b filename:line在函数入口或指定文件行号下断点
b *0xaddr在内存地址处下断点
b func if var>10设置条件断点(满足条件才暂停)
info b查看所有断点信息
delete [n]删除指定编号断点(不填编号则删除所有)
disable [n] / enable [n]暂停/开启指定断点
c / continue继续运行,直到下一个断点
执行控制

控制程序的运行流程(单步、步入、步过等)。

命令说明
r / run开始运行程序
s / step步入:单步执行,遇到函数会进入函数内部
n / next步过:单步执行,遇到函数不进入,直接跳过
si / ni指令级的步入/步过(针对汇编指令)
finish运行至当前函数结束并返回
u / until运行直到退出循环,或运行至指定行号
call func调用指定的函数(如call my_func())
q / quit退出GDB
变量与表达式

查看和监视变量、表达式的值。

命令说明
p / print expr打印表达式的值(如p a, p func(1))
display expr每次单步调试暂停时自动打印该表达式
watch expr设置监视点,当表达式值改变时自动暂停程序
whatis var查询变量或函数的数据类型
set args arg1设置程序运行时的命令行参数
show args显示当前设置的运行参数
寄存器与汇编

查看底层状态和汇编代码。

命令说明
info r / info registers打印所有寄存器的值
i r rax打印特定寄存器(如rax)的值
p/x $rax以十六进制格式打印rax寄存器的值
disas func / disas addr反汇编指定函数或地址的代码
disas /r / disas /sr显示汇编代码及对应的机器码(硬编码)/ 显示汇编、机器码及源码(混合模式)
display /i $pc每次单步调试时自动显示当前汇编指令
set disassembly-flavor intel / att切换汇编显示格式为Intel风格/AT&T风格
栈与源码

查看调用堆栈和源代码。

命令说明
bt / where查看当前函数调用堆栈(Backtrace)
up / down在堆栈帧之间向上/向下切换
l / list列出源代码(默认显示当前行附近10行)
l line / l func显示指定行号或函数的源码
info locals显示当前栈帧内的所有局部变量
info program查看程序运行状态(是否运行、进程号、暂停原因)

(二)xdbg调试

x64dbg是一款专为Windows平台设计的开源动态调试器,支持对x86和x64架构的应用程序进行实时调试与分析。它通过直观的图形界面,让使用者能够深入观察程序的执行流程,包括反汇编代码、寄存器状态、内存数据和调用堆栈等关键信息。调试过程中,可以通过设置软件断点、硬件断点或内存断点来精确控制程序在特定位置暂停,从而逐条指令地跟踪程序行为。其内置的汇编器和脚本系统,支持在运行时修改指令或自动化调试任务,极大地提升了逆向分析的效率,是分析程序逻辑、定位漏洞或破解软件保护的常用工具。

四、逆向分析

(一)反汇编

反汇编是将计算机程序的可执行机器码转换回人类可读的汇编语言的过程,它是逆向工程中最基础也是最核心的环节。由于大多数商业软件不提供源代码,安全研究人员和逆向工程师必须依赖反汇编技术来理解程序的底层运作机制。这一过程利用如Zydis等反汇编引擎,将二进制指令流解析为助记符,从而还原出程序的控制流图、函数调用关系以及数据处理逻辑。通过反汇编,分析者可以绕过高级语言的抽象层,直接审视代码如何与操作系统API交互、如何管理内存以及如何处理加密算法,为后续的漏洞挖掘或逻辑还原奠定坚实基础。

(二)反编译

反编译是在反汇编的基础上,进一步尝试将底层的汇编代码或中间代码还原为高级编程语言(如C、C++或伪代码)的技术过程。与反汇编关注指令细节不同,反编译旨在恢复程序的宏观结构和语义,通过复杂的算法识别变量类型、控制结构(如循环、条件判断)以及函数原型。工具如Snowman或IDA Pro的F5插件,能够将晦涩难懂的跳转指令重组为结构清晰的伪代码,极大地降低了理解复杂程序逻辑的认知负担。尽管反编译生成的代码往往缺乏原始变量名和注释,且难以做到与源码完全一致,但它能快速揭示算法的核心逻辑,是进行恶意代码分析和软件功能复现的高效手段。

(三)工具的使用

  1. 反编译程序,搜索相关字符串,便于进一步分析。

  2. 将关键变量改名,从一些无语义的变量名改为有语义的变量名,将数据类型进行初步修复,结构体的定义,数组的结构,布尔类型的修改(一般识别为unsigned char),字符指针的修复((long) 即char *)根据函数调用图分析函数关键调用代码 ,对代码预处理。

  3. 导出关键代码便于后续处理。

  4. 将数据类型规范化,整理代码,简化过于复杂的逻辑,例如把while改为for语句,编写伪代码。

  5. 根据伪代码编写破解程序,并编译调试,获取结果。