逆向工程数学函数
Reverse engineering math functions
我正在将一些汇编代码转换为 C 代码,但在使用以下代码时遇到问题:
mov [=10=]x51eb851f,%edx
mov %ecx,%eax
imul %edx
sar [=10=]x5,%edx
mov %edx,%edi
mov %ecx,%eax
sar [=10=]x1f,%eax
sub %eax,%edi
imul [=10=]x64,%edi,%eax
sub %eax,%ecx
%ecx
存储我们的参数,它是一个 int 类型(我们可以称它为 x)。
我理解前三个步骤实际上是在做x / 100
。
但是我在下面的步骤中感到困惑。
一些帮助将不胜感激。
gcc 9.3.1 for x86 gcc -O3 -S -m32
转换以下 C 代码:
int foo(int x)
{
return x%100;
}
进入以下程序集:
foo:
movl 4(%esp), %ecx
movl 74389535, %edx
movl %ecx, %eax
imull %edx
movl %edx, %eax
movl %ecx, %edx
sarl , %eax
sarl , %edx
subl %edx, %eax
imull 0, %eax, %eax
subl %eax, %ecx
movl %ecx, %eax
ret
除了一些小的重新排序和整个函数符合 x86 ABI 之外,这实际上是相同的。
你是对的,前三个步骤是做 x / 100 -- 或多或少乘以 1/100 -- 但是 直到 sub %eax, %edi
.
之后除法才完成
所以,为了回答您关于前三个步骤之后的问题,这里是代码片段,注释:
mov [=10=]x51eb851f,%edx # magic multiplier for signed divide by 100
mov %ecx,%eax # %ecx = x
imul %edx # first step of division signed x / 100
sar [=10=]x5,%edx # q = %edx:%eax / 2^(32+5)
mov %edx,%edi # %edi = q (so far)
mov %ecx,%eax
sar [=10=]x1f,%eax # %eax = (x < 0) ? -1 : 0
sub %eax,%edi # %edi = x / 100 -- finally
imul [=10=]x64,%edi,%eax # %eax = q * 100
sub %eax,%ecx # %ecx = x - ((x / 100) * 100)
注意:
通常,在这种除法乘法技术中,乘法产生的结果按比例放大 2^(32+n)(用于 32 位除法)。在这种情况下,n = 5。乘法的完整结果是 %edx:%eax
,舍弃 %eax
除以 2^32。 sar , %edx
除以 2^n -- 因为这是一个带符号的除法,所以需要算术移位。
遗憾的是,对于有符号除法,移位后的 %edx
并不完全是商。如果被除数是 -ve(并且假设除数是 +ve)需要加上 1
来得到商。所以 sar [=17=]x1f, %eax
如果 x < 0 给出 -1,否则给出 0。 sub %eax, %edi
完成除法。
这一步同样可以通过 shr [=19=]x1f, %eax
和 add %eax, %edi
实现。或者 add %eax, %eax
和 adc [=22=], %edi
。或者 cmp [=23=]x80000000, %ecx
和 sbb $-1, %edi
——这是我最喜欢的,但遗憾的是现在保存 mov %ecx, %eax
什么也没有保存,而且无论如何 cmp [=23=]x80000000, %ecx
是一个很长的指令 :-(
不清楚为什么这会将商 q 改组为 %edi
—— 如果它留在 %edx
中,它仍然会在 imul [=29=]x64,%edx,%eax
.
之后到达
我正在将一些汇编代码转换为 C 代码,但在使用以下代码时遇到问题:
mov [=10=]x51eb851f,%edx
mov %ecx,%eax
imul %edx
sar [=10=]x5,%edx
mov %edx,%edi
mov %ecx,%eax
sar [=10=]x1f,%eax
sub %eax,%edi
imul [=10=]x64,%edi,%eax
sub %eax,%ecx
%ecx
存储我们的参数,它是一个 int 类型(我们可以称它为 x)。
我理解前三个步骤实际上是在做x / 100
。
但是我在下面的步骤中感到困惑。
一些帮助将不胜感激。
gcc 9.3.1 for x86 gcc -O3 -S -m32
转换以下 C 代码:
int foo(int x)
{
return x%100;
}
进入以下程序集:
foo:
movl 4(%esp), %ecx
movl 74389535, %edx
movl %ecx, %eax
imull %edx
movl %edx, %eax
movl %ecx, %edx
sarl , %eax
sarl , %edx
subl %edx, %eax
imull 0, %eax, %eax
subl %eax, %ecx
movl %ecx, %eax
ret
除了一些小的重新排序和整个函数符合 x86 ABI 之外,这实际上是相同的。
你是对的,前三个步骤是做 x / 100 -- 或多或少乘以 1/100 -- 但是 直到 sub %eax, %edi
.
所以,为了回答您关于前三个步骤之后的问题,这里是代码片段,注释:
mov [=10=]x51eb851f,%edx # magic multiplier for signed divide by 100
mov %ecx,%eax # %ecx = x
imul %edx # first step of division signed x / 100
sar [=10=]x5,%edx # q = %edx:%eax / 2^(32+5)
mov %edx,%edi # %edi = q (so far)
mov %ecx,%eax
sar [=10=]x1f,%eax # %eax = (x < 0) ? -1 : 0
sub %eax,%edi # %edi = x / 100 -- finally
imul [=10=]x64,%edi,%eax # %eax = q * 100
sub %eax,%ecx # %ecx = x - ((x / 100) * 100)
注意:
通常,在这种除法乘法技术中,乘法产生的结果按比例放大 2^(32+n)(用于 32 位除法)。在这种情况下,n = 5。乘法的完整结果是
%edx:%eax
,舍弃%eax
除以 2^32。sar , %edx
除以 2^n -- 因为这是一个带符号的除法,所以需要算术移位。遗憾的是,对于有符号除法,移位后的
%edx
并不完全是商。如果被除数是 -ve(并且假设除数是 +ve)需要加上1
来得到商。所以sar [=17=]x1f, %eax
如果 x < 0 给出 -1,否则给出 0。sub %eax, %edi
完成除法。这一步同样可以通过
shr [=19=]x1f, %eax
和add %eax, %edi
实现。或者add %eax, %eax
和adc [=22=], %edi
。或者cmp [=23=]x80000000, %ecx
和sbb $-1, %edi
——这是我最喜欢的,但遗憾的是现在保存mov %ecx, %eax
什么也没有保存,而且无论如何cmp [=23=]x80000000, %ecx
是一个很长的指令 :-(不清楚为什么这会将商 q 改组为
%edi
—— 如果它留在%edx
中,它仍然会在imul [=29=]x64,%edx,%eax
. 之后到达