【问题标题】:How to reverse strings that have been obfuscated using floats and double?如何反转使用浮点数和双精度数混淆的字符串?
【发布时间】:2022-01-20 21:43:05
【问题描述】:

我正在开发一个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 组合,这显然需要太多时间来计算。

你们知道我应该如何着手解决这个问题吗?

编辑:这是二进制文件的链接:https://filebin.net/dpfr1nocyry3sijk

【问题讨论】:

  • 能否提供二进制文件进行分析?我想这是关于存储在内存中的double 数据结构的技巧
  • 嗨@waynelpu,我在答案中提供了二进制文件的链接,谢谢您的回复。现在我正在尝试检查满足条件的 dVar1 的每个字节,并将其除以它的先前值(在 printf 之后)的平方。我仍然有大量不同的可能值,但没有一个看起来可能是标志。看起来它确实是关于将双精度值的每个字节映射到一个字符串,但是有很多不同的组合,它使得计算任务非常苛刻
  • 0x41524146

标签: c reverse-engineering


【解决方案1】:

您可以通过编辑变量类型来调整 Ghidra 反向结果。基于 scanf const string %32s 你的local_38 应该是char [32]

在第一个if之前,有一些字符交换。

而第一个if 语句会给你一个很长的标志约束

此时可以确认flag的一部分是FARADAY{d0ubl3_@nd_f1o@t,那么就是本次挑战的主要部分了。

它根据标志打印x,y,z,但你很快就会发现x和y受if约束,所以你只需要解决z来得到标志,所以你认为你需要暴力破解所有可打印 ascii 的双值限制。

但是如果声明说这个双精度的 byte0 必须是 _ 并且那里有一个数学约束,那么简单的数学告诉 dVar2 - 4088116.817143337 &lt;= 1.192092895507813e-07 并且它来自 dVar2 非常接近 4088116.817143337 而这个 double 中的字节 3 和字节 7 将交换

通过反向结果:dVar2 = y*y*x*x/z,求解这个方程,你可以说 z 必须靠近 407.2786840401004 并且压缩到小端是 `be}uty@。基于双重内部结构格式,MSB会影响指数,所以你可以确保最后一个字节是@,并且它表明byte0和byte3现在通过约束和标志与{}对的通用格式固定。

所以最后,您只需要 bureforce 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}

【讨论】:

  • 这是一个解决方案!我三天前解决了,我完全忘记更新帖子了:D 实际上,您不需要强制任何东西,最后一个不等式有点误导,您绝对可以将 dVar1 替换为 4088116.xxxx 以便满足您的条件,然后像你说的那样做反向算法。所以你不需要计算满足不等式的 dVar1 的每个值。但是,非常棒的答案,非常清楚,尤其是对我来说,我一直在与浮点表示作斗争,这个答案就像蛋糕上的樱桃。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
  • 2015-06-29
  • 1970-01-01
  • 2021-06-25
相关资源
最近更新 更多