如何简化程序集平移右移 32 Xor 绝对数和值
Posted
技术标签:
【中文标题】如何简化程序集平移右移 32 Xor 绝对数和值【英文标题】:How to simplify Assembly Translation Shift Right by 32 Xor Absolute Number And Value 【发布时间】:2014-04-26 10:57:36 【问题描述】:我不知道原始代码,但我认为右移和绝对不会这么复杂。
这是反编译后的 IDA PRO 代码重命名后的样子
char Ship; //Could be 0-7 (8 is reversed for special purpose)
char NewShip = 1; //Could be 0-7 (8 is reversed for special purpose)
short Frequency = 0; //This could be from 0 to 9999
bool NumberToFrequency = true;
Frequency = GetNextFrequencyToJoin(player->MyArena);
if ( NumberToFrequency )
//TODO: maybe the below is just Frequency % 7; ?
NewShip = (((unsigned long)Frequency >> 32) ^ abs(Frequency) & 7) - ((unsigned long)Frequency >> 32);
Ship = NewShip;
else
Ship = NewShip;
这是一个IDEOne测试http://ideone.com/Q2bEjU
似乎NewShip = abs(frequency) & 7);
是我真正需要的,似乎我通过循环测试了所有可能性,它永远不会搞砸。
另一个反编译器给了我这个结果
asm("cdq ");
NewShip = ((Var1 ^ Var2) - Var2 & 7 ^ Var2) - Var2;
没有右移或任何东西对我来说仍然陌生,可能显示了绝对数字是如何工作的,但仍然不知道右移 32 的来源。
NumberToFrequency
假设要做的是使频率与船舶相同,但当然频率超过 7,因此剩余值仍应转换为船舶值,因此我假设它只是 7 的模数 %
.
但是为什么这么复杂的代码可能意味着完全不同的东西呢?我只是问代码是什么意思。我将在下面添加汇编代码。我什至在下面的程序集中找不到 Shift right 32 我很确定它在同一个地方。
.text:0040DD3A mov ecx, [ebp+1Ch] ; arena
.text:0040DD3D call GetNextFrequencyToJoin
.text:0040DD42 mov ecx, [ebp+1Ch]
.text:0040DD45 mov si, ax
.text:0040DD48 mov [esp+220h+var_20C], si
.text:0040DD4D cmp [ecx+1ACCEh], ebx
.text:0040DD53 jz short loc_40DD98
.text:0040DD55 movsx eax, si
.text:0040DD58 cdq
.text:0040DD59 xor eax, edx
.text:0040DD5B sub eax, edx
.text:0040DD5D and eax, 7
.text:0040DD60 xor eax, edx
.text:0040DD62 sub eax, edx
.text:0040DD64 mov [esp+220h+var_20F], al
编辑:
我自己找到了答案,似乎那些 shift 32 >> 32
是为一些旧的 C 编译支持添加的无用垃圾,其类型匹配 32 位 DWORD 或类似的废话。
【问题讨论】:
旁注:[ida] 是首选标签([ida] 和 [ida-pro] 应该是同义词;参见:meta.***.com/questions/308568/pro-ida-ida-pro) 【参考方案1】:轮班并非毫无用处。这是 Hexray 无法在其 c 反汇编中重现的一种无组逻辑形式。
.text:0040DD55 movsx eax, si
.text:0040DD58 cdq
.text:0040DD59 xor eax, edx
.text:0040DD5B sub eax, edx
.text:0040DD5D and eax, 7
.text:0040DD60 xor eax, edx
.text:0040DD62 sub eax, edx
是重要的代码。 EDX:EAX
是 SI
的符号扩展版本,所以 EDX
是 0 或 -1。 xor
要么保持 eax
不变或反转它,sub
保持不变或加一,依此类推:
if (si < 0)
eax = ~si;
eax += 1;
eax &= 0x7;
eax = ~eax;
eax += 1;
else
eax = si & 0x7;
第一个分支仍然可以简化,但我把它留给你......
更新
只有si<0
的分支不同,这已经暗示了正在发生的事情。序列eax = ~si; eax += 1;
可以理解为two's-complement,所以插入我们对这个补码的知识就得到了
if (si < 0)
eax = -1 * si;
eax &= 0x7;
eax *= -1;
else
eax = si & 0x7;
简而言之
eax = (abs(si) & 0x7) * sign(si);
或使用带符号的模数运算符
al = si % 8;
【讨论】:
哈哈,如果可以的话,你能不能把它简化一下,我不知道发生了什么老实说我会很感激它,如果它不占用你太多时间。怀疑这里甚至使用了一个分支,它可能是abs函数本身的源代码吗? 这是否意味着它实际上是modulus
而不是 &
在这里使用?
@user3435580 抱歉,我什至没有看到。是的,它实际上是一个模数。 al = si % 8
使用签名的 modulus
运算符。
这样 0x1007 是按位掩码还是什么?以前从未见过这样的。你认为它最初是尽可能干净的吗? NewShip = abs(frequency) & 7);
似乎给出了相同的确切结果,但又是基于你所说的十六进制射线反编译错误。
@user3435580 对不起,我有一段时间感到困惑(使用 0x1007 掩码)。现在应该是正确的。【参考方案2】:
我想我明白了,我使用 IDA-PRO 的反编译器似乎在所有地方都生成了这些 Shift right 32 的 >> 32
,并且在我看到这个的所有情况下都使用了 abs()
函数,它看起来就像绝对数函数的无用包装。
我找到的一些例子。
//1
((((unsigned long)i >> 32) ^ abs(i)) - ((unsigned long)i >> 32))
//2
(((unsigned long)encryption->field_25E >> 32) ^ abs(encryption->field_25E)) - ((unsigned long)encryption->field_25E >> 32);
//3
((((unsigned long)i >> 32) ^ abs(i)) - ((unsigned long)i >> 32))
//4
(((unsigned long)(v104->field_A8 + 1) >> 32) ^ abs(*((unsigned char*)&(v104->field_A8)) + 1) & 7) - ((unsigned long)(v104->field_A8 + 1) >> 32);
//5
(((unsigned long)v11 >> 32) ^ abs(v11)) - ((unsigned long)v11 >> 32);
//6
(((unsigned long)v4->field_262 >> 32) ^ abs(v4->field_262)) - ((unsigned long)v4->field_262 >> 32)
//7
(((unsigned long)v18 >> 32) ^ abs(v18)) - ((unsigned long)v18 >> 32);
//8 (not refactored yet).
((((unsigned long)*(unsigned int *)(v1 + 610) >> 32) ^ abs(*(unsigned int *)(v1 + 610))) - ((unsigned long)*(unsigned int *)(v1 + 610) >> 32)
您可能还会在另外 1 个地方看到这些 >> 32
,我已经知道它们只是优化了研究部门,看起来更加不同。
像这样疯狂的事情(我用我的正则表达式工具修复了这个问题)
(signed int)((unsigned int)v130 + ((unsigned long)(18446744071705233545i64 * (signed int)v130) >> 32)) >> 5;
//Originally it looked something like this
LODWORD(v202) = (signed int)((_DWORD)v202 + (0xFFFFFFFF88888889ui64 * (signed int)v202 >> 32)) >> 5;
//Or without the hexadecimal values
LODWORD(v202) = (signed int)((_DWORD)v202 + ((unsigned __int64)(18446744071705233545i64 * (signed int)v202) >> 32)) >> 5;
//You will see it getting used like this
(signed int)(((unsigned int)v202 >> 31) + v202)
But what it really means is
v202 / 60
http://www.hexblog.com/?p=17 讨论了用于将其转换回 / 60
的方程式
【讨论】:
以上是关于如何简化程序集平移右移 32 Xor 绝对数和值的主要内容,如果未能解决你的问题,请参考以下文章