数据宽度
或许你曾经有这样的疑惑,有符号数在二进制的表示中,以1开头的表示为负数,那么10000000是表示的-1还是128呢 ?
在这里引入数据宽度的概念,即8位二进制数能够存储的数据范围为-128~127,很显然,上面存储的数,在数据宽度为8位时,表示-1,同理-127表示为10000001,-2表示为11111110,这里关于原码、补码的概念不加以赘述,其本质都是为了表示数值。
基于x86架构的CPU的数据宽度为32位,表示正负范围为:0~7FFFFFFF 80000000~FFFFFFFF
计量单位有:byte、word、dword、qword,分别表示字节,字,双字,四字,单字节表示8位。
逻辑运算
运算符说明实例&(and)按位与,同时为1,才能得110110011 & 10110101 = 10110001|(or)按位或,存在1时,结果为110110011 | 10110101 = 10110011^(xor)按位异或,不同时,结果为110110011 ^ 10110101 = 00000110!(not)按位非,单运算符,结果取反!10110011=01001100异或加密解密
加密数据:00110001
密钥:00010101
加密结果:00110001 ^ 00010101 = 00100100
解密:00100100 ^ 00010101 = 00110001
基本指令
mov
格式:mov 目标操作数,源操作数
作用:拷贝源操作数到目标操作数。
注意:
1、源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元. 2、目标操作数可以是通用寄存器、段寄存器或者内存单元. 3、操作数的宽度必须一样. 4、源操作数和目标操作数不能同时为内存单元.指令说明MOV r/m8,r8将8位寄存器的内容拷贝到8位寄存器或8位的内存中。MOV r/m16,r16将16位寄存器的内容拷贝到16位寄存器或16位的内存中。MOV r/m32,r32将32位寄存器的内容拷贝到32位寄存器或32位的内存中。MOV r8,r/m8将8位寄存器或8位的内存操作数拷贝到8位寄存器中。MOV r16,r/m16将16位寄存器或16位的内存操作数拷贝到16位寄存器中。MOV r32,r/m32将32位寄存器或32位的内存操作数拷贝到32位寄存器中。MOV r8, imm8将8位立即数的内容拷贝到8位寄存器中。MOV r16, imm16将16位立即数的内容拷贝到16位寄存器中。MOV r32, imm32将32位立即数的内容拷贝到32位寄存器中。以上指令取自Intel白皮书,释义如下:
r 通用寄存器 m 代表内存 imm 代表立即数 r8 代表8位通用寄存器 m8 代表8位内存imm8 代表8位立即数
lea
LEA指令将存储器操作数mem的4位16进制偏移地址送到指定的寄存器,这里,源操作数必须是存储器操作数,目标操作数必须是通用寄存器。
格式:lea r,m。
功能:获取内存编号而非内存中的内容。
别问,问就是背
addandorxornotsubADD AL, imm8AND AL, imm8OR AL, imm8XOR AL, imm8NOT r/m8SUB AL, imm8ADD AX, imm16AND AX, imm16OR AX, imm16XOR AX, imm16NOT r/m16SUB AX, imm16ADD EAX, imm32AND EAX, imm32OR EAX, imm32XOR EAX, imm32NOT r/m32SUB EAX, imm32ADD r/m8, imm8AND r/m8, imm8OR r/m8, imm8XOR r/m8, imm8SUB r/m8, imm8ADD r/m16,imm16AND r/m16,imm16OR r/m16,imm16XOR r/m16,imm16SUB r/m16,imm16ADD r/m32,imm32AND r/m32,imm32OR r/m32,imm32XOR r/m32,imm32SUB r/m32,imm32ADD r/m16, imm8AND r/m16, imm8OR r/m16, imm8XOR r/m16, imm8SUB r/m16, imm8ADD r/m32, imm8AND r/m32, imm8OR r/m32, imm8XOR r/m32, imm8SUB r/m32, imm8ADD r/m8, r8AND r/m8, r8OR r/m8, r8XOR r/m8, r8SUB r/m8, r8ADD r/m16, r16AND r/m16, r16OR r/m16, r16XOR r/m16, r16SUB r/m16, r16ADD r/m32, r32AND r/m32, r32OR r/m32, r32XOR r/m32, r32SUB r/m32, r32ADD r8, r/m8AND r8, r/m8OR r8, r/m8XOR r8, r/m8SUB r8, r/m8ADD r16, r/m16AND r16, r/m16OR r16, r/m16XOR r16, r/m16SUB r16, r/m16ADD r32, r/m32AND r32, r/m32OR r32, r/m32XOR r32, r/m32SUB r32, r/m32
OD技巧
快捷键
ctrl+g : 跳转到指定输入地址
ctrl+f:搜索命令的位置
f2 : 设置断点,切换断点
f8: 单步步过
f9:运行
f12:暂停
窗口布局
- 数据窗口和其他窗口的数据位置相反,为小端序,即高位数据存放在低位地址。
- 命令接口常用命令:
- dd、dw、db,按四字、双字、字节显示对应内存数据。
缓存清理
有时OD打开之后,会出现反汇编窗口一段代码变灰或者提示哪里下了断点的情况,这是OD同级目录下的udd文件夹下的缓存文件导致的,删掉里面的文件即可解决该问题。
寄存器
通用寄存器主要用途编号(二进制)32位16位8位EAXAXAL累加器000ECXCXCL计数001EDXDXDLI/O指针010EBXBXBLDS段的数据指针011ESPSPAH堆栈指针100EBPBPCHSS段的数据指针101ESISIDH字符串操作的源指针;SS段的数据指针110EDIDIBH字符串操作的目标指针;ES段的数据指针11132位专用寄存器主要用途存储数据的范围EIP保存当前下一条运行指针地址0 - 0xFFFFFFFF
辅以神兵OD,逆向美哉!
内存
基本概念
寄存器位于CPU内部,执行速度快,但比较贵;而内存通过引脚与CPU相连,速度相对较慢,但成本较低,所以可以做的很大。寄存器和内存没有本质区别,都是用于存储数据的容器,都是定宽的。
内存的数量特别庞大,无法每个内存单元都起一个名字,所以用编号来代替,我们称计算机CPU是32位或者64位,主要指的就是内存编号的宽度(寻址宽度),而不是寄存器的宽度。
计算机内存的每一个字节会有一个编号(即内存编号的单位是字节),编号从0开始,32位计算机的编号最大是32位,也就是FFFFFFFF,也就是说,32位计算机内存寻址的最大范围是FFFFFFFF+1 , 即4GB,这也是为什么我们在一个XP的系统上面如果物理内存超过4G是没有意义的原因。
[内存编号]表示地址,每个内存单元的宽度为8,超过8位的数据会溢出到下一个内存单元。
内存读写
- 内存读:mov eax,dword ptr ds:[0x0012FF34]
- 内存写:mov dword ptr ds:[0x0012FF34],0x12345678
- ptr: Point 代表后面是一个指针 (指针的意思就是里面存的不是普通的值,而是个地址)
- 注意:地址编号不要随便写,因为内存是有保护的,并不是所有的内存都可以直接读写(需要特别处理)
寻址公式
1. [立即数]
#内存读mov eax,dword ptr ds:[0x19FF74]#内存写mov dword ptr ds:[0x19FF74],0xAABBCCDD#获取内存编号lea ecx,dword ptr ds:[0x19FF74]
2. [reg]
#内存读mov ecx,0x19ff74mov eax,dword ptr ds:[ecx]#内存写mov dword ptr ds:[ecx],0x87654321#获取内存编号lea eax,dword ptr ds:[ecx]
3. [reg+立即数]
#内存读mov ecx,0x19ff74mov eax,dword ptr ds:[ecx+4]#内存写mov dword ptr ds:[ecx+0xd],0x87654321#获取内存编号lea eax,dword ptr ds:[ecx+4]
*4. [reg+reg{1,2,4,8}]**:注意,这里只能取1,2,3,4中的一个。
#内存读mov ecx,0x19ff74mov edx,2mov eax,dword ptr ds:[ecx+edx*4]#内存写mov dword ptr ds:[ecx+edx*4],0x87654321#获取内存编号lea eax,dword ptr ds:[ecx+edx*4]
*5. [reg+reg{1,2,4,8}+立即数]**
#内存读mov ecx,0x19ff74mov edx,2mov eax,dword ptr ds:[ecx+edx*4+4]#内存写mov dword ptr ds:[ecx+edx*4+4],0x87654321#获取内存编号lea eax,dword ptr ds:[ecx+edx*4+4]
堆栈操作
堆栈模型:
压入数据:
#模拟栈顶栈底mov ebx,0x13ffdc #basemov edx,0x13ffdc #top#方式1mov dword ptr ds:[edx-4],0xaaaaaaaasub edx,4#方式2sub edx,4mov dword ptr ds:[edx],0xaaaaaaaa#方式3mov mov dword ptr ds:[edx-4],0xaaaaaaaalea edx,dword ptr ds:[edx-4]#方式4lea edx,dword ptr ds:[edx-4]mov mov dword ptr ds:[edx],0xaaaaaaaa读取数据:
#通过base加偏移读取第n个压入的数,n需指定mov esi,dword ptr ds:[ebx-4*n]#通过top加偏移读取mov esi,dword ptr ds:[ebx+4*(n-1)]弹出数据:
#方法1mov ecx,dword ptr ds:[edx]lea edx,dword ptr ds:[edx+4]#方法2mov esi,dword ptr ds:[edx]add edx,4#方法3lea edx,dword ptr ds:[edx+4]mov edi,dword ptr ds:[edx-4]push指令:完成上述模拟的压入数据操作,ebx和esp分别为占地和栈顶,由cpu调配,随着压栈操作而自动变化,与容器宽度有关加减2或4。
- push r32:esp-4
- push r16:esp-2
- push m16:esp-2
- push m32:esp-4
- push imm8/imm16/imm32:esp-4
pop指令:完成上述模拟的弹出数据操作,ebx和esp分别为占地和栈顶,由cpu调配,随着出栈操作而自动变化。
- pop r32:esp+4
- pop r16:esp+2
- pop m16:esp+2
- pop m32:esp+4
pushad指令:将8个通用寄存器的内容压栈,现场保护。
popad指令:pushad的逆操作,恢复现场。
标志寄存器
别问,问就是背
CF
进位标志(Carry Flag),如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0。
MOV AX,0xFF00ADD AX,0x0101
PF
奇偶标志PF(Parity Flag):奇偶标志PF用于反映运算结果中最后一个字节中“1”的个数的奇偶性,如果“1”的个数为偶数,则PF的值为1,否则其值为0。
00010011 00010010 mov ax,0x131200010011 00010011 偶数 ,最后一字节为奇数-->p=0add ax,100010011 00010100 奇数,最后一字节为偶数-->p=1add ax,1
AF
辅助进位标志(Auxiliary Carry Flag),在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
- 在字操作时,发生低字节向高字节进位或借位时;
- 在字节操作时,发生低4位向高4位进位或借位时。
MOV AX,0x100fADD AX,0x4
ZF
零标志(Zero Flag):零标志ZF用来反映运算结果是否为0,如果运算结果为0,则其值为1,否则其值为0。
SF
符号标志(Sign Flag):符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
mov al,0x82add al,1
OF
溢出标志(Overflow Flag):溢出标志OF用于反映有符号数加减运算所得结果是否溢;如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
最高位进位与溢出的区别:
- 进位标志表示无符号数运算结果是否超出范围.
- 溢出标志表示有符号数运算结果是否超出范围.
是否溢出的判别规律:
- 正 + 正 = 正 如果结果是负数,则说明有溢出
- 负 + 负 = 负 如果结果是正数,则说明有溢出
- 正 + 负 永远都不会有溢出.
DF
方向标志位,默认从右操作数到左操作数,可以通过CLD和STD指令修改DF的值。
- 清方向位指令CLD(Clear Direction Flag):DF←0
- 置方向位指令STD(Set Direction Flag):DF←1
相关指令
ADC
带进位加法(C位参与运算),两边不能同时为内存,且数据宽度要一致。
格式:ADC R/M,R/M/IMM
- ADC AL,CL
- ADC BYTE PTR DS:[19ff74],2
- ADC BYTE PTR DS:[19ff74],AL
SBB
带借位减法(C位参与运算),两边不能同时为内存,且数据宽度要一致。
格式:SBB R/M,R/M
- SBB AL,CL
- SBB BYTE PTR DS:[19ff74],2
- SBB BYTE PTR DS:[19ff74],AL
XCHG
交换数据,两边不能同时为内存,且数据宽度要一致。
格式:XCHG R/M,R/M
- XCHG AL,CL
- XCHG DWORD PTR DS:[19ff74],EAX
- XCHG BYTE PTR DS:[19ff74],AL
MOVS
移动数据,把ESI存储的内存地址中的数据复制到EDI存储的内存地址中,每复制一次,两个操作数均向上或下偏移4/2/1个字节(地址递增或递减,便于复制下一串内容),当D=0时递增,D=1时递减。
- MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI],简写为movsb
- MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI],简写为MOVSW
- MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI],简写为movsd
STOS
将AL/AX/EAX的值存储到[EDI]指定的内存单元,D=0时地址递增,D=1时地址递减。
- STOS BYTE PTR ES:[EDI],简写为STOSB
- STOS WORD PTR ES:[EDI],简写为STOSW
- STOS DWORD PTR ES:[EDI],简写为STOSD
REP
按计数寄存器 (ECX) 中指定的次数重复执行字符串指令。
JCC
比较指令
cmp
格式:CMP R/M,R/M/IMM
该指令是比较两个操作数,实际上,它相当于SUB指令,但是相减的结果并不保存到第一个操作数中;只是根据相减的结果来改变零标志位的(其他标志位也可以更改),当两个操作数相等的时候,零标志位置1。
test
指令格式:TEST R/M,R/M/IMM
该指令在一定程序上和CMP指令时类似的,两个数值进行与操作,结果不保存,但是会改变相应标志位;用这个指令,可以确定某寄存器是否等于0。
jmp
无条件转移指令,仅可以修改EIP的值,相当于mov eip,立即数/寄存器。
格式:jmp 寄存器/立即数
call
call指令用于调用子程序,先将当前的EIP压栈保存,然后jmp 目标地址,相当于:
PUSH eipmov eip,地址A/寄存器格式:call 地址A/寄存器
ret
ret指令用于回到程序调用点,与call配套使用,先将eip出栈,然后jmp eip,相当于:
lea esp,[esp+4]mov eip,[esp-4]格式:ret
别问,问就是背
指令解释条件例子JE、JZ结果为0时跳转(相等时跳转)ZF=1cmp al,al jz 0x19ff74JNE, JNZ结果不为零则跳转(不相等时跳转)ZF=0cmp al,cl jnz 0x19ff74JS结果为负则跳转SF=1mov al,1 sub al,3 js 0x19ff74JNS结果为非负则跳转SF=0mov al,2 sub al,1 jns 0x19ff74JP, JPE结果中1的个数为偶数则跳转PF=1cmp al,al jp 0x19ff74JNP, JPO结果中1的个数为偶数则跳转PF=0mov al,1 cmp al,0xff jnp 0x19ff94JO结果溢出了则跳转OF=1mov al,07f add al,4 jo 0x19ff74JNO结果没有溢出则跳转OF=0mov al,07f add al,0 jno 0x19ff74JB, JNAE小于则跳转 (无符号数)CF=1mov al,2 mov cl,1 cmp cl,al jb 0x19ff90JNB, JAE大于等于则跳转 (无符号数)CF=0mov al,2 mov cl,1 test cl,al jb 0x19ff90JBE, JNA小于等于则跳转 (无符号数)CF=1 or ZF=1cmp al,al jbe 0x19ff90JNBE, JA大于则跳转(无符号数)CF=0 and ZF=0mov al,2 mov cl,1 cmp al,cl jnbe 0x19ff90JL, JNGE小于则跳转 (有符号数)SF≠ OFmov al,1 mov cl,2 cmp al,cl jl 0x19ff74JNL, JGE大于等于则跳转 (有符号数)SF=OFmov al,1 mov cl,2 cmp cl,al jnl 0x19ff74JLE, JNG小于等于则跳转 (有符号数)ZF=1 or SF≠ OFmov al,2 mov cl,1 cmp cl,al jle 0x19ff90JNLE, JG大于则跳转(有符号数)ZF=0 and SF=OFmov al,2 mov cl,1 cmp cl,al jnle 0x19ff90first crackme
程序链接
信息收集
设置断点
在Win32弹窗接口下断点:bp MessageBoxA
运行程序
跟踪堆栈
右键堆栈窗口栈顶处[0019FDB4],Follow In Disassember,观察反汇编窗口,如下:
破解尾声
禁用掉之前win32的断点后,再次点击小三角运行程序,并输入注册信息,点击确定,观察反汇编窗口,如下:
跟踪堆栈,回溯上判断程序的入口,在jcc指令处设置断点,并清除其它断点,然后修改jcc指令为相反逻辑,如下:
保存程序
先将断点取消,然后右键反汇编窗口,选择Copy to executable->All Modifications,这是跳转到保存结束的界面,右键反汇编窗口选择save即可。
新的征程
运行破解之后的程序CRACKME233.EXE,如果输入注册信息包含数字的话,第一次弹窗仍然为判断失败的提示,点击确定后才弹出判断成功的提示,接下来想办法干掉第一次判断失败的提示!
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |