C语言汇编

VC6基础操作

F7:编译、F5:调试、F9:设置断点、F10:单步步过、F11:单步进入、shift+F5:结束调试

调试过程打开寄存器窗口和反汇编窗口

裸函数

编译器不会管的函数

__declspec(naked) Func()

调用空的裸函数会出现错误,因为有call,却没有ret

进入后直接跳到int3,运行程序会报错

解决这种只需要加入汇编语句ret,在C语言程序加入汇编语句,需要用到__asm{}。也就是说可以自己在里面写汇编代码来实现需求。

执行完这段代码,ret回来了

调用约定

__cdecl(c、c++默认) 从右至左入栈 调用者清理栈
__stdcall 从右至左入栈 自身清理堆栈(内平栈)子函数平衡堆栈
__fastcall ECX/EDX传送前两个,剩下的从右至左,寄存器传递速度更快。当只有两个参数传递进去,不需要平衡堆栈 自身清理堆栈

所以不能通过ret来分析函数参数的个数

__cdecl

外平栈

内部只有ret

__stdcall

右边的参数先入栈

可以看到这里call之后没有add来回复堆栈平衡,进入调用函数

可以看到ret变成了ret 8,这就是内平栈

__fastcall

两个参数存放在寄存器

没有修改堆栈,所以不需要add回复堆栈

有push和mov,外面没有add,进去看

也是内平栈

参数个数

公式一:寄存器+ret 4=参数个数

公式二:寄存器+[esp+8]+[ebp+0x]=参数个数

寻找程序入口

callstack,调用窗口

main是我们写的程序的入口,但是不是真正程序的入口

因为在main函数之前需要调用如下函数

在callstack发现这个函数

Getversion()

_headp_int()

GetCommandLineA()

_crtGetEnvironmentStringA()

_setargv()

_setenvp()

_cinit()

main函数具有三个参数,所以要寻找具有三个参数的函数,找三个push,并且调用完会add 0xc

这里很符合

下断点进入,这里才是main函数

数据类型与数据存储

1、存储数据的宽度

2、存储数据的格式

3、作用范围

基本类型

整数类型

char、int、long、short:字节数1、4、4、2,对应上byte、dword、dword、word,long long在VC6对应__int 64

只会根据数据宽度进行操作,超出数据宽度的不会做修改

g存储的只有0x56

数据窗口是小端序

有符号与无符号

C语言默认是有符号数

在内存中存储时无区别,但是在类型转换、比较大小和数学运算时需要注意

浮点类型

float、double在存储方式遵从IEEE的规范

局部变量和全局变量的区分

局部变量是以ebp-开头的

全局变量在编译完之后地址就不会改变

直接放进地址,所以就是全局变量

在vs2022会因为编译器版本问题,出现不同的汇编指令,但都是大同小异

if语句

cmp+jcc指令

cmp相当于减法,前面一个减后面一个,因为x>y时继续执行,所以跳转指令的条件是小于等于

改成>=之后,汇编指令变成了jl,汇编指令是和C语言反着来的

<

==

多分支语句

x<=y的情况直接跳转到else中

x>y则往后执行,执行完之后jmp跳转到else语句的后面

因为mov两边不能都是地址,所以需要用到寄存器

if、else if、else

返回值

内部得到eax的值,eax一般用来存储返回值

参数传递4个字节

压栈的时候都是eax

循环语句

switch语句反汇编

当分支较少时采取if……else if ……else的方式

case连续

创建大表

sub

这里修改参数为103,case条件也修改,发现sub的值发生了变化,所以可以知道sub的值对应最小的case条件的值,这样对应上了大表的位置,所以sub是为了跳转到生成的大表

正好是case 103的地址

case连续但中断

101、102、103的情况使用default的地址填充

只要有断开的,就会浪费一片内存地址,所以当间隔太远就不会使用这种方式

但是当间隔比较大的时候,出现了新情况

因为已经清空了edx,所以可使用该寄存器,这句话相当于把0x004010dd+eax的值对应地址的内容放入到dl中,dl是八位,对应1个字节,这个就是小表

当连续但相差较远时会采用小表

case不连续

当差值太大,不会生成大表,会直接采取if……else结构

先判断大于je,再判断等于cmp+jmp

while循环反汇编

je是当ZF标志位为0是跳转,test是按位与操作,用于判断寄存器的值是否为0

do……while反汇编

for循环反汇编

数组在内存的存储和寻址

这里数组的存储是从高位往低位存储,也就是从右到左开始存储到缓冲区中,替换缓冲区的数值

可以看到数组的存储

再看看数组引用

[ebp-4]是第一个形参的地址

[ebp-1ch]对应上数组首元素的地址,eax是存储着第一个形参,*****4是因为是int型数组,如果是short,则*2

,数组比较常见的形式就是[ebp+寄存器*数组类型对应的字节大小-数字]

因为数组下标存在变量,而mov两边不能同时是地址,所以需要先用寄存器存储下标,而直接引用就不需要,即arr[1]

字符串存储

因为每个寄存器最多存储四个字节,所以需要用到多个寄存器来存储

在数据窗口中是这样存放的

寄存器可以重复使用,比如长度不够的情况