如何反转使用 float 和 double 混淆的字符串?
How to reverse strings that have been obfuscated using floats and double?
我正在研究 crackme,但在理解我应该检索的标志时遇到了一些困难。
我已经使用 radare2 和 ghidra 反汇编了二进制文件,ghidra 返回以下伪代码:
undefined8 main(void)
{
long in_FS_OFFSET;
double dVar1;
double dVar2;
int local_38;
int local_34;
int local_30;
int iStack44;
int local_28;
undefined2 uStack36;
ushort uStack34;
char local_20;
undefined2 uStack31;
uint uStack29;
byte bStack25;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
__printf_chk(1,"Insert flag: ");
__isoc99_scanf(&DAT_00102012,&local_38);
uStack34 = uStack34 << 8 | uStack34 >> 8;
uStack29 = uStack29 & 0xffffff00 | (uint)bStack25;
bStack25 = (undefined)uStack29;
if ((((local_38 == 0x41524146) && (local_34 == 0x7b594144)) && (local_30 == 0x62753064)) &&
(((iStack44 == 0x405f336c && (local_20 == '_')) &&
((local_28 == 0x665f646e && (CONCAT22(uStack34,uStack36) == 0x40746f31)))))) {
dVar1 = (double)CONCAT26(uStack34,CONCAT24(uStack36,0x665f646e));
dVar2 = (double)CONCAT17((undefined)uStack29,CONCAT43(uStack29,CONCAT21(uStack31,0x5f)));
__printf_chk(0x405f336c62753064,1,&DAT_00102017);
__printf_chk(dVar1,1,"y: %.30lf\n");
__printf_chk(dVar2,1,"z: %.30lf\n");
dVar1 = dVar1 * 124.8034902710365;
dVar2 = (dVar1 * dVar1) / dVar2;
round_double(dVar2,0x1e);
__printf_chk(1,"%.30lf\n");
dVar1 = (double)round_double(dVar2,0x1e);
if (1.192092895507812e-07 <= (double)((ulong)(dVar1 - 4088116.817143337) & 0x7fffffffffffffff))
{
puts("Try Again");
}
else {
puts("Well done!");
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
旗帜的一部分很容易看出来,但另一部分更有趣:
if (1.192092895507812e-07 <= (double)((ulong)(dVar1 - 4088116.817143337) & 0x7fffffffffffffff))
据我了解,我必须根据此条件生成标志的缺失部分。问题是我完全不知道该怎么做。
根据这一行,我可以假设这个缺失的部分大小为 8 个字节:
dVar2=(double)CONCAT17((undefined)uStack29,CONCAT43(uStack29,CONCAT21(uStack31,0x5f)));`
考虑到标志通常是 ascii,带有一些特殊字符,比方说,每个字节的值从 0x21 到 0x7E,这几乎是 8^100 种组合,这显然需要太多时间来计算。
你们知道我应该如何解决这个问题吗?
编辑:这是二进制文件的 link:https://filebin.net/dpfr1nocyry3sijk
您可以通过编辑变量类型来调整 Ghidra 反向结果。基于 scanf const string %32s
你的 local_38
应该是 char [32]
.
在第一个if
之前,有一些字符交换。
第一个 if
语句给你一个长的标志约束
此时可以确认部分flag是FARADAY{d0ubl3_@nd_f1o@t
,接下来就是本次挑战的主要部分了
它是根据flag打印x,y,z,但是你很快会发现x,y是被if约束的,所以你只需要解z就可以得到flag,所以你觉得需要通过可打印的 ascii 强制所有双重值限制。
但是如果语句说这个 double 的字节 0 必须是 _
并且那里有一个数学约束,那么有一个限制,简单的数学告诉 dVar2 - 4088116.817143337 <= 1.192092895507813e-07
它来了 dVar2
非常接近4088116.817143337
并且这个 double 中的字节 3 和字节 7 将交换
通过反转结果:dVar2 = y*y*x*x/z
,求解这个等式你可以说 z 必须靠近 407.2786840401004
并且打包到小端是 `be}uty@
。基于双内部结构格式,MSB 会影响指数,所以你可以确保最后一个字节是 @
并且它显示 byte0 和 byte3 现在通过约束和标记通用格式与 {}
对固定。
所以最后,你只需要强制 5 个字节的可打印 ascii 就可以解决这个问题。
import string, struct
from itertools import product
possible = string.ascii_lowercase + string.punctuation + string.digits
for nxt in product(possible, repeat=5):
n = ''.join(nxt).encode()
s = b'_' + n[:2] + b'}' + n[2:] + b'@'
rtn = struct.unpack("<d", s)[0]
rtn = 1665002837.488342 / rtn
if abs(rtn - 4088116.817143337) <= 0.0000001192092895507812:
print(s)
而宾果游戏的标志是 FARADAY{d0ubl3_@nd_f1o@t_be@uty}
我正在研究 crackme,但在理解我应该检索的标志时遇到了一些困难。 我已经使用 radare2 和 ghidra 反汇编了二进制文件,ghidra 返回以下伪代码:
undefined8 main(void)
{
long in_FS_OFFSET;
double dVar1;
double dVar2;
int local_38;
int local_34;
int local_30;
int iStack44;
int local_28;
undefined2 uStack36;
ushort uStack34;
char local_20;
undefined2 uStack31;
uint uStack29;
byte bStack25;
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
__printf_chk(1,"Insert flag: ");
__isoc99_scanf(&DAT_00102012,&local_38);
uStack34 = uStack34 << 8 | uStack34 >> 8;
uStack29 = uStack29 & 0xffffff00 | (uint)bStack25;
bStack25 = (undefined)uStack29;
if ((((local_38 == 0x41524146) && (local_34 == 0x7b594144)) && (local_30 == 0x62753064)) &&
(((iStack44 == 0x405f336c && (local_20 == '_')) &&
((local_28 == 0x665f646e && (CONCAT22(uStack34,uStack36) == 0x40746f31)))))) {
dVar1 = (double)CONCAT26(uStack34,CONCAT24(uStack36,0x665f646e));
dVar2 = (double)CONCAT17((undefined)uStack29,CONCAT43(uStack29,CONCAT21(uStack31,0x5f)));
__printf_chk(0x405f336c62753064,1,&DAT_00102017);
__printf_chk(dVar1,1,"y: %.30lf\n");
__printf_chk(dVar2,1,"z: %.30lf\n");
dVar1 = dVar1 * 124.8034902710365;
dVar2 = (dVar1 * dVar1) / dVar2;
round_double(dVar2,0x1e);
__printf_chk(1,"%.30lf\n");
dVar1 = (double)round_double(dVar2,0x1e);
if (1.192092895507812e-07 <= (double)((ulong)(dVar1 - 4088116.817143337) & 0x7fffffffffffffff))
{
puts("Try Again");
}
else {
puts("Well done!");
}
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
旗帜的一部分很容易看出来,但另一部分更有趣:
if (1.192092895507812e-07 <= (double)((ulong)(dVar1 - 4088116.817143337) & 0x7fffffffffffffff))
据我了解,我必须根据此条件生成标志的缺失部分。问题是我完全不知道该怎么做。
根据这一行,我可以假设这个缺失的部分大小为 8 个字节:
dVar2=(double)CONCAT17((undefined)uStack29,CONCAT43(uStack29,CONCAT21(uStack31,0x5f)));`
考虑到标志通常是 ascii,带有一些特殊字符,比方说,每个字节的值从 0x21 到 0x7E,这几乎是 8^100 种组合,这显然需要太多时间来计算。
你们知道我应该如何解决这个问题吗?
编辑:这是二进制文件的 link:https://filebin.net/dpfr1nocyry3sijk
您可以通过编辑变量类型来调整 Ghidra 反向结果。基于 scanf const string %32s
你的 local_38
应该是 char [32]
.
在第一个if
之前,有一些字符交换。
第一个 if
语句给你一个长的标志约束
此时可以确认部分flag是FARADAY{d0ubl3_@nd_f1o@t
,接下来就是本次挑战的主要部分了
它是根据flag打印x,y,z,但是你很快会发现x,y是被if约束的,所以你只需要解z就可以得到flag,所以你觉得需要通过可打印的 ascii 强制所有双重值限制。
但是如果语句说这个 double 的字节 0 必须是 _
并且那里有一个数学约束,那么有一个限制,简单的数学告诉 dVar2 - 4088116.817143337 <= 1.192092895507813e-07
它来了 dVar2
非常接近4088116.817143337
并且这个 double 中的字节 3 和字节 7 将交换
通过反转结果:dVar2 = y*y*x*x/z
,求解这个等式你可以说 z 必须靠近 407.2786840401004
并且打包到小端是 `be}uty@
。基于双内部结构格式,MSB 会影响指数,所以你可以确保最后一个字节是 @
并且它显示 byte0 和 byte3 现在通过约束和标记通用格式与 {}
对固定。
所以最后,你只需要强制 5 个字节的可打印 ascii 就可以解决这个问题。
import string, struct
from itertools import product
possible = string.ascii_lowercase + string.punctuation + string.digits
for nxt in product(possible, repeat=5):
n = ''.join(nxt).encode()
s = b'_' + n[:2] + b'}' + n[2:] + b'@'
rtn = struct.unpack("<d", s)[0]
rtn = 1665002837.488342 / rtn
if abs(rtn - 4088116.817143337) <= 0.0000001192092895507812:
print(s)
而宾果游戏的标志是 FARADAY{d0ubl3_@nd_f1o@t_be@uty}