如何暴力破解有损 AND 例程?
How to bruteforce a lossy AND routine?
我想知道是否有任何标准方法可以通过蛮力逆转 AND 例程。
例如我有以下转换:
MOV(eax, 0x5b3e0be0) <- Here we move 0x5b3e0be0 to EDX.
MOV(edx, eax) # Here we copy 0x5b3e0be0 to EAX as well.
SHL(edx, 0x7) # Bitshift 0x5b3e0be0 with 0x7 which results in 0x9f05f000
AND(edx, 0x9d2c5680) # AND 0x9f05f000 with 0x9d2c5680 which results in 0x9d045000
XOR(edx, eax) # XOR 0x9d045000 with original value 0x5b3e0be0 which results in 0xc63a5be0
我的问题是如何暴力破解和反转这个例程(即将 0xc63a5be0 转换回 0x5b3e0be0)
我的一个想法(没有用)是使用 PeachPy 实现:
#Input values
MOV(esi, 0xffffffff) < Initial value to AND with, which will be decreased by 1 in a loop.
MOV(cl, 0x1) < Initial value to SHR with which will be increased by 1 until 0x1f.
MOV(eax, 0xc63a5be0) < Target result which I'm looking to get using the below loop.
MOV(edx, 0x5b3e0be0) < Input value which will be transformed.
sub_esi = peachpy.x86_64.Label()
with loop:
#End the loop if ESI = 0x0
TEST(esi, esi)
JZ(loop.end)
#Test the routine and check if it matches end result.
MOV(ebx, eax)
SHR(ebx, cl)
TEST(ebx, ebx)
JZ(sub_esi)
AND(ebx, esi)
XOR(ebx, eax)
CMP(ebx, edx)
JZ(loop.end)
#Add to the CL register which is used for SHR.
#Also check if we've reached the last potential value of CL which is 0x1f
ADD(cl, 0x1)
CMP(cl, 0x1f)
JNZ(loop.begin)
#Decrement ESI by 1, reset CL and restart routine.
peachpy.x86_64.LABEL(sub_esi)
SUB(esi, 0x1)
MOV(cl, 0x1)
JMP(loop.begin)
#The ESI result here will either be 0x0 or a valid value to AND with and get the necessary result.
RETURN(esi)
也许您可以推荐一篇专门针对此的文章或书籍?
不是有损,最后的操作是异或。
整个例程可以用 C 建模为
#define K 0x9d2c5680
uint32_t hash(uint32_t num)
{
return num ^ ( (num << 7) & K);
}
现在,如果我们有两位 x 和 y 以及运算 x XOR y,当 y 为零时,结果为 x.
因此,给定两个数字 n1 和 n2 并考虑它们的 XOR,这些位或 n1 与n2 中的零将使其成为结果 不变(其他将被翻转)。
所以在考虑num ^ ( (num << 7) & K)
时我们可以识别num
与n1和(num << 7) & K
与n2.
由于 n2 是一个与,我们可以知道它必须至少具有与 K 相同的零位。
这意味着 num
的每一位对应于常量 K 中的零位将使其在结果中保持不变。
因此,通过从结果中提取这些位,我们已经有了一个部分反函数:
/*hash & ~K extracts the bits of hash that pair with a zero bit in K*/
partial_num = hash & ~K
从技术上讲,因子 num << 7
也会在 AND 的结果中引入其他零。我们确定最低7位必须为零。
但是 K 已经有最低 7 位零,所以我们不能利用这个信息。
所以我们在这里只使用 K,但如果它的值不同,你需要考虑 AND(实际上,这意味着将 [=43= 的低 7 位清零) ]K).
这给我们留下了 13 个未知位(对应于 K 中设置的位)。
如果我们暂时忘记 AND,我们会得到 x ^ (x << 7)
意思是
hi = numi for i 从 0 到 6(含)
hi = numi ^ numi-7 for i 从 7 到 31(含)
(第一行是因为right-hand的低7位为0)
据此,从h7开始往上,我们可以得到num7为h7 ^ num0 = h7 ^ h0.
从第 7 位开始,相等性不起作用,我们需要使用 numk(对于合适的 k)但幸运的是我们已经计算了它在上一步中的值(这就是我们从低到高开始的原因)。
AND 对此所做的只是限制索引 i 运行的值,特别是仅限制在 K[=108 中设置的位=].
因此,要填写剩下的 13 位,您需要做的是:
part_num7 = h7^part_num0
part_num9 = h9^part_num2
part_num12 = h12^part_num5
...
part_num31 = h31^part_num24
请注意,我们利用了 part_num0..6 = h0..6.
这是一个反转函数的 C 程序:
#include <stdio.h>
#include <stdint.h>
#define BIT(i, hash, result) ( (((result >> i) ^ (hash >> (i+7))) & 0x1) << (i+7) )
#define K 0x9d2c5680
uint32_t base_candidate(uint32_t hash)
{
uint32_t result = hash & ~K;
result |= BIT(0, hash, result);
result |= BIT(2, hash, result);
result |= BIT(3, hash, result);
result |= BIT(5, hash, result);
result |= BIT(7, hash, result);
result |= BIT(11, hash, result);
result |= BIT(12, hash, result);
result |= BIT(14, hash, result);
result |= BIT(17, hash, result);
result |= BIT(19, hash, result);
result |= BIT(20, hash, result);
result |= BIT(21, hash, result);
result |= BIT(24, hash, result);
return result;
}
uint32_t hash(uint32_t num)
{
return num ^ ( (num << 7) & K);
}
int main()
{
uint32_t tester = 0x5b3e0be0;
uint32_t candidate = base_candidate(hash(tester));
printf("candidate: %x, tester %x\n", candidate, tester);
return 0;
}
由于最初的问题是如何“暴力破解”而不是解决问题,所以我最终想出了同样有效的方法。显然它容易出错,具体取决于输入(可能超过 1 个结果)。
from peachpy import *
from peachpy.x86_64 import *
input = 0xc63a5be0
x = Argument(uint32_t)
with Function("DotProduct", (x,), uint32_t) as asm_function:
LOAD.ARGUMENT(edx, x) # EDX = 1b6fb67c
MOV(esi, 0xffffffff)
with Loop() as loop:
TEST(esi,esi)
JZ(loop.end)
MOV(eax, esi)
SHL(eax, 0x7)
AND(eax, 0x9d2c5680)
XOR(eax, esi)
CMP(eax, edx)
JZ(loop.end)
SUB(esi, 0x1)
JMP(loop.begin)
RETURN(esi)
#Read Assembler Return
abi = peachpy.x86_64.abi.detect()
encoded_function = asm_function.finalize(abi).encode()
python_function = encoded_function.load()
print(hex(python_function(input)))
我想知道是否有任何标准方法可以通过蛮力逆转 AND 例程。 例如我有以下转换:
MOV(eax, 0x5b3e0be0) <- Here we move 0x5b3e0be0 to EDX.
MOV(edx, eax) # Here we copy 0x5b3e0be0 to EAX as well.
SHL(edx, 0x7) # Bitshift 0x5b3e0be0 with 0x7 which results in 0x9f05f000
AND(edx, 0x9d2c5680) # AND 0x9f05f000 with 0x9d2c5680 which results in 0x9d045000
XOR(edx, eax) # XOR 0x9d045000 with original value 0x5b3e0be0 which results in 0xc63a5be0
我的问题是如何暴力破解和反转这个例程(即将 0xc63a5be0 转换回 0x5b3e0be0)
我的一个想法(没有用)是使用 PeachPy 实现:
#Input values
MOV(esi, 0xffffffff) < Initial value to AND with, which will be decreased by 1 in a loop.
MOV(cl, 0x1) < Initial value to SHR with which will be increased by 1 until 0x1f.
MOV(eax, 0xc63a5be0) < Target result which I'm looking to get using the below loop.
MOV(edx, 0x5b3e0be0) < Input value which will be transformed.
sub_esi = peachpy.x86_64.Label()
with loop:
#End the loop if ESI = 0x0
TEST(esi, esi)
JZ(loop.end)
#Test the routine and check if it matches end result.
MOV(ebx, eax)
SHR(ebx, cl)
TEST(ebx, ebx)
JZ(sub_esi)
AND(ebx, esi)
XOR(ebx, eax)
CMP(ebx, edx)
JZ(loop.end)
#Add to the CL register which is used for SHR.
#Also check if we've reached the last potential value of CL which is 0x1f
ADD(cl, 0x1)
CMP(cl, 0x1f)
JNZ(loop.begin)
#Decrement ESI by 1, reset CL and restart routine.
peachpy.x86_64.LABEL(sub_esi)
SUB(esi, 0x1)
MOV(cl, 0x1)
JMP(loop.begin)
#The ESI result here will either be 0x0 or a valid value to AND with and get the necessary result.
RETURN(esi)
也许您可以推荐一篇专门针对此的文章或书籍?
不是有损,最后的操作是异或。
整个例程可以用 C 建模为
#define K 0x9d2c5680
uint32_t hash(uint32_t num)
{
return num ^ ( (num << 7) & K);
}
现在,如果我们有两位 x 和 y 以及运算 x XOR y,当 y 为零时,结果为 x.
因此,给定两个数字 n1 和 n2 并考虑它们的 XOR,这些位或 n1 与n2 中的零将使其成为结果 不变(其他将被翻转)。
所以在考虑num ^ ( (num << 7) & K)
时我们可以识别num
与n1和(num << 7) & K
与n2.
由于 n2 是一个与,我们可以知道它必须至少具有与 K 相同的零位。
这意味着 num
的每一位对应于常量 K 中的零位将使其在结果中保持不变。
因此,通过从结果中提取这些位,我们已经有了一个部分反函数:
/*hash & ~K extracts the bits of hash that pair with a zero bit in K*/
partial_num = hash & ~K
从技术上讲,因子 num << 7
也会在 AND 的结果中引入其他零。我们确定最低7位必须为零。
但是 K 已经有最低 7 位零,所以我们不能利用这个信息。
所以我们在这里只使用 K,但如果它的值不同,你需要考虑 AND(实际上,这意味着将 [=43= 的低 7 位清零) ]K).
这给我们留下了 13 个未知位(对应于 K 中设置的位)。
如果我们暂时忘记 AND,我们会得到 x ^ (x << 7)
意思是
hi = numi for i 从 0 到 6(含)
hi = numi ^ numi-7 for i 从 7 到 31(含)
(第一行是因为right-hand的低7位为0)
据此,从h7开始往上,我们可以得到num7为h7 ^ num0 = h7 ^ h0.
从第 7 位开始,相等性不起作用,我们需要使用 numk(对于合适的 k)但幸运的是我们已经计算了它在上一步中的值(这就是我们从低到高开始的原因)。
AND 对此所做的只是限制索引 i 运行的值,特别是仅限制在 K[=108 中设置的位=].
因此,要填写剩下的 13 位,您需要做的是:
part_num7 = h7^part_num0
part_num9 = h9^part_num2
part_num12 = h12^part_num5
...
part_num31 = h31^part_num24
请注意,我们利用了 part_num0..6 = h0..6.
这是一个反转函数的 C 程序:
#include <stdio.h>
#include <stdint.h>
#define BIT(i, hash, result) ( (((result >> i) ^ (hash >> (i+7))) & 0x1) << (i+7) )
#define K 0x9d2c5680
uint32_t base_candidate(uint32_t hash)
{
uint32_t result = hash & ~K;
result |= BIT(0, hash, result);
result |= BIT(2, hash, result);
result |= BIT(3, hash, result);
result |= BIT(5, hash, result);
result |= BIT(7, hash, result);
result |= BIT(11, hash, result);
result |= BIT(12, hash, result);
result |= BIT(14, hash, result);
result |= BIT(17, hash, result);
result |= BIT(19, hash, result);
result |= BIT(20, hash, result);
result |= BIT(21, hash, result);
result |= BIT(24, hash, result);
return result;
}
uint32_t hash(uint32_t num)
{
return num ^ ( (num << 7) & K);
}
int main()
{
uint32_t tester = 0x5b3e0be0;
uint32_t candidate = base_candidate(hash(tester));
printf("candidate: %x, tester %x\n", candidate, tester);
return 0;
}
由于最初的问题是如何“暴力破解”而不是解决问题,所以我最终想出了同样有效的方法。显然它容易出错,具体取决于输入(可能超过 1 个结果)。
from peachpy import *
from peachpy.x86_64 import *
input = 0xc63a5be0
x = Argument(uint32_t)
with Function("DotProduct", (x,), uint32_t) as asm_function:
LOAD.ARGUMENT(edx, x) # EDX = 1b6fb67c
MOV(esi, 0xffffffff)
with Loop() as loop:
TEST(esi,esi)
JZ(loop.end)
MOV(eax, esi)
SHL(eax, 0x7)
AND(eax, 0x9d2c5680)
XOR(eax, esi)
CMP(eax, edx)
JZ(loop.end)
SUB(esi, 0x1)
JMP(loop.begin)
RETURN(esi)
#Read Assembler Return
abi = peachpy.x86_64.abi.detect()
encoded_function = asm_function.finalize(abi).encode()
python_function = encoded_function.load()
print(hex(python_function(input)))