逆向工程数学函数

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^32sar , %edx 除以 2^n -- 因为这是一个带符号的除法,所以需要算术移位。

  • 遗憾的是,对于有符号除法,移位后的 %edx 并不完全是商。如果被除数是 -ve(并且假设除数是 +ve)需要加上 1 来得到商。所以 sar [=17=]x1f, %eax 如果 x < 0 给出 -1,否则给出 0。 sub %eax, %edi 完成除法。

    这一步同样可以通过 shr [=19=]x1f, %eaxadd %eax, %edi 实现。或者 add %eax, %eaxadc [=22=], %edi。或者 cmp [=23=]x80000000, %ecxsbb $-1, %edi——这是我最喜欢的,但遗憾的是现在保存 mov %ecx, %eax 什么也没有保存,而且无论如何 cmp [=23=]x80000000, %ecx 是一个很长的指令 :-(

  • 不清楚为什么这会将商 q 改组为 %edi —— 如果它留在 %edx 中,它仍然会在 imul [=29=]x64,%edx,%eax.

  • 之后到达