我是靠谱客的博主 细心萝莉,最近开发中收集的这篇文章主要介绍除法运算逆向分析除法运算逆向分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

除法运算逆向分析

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


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


相关数学公式

当b>0时有,

ab=ab+1b


ab=a+b1b

当b<0时,有,
ab=ab1b


ab=a+b+1b


1.除数为2的幂

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

[x2n]=x2nx2n=x+2n12nx0x<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内容为 2n1 ,当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)

易得公式,

xox2no12n

c=2no ,这个值被称为Magic Number。于是就有
xox2no12nxc2n

_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,edxshr eax,1Fh用来取得乘积结果符号位,乘积为正时eax存放00000000h,乘积位负时存放00000001h。最后,add edx,eax的原因是:
x<0[xo] ,有

[xo]=xc2n=xc2n+1

(这里我认为只要x<0,编译器能够确认 xc2n 这里必定不是整数)
o=2nc=23338E38E39h=8.9999999 ,推导出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)

易得公式,

xox232+232+no232232+n

此式中,魔数 c=232+no232

_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,edxshr ecx,1add ecx,edxshr ecx,2这4句可以用一个计算式来表示:

xxc2322+xc23222

化简得:
x232+c235

232+n=235n=3o=232+n232+c=235232+24924925h=6.999997 ,推出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+yy=y232xy=xy+x232yyy

易得公式,
xox2no12n(x(2no232)+x232)12nxoxy(y)12n(xy+x232)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
;...负数调整

上述代码转换成公式:

(esieax+232esi)1234cc=2noo=2nc=23492492493h=6.9999997

反推出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=xc12n(c<0)c=2n|o|=2n|o|=2322n|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=ecxeax233|o|=2n232c=23323299999999h=4.9999995

于是反推出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|=2n232c 求解除数|o|,即可恢复除法原型。

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

p=o,(p>0)xo=xp(x2np12n)((x(2np232)+x232)12n)(x((2np232))x232)12n(x(2322n|o|)x232)12nxc2322n()c=2np232<033yc=(2np232)=2322np=2322n|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=edxeax232ecx22=ecxeax232ecx234=ecxeax232234ecxo=ecxeax232234|o|=2n232c=2342326DB6DB6Dh=6.9999997

于是反推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|=2n232c() 求解除数|o|,即可恢复除法原型。

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

最后

以上就是细心萝莉为你收集整理的除法运算逆向分析除法运算逆向分析的全部内容,希望文章能够帮你解决除法运算逆向分析除法运算逆向分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部