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
    }
}
posted @ 2023-02-18 00:22  Hacker&Cat  阅读(205)  评论(0编辑  收藏  举报