LXIOS

初见二进制/汇编/1.4标志寄存器_JCC

2025-11-09

根据教程视频,以下均以32位计算机为例

Push的时候是立即数就永远-4
Push 寄存器时,al(八位),不行
Push ax(16位) 可以,会-2
Push32位寄存器/容器,会-4
也可以push一块内存(dword),把这内存的值push到目标位置(同样-4)
Push word,则-2
Push的是跟容器的宽度一样的,同样内存也不能push八位

Pop同理无8位,按照宽度pop,ax就+2…..

堆栈平衡

Pushad指令
把8个通用寄存器的值存到内存(堆栈)去了
Popad指令
把刚刚pushad的值又放回去了(你用pushad折腾完了想恢复原来的模样就popad一下)

所需知识

PE结构

下断点
Bp xxx→让xxx一执行的时候就断下来,下断点了

Win32 Api

什么是函数调用

熟悉堆栈,画堆栈图
栈顶最开始存储的是调用这个函数的时候函数的返回地址,follow过去看call的位置,看函数在哪调用的

Call jcc 标志寄存器
Je看0/1,0就没跳
Cmp (compare比较)
Jcc指令由标志寄存器控制,JE→由Z控制,改后面的0/1就可以控制是否运行
或直接把je改成jmp保存就跳到那边比较方便

标志寄存器(EFL)

标色的位无法控制是默认的
研究这种先确定宽度
ELF一般都给你拆好了,但自己也要会看
Eg:ELF是202,→0010 0000 0010,你看这里的CF对应最后一个位,0.然后倒二个是默认的1,P位是0,P后面一位默认0…..

1.进位标志寄存器CF(对应整个标志寄存器最后一个值)C
C看的是最高位有没有进位,不是数据的最低位!!!
然后再看最高位是否产生进位借位,最高位由数据宽度决定。
Eg:
对于55550000
mov al,0xFF
Add al,1
然后此时C会从0变成1,放C那边了,因为al是8位,此时并没有改变到原本值的数字!对应寄存器还是显示55550000(所以要说确定数据宽度)

2.奇偶标志寄存器PF
用来反映结果里(转成二进制后)1的个数,也就是1的个数的奇偶性,1为偶数个就是1,奇数个就是0

3.辅助进位标志AF
32位宽转换到16进制,如FFFFFFFF,它看什么,此时看FFFFFFFF标注的那一位是否向前进位,若进则变为1
16位时候,FFFF;8位时候,FF

4.零标志ZF
用来反映运算结果是否为0,它变成1则表示你的计算结果是0!!
Mov这种就不算运算了,不改寄存器!!
Xor,sub
清零可以xor自己!且影响标志位Z

5.符号标志SF
反映运算结果的符号位,运算结果与最高位相同(二进制,对应宽度最高位)

6.溢出标志OF
同样是以自己角度定义,所以加出来值不会变,变得是我们看待它的方式
C位溢出
做无符号运算时最应该关注的寄存器是C!因为此运算主要改变的是C,所以我们看c相当于看无符号有没有溢出,以此为标志,以下同理
O位溢出
有符号运算就看OF这个寄存器

在这种表达下,一个正数加一个负数永远不会溢出!!
正正相加结果负数/负负相加结果正数 则溢出!
此时OF是1

C O都溢出也有可能。

指令

ADC(carry)指令:带进位加法
两边不能同时内存,且宽度要一样

SBB(borrow):带借位减法
同上

XCHG:交换数据
同上,且不能是立即数

MOVS:移动数据,内存-内存,前后宽一样
后面值取相应位到前面,可写为movsd,代表一次四字节,以此类比其他
注意,移完栈top/base会各加相应4或2等等,取决于字节

方向位寄存器DF(direction)
0则加(正方向)改变地址栈针这样
1则减(负方向)

STOS:将AL/AX/EAX的值存到[EDI]指定的内存单元
一定要写ES:[EDI]
向EDI存多少,由前面要求的字,字节等决定,EDI的增长方向由D决定

REP(repeat,重复):按计数寄存器ECX中指定的次数重复执行后面写的指令(注意是十六进制的数字,记得换算,例如10是16)

补充:CF借位,最高位进行借位,则其值为1.

PF,以1的奇偶个数来取0或1,看的是最低有效字节!!!即为只检验最后一字节的奇偶
OF,有符号的溢出。符号位有进位,则1。最高有效位(紧挨符号位那个),有进位,则0;
以上两个的xor为OF值

想要改变cpu的行为,修改EIP寄存器(决定cpu下一行执行的代码/存储地址)

Mov不可以改变eip,但可利用jmp改变eip!(没有附加条件)Jmp没法跳转!!唯一的作用就是修改eip!!!
本质是Cpu走的时候根据eip的值决定去哪里

哪些代码可以修改eip?

Jmp(无条件直接走)
Jmp寄存器/立即数

Call指令(出去旅游)
记得下断点
Mov eip,同时也修改了ESP!把call下一行指令的地址(call地址加5(字节))压到栈里(返回地址)。Eip检查/堆栈检查,调用程序时以及把返回地址压到堆栈里去了,检查就是检查返回地址是不是允许的那个值。

Ret(return旅游回家)
Call与ret成对出现。
ret相当于pop eip(同样也不允许使用这个指令)

比较指令

Cmp指令
用于比较两个数是否相等,第一个数是否比第二个数大,当s被更改时,则第一个数一定比第二个数小(因为最高位被更改了,即为溢出)
Cmp r/m,r/m/imm
不能同时为内存/宽度要一样
当使用时,只改变标志寄存器的值
实质是相减来比较,相当于sub,但不改变寄存器的值,只更改标志寄存器。

Test指令
一定程度上与cmp类似,两个目标进行AND操作,结果不保存(不更改除了标志寄存器外的东西)
常见用法:判断某个寄存器是否为空(全0),test eax,eax
因为只要存在一位不为0,那么计算结果不为0
可影响P(最后一个有效字节)…这个情景下主要还是看Z(是否为0)

Jcc 注意有符号无符号和怎么跳的
不影响值,不影响堆栈,只影响eip,与jmp区别在于他加了跳转条件
不管上面指令是什么,它不认识,只要现在标志寄存器符合条件就跳!

JE/JZ,选其一,二者一样。jmp equal和jmp zero,意思为结果为零ZF=1时跳转

JNE/JNZ,jmp not equals/jmp not zero,ZF=0时候跳转(就是结果不为0)

JS,只要SF为1就跳(结果为负时跳转)

JNS同理,SF=0则跳转

JP/JPE,PF=1时跳(结果中1的个数为偶数)

JNP/JPO,PF=0

JO,OF=1,结果溢出了则跳转
JNO,结果非溢出跳转OF=0

JB/JNAE小于时跳转CF=1

JNB/JAE 大于等于CF=0

JBE/JAE小于等于CF=1/ZF=1

JNBE/JA,大于则跳转(无符号数),CF=0&&ZF=0

JL/JNGE小于则跳转(有符号数)SF!=OF

JNL/JGE大于等于则跳转(有符号数)SF=OF

JLE/JNG小于则跳转(有符号数)ZF=1/SF!=OF

JNLB/JG大于则跳转(有符号数)ZF=0&&SF=OF

b小于
Nlb不小于也不等于
G是大于的意思

← Back to Home