取模运算的汇编奥秘揭秘

除法已经说完了,来说一下取模。

代码

汇编

wingw

main(int, char**):

push %rbp

mov %rsp,%rbp

sub $0x20,%rsp

mov %ecx,0x10(%rbp)

mov %rdx,0x18(%rbp)

call 0x7ff7410d1837 ; <__main> mov 0x10(%rbp),%eax ;eax = argc

cltd

shr $0x1d,%edx ;edx >> 29(这里相当于判断)

add %edx,%eax ;eax = argc + (argc <0 ? 7 : 0)

and $0x7,%eax ;eax = eax & 7

sub %edx,%eax ;减回修正量

mov %eax,%edx

lea 0x88f5(%rip),%rax ; 0x7ff7410da000 mov %rax,%rcx

call 0x7ff7410d8e40 ; <printf(char const*, ...)> mov 0x10(%rbp),%edx ;edx = argc

movslq %edx,%rax

imul $0x38e38e39,%rax,%rax ;rax = argc * 0x38e38e39

shr $0x20,%rax ;rax = rax >> 32(逻辑)

sar %eax ;eax = eax >> 1(算数)

mov %edx,%ecx ;ecx = argc >> 31

sar $0x1f,%ecx ;ecx >> 1

sub %ecx,%eax ;ecx = ecx -- eax(%eax = argc / 9)

mov %eax,%ecx ;ecx = eax

shl $0x3,%ecx ;ecx >> 3

add %eax,%ecx ;ecx = eax + ecx(%ecx = (%eax << 3) + %eax = 8*q + q = 9*q)

mov %edx,%eax

sub %ecx,%eax ;eax = argc - 9 * (argc / 9)

mov %eax,%edx

lea 0x88bf(%rip),%rax ; 0x7ff7410da000 mov %rax,%rcx

call 0x7ff7410d8e40 ; <printf(char const*, ...)> mov $0x0,%eax

add $0x20,%rsp

pop %rbp

ret

msvc

64->64

main(int,char **):

movq %rdx, 0x10(%rsp)

movl %ecx, 0x8(%rsp)

pushq %rdi

subq $0x20, %rsp

movl 0x30(%rsp), %eax ; eax = argc

cltd ;扩展eax

andl $0x7, %edx ;edx = edx & 7

addl %edx, %eax ;edx = edx & eax

andl $0x7, %eax ;eax = eax = & 7

subl %edx, %eax ;(argc + (argc<0?7:0)) & 7 - (argc<0?7:0)

movl %eax, %edx

leaq 0x86ca(%rip), %rcx

callq 0x140001195 ; printf movl 0x30(%rsp), %eax ;eax = aegc

cltd ;扩展

movl $0x9, %ecx ;eax = argc / 9

idivl %ecx ;eax = argc /9,edx = argc % 9

movl %edx, %eax

movl %eax, %edx

leaq 0x86b6(%rip), %rcx

callq 0x140001195 ; printf xorl %eax, %eax

addq $0x20, %rsp

popq %rdi

retq

64->32

main(int,char **):

pushl %ebp

movl %esp, %ebp

movl 0x8(%ebp), %eax

andl $0x80000007, %eax ; imm = 0x80000007 jns 0x4014d2 ; <+18> at main.cpp:4(%eax 的符号位为 0(argc >= 0)时跳转) decl %eax

orl $-0x8, %eax

incl %eax ;负数取模修正

pushl %eax

pushl 0x606b30 *; imm = 0x606B30* calll 0x40119a *; _printf* addl 0x8, %esp

movl 0x8(%ebp), %eax ;eax = argc

cltd ;扩展(后面是正常的除法操作)

movl $0x9, %ecx

idivl %ecx

pushl %edx

pushl 0x606b38 *; imm = 0x606B38* calll 0x40119a *; _printf* addl 0x8, %esp

xorl %eax, %eax

cmpl %esp, %ebp

calll 0x4010ff ; __RTC_CheckEsp popl %ebp

retl

对于2的幂的取模有一下四种方案:

方案1

对2的k次方取余,余数只需要被除数低k位的值就可以,如果是负数需要在k位之前补1。

代码模板:

mov reg, 被除数

and reg , 80000007h

jns lab1

or reg,0ffffff8h

lab1:

余数的值非0没有问题;如果为0他就是错误的。需要进行调整。方法是在or运算之前-1,or算完了再+1。如果余数不为0不影响计算结果。

代码模板:

mov reg,被除数

and reg,80000007h(这个数由2^k决定)

jns lab1

dec reg

or reg,fffffff8

inc reg

lab1:

碰到这个就能判定是取模代码,模型是变量对2k 执行取模运算。还原就直接去掉高位保留低位代码,统计地位有多少个1就能得到k值。

方案2

对2的k次方取余对于正数使用方案1,负数可以使用下面的调整公式。

x% 2 k = x+ 2 k - 1 & 2 k -1 - 2 k-1

遇到这个公式页可以判定是取模代码,原型是变量对2k 执行取模运算。还原就是直接看and reg,0fffffff8统计低位一共保留有多少0即可得到k的值。

方案3

对2的k次方取余,对正数使用以下调整公式

x% 2 k =x- x&~ 2 k-1

对负数使用以下调整公式

x% 2 k =x- x+ 2 k -1 &~ 2 k-1

遇到这个公式页可以判定是取模代码,原型是变量对2k 执行取模运算。还原就是直接看and reg,0fffffff8统计低位一共保留有多少0即可得到k的值。

方案4

对非2的k次方取余,

r=a%b

根据性质可以得到

r=a-q*b=a- a b*b

碰到这个很简单直接看b就可以了。

相关推荐
墨云安全1 小时前
内网资产无法暴露到公网,怎么做安全检测?2026 年内网渗透测试的落地路径
开发语言·安全·网络安全·渗透测试·php
2601_967212722 小时前
安全型电源轨道系统合规选型技术指标与行业技术路线分析
大数据·经验分享·安全
山东科恩光电2 小时前
安全地毯高灵敏感应机制与耐磨材质优势及多场所防护安装维护指南
安全
goehou2 小时前
Prompt 注入实战:直接注入、间接注入和四道防线(附测试样例)
安全·ai·llm·agent·prompt注入
青岚_Nox4 小时前
C语言实战:带你手写一个带"计时功能"的猜数字小游戏
c语言
世岩清上4 小时前
工厂安全目视化标识不全该怎么整改?标准化落地全流程方案
人工智能·安全·工厂目视化·车间目视化·企业目视化·新建厂房目视化
iotxiaohu6 小时前
用 cflow 查看 C 函数调用关系
c语言·开发语言·cflow
AliCloudROS6 小时前
用阿里云 ChatOps Agent 找回消失的日志:内核漏洞修复换盘后的 SLS 采集断流排查
安全·阿里云
天行健,君子而铎6 小时前
知形-数据库风险监测系统:非侵入式、可溯源、差异化的数据库安全方案
网络·数据库·安全