我是靠谱客的博主 细心萝莉,这篇文章主要介绍除法运算逆向分析除法运算逆向分析,现在分享给大家,希望可以做个参考。

除法运算逆向分析

  • 除法运算逆向分析
    • 相关数学公式
    • 除数为2的幂
    • 除数为负的2的幂
    • 除数为非2的幂
    • 除数为负的非2的幂


由于除法指令的指令周期较长,效率低,所以编译器想尽办法用其他指令组合代替除法指令。所以C/C++除法运算的逆向分析较其他运算复杂很多,在此做一下总结


相关数学公式

当b>0时有,

⌊ab⌋=⌈a−b+1b⌉

且
⌈ab⌉=⌊a+b−1b⌋

当b<0时,有,
⌊ab⌋=⌈a−b−1b⌉

且
⌈ab⌉=⌊a+b+1b⌋


1.除数为2的幂

sar指令相当于向下取整,即 ⌊x2n⌋ ,而C语言除法结果是向零取整,即 [x2n] 。
所以有:

[x2n]=⎧⎩⎨⎪⎪⎪⎪⎪⎪⌊x2n⌋⌈x2n⌉=⌊x+2n−12n⌋x≥0x<0

下面看例子:

//.c
printf("nVarOne/8=%d",nVarOne/8);

编译后反汇编:

;.asm
mov
eax,dword ptr [ebp-4]
cdq
and
edx,7
add
eax,edx
sar
eax,3
...

and edx,7使得当nVarOne为负数时edx内容为 2n−1 ,当nVarOne为正数时edx为0,最后sar eax,3相当于除以 2n 。


2.除数为负的2的幂

直接给例子:

//.c
printf("nVarOne/-8=%d",nVarOne/-8);

编译后反汇编:

;.asm
mov
eax, [esp+4]
cdq
and
edx,7
add
eax,edx
sar
eax,3
neg
eax
...

除了最后一句其余和上面相同, neg eax相当于对计算结果取负。


接下来讨论难点3和4

3.除数为非2的幂

下面举例由汇编逆向推C代码:


情景1(MagicNumber ⩽ 0x7FFFFFFF)

易得公式,

xo⇔x∗2no∗12n

令 c=2no ,这个值被称为Magic Number。于是就有
xo⇔x∗2no∗12n⇔x∗c2n

_main proc near
arg_0= dword ptr 4
mov
ecx,[esp+arg_0]
mov
eax,38E38E39h
imul
ecx
sar
edx,1
mov
eax,edx
shr
eax,1Fh
add
edx,eax
push
edx
push
offset Format
;"%d
call
_printf
add
esp,8
retn
_main
endp

其中,sar edx,1算术右移1位, shr eax,1Fh逻辑右移31位。
首先,ecx获取参数,eax获取魔数,(edx,eax)=eax*ecx,乘积低4B的eax内容抛弃,只使用乘积高4B的edx内容,这样就相当于乘积右移32位,再加上sar edx,1右移的1位,共右移33位,即n=33,mov eax,edx与shr eax,1Fh用来取得乘积结果符号位,乘积为正时eax存放00000000h,乘积位负时存放00000001h。最后,add edx,eax的原因是:
当 x<0且[xo]不为整数时 ,有

[xo]=⌈x∗c2n⌉=⌊x∗c2n⌋+1

(这里我认为只要x<0,编译器能够确认 x∗c2n 这里必定不是整数)
∴o=2nc=23338E38E39h=8.999999……≈9 ,推导出C代码,

printf("%d",argc/9);

小结:

;x*(2^n/o)
mov
eax,MagicNumber
imul
...
;/2^n
sar
edx, ...
mov
reg,edx
shr
reg,1Fh
;负数调整
add
edx,reg

当遇到以上指令序列,基本可判定是除法优化后的代码。MagicNumber<=7fffffffh,编译器在imul和sar之间未产生任何调整指令,故认定除数为正数。统计右移总次数确定公式中的n值,使用公式 o=2nc(魔数) 得到除数o的近似值。即可恢复除法原型。


情景2(MagicNumber ⩽ 0x7FFFFFFF)

易得公式,

xo⇔x∗232+232+no−232232+n

此式中,魔数 c=232+no−232

_main proc near
arg_0= dword ptr 4
mov
ecx,[esp+arg_0]
mov
eax,24924925h
mul
ecx
sub
ecx,edx
shr
ecx,1
add
ecx,edx
shr
ecx,2
push
ecx
push
offset
Format ;"nVarTwo/7=%drn"
call
_printf
add
esp,8
xor
eax,eax
retn
_main endp

sub ecx,edx,shr ecx,1,add ecx,edx,shr ecx,2这4句可以用一个计算式来表示:

x−x∗c2322+x∗c23222

化简得:
x∗232+c235

∴232+n=235⇒n=3,o=232+n232+c=235232+24924925h=6.99999……≈7 ,推出C代码:

printf("nVarTwo/7="%drn",argc/7);

小结:

 mov
eax,MagicNumber
mul
reg
sub
reg,edx
shr
reg,1
add
reg,edx
(shr
reg,A)

如果遇到以上指令序列,基本可判定是除法优化后的代码。统计右移总次数以确定公式中的n值,使用公式 o=232+n232+c(魔数) 求解出除数o,即可恢复除法原型。


情景3(MagicNumber ≥ 0x80000000)

编译器在计算MagicNumber时是作为无符号处理的,而imul指令是作为有符号处理的。所以当魔数 ≥0x80000000 时,实际参与乘法运算的是个负数,导致魔数与数学公式上的那个“大常数”意义不一致。

当y真<0时,由补码计算公式有:y补=232−|y真|=232+y真∴y真=y补−232∴x∗y补=x∗y真+x∗232y补这里看做无符号数y无,为正数,y真为负数。

易得公式,
xo⇒x∗2no∗12n⇒(x∗(2no−232)+x∗232)∗12n即xo⇒x∗y补(y无)∗12n⇒(x∗y真+x∗232)∗12n

_main proc near
arg_0= dword ptr 4
mov
esi,[esp+arg_0]
mov
eax,92492493h
imul
esi
add
edx,esi
sar
edx,2
;...负数调整

上述代码转换成公式:

(esi∗eax+232∗esi)∗1234∵c是编译器求魔数运算按公式c=2no无符号运算得到的。∴可以用公式o=2nc=23492492493h=6.999999……≈7

反推出C代码:

printf("%d",argc/7);

小结:

 mov
eax,MagicNumber;MagicNumber>7fffffffh
imul
reg
add
edx,reg
sar
edx,...
mov
reg,edx
shr
reg,1Fh
add
edx,reg

当遇到以上指令序列时,基本可判定是除法优化后的代码。当MagicNumber ≥80000000h ,编译器会在imul和sar之间产生调整作用的add指令,故可认定除数为正。*统计右移的总次数以确定公式中的n值,然后使用公式 o=2nc(魔数) 求解除数o,即可恢复除法原型。

4.除数为负的非2的幂

易得公式:

xo=x∗c∗12n(c<0)c=−2n|o|=2n|o|求补=232−2n|o|

情景1(MagicNumber ≥ 0x80000000)

_main proc near
arg_0= dword ptr 4
mov
ecx,[esp+arg_0]
mov
eax,99999999h
imul
ecx
sar
edx,1
mov
eax,edx
shr
eax,1Fh
add
edx,eax
push
edx
push
offset Format ;"%d"
call
_printf
add
esp,8
xor
eax,eax
retn
_main endp

代码体现的表达式:

edx=ecx∗eax233|o|=2n232−c=233232−99999999h=4.999999……≈5

于是反推出C代码为:

printf("%d",argc/-5);

小结:

 mov
eax,MagicNumber(>=0x7fffffff)
imul
reg
sar
edx,...
mov
reg,edx
shr
reg,1Fh
add
edx,reg

如遇到以上指令序列,则基本可判定是除法优化后的代码。MagicNumber ≥80000000h ,编译器在imul和sar之间未产生任何调整指令,故可认定除数为负。*统计右移总次数以确定公式中的n值,然后使用公式 |o|=2n232−c(魔数) 求解除数|o|,即可恢复除法原型。

情景2(MagicNumber ⩽ 0x7FFFFFFF)
当MagicNumber<=7FFFFFFFFh时,除数也有可能是负数。(为什么会有这种情景?这样可以使数学式中 c=2no 表示的范围更大)
为了使 x∗c2n中c=2no(o<0) 表示更小的负数,编译器用类似3中情景3的方法,

设p=−o,(p>0)xo=−xp⇒−(x∗2np∗12n)⇒−((x∗(2np−232)+x∗232)∗12n)⇒(x∗(−(2np−232))−x∗232)∗12n⇒(x∗(232−2n|o|)−x∗232)∗12n⇒x∗c−2322n(对应代码转换的公式)−c=2np−232<0(3中情景3的y真)c=−(2np−232)=232−2np=232−2n|o|>0

_main proc near
arg_0= dword ptr 4
mov
ecx,[esp+arg_0]
mov
eax,6DB6DB6Dh
imul
ecx
sub
edx,ecx
sar
edx,2
mov
eax,edx
shr
eax,1Fh
add
edx,eax
push
edx
push
offset Format ;"%d"
call
_printf
add
esp,8
retn
_main endp

上面代码转换成公式:

edx=edx∗eax232−ecx22=ecx∗eax−232∗ecx234=ecx∗eax−232234ecxo=ecx∗eax−232234|o|=2n232−c=234232−6DB6DB6Dh=6.999999……≈7

于是反推C代码:

printf("%d",argc/-7);

小结:

 mov
eax,MagicNumber(<=7fffffffh)
imul
reg
sub
edx,reg
sar
edx,...
mov
reg,edx
shr
reg,1Fh
add
edx,reg

当遇到以上指令序列时,基本判定是除法优化后的代码。MagicNumber ⩽ 7fffffffh,imul和sar之间有sub指令来调整乘积,故认定除数为负数。统计右移总次数以确定公式中的n值,然后使用公式 |o|=2n232−c(魔数) 求解除数|o|,即可恢复除法原型。

如何从汇编代码中区分正负除数?
∙ 当MagicNumber最高位为1时( ≥80000000h ),对于正除数,MagicNumber为原码形式,编译器会在imul和sar之间产生调整作用的add指令。如果没有,则MagicNumber为补码形式。
∙ 当MagicNumber最高位为0时( ⩽7FFFFFFFh ),对于负除数,编译器会在imul和sar之间产生调整作用的sub指令。
这些应作为区分负除数的重要依据。

最后

以上就是细心萝莉最近收集整理的关于除法运算逆向分析除法运算逆向分析的全部内容,更多相关除法运算逆向分析除法运算逆向分析内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(282)

评论列表共有 0 条评论

立即
投稿
返回
顶部