C语言 & 汇编基础
1.基础概念
C语言代码->汇编语句->硬编码(CPU处理)
PDB文件
存有变量、函数名等信息,要关闭 VS 的功能防止对方利用.pdb逆向(但是关闭的时候调试不能看反汇编)
VS,项目->属性->链接器->调试->生成调试信息(否)
数据宽度
char - byte
short - word
int - dword
有符号数&无符号数
无符号:0 - FF(255)
有符号:1 1111111(-128) - 0 1111111(127)
内存上相同,JCC语句使用不同
EXE在内存的分区
代码区(rx可读可执行)
栈区(rw可读可写):局部变量、参数、临时数据
堆区(rw):malloc、new开辟空间
全局区(静态区)(rw)
常量区(r)
2.变量
如果用VS看反汇编会简化地址,建议用VC++6.0(也会简化一点点指令)
局部变量
在栈中(必须初始化赋值),例:
void function(){
int i = 1;
}
mov dword ptr ds:[ebp-0x4],0x1
参数
在栈外,例:
void function(int i){
i = 2;
}
int main(){
function(1);
return 0;
}
传值先入栈(调用约定非__fastcall),再call
push 0x1
call 0x00401005
栈上移后变量在栈外
mov dword ptr ds:[ebp+0x8],0x2
全局变量
通过立即数寻址,例:
int i;
void function(){
i = 1;
}
mov dword ptr ds:[0x00427e3c],0x1
3.结构体
语法,例:
struct x{
int i;
}x1,x2;
struct x x3;
x1.i = 1;
x2.i = 2;
x3.i = 3;
结构体对齐,默认8字节
自定义字节数(不同值对空间的填充情况不同),例:
#pragma pack(2) // 可以为 1、2、4、8
struct x{
char a;
int i;
}x1;
#pragma pack()
printf("%d",sizeof x1); // 6
4.条件 & 循环语句
除了do...while,反汇编与代码比较逻辑相反
条件语句
if...,例:
int i = 1;
int j = 2;
if (i <= j) {
i = 3;
}
;--赋值--
mov dword ptr ds:[ebp-0x4],0x1
mov dword ptr ds:[ebp-0x8],0x2
;--比较--
mov eax,dword ptr ds:[ebp-0x4]
cmp eax,dword ptr ds:[ebp-0x8]
jg 0x00401055
;--true--
mov dword ptr ds:[ebp-0x4],0x3
if...else...、if..else if..等(有jump),例:
int i = 1;
int j = 2;
if (i <= j) {
i = 3;
}
else{
j = 4;
}
;--比较--
mov eax,dword ptr ds:[ebp-0x4]
cmp eax,dword ptr ds:[ebp-0x8]
jg 0x0040d4c7
;--true--
mov dword ptr ds:[ebp-0x4],0x3
jmp 0x0040d4ce
;--else--
mov dword ptr ds:[ebp-0x8],0x4
循环语句
while...,例:
while(1){
int i = 1;
}
;--比较--
mov eax,0x1
test eax,eax
je 0x0040d4ba
;--true--
mov dword ptr ds:[i],1
jmp 0x0040d4a8
do...while(反汇编与代码比较逻辑相同),例:
do{
int i = 1;
}while(1);
;--do--
mov dword ptr ds:[i],0x1
;--比较--
mov eax,0x1
test eax,eax
jne 0x0040d4a8
for...,例:
for(int i=1; i>0; i++){
int j = 1;
XXXXXXXX mov dword ptr ds:[ebp-0x4],0x1
XXXXXXXX jmp 0x0040d4ba
0040D4B1 mov eax,dword ptr ds:[ebp-0x4]
XXXXXXXX add eax,0x1
XXXXXXXX mov dword ptr ds:[ebp-0x4],eax
0040D4BA cmp dword ptr ds:[ebp-0x4],0x0
XXXXXXXX jle 0x0040d4c9
XXXXXXXX mov dword ptr ds:[j],0x1
XXXXXXXX jmp 0x0040d4b1
5.函数
保留现场(调用约定)
根据调用约定使函数调用前后,栈和一些数据不变
参数都是从右向左顺序入栈function(3, 2, 1)
__cdecl(外平栈,C语言默认),例:
void function(int i){
i = 2;
}
int main(){
function(1);
return 0;
}
;参数入栈,栈顶-4
push 0x1
;调用函数
call 0x00401019
;栈顶+4(外平栈)
add esp,0x4
;存栈底
push ebp
;--全栈移动到原位置上面--
mov ebp,esp
sub esp,0x40
;--存原数据--
push ebx
push esi
push edi
;--填充缓冲区--
lea edi,dword ptr ss:[ebp-0x40]
mov ecx,0x10
mov eax,0xCCCCCCCC
rep stos dword ptr es:[edi]
;--函数功能(i=2)--
mov dword ptr ss:[ebp+0x8],0x2
;--还原数据--
pop edi
pop esi
pop ebx
;--栈位置回移、最后函数结束跳回--
mov esp,ebp
pop ebp
retn
__stdcall(内平栈),例:
void __stdcall function(int i){
i = 2;
}
int main(){
function(1);
return 0;
}
push 0x1
call 0x00401019
...
;--内平栈--
retn 0x4
__fastcall(左两个参数存入ecx、edx,内平栈),例:
void __fastcall function(int i, int j, int k,int l){
i = j = k = l = 1;
}
int main(){
function(1, 2, 3, 4);
return 0;
}
push 0x4
push 0x3
mov edx,0x2
mov ecx,0x1
call 0x00401019
...
push ebx
push esi
push edi
push ecx
...
;--左两个参数入栈--
pop ecx
mov dword ptr ds:[ebp-0x8],edx
mov dword ptr ds:[ebp-0x4],ecx
;--函数功能--
mov dword ptr ds:[ebp+0x0c],0x1
mov eax,dword ptr ds:[ebp+0x0C]
mov dword ptr ds:[ebp+0x8],eax
mov ecx,dword ptr ds:[ebp+0x8]
mov dword ptr ds:[ebp-0x8],ecx
mov edx,dword ptr ds:[ebp-0x8]
mov dword ptr ds:[ebp-0x4],edx
;----
...
;--内平栈--
retn 8
参数传递
例:
void function(i){
i = 2;
}
int main(){
int i = 1;
function(i);
return 0;
}
;--int i = 1;--
mov dword ptr ds:[ebp-0x4],0x1
;--调用函数--
mov eax,dword ptr ds:[ebp-0x4]
push eax
call 0x00401005
add esp,0x4
...
mov dword ptr [ebp+0x8],0x2
...
裸函数
自定义函数功能的汇编指令,例:
void __declspec(naked) function(){
__asm{
push eax // eax入栈会影响栈
add esp,0x4 // 降栈顶
retn
}
}