全局变量的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
=
=
=