如何反转使用 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}