汇编零碎笔记

全局变量的mov

先把项目--属性--C/C++ --优化--优化禁用掉,不然汇编里会被优化掉我们

要分析的代码

写一个全局变量并有初始值

然后在main里对全局再赋值,前面写个messagebox的API

设置为Release编译,然后拖到OD里对messagebox的函数下断

在堆栈里面返回调用此地方的反汇编

int a = 1000;

a = 0x00167367;

在汇编里

a= ds:[01303018]=000003E8=1000

mov dword ptr ds:[a],0x167367  

很明显,C语言里变量,都是一个地址,a的地址是01303018,在地址里面存数

就是[01303018]=000003E8 , []号是代表取地址的数值

赋值语句就是用mov

dword是4字节的意思,对应的是int

如果是C语言里short类型类型,那么这个mov后面就是word

mov word ptr ds:[a],0x1677 

ptr是pointer的缩写,相当于这是一个指针了,ptr的意思就是把数值存入地

址里

OD如果出现鼠标选不中,hex那一列出现很多问号,那么就重新分析

messageboxW的地址771FFD3F

================================

函数的形式

记得把优化给禁止掉

C语言里一个简单的函数

15行便是对应如下

参数是利用push传递进去的,从右往左传

在add函数内的汇编

add便是加法指令,函数体外,ebp+8是第一个参数,ebp+C是第二个参数

我们是a+b

所以参数传进去的时候是反的,但是拿出来用的时候是正的(从左向右)

eax也是返回值

==============================

add  lea

之前用的是都messageboxW来定位,现在用printf也一样可以,不会弹框

更加方便一点,直接bp printf

C语言里的内联汇编

add是加法,leax为取地址赋值,mov为直接赋值

字节修饰符

eax是32位寄存器

ax是16位,ah是高8位 al是低8位

比如一个数值是00123456   那么al就是56,ah就是34

ax就是3456,eax就是00123456 

如果是mov bype eax,ecx 那么就是把ecx的al部分给eax

=================================

movsx movzx

movsx 操作数1,操作数2

movzx 操作数1,操作数2

操作数2的空间必须小于操作数1

一般涌来完成小存储单元到大存储单元的数据传输

比如

movsx eax,cx     movzx ebx,ax

movsx eax,cx 会用操作数2的符号位扩展操作数1剩下的空间

就是说如果操作数2是一个负数,负数的符号位是1,那么前面的那4个字节

都会用1填充

movzx 只会用0来扩展操作数1剩下的空间

	int i = 0x12345678;
	char c = 0x12;
	i = c;
	i = (unsigned char)c;
00BE1093    C745 F8 7856341>mov dword ptr ss:[ebp-0x8],0x12345678
00BE109A    C645 FF 12      mov byte ptr ss:[ebp-0x1],0x12
00BE109E    0FBE45 FF       movsx eax,byte ptr ss:[ebp-0x1]
00BE10A2    8945 F8         mov dword ptr ss:[ebp-0x8],eax
00BE10A5    0FB64D FF       movzx ecx,byte ptr ss:[ebp-0x1]                     ; movzx对应的是无符号传送
00BE10A9    894D F8         mov dword ptr ss:[ebp-0x8],ecx

=======================================

lea 指令的注意

即使i是char类型的,汇编里面也是 lea eax,dword xxxxx

也是dword,因为lea是取地址,地址是4字节的

且lea的操作数1必须是寄存器

=======================================

od调试总结

alt+b切换到断点窗口

上面这些都可以通过alt+ 来调用,不过用的最多还是

alt+B  断点窗口    alt+K 堆栈调用窗口

F4 运行到选定行

+ 转到下一步, -上一步 * 转到当前行

ctrl+g 转到地址

ctrl+s  搜索命令序列

ctrl+a  分析代码

shift+f2 条件断点

在数据窗口

bp 地址  下断点,可以直接写函数,也可以写地址

bc  地址    清楚断点

dd 地址 以4个字节形式查看

dw 以16位2个字节的形式显示

db  以单字节形式查看

? 寄存器  可以查看某个寄存器的数值

======================================

sub 操作数1 操作数2   数1=数1-数2   操作1不能为立即数和地址

int a=109,b=8;

a=a-b

==========================================

cmp a,b

俩个操作数想减,a-b=0 即a=b,则zf(zero flag)标志位为1 

还有其他的标志位为会改变,现在只讨论zf

sub也会改变标志位,和cmp是一样的

je判断zf标志位,如果zf标志位为1则跳转,0则不跳

cmp和je一般成对出现, 合起来就是代表if

如果是if(a==3) 那么就是jne 如果标志位为1则不跳转,为0则跳转

	int a = 1;
	_asm
	{
		mov eax, 3
		sub eax, a
		jnz end;         //这里是分号
	}
	printf("start");
end:  //这里是冒号
	printf("end");

会输出end

========================================

条件转移指令

jl/jnge 

jl  有符号,判断sf(singal flag)标志位

    cmp的功能还是2个操作数想减,但是当操作数1-操作数2<0的时候

    那么sf标志位就为1,因为减出来负数嘛

    jl就是判断sf标志位,如果sf标志位为1,那么就是跳转,为0不跳转

jnge 有符号,为当操作数1-操作数2<=0的时候

那么sf标志位就为1,

jg/jnle

jg  判断sf标志位,如果为0则跳转,

jnle  判断sf标志位,如果为0则跳转

jle/jng

jle  有符号 cmp的功能还是2个操作数想减,但是当操作数1-操作数2>0的

     时候那么sf标志位就为1, jle就是判断sf标志位,如果sf标志位为1,那么就

     是跳转,为0不跳转

jng  有符号  当操作数1-操作数2>=0的时候,那么sf标志位就为1

当然这指令还涉及到其他标志位

sf zf of(overflow flag 溢出标志位)  这3个只要有一个是1,那么就跳转

上面这些jxx是什么的,只要记住什么标志位跳转即可

上面的图,红箭头是指汇编里的条件,2个操作数相等,不是C语言里的

恰好是和C语言里的写法想法

说明

je 判断zf标志位=1, 则跳转,汇编里面是操作数1==操作2,由cmp进行比较

无符号数,就是说没有负数,如果是负数,那么就是一串很大的数字

所以无符号数 的数 负数比正数要大,比如 -1的无符号数就是FFFFFFFF

转到10进制就是很大的数值

=================================

cdecl函数调用约定表示从参数从右到左压栈

push 213  把123压入堆栈

pop ecx 把当前堆栈弹到ecx里

call 会压入eip

ret 会弹出eip

ebp esp

ebp+8上一个call的第一个参数

默认的函数调用约定是 _cdecl

下面是add2的函数内部

可以看到_cdecl会在函数外平栈

而_stdcall 是在函数内平栈,也就是利用return来平栈,

也是和_cdecl一样从右到左压栈

fastcall函数调用约定

fastcall直接用寄存器来传递参数,

add3函数的内部如下

会在内部直接sub esp,8直接分配堆栈

且会利用mov esp,ebp来进行esp的还原

但是一般只会利用俩个寄存器,如果参数过多,会用push来代替

且是在内部平栈

=======================================

switch结构

有一个新的数值到来,会先用新传进去来的

减去最小值得到数组的下标,

得到一串这个数值,05就是defalut,00是第一个01是第二个

会用最大值减去最小值=x,如果新的数值没有在

最小值~x的范围内,那么就会调用default

如果在范围内

就会跳到跳转表,edx+00xxxx   这个edx就是我们的得到的数组下标*4

从而跳转到对应的地址

========================================

for循环

===========================================

inc   操作数自增1  操作数只能是寄存器

dec  操作数自减1

项目优化要设置一下,最大化速度或者最小化大小都可以

while循环

浮点指令

FLD就相当于push,只不过是push到浮点栈里,浮点栈是st0-st7

浮点栈都是80位寄存器,也就是10字节

执行了FLD之后会向st0压入8.765

qword是64位 8字节

FSTP就相当于pop,把st0的数值弹到ebp-4里面

Fadd就相当于把st0里的数值和[4020E8]里的数值相加,注意这时候是

QWORDL,64位浮点数相加,数值依然放到st0里面

如果C语言里是double,那么全部都是qword,float只有进行浮点运算的

时候才会是qword

fmul是乘法 

浮点指令的操作数只支持[地址或地址+偏移] 这样写

FILD和fld功能一样,只是FILD是截断压入,就是小数位全部置为0

一般在C语言里是浮点数和整数相加,整数自动变为浮点数,会有这样的汇编

这里会调用小数转整数的call,那么其实只是截断,并是会转化到整数

i依然是浮点数,只不过小数部分没有了

call 的内部

CVTTPS2PI 操作数1,操作数2   操作数1为浮点寄存器,操作数2必须

是[寄存器]

作用是将小数部分截断取整,会把整数部分存放在eax里面

==========================================

SAR edx,8  算数右移指令,右移8位就是1个字节,符号位保持不变,可用于

有符号数除

SHR是逻辑右移指令,功能是将操作数右移,原最低位移入进位标志CF,

原最高位补0

右移一位相当于除以2

shl是逻辑左移指令,将最后移出的一位写入CF中,低位用0补充

sal 是算数左移指令,将最后移出的一位写入CF中,低位用0补充

shl和sal是相同的

左移一位相当于乘以2

还有循环左移右移,这个不做了解

===========================================

or    只要有一个条件成立即为成立,0代表条件不成立,1代表条件成立

1 : 1=1

1 : 0=1

0 : 0=0

and   要有2个条件成立才成立,0代表条件不成立,1代表条件成立

1 : 1=1

0 : 0=0

1 : 0=0

not 按位取反  1111 取反后是0000

xor按位异或   

只有在两个比较的位不同时其结果是1,否则结果为0

就是相同即为0,不相同为1

1 : 1=0

0 : 0=1

1 : 0=1

=

=

=


 

发布了143 篇原创文章 · 获赞 36 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/yzj17025693/article/details/80935163