汇编语言
不会汇编,强行刚软保,瑟瑟发抖稳得一匹
本文的编撰仅面向作者本人
前置知识
C语法
- 指针作差得到区间元素个数。指针的类型参与计算!
- 要动态改变函数体,需要在编译期赋予权限。
1 |
__asm int 3
加入汇编断点(OD)。要在OD中启用StrongOD,禁用跳过int3;并设置测试选项
Windows exe结构
- 401000H这个内存地址对应的文件地址是1000H
工具
Quickview:更改EXE数据
- 按
F5
可以按地址定位 - 提供三种模式:16进制格式,汇编格式,纯文本格式;按
F2
切换到32位模式 - 按
Alt+F9
保存 - 按
ins
选择块(开始和结束各按一次),选择后按shift
底部出现块工具
OD:单步跟踪EXE
- 可以用来追踪去壳代码
- 可以dump数据块到文件
- 可以设置硬件断点,当某储存位置发生更改时中断
Alt+B
管理断点,空格
切换
IDA:静态分析EXE
- 按
G
定位 - 可通过加壳反抗静态分析。
方法
面对加壳
- 用OD动态追踪,尝试还原被加壳代码(等待去壳),再用QV还原EXE(用块复制的办法加快速度)
面对序列号
观察到现象:弹框并消失。可行办法:跟踪CreateWindow/ShowWindow/DestoryWindow(推荐)
用OD动态追踪,尝试中断DestoryWindow,观察堆栈顶端找到出栈后的程序运行位置(或直接跑retn)
对不当场判断的场合(没有明显现象) 怀疑有文件/注册表读写
FileMon + RegMon = ProcMon
RegCreateKey()
RegQueryValue()
RegSetValue()CreateFile
CreateFileEx
ReadFile
WriteFile软件断点:当前断点指令的首字节改为
0xCC
(对应的汇编指令为int 3)。 可能被程序自检。硬件断点:选中某条指令->断点->硬件执行。 检查已设置的断点:调试->硬件断点... 原理:CPU调试寄存器保存了断点地址和断点条件,每个周期都会检查条件。触发条件有
execute
,read
,write
。 要设置读/写断点,选中变量的首字节->右键->断点->硬件写入->DWORD(对应的类型)。要设置DWORD断点对变量首字节有要求。读/写完成后到达断点。
可以用printf("%p",...)
知道要跟踪的变量的地址。利用读/写硬件断点,当操作序列号时就能断住。
Ctrl+B
搜索已输入的序列号找到内存地址。跳跃几次看到rep movs
(字符串拷贝),此时取消断点,回到用户代码,跳出几次,看到getText如果进入系统代码,则应跳出到用户代码
Alt+F9
,再按Ctrl+F9
跳出一些函数。CallWindowProcA
的作用是由系统内核来回调用户事先写的消息处理函数
汇编规则
- cdq edx:eax, idiv, edx = edx:eax %n
逆向工程历史
- 微软开发
debug
,codeview
, 宝蓝开发TurboDebug
;
debug
debug
编程:a(assemble) 地址+汇编语言,偏移地址固定从100开始 (OD中改指令相当于a,若下一条语句实效,会有安全措施,改90NULL)。- u(unassemble) 反汇编,指定开始地址和末地址(l+长度亦可),反汇编对应地址内的机器语言。
- r(register) 查看寄存器 SF+PL-NG UP上DN下. r + 寄存器名字, 修改内存
- p (optional 地址) 单步前进,目标地址是执行完毕的
- g (optional =首地址 (optional) 末地址) (从csip)跳跃直到int 3(末地址是未执行的);
- d(dump) 查看内存 可以指定(d cs:100 120 || d cs:100 l20)
- e(edit) 修改内存 地址 + "string" || hex
- t(track in)
加密软件
lock89
,lockup
, 作者:杨道沅, 硬盘指纹加密技术lock93
,lock93NT
, 周辉BitLock
, 雷军
驻留内存程序
需要技术
- 驻留内存
- 中断
es:[bx]
0:32是一个中断向量的地址
0:0 ~ 0:3FF 是中断向量表,每个中断向量占用4字节。 例如int 00h
的中断向量储存在0:0 ~ 0:3之间; int 01h
的中断向量储存在0:4 ~ 0:7之间。
当cpu执行int 00h
时:
pushf 保护当前标志位(flag)的状态
push cs 当前短地址
push 下条指令的偏移地址
cli
jmp 8756h:3412h
// cli disable interrupt (clear interrupt)
// sti enable. (set interrupt)
8756h:3412h 是中断服务程序的入口地址
……
iret; 中断返回
当cpu执行iret
时:
pop ip;
pop cs;
popf; 从而实现恢复现场。
1 | ; 调换中断向量表 |
1 | jmp dword ptr cs:[old_8h] ; 中断链接 old_8h定义在code段内是必要的 |
1 | int_8h: ; 中断服务 |
驻留
1 | install: |
gamebuster
1 | mount c:... d:\... |
dosboxdeb
dos mcbs 获得当前程序的PSP (memory control block)
u 01dd:1785 // 查看反汇编
单步中断
1 | pushf |
某种病毒
修改int 21h
中断向量为xxxx:yyyy
,原向量保存在内部变量old_21h
中。新指令为cmp ah 4Bh
,判断是否要执行程度。
1 | je infect_exe |
原始的int 21h
中断向量的段地址<=70h
假装调用一个无用的int 21h
功能,如获取dos版本号
1 | mov ah, 30h |
EXE文件格式
+00 4D, 5A; EXE标志“MZ”
+02 50, 01; 最后一个扇区的字节数为150h (例外:如果是00 00则最后一个扇区是满载)
+04 02, 00; EXE占用的扇区数量(200字节/一个扇区)
+06 02, 00; 重定位项数
+08 02, 00; 文件头的节长度=20h,所以字节长度=200h (20h x 10h)
+0A 00, 00; 至少内存 DOS是单任务的,所以内存限制没有太大意义
+0C FF, FF; 至多内存
+0E 05, 00; SS与程序首段地址的距离 (程序首段地址在运行时确定, = psp+10h)
+10 00, 01; SP = 100h
+12 E8, 20; EXE文件头的校验值
+14 28, 00; IP=0028h ; 注意此处先IP后CS CS:IP第一句指令
+16 02, 00; CS与程序首段地址的距离
+18 1E, 00; 重定位表的偏移地址
; 下面是重定位表。每个重定位项4个字节。每两个字节为一个信息。
+1E 01, 00; 重定位的偏移地址=0001h
+20 02, 00; Δ=0002h,
; 首段地址+Δ=重定位的段地址
;
+22 0D, 00; 重定位的偏移地址=000Dh
+24 02, 00; Δ=0002h,
; 首段地址+Δ=重定位的段地址
DOS运行程序时,文件头不载入内存,所以内存头可认为是文件头长度
200h+2:1 = 0:200h + 0:21 = 0:211
1 | mov ah, 2 |
1 | code segment |
注意加密堆栈后引发的解密对新数据的影响的问题。
保存ss和sp,之后修改它为一段预留空间
保护模式
32位系统下
保护模式 | 实模式 | |
---|---|---|
最大段长度 | 4G | 64K |
越界访问 | 运行时错误 | 允许 |
物理寻址 | 查gdt表 | 基址*10h+10h+偏移 |
查gdt表
设ds=10h
,现通过查gdt表获得其物理地址。gdt表储存描述符,每个占8字节。表的首地址存在于gdtr
寄存器。
gdt+10h xx xx xx xx xx xx xx xx
idt中断描述符表
首地址存在于idtr
中。每项有8字节。基址2字节,偏移4字节,剩余2字节记录权限。
1 | lgdt fword ptr gdt ; 把gdt表的首地址和长度载入到gdtr寄存器 |