我需要帮助弄清楚此C代码的作用

问题描述 投票:0回答:1

我从Ghidra获得了这段C代码,但我不太清楚它在做什么。我怀疑有某种根源吗?

传入的两个arg是平方和(有时是2,有时是3个项),还有一个额外的值,例如0x18、0x10或0(有时不存在此arg!)

uint FUN_80059070(uint param_1,uint param_2)

{
  uint uVar1;
  uint uVar2;
  uint uVar3;
  uint uVar4;
  uint uVar5;
  uint uVar6;

  uVar5 = 0;
  uVar4 = 1;
  uVar6 = 0;
  uVar3 = 1 << (param_2 & 0x1f);
  while ((uVar3 < param_1 && (uVar3 << 2 != 0))) {
    uVar4 = uVar4 + 1;
    uVar3 = uVar3 << 2;
  }
  uVar1 = 1 << (uVar4 + (param_2 - 1) & 0x1f);
  while (uVar3 != 0) {
    uVar2 = uVar5 << (uVar4 & 0x1f);
    if ((int)uVar4 < 0) {
      uVar2 = uVar5 >> (-uVar4 & 0x1f);
    }
    uVar2 = uVar2 + uVar6 + uVar3;
    if (uVar2 <= param_1) {
      uVar5 = uVar5 + uVar1;
      uVar6 = uVar2;
    }
    uVar1 = uVar1 >> 1;
    uVar3 = uVar3 >> 2;
    uVar4 = uVar4 - 1;
  }
  return uVar5;
}
c function reverse-engineering ghidra
1个回答
1
投票

理解代码的一种不错的方法是重构它。

首先,创建一个测试功能和几个测试用例。然后,您重写该函数。它看起来像这样。它非常简单,对于较大的重构,我将使其变得更加复杂。

bool test(uint param_1, uint param_2) 
{
    return (FUN_80059070(param_1, param_2) == my_func(param_1, param2));
}

int main()
{
    uint test_cases[3][2] = { {0,0}, {8, 12}, {12, 14}};

    for(int i=0; i<3; i++) {
        if(! test(test_cases[i][0], test_cases[i][1])) {
             printf("Case %d with values %d and %d failed\n", 
                     i, test_cases[i][0], test_cases[i][1]);
             exit(EXIT_FAILURE);
        }
    }

    printf("All tests passed\n");
}

由于您已经知道参数的条件,请考虑编写一个代码片段为您创建测试用例。创建许多测试用例,但是要注意溢出的风险。

之后,您可以开始重构过程。首先将FUN_80059070的整个主体复制到my_func,然后替换行和代码块。

例如,首先通过谷歌搜索和测试不同的值来研究1 << (param_2 & 0x1f);的实际作用。当您了解它的作用后,就可以创建一个函数。

uint describing_name(uint x) { return (x & 0x1f); }

并将初始化uVar3的行更改为

uVar3 = 1 << describing_name(param_2);

然后走一小步。例如,uVar3 << 2等效于uVar * 4,但后者更易于阅读。在更一般的情况下,x << yx * pow(2,y)相同。请注意,pow具有签名double pow(double, double),因此可以强制转换或编写您自己的整数变量。

然后迭代遍历代码并每次都运行测试。如果代码的某些部分特别棘手,则可以或当然为该功能创建带有适当测试用例的单独测试。

[请注意,用<<替换pow不一定总是有意义的。有时它们被用于位操作,有时也被用作更快的乘法。对于编译器性能较差或没有优化器的编译器,其性能可能会产生巨大差异。在那些情况下,用pow替换它们是有意义的,但在其他情况下,<<可用于除去最高有效位。

例如,我不知道系统上有多少位uint,但是如果设置除15位最低有效位以外的所有位,x & 0x1f将返回获得的数字。大小端在这里可能会很重要。我不这么认为,但是我不确定。如果我是正确的,则x & 0x1fx % 32相同,这是模运算。这也是常见的优化。位移比乘法和模运算要快得多。因此,我们可以将功能describing_name重命名为modulo32

if((int)uVar4 < 0)基本上是一种“聪明”的方法,用于检查最高位是否被置位,或者uVar4所包含的数字是否大于signed int所代表的数字。两种解释是等效的。

现在看起来像这样:

uint modulo32(uint x) { return (x & 0x1f); }

bool larger_than_INT_MAX(uint x) { return (int)x<0; }

uint my_func(uint param_1, uint param_2)
{
         uint uVar1, uVar2, uVar3, uVar4, uVar5, uVar6;

         uVar5 = 0;
         uVar4 = 1;
         uVar6 = 0;
         uVar3 = powi(2, modulo32(param_2));
         while ((uVar3 < param_1 && (uVar3 * 4 != 0))) {
                 uVar4 = uVar4 + 1;
                 uVar3 = uVar3 * 4;
         }

         uVar1 = powi(2, uVar4 + (modulo32(param_2-1)));
         while (uVar3 != 0) {
                 uVar2 = uVar5 * powi(2, modulo32(uVar4));
                 if (larger_than_INT_MAX(uVar4)) {
                         uVar2 = uVar5 / powi(2, -uVar4);
                 }

                 uVar2 = uVar2 + uVar6 + uVar3;
                 if (uVar2 <= param_1) {
                         uVar5 = uVar5 + uVar1;
                         uVar6 = uVar2;
                 }
                 uVar1 = uVar1 / 2;
                 uVar3 = uVar3 / 4;
                 uVar4 = uVar4 - 1;
         }
         return uVar5;
}

powi是我编写的简单整数幂函数。上面的代码仍然不是很容易理解,但是至少怪异的位操作和“聪明的”代码部分不受影响。

现在我注意到了一些。 uVar3 * 4 != 0作为数学运算实际上没有意义,因为这仅对uVar3 == 0成立。但这是要检查除两个最高有效位以外的所有位是否为零。因此,您可以使用以下功能替换它:

// Assuming 16-bit system
bool fourteen_least_significant_bits_are_not_zero(uint x) {
    return x << 2 != 0;
}

当您更了解代码的实际含义时,请替换函数名称或使用注释。当您知道它们的用途时,也请替换漂亮的匿名名称uVar1uVar2等。

此后,我建议尝试重命名此功能:

void describing_name(uint *uVar3p, uint *uVar4p, uint param_1)
         // These are declared so that you can just copy paste the code
         uint uVar3 = *uVar3p;
         uint uVar4 = *uVar4p;

         // Copy paste with no modifications
         while ((uVar3 < param_1 && 
                 fourteen_least_significant_bits_are_not_zero(uVar3)) {
                 uVar4 = uVar4 + 1;
                 uVar3 = uVar3 * 4;
         }

         // And write back the values
         *uVar3p = uVar3;
         *uVar4p = uVar4;
}

将while循环替换为:

describing_name(&uVar3, &uVar4, param_1);

重构代码通常是理解它的最佳方法。请记住,重构时测试至关重要。

© www.soinside.com 2019 - 2024. All rights reserved.