逆向工程课程随记+总复习
本文最后更新于 2025年5月9日 下午
复习时间很充沛,打算从头到尾把ppt和相关的实验知识
边摸鱼边捋一遍。章节顺序按照老师的ppt顺序
涉及到栈的没整理,pwn玩了这么久栈,没必要整了
(懒得整了)。
上课让写交过的东西
- 静态桥与动态桥
- 栈附近,哪里是传入的函数参数,哪里是局部变量(32位)
- 病毒置入文件的操作
[TOC]
第一章 数据的存储及表示形式
- 这里的单位:字节(byte)、字(双字节word)、双字(四字节dword)、八字节(qword)
- 注意,在计组中,机器字长也成为字,这里的字一般是32位或64位的。(所以还是喜欢叫它内存单元喵~)
- 注意,可执行文件一般采用小端序储存数据;TCP/IP中传递数据以网络字节序传输,通常是以大端序传输。
- d代表数据、db是一个字节的数据、dw是一个字的数据、dd是双字
第二章 x86汇编语言介绍
pwn狗只把这些常用传参rdi、rsi、rdx、rcx、r8、r9,以及系统调用要动rax记得死死的,其他它们的理论作用还真没记过,这里记一记
通用寄存器
- 数据寄存器:eax、ebx、ecx、edx
- 指针变址寄存器:ebp、esp、esi、edi
累加器eax
在加法、乘法、除法指令中的默认寄存器、存储函数返回值。
基址寄存器ebx,ds段中的数据指针。
咦?ds,看到过,不知道啥用,问问gpt
现代操作系统和 64 位处理器架构已不再依赖传统的段式寻址,因此段寄存器(如
DS)的作用已经被削弱。在用户模式下,程序无法直接访问段寄存器的值,而是通过虚拟内存和分页机制来访问内存。
DS(数据段)外,常见的段寄存器还有:**
CS**(Code Segment):指向代码段,用于存储程序指令。**
SS**(Stack Segment):指向堆栈段,用于存储程序的栈数据。**
ES**(Extra Segment):通常用于额外的数据段。
FS和 **GS**:这些是附加的段寄存器,用于存储额外的数据段或特定的操作系统用途。也就是其实不怎么用了。
- 计数器ecx,用于循环中使用
补充一下loop指令
loop 地址:先将ecx-1,如果不为0,则跳转到该地址。
edx:用于存放整数除法产生的余数
ebp、esp,栈底栈顶指针,不赘述了(函数传参以及栈帧太熟了,略)
eip,指令指针寄存器
标志寄存器(成为flags或PSW,也就是程序状态字寄存器),32位中扩展为32位寄存器,成为EFLAGS。

OF(overflow flag)、SF(sign flag)、ZF(zero flag)、PF、CF意如其名,略
AF:辅助进位标志,低字节向高字节进位或借位时置1(若是字节操作,低四位向高四位借位时置1)
DF、IF、TF没怎么细了解过,不怎么常用于目前我浅陋的知识需要的判断,直接截个课件了

汇编指令集
- mov、lea(都是把右给左,intel_x86架构下)
- pushad、popad 在栈上按顺序压入32位通用寄存器,顺序是eax、ecx、edx、ebx、esp、ebp、esi、edi。
- jmp、call、leave
- ret n 代表pop rip 后,再往下弹出n个字节的栈(也就是esp - n)
各种寻址:计组学了,略。
第三章od
就是介绍od的使用,没啥东西。
关于断点去搜了搜
shift + f12 可以设置条件断点
he 硬件断点
bp 软件断点。
第四章 关于PE文件(可移植的执行体)
由于一开始的导入图片的图床崩了,后续重新传了一遍图片,但有的图片本地没有了,可能有点乱序和少东西???我现在没有发现哪儿少啥了
懒得再改了:dog:
已经弄了好几遍了,弄了好久,好累,有谁发现哪儿对不上或者少啥戳一下我,主要内容是顺着老师的ppt捋的
dll文件、exe文件都属于pe文件
虚拟地址中,32位的一页为4kB(0x1000),64位的为8kb(0x2000)
物理扇区以512字节(0x200)为一个单位对齐,因此PE文件在内存中的尺寸要比文件大。
pe文件有固定的开始特征,MZ头与PE头
总体PE结构

DOS头剩余的作用
- 在0x3c(地址固定)的位置处有一个B0,这个位置记录了PE头的偏移位置。

PE头标识以及标准PE头


- PE文件中自定义节的数量,在节表来描述节的信息,每个节的描述信息都固定40个字节。
- 标志集合如果为0x2000就是dll(这是从网上搜到有人回复的,但是存疑)(网上是对的标志位)

这是询问的gpt关于这个标志集合的解释(gpt有误,还好去问了问官方文档,下面这个是错的哦)

官方文档给出的表格:

这是一个dll文件对应的标志(?????)
问了老师。。。小端序去读:0x210e
老师建议用pe editor去看

pe文件可能的类型:
在 PE 文件格式中,根据用途和特性,主要可以分为以下几种类型:
- 可执行文件(Executable, EXE)
- 特点:
- 包含程序入口点(Entry Point)。
- 可直接加载到内存中由操作系统运行。
- 通过链接器将目标文件链接生成。
- 典型扩展名:
.exe - 用途:
- 普通的 Windows 应用程序。
- 动态链接库(Dynamic Link Library, DLL)
- 特点:
- 没有程序入口点,不能独立执行。
- 提供函数和资源供其他程序调用。
- 支持多进程共享代码和数据。
- 典型扩展名:
.dll - 用途:
- 系统和应用程序的共享库(如
kernel32.dll,user32.dll)。
- 系统和应用程序的共享库(如
- 系统文件(System File, SYS)
- 特点:
- 用于操作系统的内核模式。
- 通常是设备驱动程序或系统服务的实现。
- 加载时需要内核的帮助。
- 典型扩展名:
.sys - 用途:
- Windows 内核模块或驱动程序。
- 对象文件(Object File, OBJ)
- 特点:
- 是编译器生成的中间文件。
- 由链接器处理后生成最终的可执行文件或库。
- 包含代码和数据的片段以及重定位信息。
- 典型扩展名:
.obj - 用途:
- 静态库或最终程序的构建过程。
- 静态库(Static Library, LIB)
- 特点:
- 包含多个目标文件的集合。
- 链接到最终的可执行文件中。
- 不需要在运行时单独加载。
- 典型扩展名:
.lib - 用途:
- 提供代码重用功能的静态模块。
- 引导程序文件(Boot File)
- 特点:
- 用于操作系统引导阶段。
- 一些特定情况下被解释为 PE 文件。
- 典型扩展名:无固定扩展名(如
bootmgr)。 - 用途:
- 系统启动相关。
这些类型文件的基础格式是相同的,但具体的标志、段结构和用途有所不同。了解这些类型有助于分析不同的 PE 文件及其作用。
这个文件有5个节,如下:

扩展PE头
总体结构:


数据示例:

红框再往后,0x00001000,0x00000200,分别代表在内存和文件的对齐方式(对应32位)
PE扩展头的最后一个字段是16个元素的数组,里面定义了PE文件中各种数据的目录信息,如导出表、导入表、资源等16种。
节表(每一节40字节,0x28)

如这里是text节表

0x1092代表实际字节数,0x1000是RVA,0x1200是对齐后的尺寸,0x0400是原始数据在文件中的数据,也就是最下面的红框。
寻找kernel.32.dll的基地址
去除低两个字节的值,只保留高两个字节的高位地址,windows中32位程序的入口地址通常应该通常以7000 0000 H开始。如同linux中可执行文件,64位以7f开头,32位以ff开头。
不断寻找MZ与PE标志,如果找到MZ标志,就将它对应的地址+0x3c,把里面对应的地址加载到找到MZ时的基地址中,比对是否为PE,如果是,则代表这是真正的入口地址。

总程序

jb代表进位或借位信号
大于的话,CF标志(借位)为0,不会进行跳转。
流程图(这是老师留的作业,不理解的自己跟着画一遍,别直接截走):(第一步写成将低两位字节的地址置为0更清楚一点)

关于导入表

PE位置+0x78是16个表项的序列信息。

该字段定义了 PE 文件中出现的所有不同类型的数据的目录信息。如导出表、导入表、资源等。 16种
8个字节记录一个目录,0x00002010是虚拟偏移,0x0000003c(60)是长度
- 该偏移位于.rdata段,物理对应的起始为0x600,文件中的导入表目录就是0x610位置

- 每个导入表的表项有0x14字节(20字节),这里涉及到了两个导入的库,2个表项,40字节,但是需要最后一个表项用全0表示终止,所以总共用了3个表项,60字节。(导入表表项的数量只与导入的库的数量有关)

以下分别是桥1,指向链接库的指针,桥2

桥一虚拟偏移2008,文件偏移608
桥1指向的内容静态与动态都是一个指向索引+字符串的地址
桥2静态与桥1指向一致,动态则指向是一个指向载入的函数的地址。

由于实验让画静态与动态关于winresult.dll相关信息,不再重复上面的那个文件的课上内容,下面用实验的要求的文件进行分析。
导入表2084,对应于文件的684

这4个框是导入的3个dll库和一个终止标志

- 桥1里hint的值是索引值
第三个框,dll库地址为227a(87a)->winresult.dll,是需要分析的库

导出表



节对应rva2000->foa800
nName 94c->2190—-990-> “winresult.dll”
nBase 950->01(这是起始序号,函数编号-起始序号就是索引号)
Address of functions 95c->2168—-968 ->数组(1183、1022逐个的RVA偏移)
Address of Names 960->2178—-978 ->数组(219E(99E->函数名字)、21AB)(对应相加)
Address of NameOrdinals 964->2188—988->数组(0000、0001、0002、0003)(这是索引号)
也可以得知函数编号分别为1、2、3、4.(第一张图的index就是函数编号)

无导入表的情况下索引函数地址与导入库

其中sub_4010A4是根据kernel32.dll的导出表,用字符串来索引函数地址。
以下是该函数

这里解释一下:
v7是字符串长度包括\x00
v2=v6
v2[9]与v6[9]都是函数序列地址表
v2[8]是函数名称地址表
v2[7]是导出函数地址表
主要解释一下最后的返回值。
kernel32_base+v6[7]找到导出函数地址表
4*序列号来索引
序列号用函数序列地址表来索引(kernel32_base+v6[9])
这个地址要乘对应的偏移来找到正确的指向函数
v3是函数名称地址表中对应于正确函数的那一项的地址
v3 - kernel_base - v6[8] (这个是函数名称地址表的起始地址)得到正确的函数距离起始地址的字节数,这里存的每一项都是两个字节,右移1位就是正确的序列号。
找到正确的序列号后,用导出函数地址表索引到对应的函数偏移,然后返回用kernel32的基地址与偏移相加得到的可以使用的函数地址。