推荐肉丝r0ysue课程(包含安卓逆向与js逆向):
做了笔记从来不看系列……丢云端清本地了
文章目录
滴水
2015-01-12(进制01)
2015-01-13(进制01)
2015-01-14(数据宽度_逻辑运算)
逻辑运算
- 或 (or |)
- 与 (and &)
- 异或 (xor ^)
- 非 (not !)
cpu中如何计算2+3
X:0010
Y:0011
xor:0001
保存至R:0001
and:0010
左移:<<1 -> S:0100
判断是否等于0
若等于0 ,则结果为R
若不等于0
X = R = 0001
Y = S = 0100
xor:0101
保存至R:0101
and -> R:0
<<1 -> R:0
判断是否等于0
等于0,则结果为R
最终结果 R:0101
32位通用寄存器 | 主要用途 |
---|---|
EAX | 累加器 |
ECX | 计数 |
EDX | I/O指针 |
EBX | DS段的数据指针 |
ESP | 堆栈指针 |
EBP | SS段的数据指针 |
ESI | 字符串操作的源指针 SS段的数据指针 |
EDI | 字符串操作的目的指针 ES段的数据指针 |
2015-01-15(通用寄存器_内存读写)
2015-01-16(内存地址_堆栈)
-
PUSH指令:
-
PUSH r32
-
PUSH r16
-
PUSH m16
-
PUSH m32
-
PUSH imn8/ imm16/ imm32
-
-
POP指令
- POP r32
- POP r16
- POP m16
- POP m32
2015-01-19(标志寄存器)
-
进位标志CF (Carry Flag) :如果运算结果的最高位(如8位的第九位)产生了一个进位或借位,那么,其值为1,否则其值为0。(无符号)
-
奇偶标志PF (Parity F1ag):奇偶标志PF用于反映运算结果二进制中“1”的个数的奇偶性。如果“1”的个数为偶数,则PP的值为1,否则其值为0。
只看最后一个字节
-
辅助进位标志AF (Auxiliary Carry Flag): .
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0;
(1)、在字操作时,发生低字节向高字节进位或借位时;
(2)、在字节操作时,发生低4位向高4位进位或借位时。 -
零标志ZF (Zero Flag): 零标志ZF用来反映运算结果是否为0。
如果运算结果为0,则其值为1,否则其值为0。在判断运算结果是否为0时,可使用此标志位。 -
符号标志SF (Sign F1ag): 符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
-
溢出标志0F (Overflow Flag):溢出标志0F用于反映有符号数加减运算所得结果是否溢出。
如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1, 否则,OF的值被清为0。OF = 符号位进位 xor 最高有效数值位向符号位产生的进位
溢出主要是给有符号运算使用的,在有符号的运算中,有如下的规律:
正+正=正如果结果是负数,则说明有溢出
负+负=负如果结果是正数,则说明有溢出
正+负永远都不会有溢出.
-
ADC指令:带进位加法
格式: ADC R/M, R/M/ IMM操作对象1=操作对象1 + 操作对象2 + CF
两边不能同时为内存,宽度要一样
-
SBB指令:带借位减法
格式: SBB R/M, R/M/IMM操作对象1=操作对象1 - 操作对象2 - CF
两边不能同时为内存,宽度要一样
-
XCHG指令:交换数据
格式: XCHG R/M, R/M/IMM
两边不能同时为内存宽度要一样 -
movsx 带符号位拓展,高位拓展为符号位
-
movzx 不带符号位拓展,高位拓展为0
-
MOVS指令:移动数据 内存—内存
BYTE/WORD/ DWORD-
MOVS BYTE PTR ES:[EDI],BYTE PTR DS: [ESI] 简写为: MOVSB
-
MOVS WORD PTR ES:[EDI],BYTE PTR DS: [ESI]
简写为: MOVSW -
OVS DWORD PTR ES:[EDI], BYTE PTR DS:[ESI]
简写为: MOVSDDF标志位看方向,=1 减;等于0,加
-
-
STOS指令:将A1/AX/EAX的值存储到[EDI]指定的内存单元
- STOS BYTE PTR ES: [EDI]
简写为STOSB - STOS WORD PTR ES: [EDI]
简写为STOSW - STOS DWORD PTR ES:[EDI]
简写为STOSD
- STOS BYTE PTR ES: [EDI]
-
REP指令:按计数寄存器
(ECX) 中指定的次数重复执行字符串指令
2015-01-20(JCC补录)
指令 | 含义 | 标志位 |
---|---|---|
JE, JZ | 结果为零则跳转(相等时跳转) | ZF=1 |
JNE,JNZ | 结果不为零则跳转(不相等时跳转) | ZF=0 |
JS | 结果为负则跳转 | SF=1 |
JNS | 结果为非负则跳转 | SF=0 |
JP,JPE | 结果中1的个数为偶数则跳转 | PF=1 |
JNP,JPO | 结果中1的个数为偶数则跳转 | PF=0 |
JO | 结果溢出了则跳转 | OF=1 |
JNO | 结果没有溢出则跳转 | 0F=0 |
JB,JNAE | 小于则跳转(无符号数) | CF=1 |
JNB,JAE | 大于等于则跳转(无符号数) | CF=0 |
JBE,JNA | 小于等于则跳转(无符号数) | CF=1 or ZF=1 |
JNBE,JA | 大于则跳转(无符号数) | CF=0 and ZF=0 |
JL,JNGE | 小于则跳转(有符号数) | SF≠OF |
JNL,JGE | 大于等于则跳转(有符号数) | SF=OF |
JLE,JNG | 小于等于则跳转(有符号数) | ZF=1 or SF≠OF |
JNLE,JG | 大于则跳转(有符号数) | ZF=0 and SF=OF |
JAE---->jump when above or equal
JNB---->jump when not below
JB---->jump when below
JNAE---->jump when not above or equal
JBE---->jump when below or equal
JNA---->jump when not above
JG---->jump when greater
JNLE---->jump when not less or equal
有符号拓展时用符号拓展,无符号数拓展时用0拓展
MOVSX 先符号拓展,再传送(赋值)(即正数0拓展,负数1拓展)
MOVZX 先0拓展,再传送
2015-01-23(C语言完整版)
空函数汇编代码
push ebp # 压入ebp,即旧的栈底指针
mov ebp,esp # 开始一个新的栈帧
sub esp,40h # 为当前函数申请一段栈空间
push ebx
push esi
push edi # 保存旧的寄存器值
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi] # 将申请的栈空间用CC填充(即int 3)
##############
##函数核心功能##
##############
pop edi
pop esi
pop ebx # 恢复寄存器值
mov esp,ebp # 取消新的栈帧
pop ebp # 弹出旧栈帧的栈底指针
ret # 返回
裸函数
void __declspec(naked) Plus() //中间 决定是否为裸函数
c语言中写汇编语言
__asm
{
//汇编指令
}
main 或WinMain 是“语法规定的用户入口”,而不是“应用程序入口”。应用程序入口通常是启动函数。
main 函数被调用前要先调用的函数如下:
GetVersion()
:获取操作系统版本_heap_init()
GetCommandLineA()
_crtGetEnvironmentStringsA()
_setargv()
_setenvp()
_cinit()
这些函数调用结束后就会调用main函数,根据main 函数调用的特征,将3 个参数压入栈内作为函数的参数。main()函数有3个参数
2015-01-26 (c语言02_数据类型)
float,double遵循IEEE
float存储方式
31 | 30 | 22 0 |
---|---|---|
1位 | 8位 | 23位 |
符号位 | 指数部分|阶码 | 尾数 |
-
将float转换位内存存储格式
-
先转换为二进制形式
-
将小数点左移或右移n位(科学计数法)左移为+,右移为-,得到阶码真实值
实际阶码存储时候,会将阶码真实值+127,进行存储
存储阶码 = 真实阶码 + 127
-
符号位 为小数的符号位,小数为正则为0,小数为负则为1
-
尾数 从 小数点 后取23位存入尾数,右端用0补足
-
-
例如:8.25
-
整数部分:1000 ↑
小数部分:01 ↓
1000.01
-
小数点左移3位,变为1.00001则真实阶码为+3,存储阶码为+3+127,转换为二进制:1000 0010
-
符号位,整数,符号位为0
-
尾数01,右端用0补足,则为00001000000000000000000
最终为0100 0001 0000 0100 0000 0000 0000 0000
转换为16进制为41040000
-
double同float 64位
63 | 62 | 51 0 |
---|---|---|
1位 | 11位 | 52位 |
符号位 | 指数部分|阶码 | 尾数 |
2015-02-05 (结构体 字节对齐)
#pragma pack的基本用法为:
#pragma pack( n ) //结构体按n为字节对齐
**自身对齐值:**数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2;
**指定对齐值:**编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
**有效对齐值:**自身对齐值和指定对齐值中较小的那个对齐参数: n为字节对齐数,其取值为1、2、4、8,默认是8。
- 原则一:数据成员对齐规则:结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储).
- 原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
- 原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.)
- 原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准.也就是说,结构体成员的偏移量应该取二者的最小值.
2015-02-06 (switch语句反汇编)
- switch语句在多个连续分支的情况下会生成地址表来查询
- 若switch大部分分支连续,少部分不连续,会在地址表后生成偏移表
- 若分支间隔较大且分支太少,与if else语句无区别
指针
-
指针的指针:二级指针、三级指针、n级指针
-
指针数组
int * arr[10]; //一个长度为10的数组,其中每一个元素都为一个int*类型的指针
-
数组指针
int (* arrPtr)[10] = NULL; // 一个指针,它指向一个有10个int元素的数组 //若有 int arr[]={...}; *arrPtr = arr; //有以下式子成立 arrPtr = arr; *arrPtr = arr; *(arrPtr+0) = arr; arrPtr[0] = arr;
-
结构体指针:指向结构体的指针
struct stu{ int id; int level; }player; stu * Ptr; Ptr->id = 0; Ptr->level = 20;
-
指针函数:返回值为指针的函数
int arr[] = {...}; int * func() { int * p = arr; return p; }
-
函数指针:
double (*funcPtr)(double, double);//定义了一个指向函数的指针,该函数具有两个 double 类型的参数和 double 类型的返回值 double result; funcPtr = pow; // 使得funcPtr指向函数pow() // 因此,表达式*funcPtr获得函数pow() result = (*funcPtr)( 1.5, 2.0 ); // 通过funcPtr调用函数 result = funcPtr( 1.5, 2.0 ); // 与上等效的函数调用
double Add( double x, double y ) { return x + y; } double Sub( double x, double y ) { return x - y; } double Mul( double x, double y ) { return x * y; } double Div( double x, double y ) { return x / y; } // 具有5个函数指针的数组,这些函数需要两个double类型参数,返回值为double类型 double (*funcTable[5])(double, double) = { Add, Sub, Mul, Div, pow }; // 初始化器列表 //自己的解释:funcTable是一个长度为5指针数组,其中数组内的每一个元素都是一个函数指针
位运算
-
SAL:算术左移
低位补零,高位存进CF标志位
例:CF=0 1000 0001 -> 0000 0010 CF = 1
-
SAR:算数右移
低位存进CF标志位,高位不变并且右移
例:CF=0 1000 0001 -> 1100 0000 CF = 1
-
SHL:逻辑左移,同算术左移
-
SHR:逻辑右移
高位补零,低位存进CF标志位
例:CF=0 1000 0001 -> 0100 0000 CF = 1
-
ROL:循环左移,循环移动,每次移出的位赋值给CF并补到另一边
例:CF=0 1000 0000 -> 0000 0001 CF = 1
-
ROR:循环右移,循环移动,每次移出的位赋值给CF并补到另一边
例:CF=0 0000 0001 -> 1000 0000 CF = 1
-
RCL:带进位循环左移,将CF标志位也放在循环里一起移动,多出来的移到CF,少的用CF位补
例:CF=1 0000 1000 -> 0001 0001 CF = 0
-
RCR:带仅为循环右移,将CF标志位也放在循环里一起移动
例:CF=0 0001 0001 -> 0000 1000 CF = 1
C语言中,左移<< 为逻辑左移
右移>> 有符号:SAR 无符号:SHR
汇编笔记
第二章 寄存器
用Debug的R命令查看、改变CPU寄存器的内容;
用Debug的D命令查看内存中的内容;
用Debug的E命令改写内存中的内容;
用Debug的U命令将内存中的机器指令翻译成汇编指令;
用Debug的T命令执行一条机器指令;
用Debug的A命令以汇编指令的格式在内存中写入一条机器指令。
cs 指令的段寄存器 IP:偏移
ds 数据的段寄存器 [xxx]:偏移
ss 栈的段寄存器 sp:偏移 ss:sp指向栈顶
bx 存放的数据作为偏移,段地址在ds中
inc bx bx = bx + 1
dec bx bx = bx - 1
cx 存放循环次数
未整理指令
loop指令
-
loop 标号
-
(cx) = (cx) - 1 判断cx的值 //(cx)表示cx里面的值
-
先讲(cx)-1 若(cx)不为0,则转向s
-
mov cx,11 s: add ax,ax loop s mov cx,循环次数 s: 循环执行的程序段 loop s
用循环累加来实现乘法,使用dx寄存器进行累加
mov ax,2000h
mov dx,ax
mov al,ds:[0] //把2000:0地址里的值给al
串传送指令movsb
相当于进行下面几步操作
((es)*16+(di)) = ((ds)*16+(si))
//mov es:[di],byte ptr ds:[si]
若df = 0 则:
(si)++
(di)++
若df = 1 则:
(si)--
(di)--
movsb传送一个字节
也可传送一个字,使用movsw
常与rep配合使用
rep movsb
用汇编语言来描述
s:movsb
loop s
可实现(cx)个字符的传送
对df进行设置
cld指令:将df位置0
std指令:将df位置1
例:用串传送指令,将data段中的第一个字符串复制到它后面的空间中
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
code segment
mov ax,data
mov ds,ax
mov si,0 ;ds:si指向data:0
mov es,ax
mov di,16 ;es:di指向data:0010
mov cx,16 ;rep循环16次
cld ;设置df=0,正向传送
rep movsb
code ends
汇编指令
汇编指令 | 功能 |
---|---|
jmp 段地址:偏移地址 | 用指令给出的段地址修改CS,偏移地址修改IP |
jmp 某一合法寄存器 | 用寄存器中的值修改IP |
Loop 标号 | cx–;判断cx的值,若不为0则跳转到标号处执行程序,若为0则向下执行 |
and | 逻辑与指令,按位进行与运算 |
or | 逻辑或指令,按位进行或运算 |
div | 除法指令 |
adc 对象1,对象2 | 功能:对象1 = 对象1 + 对象2 + ZF |
sbb 对象1,对象2 | 功能:对象1 - 对象2 - CF |
cmp 对象1,对象2 | 计算 对象1-对象2 但是不保存结果,仅影响标志位 |
shl指令 | 逻辑左移指令 |
shr指令 | 逻辑右移指令 |
-
div指令:
- 除数:有8位和16位两种,在一个寄存器或内存单元中
- 被除数:默认放在AX或DX和AX中
- 如果除数为8位,被除数则为16位,默认在AX中存放;
- 如果除数为16位,被除数则为32位,在DX和AX中存放,DX存放高16位,AX存放低16位
- 结果:
- 如果除数为8位,则AL存储除法操作的商,AH存储除法操作的余数;
- 如果除数为16位,则AX存储除法操作的商,DX存储除法操作的余数。
- 格式:
- div 内存单元
- div 寄存器
-
mul指令
两个相乘的数:要么都是8位,要么都是16位- 如果是8位,一个默认放在al中,另一个放在8位寄存器或内存字节单元中;
- 如果是16位,一个默认放在ax中,另一个放在16位寄存器或内存字节单元中。
格式:- mul 内存单元
- mul 寄存器
-
shl逻辑左移指令
- 将一个寄存器或者内存单元中的数据向左移位
- 将最后移出的一位写入CF中
- 最低位用0补充
- 左移一位可用 shl 寄存器,1
- 左移多位必须将位数存入cl中, shl 寄存器,cl
左移一位相当于执行X=X*2
-
shr逻辑右移指令
- 将一个寄存器或者内存单元中的数据向右移位
- 将最后移出的一位写入CF中
- 最高位用0补充
- 右移一位可用 shl 寄存器,1
- 右移多位必须将位数存入cl中, shl 寄存器,cl
右移一位相当于执行X=X/2
转移指令 | 条件及含义 |
---|---|
jcxz指令 | 如果(cx)==0,跳转 |
loop指令 | cx=cx-1;如果cx不等于0,跳转 |
ret指令 | 相当于pop ip,更改ip的内容 |
retf指令 | 相当于pop ip,pop cs;更改ip和cs的内容 |
call 标号 | 相当于push ip;jmp near ptr 标号 |
call far ptr 标号 | 相当于push cs,push ip;jmp far ptr 标号 |
条件跳转指令
条件跳转指令 | 含义 | 检测的相关标志位 |
---|---|---|
je | 等于则跳转 | zf==1 |
jne | 不等于则跳转 | zf==0 |
jb | 低于则跳转 | cf==1 |
jnb | 不低于则跳转 | cf==0 |
ja | 高于则跳转 | cf==0且zf==0 |
jna | 不高于则跳转 | cf==1或zf==1 |
j:jump
e:equal相等
b:below低
a:above高
标志寄存器
标志寄存器 | 含义 |
---|---|
ZF标志位 | 判断指令执行结果是否为0 若结果为0则ZF=1;结果不为0则ZF=0 |
PF标志位 | 判断指令执行结果 的二进制位 的1的个数 若1的个数为偶数,则PF=1 |
SF标志位 | 判断指令执行结果 是否为负 如果为负,则SF=1;若为非负,则SF=0 |
CF标志位 | 进行无符号数运算时,结果 如果进位、或者借位,则CF=1 |
OF标志位 | 进行有符号数运算时,结果 若有溢出,则OF=1;若无溢出,则OF=0 |
DF标志位 | DF执行串处理指令时,控制每次操作后si,di的增减;df0 每次操作后si,di递增;df1 每次操作后si,di递减 |
逆向工程核心原理学习笔记
第1章 关于逆向工程
第2章 逆向分析Hello World!程序
2.3.1 调试器指令
指令 | 快捷键 | 含义 |
---|---|---|
Restart | Ctrl+F2 | 重新开始调试(终止正在调试的进程) |
Step Into | F7 | 执行一句OP code(操作码),若遇到调用命令(CALL),将进入函数代码内部 |
Step Over | F8 | 执行一句OP code(操作码),若遇到调用命令(CALL),仅执行函数本身,不跟随进入 |
Execute till Return | Ctrl+F9 | 一直在函数代码内部运行,直到遇到RETN命令,跳出函数 |
Go To | Ctrl+G | 移动到指定地址,用来查看代码或内存,运行时不可用 |
Execute till Cursor | F4 | 执行到光标位置,即直接转到要调试的地址 |
Comment | ; | 添加注释 |
User-defined comment | 鼠标右键菜单 → Search for → User-defined comment | |
Label | : | 添加标签 |
User-defined label | 鼠标右键菜单 → Search for → User-defined label | |
Set/Reset BreakPoint | F2 | 设置或取消断点(BP) |
Run | F9 | 运行(若设置了断点,则执行至断点处) |
Show the current EIP | * | 显示当前EIP (命令指针)位置 |
Show the previous Cursor | - | 显示上一个光标的位置 |
Preview CALL/JMP address | Enter | 若光标处有CALL/JMP等指令,则跟踪并显示相关地址(运行时不可用,简单查看函数内容时非常有用) |
All referenced text strings | 鼠标右键菜单Search for → All referenced text strings | 查看代码中引用的字符串 |
All intermodular calls | 鼠标右键 → Search for(查找) → All intermodular calls(所有模块间的调用) | 查看代码中调用的所有API函数 |
Name in all modules | 鼠标右键 → Search for(查找) → Name in all modules(所有模块中的名称) | 查看所有API函数 |
Edit data | Ctrl+E | 编辑数据 |
Assemble | Space | 编写汇编代码 |
Copy to executable file | 鼠标右键菜单Copy to executable file | 创建文本副本(修改的项目被保留) |
2.3.3 设置“大本营”的四种方法
1.Goto命令
执行Goto(Ctrl+G)命令,输入[地址],点击【OK】,光标自动定位到[地址]处,F4执行到光标位置
2. 设置断点
F2在当前光标位置下断点,Alt+B打开断点窗口
3. 注释
摁“;”在指定地址添加注释,并可以通过查找命令找到它。
鼠标右键 → 查找 → 用户自定义注释
红字显示部分即是光标所处位置。注释位置和光标位置重合时,将仅以红字方式显示。
双击相应注释,光标将自动定位到相应位置。
4. 标签
摁“:”在指定地址添加注释,并可以通过查找命令找到它。
鼠标右键 → 查找 → 用户自定义标签
红字显示部分即是光标所处位置。注释位置和光标位置重合时,将仅以红字方式显示。
双击相应注释,光标将自动定位到相应位置。
2.4 快速查找指定代码的四种方法
2.4.1 代码执行法
当程序功能非常明确时,逐条执行指令来查找需要查找的位置。
2.4.2 字符串检索法
2.4.3 API检索法(1):在调用代码中设置断点
鼠标右键 → Search for(查找) → All intermodular calls(所有模块间的调用)
调出窗口 Alt+R
2.4.4 API检索法(1):在API代码中设置断点
鼠标右键 → Search for(查找) → Name in all modules(所有模块中的名称)
调用窗口 Alt+N
2.5 使用“打补丁”方式修改字符串
直接修改字符串缓冲区
-
在数据窗口摁Ctrl+G快捷键执行Goto命令,输入[字符串的地址]进入字符串缓冲区
或者选中字符串地址调用命令,右键→数据窗口中跟随→立即数
-
用鼠标选中字符串,Ctrl+E快捷键打开编辑窗口,进行修改.(字符串结尾要以NULL结束,需要在HEX项目中添加“00 00”)
-
选中修改的地方,右键→保存到可执行文件,在弹出的Hex窗口中单击右键→保存文件
在其他内存区域新建字符串并传递给消息函数
- 在数据窗口向下拖动浮条,找到一段足够长由NULL填充的区域
- 快捷键Ctrl+E写入新字符串
- 在反汇编窗口,选中调用原字符串的代码,按空格键,将[原字符串地址]修改为[新字符串地址]
但无法保存
小结
-
OllyDbg常用命令
参见2.3.1调试器指令 -
Assembly(汇编语言)基础指令
参见汇编指令 -
术语
术语 | 说明 |
---|---|
VA (Virtual Address) | 进程的虚拟地址 |
OP code (OPeration code) | CPU指令 (字节码byte code) |
PE (Portable Executable) | Windows可执行文件 (EXE、DLL、SYS等) |
第3章 小端序标记法
字节序是多字节数据在计算机内存中存放的字节顺序。字节序主要分为小端序和大端序两大类。
BYTE b = 0x12;
WORD w = 0x1234;
DWORD dw = 0x12345678;
char str[] = "abcde";
大端序与小端序的不同
TYPE | Name | SIZE | 大端序类型 | 小端序类型 |
---|---|---|---|---|
BYTE | b | 1 | [12] | [12] |
WORD | w | 2 | [12] [34] | [34] [12] |
DWORD | dw | 4 | [12] [34] [56] [78] | [78] [56] [34] [12] |
char[] | str | 6 | [61] [62] [63] [64] [65] [00] | [61] [62] [63] [64] [65] [00] |
字符串str存储时用ASCII码表示,字符串最后是以NULL结尾的。
- 数据类型为字节型(BYTE)时,长度为1个字节,保存这样的数据时,无论采用大端序还是小端序,字节顺序都是一样的。
- 数据长度为2个字节以上(含2个字节)时,采用不同字节序保存它们形成的存储顺序是不同的。
- 使用大端序存储数据时:
内存地址低位存储数据的高位,地址高位存储数据的低位,这是一种最直观的字节存储顺序。(数据位左高右低) - 使用小端序存储数据时:
内存地址低位存储数据的低位,地址高位存储数据的高位,保存的字节顺序被倒转 - 字符串因被保存在一个字符(char)数组str中,字符数组在内存中是连续的,此时向字符数组中存放数据,无论采用大端序还是小端序,存储顺序都相同。
- 使用大端序存储数据时:
第4章 IA-32寄存器基本讲解
4.2.1 通用寄存器
通用寄存器 | 寄存器名称 | 额外描述 |
---|---|---|
EAX | (针对操作数和结果数据的)累加器 | 一般用于函数返回值,所有Win32 API函数都会把返回值保存到EAX内返回 |
EBX | (DS段中的数据指针)基址寄存器 | |
ECX | (字符串和循环操作的)计数器 | |
EDX | (I/O指针)数据寄存器 | |
EBP | (SS段中栈内数据指针)拓展基址指针寄存器 | EBP表示栈区域的基地址 |
ESI | (字符串操作源指针)源变址寄存器 | |
EDI | (字符串操作目标指针)目的变址寄存器 | |
ESP | (SS段中栈指针)栈指针寄存器 | ESP表示栈区域的栈顶地址 |
4.2.2 段寄存器
段寄存器 | 寄存器名称 | 额外描述 |
---|---|---|
CS | Code Segment,代码段寄存器 | CS寄存器用于存放应用程序代码所在段的段基址 |
SS | Stack Segment,栈段寄存器 | SS寄存器用于存放栈段的段基址 |
DS | Data Segment,数据段寄存器 | DS寄存器用于存放数据段的段基址 |
ES | Extra(Data) Segment,附加(数据)段寄存器 | |
FS | Data Segment,数据段寄存器 | |
GS | Data Segment,数据段寄存器 | ES、FS、GS寄存器用来存放程序使用的附加数据段的段基址 |
4.2.3 标志寄存器
标志寄存器 | 寄存器名称 | 额外描述 |
---|---|---|
ZF | Zero Flag,零标志位 | 运算结果为0,则其值为1(Ture),否则其值为0(False) |
OF | Overflow Flag,溢出标志位 | 有符号整数溢出时,OF值被置为1,此外,MSB(最高有效位)改变时,其值也被设为1 |
CF | Carry Flag,进位标志位 | 无符号整数溢出时,其值被设为1 |
4.2.4. 指令指针寄存器EIP
指令指针寄存器 | 寄存器名称 | 额外描述 |
---|---|---|
EIP | Instruction Pointer,指令指针寄存器 | 指令指针寄存器保存着CPU要执行的指令地址 |
第5章 栈
栈内存在进程中的作用:
-
暂时保存函数内的局部变量
-
调用函数时传递参数
-
保存函数返回后的地址
-
栈是一种由高地址向低地址拓展的数据结构,按照FILO(First In Last Out,先进后出)的原则存储数据。
-
向栈压入数据时,栈顶指针减小,向低地址移动;从栈中弹出数据时,栈顶指针增加,向高地址移动。
-
栈顶指针在初始状态下指向栈底。
第6章 分析abex’ crackme#1
参见汇编指令
第7章 栈帧
动调一遍
第8章 abex’crackme#2
VB文件特征:
- VB文件使用名为MSVBVM60.dll的VB专用引擎。例:若VB代码要调用MsBox()函数,真正调用的是MSVBVM60.dll中的rtcMsgBox()函数,在该函数内部通过调用user32.dll里的MessageBoxW()函数(Win32 API)来工作。也可直接调用Win32 API
- VB程序采用Windows操作系统的事件驱动方式共工作,在main函数中不存在用户代码,用户代码存在于各个事件处理程序之中(例:按钮单击事件)
- VB字符串使用可变长度的字符串类型。直接显示的不是字符串,而是16字节大小的数据,其中持有实际动态分配的字符串缓存地址。
第10章 函数调用约定
术语说明 | |
---|---|
调用者 | 调用函数的一方 |
被调用者 | 被调用的函数 |
例:在main()函数中调用printf()函数,调用者为main(),被调用者为printf() |
- cdecl方式
- cdecl是主要在c语言中使用的方式,调用者负责处理栈。
- 参数入栈顺序:从右到左
- 函数名:前加下划线:_func
- 调用者在调用完被调用函数后,使用汇编指令来整理栈,调用者直接清理其压入栈的函数参数。
- 优点:可像c语言的printf()函数一样,向被调用函数传递长度可变的参数。
- stdcall方式
- stdcall方式常用Win32 API,该方式由被调用者清理栈。
- 参数入栈顺序:从右到左
- 函数名:前加下划线 后面紧跟@符号,其后跟着参数的尺寸
- 被调用者最后使用例如
retn 8
这样的代码来清理栈。含义:retn
和pop 8
- 优点:栈清理代码存在于被调用函数内部,与cdecl相比,代码尺寸更小。
- fastcall方式(x64)
-
fastcall方式与stdcall方式基本类似
-
参数入栈顺序:第一个和第二个参数通过ecx和edx传递,多余的参数从右到左入栈
-
函数名:前加下划线 后面紧跟@符号,其后跟着参数的尺寸
-
该方式通常会使用寄存器(而非栈内存)去传递那些需要传递给函数的部分参数(前两个),前两个参数分别使用ECX与EDX寄存器来传递。
-
优势:可实现对函数的快速调用。
但有时需要额外的系统开销来管理ECX、EDX寄存器。若ECX和EDX中存有重要数据,需先备份。- x64 linux c调用约定
-
gcc x64调用参数顺序为edi,esi,edx,ecx,r8d,r9d;
edi为第一个参数,esi为第二个参数,以此类推,再多余的参数再从右向左使用栈来传递
第11章 视频讲座
第13章 PE文件结构
字段名 | 描述 | 字段值 | 偏移 |
---|---|---|---|
WORD e_magic | magic word | 5A4D(MZ)【表明是PE文件】 | 0 |
LONG e_lfanew | 保存着指向NT头的偏移 | (E8)字段值为NT头偏移 | 3C |
DWORD Signature | pe file signature | 4550【表明是PE文件】 | E8(e_lfanew) |
WORD Machine | 可执行文件的目标CPU | 0x014c【Intel 386】 0x0200【Intel 64】 | ) |
WORD Characteristics | PE文件类型 | 0x0002(可执行文件) 0x2000(dll文件) | |
AddressOfEntryPoint(OEP) | 程序准备执行指令的第一个RVA | ||
ImageBase | PE文件的优先装载地址 | ||
SectionAlignment | 内存中节对齐的粒度 | ||
FileAlignment | 文件中节对齐的粒度 | ||
汇编指令
汇编指令 | 含义 |
---|---|
第2章 | |
CALL xxxx | 调用xxxx地址处的函数 |
JMP xxxx | 跳转到xxxx地址处 |
PUSH xxxx | 将xxxx压入栈 |
RETN | 返回,跳转到栈顶的地址 |
第6章 | |
push | 入栈指令 |
call | 调用指定位置的函数 |
int | 值加1 |
dec | 值减一 |
jmp | 跳转到指定地址 |
cmp | 比较给定的两个操作数(类似于sub命令),但是计算结果不会保留,仅改变标志位寄存器(若2个操作数的值一致,sub结果为0,ZF被置为1) |
je/jz | 条件跳转指令,若ZF为1,则跳转(也就是结果为0) |
第8章 | |
test | 逻辑比较,按位与两个操作数(类似于and命令),但计算结果不会保留,仅改变标志位寄存器(2个操作数只要有1个为0,则and的运算结果为0,ZF被置为1) |
逆向知识点
2020/3/10 异常处理
题目:【安恒杯2019】crackme
术语:
SEH: 结构化异常处理
VEH: 向量化异常处理
TopLevelEH:顶层异常处理
EXCEPTION_EXECUTE_HANDLER :该异常被处理。从异常处下一条指令继续执行
EXCEPTION_CONTINUE_SEARCH:不能处理该异常,让别人处理它吧
EXCEPTION_CONTINUE_EXECUTION:该异常被忽略。从异常处处继续执行
//调试器返回值:
DBG_CONTINUE : 等同于EXCEPTION_CONTINUE_EXECUTION
DBG_EXCEPTION_NOT_HANDLED :等同于EXCEPTION_CONTINUE_SEARCH
异常处理器处理顺序流程:
- 交给调试器(进程必须被调试)
- 执行VEH
- 执行SEH
- TopLevelEH(进程被调试时不会被执行)
- 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
- 调用异常端口通知csrss.exe
交给调试器
如果该出现异常的程序正在被调试,则该异常首先交给调试器处理(通过DebugPort)。
调试器拿到这个异常后,需要判断是否要处理该异常,如果处理该异常返回DBG_CONTINUE,否则返回DBG_EXCEPTION_NOT_HANDLED
执行VEH 向量化异常处理
如果没有被调试,或者调试器返回DBG_EXCEPTION_NOT_HANDLED,则就会检查是否存在VEH。如果存在VEH,则把异常交给他们处理。
VEH是个链表,可以存在多个Veh。每个VEH按顺序被调用。
一个VEH可以返回连个值:EXCEPTION_CONTINUE_SEARCH、EXCEPTION_CONTINUE_EXECUTION。返回EXCEPTION_EXECUTE_HANDLER是无效的,等同于EXCEPTION_CONTINUE_SEARCH。
当一个Veh返回EXCEPTION_CONTINUE_SEARCH,则把异常交给下一个VEH处理。
如果返回EXCEPTION_CONTINUE_EXECUTION,认为已经被处理,退出异常处理器在异常指令处继续执行。
从执行顺序来看,VEH是在SEH之前执行的,并且不依赖某一线程,本进程中任何线程出现异常都可以被VEH处理,所以在有些时候是很有用处的。
执行SEH 结构化异常处理
当所有的VEH都不处理该异常,该异常就会让SEH处理。
SEH是基于线程栈的异常处理机制,所以它只能处理自己线程的异常。
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter (
_In_LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);
设置异常捕获函数。
参数:lpTopLevelExceptionFilter ,函数指针。当异常发生时,且程序不处于调试模式(在vs或者别的调试器里运行)则首先调用该函数。
返回值:返回以前设置的回调函数。
- EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常,可以优雅地结束了
- EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理,其他人来吧,于是windows调用默认的处理程序显示一个错误框,并结束
- EXCEPTION_CONTINUE_EXECUTION equ -1 表示错误已经被修复,请从异常发生处继续执行
注意:在调用者进程中的所有线程已经将来创建的线程的异常处理链顶函数都会被修改为当前设定的回调函数。
VEH向量化异常处理
VEH通过使用 Win32 API 函数 AddVectoredExceptionHandler可注册新的异常处理函数,函数的参数就是指向 EXCEPTION_POINTERS 结构的指针。
同时,增加了函数地址的注册处理程序链表。由于系统中使用一个链表来存储矢量异常处理程序,程序可以安装尽可能多的向量处理器,只要有必要。
在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历 VEH 记录链表的函数, 如果没有找到可以处理异常的注册函数,再开始遍历 SEH 注册链表。
二者之间具体联系:VEH优先权高于SHE,只有所有VEH全不处理某个异常的时候,异常处理权才会到达SHE。只要目标程序中没有利用VEH,那么,你所设计的VEH将是第一个得到控制者。现在采用SEH作为异常处理的普通C/C++程序对你将不会再有干扰,可以通过使用VEH来进行hook处理了。
如果存在调试器,那么控制权转向将会发生新的变化。当异常发生后,首先通知的将会是调试器,调试器不处理才会再返回控制权给VEH;如果VEH不处理,再返回给SHE;若SEH不处理,再给调试器一个机会,如果还不处理,则交由系统处理。
相关函数
PVOID WINAPI AddVectoredExceptionHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
功能:用来向VEH链注册一个异常处理函数,
参数FirstHandler : 是否插入到VEH链首部
- 如果FirstHandler为1非0,则此注册的函数会在出现异常之后优先被触发(仅仅是优先,是否First还要看是否有其他人也注册了函数
- 如果FirstHandler为0,则此注册函数会在出现异常之后滞后被触发(理由同上)
参数VectoredHandler : 是一个要注册的异常处理回调函数
- 也就是需要执行的自定义函数
ULONG WINAPI RemoveVectoredExceptionHandler(
_In_ PVOID Handler
);
功能:从VEH链移除一个异常处理函数
参数Handler : 用前一个函数注册之后返回的指针
PVOID WINAPI AddVectoredContinueHandler(
_In_ ULONG FirstHandler,
_In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler
);
//与第一个函数类似,但有区别
ULONG WINAPI RemoveVectoredContinueHandler(
_In_ PVOID Handler
);
//与第二个函数类似,但有区别
区别:
- 第一组函数注册、移除的VEH异常处理回调函数,会在SEH链里面的异常处理函数执行之前执行
- 第二组函数注册、移除的VEH异常处理回调函数,会在SEH链里面的异常处理函数执行之后执行
常用第一组
VEH回调函数详解
VEH由AddVectoredExceptionHandler添加处理函数
处理函数有一个参数参数类型为PEXCEPTION_POINTERS结构体
结构PEXCEPTION_POINTERS中保存着当前异常的各个寄存器,堆栈,地址,等多种信息 以下是PEXCEPTION_POINTERS的展开图
VEH Hook原理
1.异常处理结构中 VEH是唯一一个可以接受到所有异常信息的处理 换句话说 所有的异常信息 都会经过VEH
2.异常信息 通常是由[数组越界,内存访问出错,无效参数,int 3]等 造成的
3.一旦发生异常 操作系统会立即便利VEH 如果有处理函数 中断线程 并由处理函数处理。
根据以上信息 可以确定一个思路 比如我们hook 消息框API 给API的首地址写入int 3 断点 当执行时会产生异常 线程暂停 转交给异常处理函数处理 此时则可以在处理函数中做手脚 比如修改堆栈参数等。
相应操作完成后 将int3(0xcc)修改回源代码 修改EIP=addr(异常地址)让此处的代码重新执行一次正确的代码,将Eflags寄存器修改为单步调试模式(Eflags |= 0x100) 返回EXCEPTION_CONTINUE_EXECUTION(-1)让程序继续运行 然后又会产生一次断点(单步异常),此时我们将已经执行过的代码重新修改为int3(0xcc) 返回-1让程序继续运行。
re笔记
IDA快捷键
Enter 跟进函数实现,查看标号对应的地址
Esc 返回跟进处
A 解释光标处的地址为一个字符串的首地址
B 十六进制数与二进制数转换
C 解释光标处的地址为一条指令
D 解释光标处的地址为数据
G 快速查找到对应地址
H 十六进制数与十进制数转换
k 将数据解释为栈变量
; 添加注释
M 解释为枚举成员
N 重新命名
O 解释地址为效据段偏移量,用于字符串标号
T 解释数据位一个结构体成员
X 转换视图到交叉参考模式
Shift+F9 添加结构体
Shit+f12 查找字符串
shift+e 提取数据
汇编基础
汇编指令集
- x86/x64
- ARM
- MIPS
常用的汇编指令
- 运算指令
- 数据转移指令
- 跳转指令
- 栈操作指令
x86寄存器
ARM寄存器
寄存器传参和栈传参
010editer
Template的语法类似于C结构体,可以自己直接编写并加载,且支持基本的C语言语法。
ida
ida的快捷键 定义数据的操作就是d 按u 是undefined成未定义的方式 按c 变成代码 按p 手动变成函数 按shift+F12,可以看到所有的字符串 按y统一编辑
调试器
- 步进 —— 单步执行,遇到call调用时会跟进而不是跳过去
- 步过 —— 单步执行,遇到call时会和别的指令一样跳过去
- 运行至指定位置 —— 相当于临时下断点,碰到循环时可以方便的跳过去
- 运行 —— 直接跑起来,而不是一条一条指令执行起来
命令行调试器
- gdb
- WINDBG
图形界面调试器
- OllyDBG(win下的老牌调试器,仅支持32位)
- x64dbg (OD已死,x64统一天下) 支持32和64位 仍在活跃开发中 插件相对较少 快捷键与OD相同
- IDA内置调试器 支持多种后端 支持远程调试
搭建调试环境
linux
ida下有一个dbgsrv中有一堆远程调试的东西
android虚拟机
机器先root 安装Xposed框架 安装XInstaller,在其他设置中勾选调试应用
去除软件保护
- 侦壳 — PEiD、ExeInfo……
- 脱壳
- 搜索脱壳机 热门壳:UPX、ASPack
- ESP定律快速脱壳(只能针对压缩壳)
- 去除花指令 使用OD脚本 手动总结特征码+修改
- 去除混淆 .net反混淆神器de4dot
定位验证代码
拿到flag:破除保护外壳、理解程序过程、找到验证函数、逆推得到flag 1. 正面长驱直入(文件比较小) - 从程序的入口点开始,逐步分析 - 层层深入最后抵达验证函数 - 静态分析 2. 从信息输入/输出处寻找(文件比较大)
常见算法分析与逆向
CTF中常见的类型
1.没算法; - 直接输出flag 2.常见的算法; - 简单异或 - 带雪崩效应的异或(雪崩效应:前面值的结果会导致后面值结果的改变) - 加密算法(RSA、AES) - 散列算法(MD5、SHA1) - 解方程 3.有趣的算法; - 走迷宫 - ……(各种脑洞)
加速CTF解题
1.边信道攻击 - CTF中可以使用pintools - 原理:监测程序执行指令数 - 应用: - 逐字节验证的题目 2.Google大法好 - 加_双引号_搜索S-Box等常量快速确定算法 3.逆向小trick - 快速找main位置 - 寻找一个大跳转 - 快速定位关键位置 - 从Function List靠前的位置开始乱翻 - 从main函数旁边翻 - 编译时不同的原文件会被分别编译为.o再由编译器合并 - 应对MFC程序 - 使用_xspy_工具查看消息处理函数 - 拖上去看感兴趣的函数就可以,比如OnClick OnCommand - 手动加载Signature - 碰到无法自动识别库函数时 - (Shift - F5) View -> Open Subviews -> Signature - (Shift - F11) View -> Open Subviews -> Type Libraries 4.调试小技巧 - 如何得知MessageBox后在程序在哪里继续运行 - 在OD或x64dbg中找到内存布局列表(OD:Alt-M,或查看 -> 内存) - 找到自己程序的代码段(通常是.text,按F2)(下区段断点) - 返回程序点击即可
CTF未来趋势
真实的逆向
目前的CTF: - 代码量小 - 结构简单 单文件 - 编码单一 - 现代语言特性少 面向过程 - 加密壳/优化少 - 语言常见 C C++ ASM 现实: - 代码量巨大 - 结构复杂 大量静态库 动态库 - 各种乱码 - 大量线代语言特性 OO、Template - 优化和加密壳十分常见 - 语言可能十分神奇 Go 、VB 、Delphi
IDA高级使用
1.设置字符串编码&格式 - 快捷键:Alt-A - 可以设置字符串类型 - 可以设置字符串编码 2.导入/导出数据 - 快捷键:Shift-E - Edit -> Import/Export Data - 操作:选定后按快捷键 - 可以方便地提取数据或修改idb数据库 3.选定大段数据 - 快捷键:Alt-L - Edit -> Begin Selection 4.批量应用类型 - 组合技能 - 操作: - 设置好第一个类型 - 利用刚才的技巧选定数据 - 按d弹出建立数据对话框 - 不勾选Create as array 5.设置间接跳转地址 - 快捷键:Alt-F11 - Edit -> Plugins -> Change the callee address 6.修复跳转表 7.IDAPython - IDA自带支持脚本 - 可以使用几乎所有IDA提供的API - 可以快速完成大量重复性操作 - 可以方便的了解IDA内部的数据和结构
HexRays出错处理
- 常见F5出错信息 格式:XXX;XXX
- positive sp value
- 成因:IDA会自动分析SP寄存器的变化量,由于缺少调用约定(栈调用约定与C调用约定),参数个数等信息,导致分析出错
- 解决方案
- 推荐:在Option -> Generala中设置显示Stack pointer,然后去检查对应地址附件调用的函数的调用约定以及栈指针变化
- 不推荐:在对应地址处按Alt-K然后输入一个较大的负值(有风险)
- call analysis failed
- 成因:F5在分析调用时,未能成功解析参数位置/参数个数
- 解决方案:
- 对于简介调用(类似call eax等),可使用_设置调用地址_的方法解决
- 对于直接调用,查看_调用目标的type_是否正确设置。_可变参数_是引发这种错误的主要原因之一
- cannot connvert to microcode
- 成因:部分指令无法被反编译
- 解决方案:
- 最常见起因是函数中间_有未设置成指令的数据字节_,按C将其设置为指令即可
- 其次常见的是_x86中的rep前缀_,比如_repXX jmp_等,可以将该指令的第一个字节(repXX前缀对应的位置)patch为0x90(NOP)
- stack frame is too big
- 成因:在分析栈帧时,IDA出现异常,导致分析出错
- 解决方案:
- 找到明显不合常理的_stack variable offset_,双击进入栈帧界面,按u键_删除对应stack variable_
- 如果是壳导致的原因,先用OD等软件脱壳
- 可能由化痔灵导致,手动或自动检查并去除花指令
- 十分罕见
- local variable allocation falled
- 成因:分析函数时,有部分变量对应的区域_发生重叠_,多见于ARM平台出现Point、Rect等8字节、16字节、32字节结构时尤其多见
- 解决方案:
- 修改对应参数为多个int
- 修改ida安装目录下的hexrays.cfg中的HOJGNORE-OVERLAPS
- F5分析结果不正确
- 成因:F5会自动删除其认为不可能到达的死代码
- 常见起因是一个函数错误的被标注成了noreturn函数
- 解决方案
- 进到目前反编译结果,找到最后被调用的函数(被错误分析的函数),双击进入再返回(破事HexRays重新分分析相应函数)
- 如果上述方案不成功,那么尽到被错误分析的函数,按Tab切换到反汇编界面,按Alt-P进入界面取消函数的Does not return属性
- positive sp value
HexRays高级使用 (F5高级使用)
- 自定义寄存器传参
- 组合技
- 使用IDA中的_ussercall和userpurge调用约定
- 设置范例
- int_usercall test (intal );
- <>中为对应值的位置
- 第一个<>中为返回值位置,注意返回值的位置即使不变也要填写
- HexRays源码级调试
- 注意:
- F5中显示的变量很可能不是变量原来的值,尤其是寄存器变量,尽量在赋值位置断下并查看值
- 对于优化后的代码,F5调用bug很多
- F5的单步不太稳定,在ARM平台可能会跑飞
- F5的运行至指定位置功能同样不稳定,可能会跑飞
- 注意:
编写IDA processor
32位和64位混合时,手动加载(Munual Load)
见招拆招 -IDA加载头文件
导入头文件 如果能拿到程序的一部分头文件,比如程序公开的API接口,那么可以通过头文件让IDA了解这个结构并辅助分析 改可变参数,…三个点
恢复符号
- 找程序的旧版本
- 大量的程序早起版本中安全意识不够强,没有删除符号等各种信息,可以利用搜索引擎搜索旧版本参考利用
- 对于苹果应用,可以使用AppAdmin获取低版本应用
- Rizzo匹配
- 看程序自带的string
- 许多程序自带了大量调试信息
- Google搜索源代码
- 许多程序早期版本或其他分支源码被流出过
- 大量库开源
- 按获得信息搜索源码(因为大量程序其实直接套用Stack Overflow答案或直接套用CSDN代码)
- 自己制作Signature
- IDA提供自动制作Signature的工具
- 请打开IDASDK68文件夹,找到flair68文件夹
多角度切入
-
一个功能会有多个切入位置
-
例如想让Win10 14393允许关闭UAC时运行 Windows Store App就有如下切入点:
-
(反向)注册表中EnableLUA键值控制UAC开闭
-
(反向)“无法使用内置管理员账户”提示
-
(
正向
)从Store App启动流程入手
- Calcexe
- AppxLauncher.exe
-
直觉硬上
- 如何区分用户代码
- 用户代码一般在程序靠前的地方
- 用户代码一般不会有奇怪算法
- 库函数同样分布在一起,如果发现了_S_consturct之类的典型库函数字符串,那么那一片就都不用看了。
搞定虚表
- 虚表是业界难题
- 虚函数的处理会因为编译器采取的ABI的不同而不同
- IDA本身不支持虚表的各种操作,而且似乎并没有要支持的意思,于是有各路大神开发了插件
- HexraysCodeXplorer
- hexrays_tool
- HexRaysPyTools
- 最后一个经过测试效果最好
首先是安装插件
- 快捷键:Shift-F
- 右键 -> Deep Scan Variable
- 操作:
- 在构造函数中执行上述操作,等待扫描完毕
- 按Alt-F8打开Structure Builder,看到有黄色部分后选择不正确的field点Disable
- 清除所有的黄色部分后即可点击Finalize,在新弹出的窗口中修改名称
推荐书目
《加密与解密》 《IDA Pro权威指南》 《软件调试》
pwn笔记
gdb快捷键
-
run //运行 缩写r
-
disas 函数名 //反编译函数
-
break *地址(函数名) //设置断点 缩写 b
-
info breakpoint //查看断点 缩写 i b
-
info register //查看寄存器 缩写 i r
-
ni //单步执行
-
si //单步步入
-
backtrace //显示上层所有stack frame 信息
-
continue //继续运行 缩写 c
-
x/wx address //查看address中的内容
w可以换成b/h/g分别对应1/2/8 byte
/后可以跟数字,表示一次列出几个
第二个x可以换成u/d/s/i以不同方式表示地址内的内容
- u:无符号整数
- d:十进制
- s:字符串
- i:指令
-
list //列出源代码
-
print 变量名 //打印变量值
-
info local //显示局部变量
-
set *address = value
将address中的值设成value,一次设4byte
可以将*换成{char/short/long}分别表示1/2/8byte
编码分析
通信领域常用编码
电话拨号编码
1-9 分别使用 1-9 个脉冲,0 则表示使用 10 个脉冲。
Morse 编码
点(·):1
划(-):111
字符内部的停顿(在点和划之间):0
字符之间的停顿:000
单词之间的停顿:0000000
字符间可用 空格 或 单斜杠/ 来分割,单词之间采用....
分割
摩斯电码解码:
敲击码
敲击码(Tap code)是一种以非常简单的方式对文本信息进行编码的方法。因该编码对信息通过使用一系列的点击声音来编码而命名,敲击码是基于 5 ×5 方格波利比奥斯方阵来实现的,不同点是是用 K 字母被整合到 C 中。
Tap Code | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
1 | A | B | C/K | D | E |
2 | F | G | H | I | J |
3 | L | M | N | O | P |
4 | Q | R | S | T | U |
5 | V | W | X | Y | Z |
源文本 | B | N |
---|---|---|
位置 | 1,2 | 3,3 |
敲击码 | … . | … … |
曼彻斯特编码
格雷编码
计算机相关编码
字母表编码
A-Z/a-z 对应 1-26 或者 0-25
ASCII编码
- 0-9, 49-57
- A-Z, 65-90
- a-z, 97-122
二进制编码
十六进制编码
Base编码
霍夫曼编码
XXencoding
URL 编码
特点:大量的百分号
Unicode 编码
源文本: The
&#x [Hex]: The
&# [Decimal]: The
\U [Hex]: \U0054\U0068\U0065
\U+ [Hex]: \U+0054\U+0068\U+0065
取证隐写前置技术
常见文件头
文件后缀名 | 文件头(Hex) | Offset | 文件尾(Hex) |
---|---|---|---|
JPEG(jpg) | FF D8 FF | 0 | FF D9 |
PNG(png) | 89 50 4E 47 0D 0A 1A 0A | 0 | AE 42 60 82 |
GIF(gif) | 47 49 46 38 | 0 | 00 3B |
zip / apk / jar | 50 4B 03 04 | 0 | 50 4B |
RAR(rar) | 52 61 72 21 1A 07 | 0 |
linux命令
file命令
file
命令根据文件头(魔法字节)去识别一个文件的文件类型。
root in ~/Desktop/tmp λ file flag
flag: PNG image data, 450 x 450, 8-bit grayscale, non-interlaced
strings
命令
打印文件中可打印的字符,经常用来发现文件中的一些提示信息或是一些特殊的编码信息,常常用来发现题目的突破口。
-
可以配合
grep
命令探测指定信息strings test|grep -i XXCTF
-
也可以配合
-o
参数获取所有 ASCII 字符偏移root in ~/Desktop/tmp λ strings -o flag|head 14 IHDR 45 gAMA 64 cHRM 141 bKGD 157 tIME 202 IDATx 223 NFdVK3 361 |;*- 410 Ge%<W 431 5duX@%
binwalk
命令
binwalk 本是一个固件的分析工具,比赛中常用来发现多个文件粘合再在一起的情况。根据文件头去识别一个文件中夹杂的其他文件,有时也会存在误报率(尤其是对Pcap流量包等文件时)。
root in ~/Desktop/tmp λ binwalk flag
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 450 x 450, 8-bit grayscale, non-interlaced
134 0x86 Zlib compressed data, best compression
25683 0x6453 Zip archive data, at least v2.0 to extract, compressed size: 675, uncompressed size: 1159, name: readme.txt
26398 0x671E Zip archive data, at least v2.0 to extract, compressed size: 430849, uncompressed size: 1027984, name: trid
457387 0x6FAAB End of Zip archive
配合 -e
参数可以进行自动化提取。
也可以结合 dd
命令进行手动切割。
root in ~/Desktop/tmp λ dd if=flag of=1.zip bs=1 skip=25683
431726+0 records in
431726+0 records out
431726 bytes (432 kB, 422 KiB) copied, 0.900973 s, 479 kB/s
linux基础命令
- Tab键 自动补齐
- 反斜杠
\
:强制换行 - Ctrl+U 将行首至光标范围间的清除
- Ctrl+K 清空至行尾
- Ctrl+L 清屏
- Ctrl+C 取消本次命令编辑
uname
命令 查看系统相关信息
-a:显示主机名、内核版本、硬件平台等详细信息
-r:显示内核版本
hostname
查看主机名
ifconfig
查看ip地址
cat proc/cpiomfo
查看CPU信息
cat /proc/meminfo
查看内存信息
shutdown -h now
立刻关机
halt
立刻关机
shutdown -r now
立刻重启
reboot
立刻重启
cp filename
复制文件或者目录
-r 递归复制整个
- 压缩命令
- gzip(bzip2同
- gzip 文件名 压缩文件名.gz
- -9 高压缩比
- -d 解压缩
- gzip(bzip2同
- tar归档命令
- 参数
-z
调用gzip程序进行解压或者压缩-j
调用bzip2程序进行解压或者压缩-c
创建归档文件, 拓展名为.tar-v
输出详细信息-f
使用归档文件-x
解开归档文件-t
列表查看包内文件
tar -cvf 终.tar file1 file2 file3 ...
将文件123打包成终.tartar -xvf 终.tar
解包终文件tar -xvf file.tar -C dir
解包tar文件释放到dir目录
- 参数
重启网络服务service network restart
ls
列出目录下的文件ls -l
列出当前目录下文件的详细信息ls - alh
列出当前目录下文件的详细信息(包括隐藏文件cd
更换工作目录pwd
显示当前所在目录cd ..
返回上级目录cd -
返回上次操作目录cd ~
返回用户目录mkdir <dir_name>
创建空白目录rmdir <dir_name>
删除空目录rm <dir_name>
删除目录或文件rm -rf *
删除全部mv dir_name destination
移动dir_name到destination目录mv dir/file newname
重命名
查看
cat file
查看文件head file
显示文件前几行tail file
显示文件后几行tac file
从最后一行开始显示直到第一行nl file
带行号查看文件more file
一页一页显示文件du -h file
查看文件大小df -h file
查看文件占用磁盘大小
进程
ps -aux | grep root
检索root用户下进程kill -9 PID
结束进程
vi编辑器
touch file
新建一个文件vi file
编辑某个文件(不存在就创建-r
用于恢复系统突然崩溃时正在编辑的文件-R
以只读方式打开文件+n
进入vi后直接位于文件的第n行,不指定n位于最后一行
- 用I/A/O 进入插入模式
- 保存文件 ,ESC 然后 w保存 q退出
- 命令模式
- dd删除(复制)光标所在行
- p粘贴
用户信息
cat /etc/passwd
查看用户信息useradd username
添加用户userdel username
删除用户groupadd groupname
添加用户组groupdel groupname
删除用户组cat /etc/group
查看组cat /etc/shadow
查看用户密码cat /etc/chadow |grep username
查看指定用户密码passwd username
修改用户密码
文件权限
4 可读 2 可写 1 可执行
chmod 777 file
配置权限可读可写可执行chmod -R 777 file
递归更改权限chmod +/-x file
添加或减少权限
find查找文件
find -name filename
查找filename这个文件
设置计划任务
-
crontab -l
查看计划任务 -
crontab -e
进入编辑模式设定计划任务格式:分 时 日 月 周 命令
ctrl + x 退出
yum命令
yum -y install 软件名
下载安装yum -y localinstal 软件包名
安装软件yum -y localinstall *.rpm
本地批量安装yumupdate
全部更新yum update 包名
更新某个包yum check-update
检查可更新的包yum grouplist
列举系统中以组安装的包yum remove 软件包名
卸载软件yum groupremove 组名
删除程序组yum deplist 包名
查看依赖关系yum clean all
清除全部缓存yum ma kecache
更新源yum list |grep 包名
查看有没有对应的包
apt-get
apt-get update
更新源apt-get upgrade
更新系统apt-get install 包名
安装软件apt-get remove 包名
删除软件apt-cache search 某个关键字
搜索需要的软件包含在哪个包里apt-get clean
清空缓存包
pip命令
pip --version
查看pip版本和路径pip --help
查看帮助pip install -U pip
升级pippip install <Package>
安装包(最新版本) 指定版本号可用==,>=,<=,>,<pip install --update <Package>
升级指定的包pip uninstall <Package>
卸载指定的包pip search <Package>
搜索包pip show
显示安装包信息pip show -f <Package>
查看指定包的详细信息pip list
列出已安装的包pip list -o
查看可升级的包- 同时装有python3和python2使用pip
python2 -m pip install XXX
python3 -m pip install XXX
文本命令
cat file |grep 要搜索的关键字
搜索包含关键字的行并显示cat file |grep -v 要去除的关键字
显示去除后的文本cat file |sort
文本排序后显示cat file |uniq
文本去掉重复的再显示cat file wc -l
计算行数file>>file
文本重定向diff file1 file2
比较两个文本的差异split -l 数量n file
将文本文件分割成数量n个小文件