编译以下最小示例时:
int mod7(int x) {
return x % 7;
}
int div7(int x) {
return x / 7;
}
生成以下代码:
将组装返回到C的最简单方法是什么(对于右侧的任意常数)?
反编译器或分析反汇编器之类的工具如何使该过程自动化? ?
#1 楼
首先不幸的是,我们似乎没有在此stackexchange中打开MathJax,因此下面的数学部分格式相当糟糕。我距离数学家也很远,所以在某些地方可能会用不上这个符号。
了解魔术数字和代码
上面代码的目的是重写一个由于除法比乘法要花更多的时钟周期,所以除法成乘法。它的周期大约是两倍,取决于CPU。因此,我们需要找到一种不错的无分支方式。如果我们分支,则很可能会因为简单地进行除法而失败。
一种方法是简单地意识到除法与与数字倒数即
![](/img/6996e4ade16baeb3262868ad623eff9d.png)
![](/img/eef04cc43110bad5573c039da0727ef4.png)
![](/img/ecbff5e385e3ae16fa81e62e7ae695e3.png)
![](/img/d4fd63c3fd0a8800db85567fb9c7e45e.png)
所以让我们尝试将其形式化:
![](/img/35a44f8e55fc15c44a8f117ab6701d34.png)
![](/img/3d19732d2cb1d79c431e229f32da867a.png)
![](/img/051d421cec879286e8590cd6e1c37559.png)
![](/img/6a87aeea640f9321ac18952cc7874baa.png)
![](/img/b23e3c3f17124c7160dc3e6b923dec24.png)
![](/img/ed333f86776bbfa39dea3e7e0054463b.png)
![](/img/74473a39852a4c19aacdd90a3f8d2ea3.png)
![](/img/dfd80844c597ce416449b9a79286a1b1.png)
![](/img/35c3a449077dd5af5b687afe8c734329.png)
我们可以重写上面的方程,如
![](/img/a1e19a0a697cd7984dafb3cb24bb7704.png)
这说明了我们的观点股息
![](/img/8622e13348dfe849a1cec877b5de134b.png)
![](/img/c1a8afc4be16fd9394bd35fc1983fa1c.png)
![](/img/69de42b1d962f372db312a825487066c.png)
研究我们的原始方程
![](/img/b5e75f8c50f2246d1365b1c44b864863.png)
![](/img/b6ca9a78fb08edb38ab246a30c52bf54.png)
![](/img/001f13f465baac25e23c91723196e41d.png)
![](/img/7d8ac221ef2f836a3eb6f505d134a191.png)
![](/img/bb0176a866913925bf41473e0cd1a67e.png)
![](/img/da0d0b6946e64a7d0db419c8fe11e9f8.png)
![](/img/8383cd20bb0ce9b3e44aff369f213a3b.png)
![](/img/c3fda73788b9a86eaef407014c8ad8a7.png)
![](/img/ce2b991cd3c8747ccd4e27baf207fe79.png)
![](/img/95a451a855fc82131f1088fb579aa5d1.png)
![](/img/04c9144e5228d533dab96046fcb934e8.png)
![](/img/b18007c98d67f5e466bb4f76b2315204.png)
将
![](/img/aa6cec654553736532096ebd581a96e0.png)
![](/img/ede36aa0cfe47e7d06896cb67fb70994.png)
![](/img/1fca7059f2de666304c237b45abb39bd.png)
![](/img/ef536d6db554c6f072d57feda306341b.png)
![](/img/b7f2410c5c27bc89b0c49c7b90eae0c2.png)
![](/img/142bfa8f9c98d5a752e09b2761883dd5.png)
![](/img/b7e81ee187464dde724a8e7d7c78e7ad.png)
![](/img/9f36194d26a1266f763b12464af7bc57.png)
![](/img/5f7e317a0f99757f848f46e725933455.png)
![](/img/a416b86044ade82bc1eda545c18df446.png)
![](/img/054c77c10cd40df6fad8f780d83017af.png)
![](/img/dec02d04f38d9e7888908d53ba5632e8.png)
![](/img/999de324461d6acd149894ba26abb1fc.png)
![](/img/1bfcff2e3afbb21141b80841536de0ca.png)
这就是乘法常数的解释以及如何到达它。现在让我们看一下代码:
; Load -1840700269
mov ecx,0x92492493
; Load n
mov eax,edi
; n * -1840700269
imul ecx
; add n to compensate for 2^32 subtraction
add edx,edi
; check the sign bit of our result
mov ecx,edx
shr ecx,0x1f
; divide by 2^2 to compensate for us using y=2^34 instead of 2^32
sar edx,0x2
mov eax,edx
; add the value of the sign bit to the final result
add eax,ecx
从幻数和代码计算除数
我还没有数学上证明这一点,但是如果你想从组装转储(如您所示的转储)中恢复除数,我们可以做一些简单的心理练习。首先,我们需要认识到以下条件成立
![](/img/c1b3e5a5976c1ced9955e95a2dad304d.png)
其中
![](/img/4559f102462a1b586f7536808b199be6.png)
![](/img/015b40a87f5c251bf7ab17a54b14b274.png)
![](/img/2da92bd932e0109e7b56f47b83da71e2.png)
![](/img/eacbf4a72e219a8e4302c0cec5775f8c.png)
![](/img/00e07a2617449729e759e4792c93205d.png)
![](/img/0e72943b09c4f927fad1dcf3340e6f45.png)
![](/img/7d9c3ed816780daf76bcfbf227d29aca.png)
![](/img/0a8c6f53b3f44bb2a83b2986481331e2.png)
来找到解决方案,另一个例子是除数31337,其被乘数为幻数140346763,右边为移位10位。
![](/img/ac5345a1561bf2e3e39b29cb8673572a.png)
最后
有关其工作原理的完整数学分解,包括所有适当的证明和算法来计算魔术数字,移位和加法,请参阅《黑客的喜悦》,第10-3章。
评论
问题不仅在于如何计算魔术常数,还在于如何获得除数。
–伊戈尔·斯科钦斯基♦
2013年3月31日12:36
我试图回答。确实没有时间制定证明,所以我不是100%确信它是正确的。
–彼得·安德森(Peter Andersson)
13年3月31日在20:01
在逆向工程的假设下(如果将const除法/模乘与其他运算混合在一起),则可以将整数乘法常数转换为二进制分数,其倒数与除法/模常数运算数有关,直至未知2乘幂的幂。由于与其他操作的混合和优化,有时无法推断2因子的未知幂。
–rwong
15年11月24日在12:59
仅供参考:堆栈交换应用程序的答案看起来不错,因为它为每个站点打开了mathjax
– Ferrybig
17年2月4日在10:00
#2 楼
这是迟来的回应。 Reko反编译器通过使用中位数执行除数和征服搜索来恢复整数除数。Reko通过识别使用64位乘积的高位字(
r * c
)的模式开始。常数乘数c
被2 ^ 32除以产生介于0.0和1.0之间的双精度浮点数。从有理数0/0和1/1开始,Reko计算一系列中位数,该中位数将浮点数括起来。从此中位数序列中,它选择最接近浮点数的有理数并返回它。该代码尚未经过充分测试-我还没有机会使用负数数字尚未,但似乎给出了合理的结果。如果您好奇的话,这里的代码是:https://github.com/uxmal/reko/blob/master/src/Decompiler/Analysis/ConstDivisionImplementedByMultiplication.cs
#3 楼
本文可能很有趣:用不变乘法进行除法。在这里加入。
评论
有时称为倒数乘法。这是简短的解释,并提供了指向更详细资源的链接。我见过Hex-Ray可以毫无问题地消化它。