【问题标题】:What does *(int32_t *)(a + 4) = b mean?*(int32_t *)(a + 4) = b 是什么意思?
【发布时间】:2019-12-22 20:40:42
【问题描述】:

我使用 retdec 反编译了一个 .so 文件(来自 Android 应用程序中的 ARM 库),在代码中我可以找到如下指令:

int32_t a = `some value`;
int32_t b = `another value`;
*(int32_t *)(a + 4) = b;

由于使用任何值运行它都会导致编译时出现警告,并且运行时出现分段错误,我不确定它的实际作用。

【问题讨论】:

  • 这意味着“获取a + 4的结果,将其视为指向int32_t的指针,取消引用该指针并将b的值分配给结果。根据您的编辑,它将存储在a 中的整数值视为一个地址,将该地址加4,并将b 的值分配给该结果地址处的对象,这……嗯?难怪它会爆炸。跨度>
  • @Dorinel Panaite 这是一个糟糕的代码。指针的大小可以等于 8。
  • @DorinelPanaite:我最喜欢的反编译描述是“把汉堡包变回奶牛”。如果这是一个糟糕的 C 语言翻译,我不会感到惊讶。
  • @Lundin:阅读问题的第一句话。它不是 GitHub 或库中的代码。它是反编译器的输出。
  • @VladfromMoscow:阅读问题的第一句话。代码是从反编译器输出的。除其他外,这意味着它不是可移植的代码;它专门与一个平台相关联,并且代表了某些机器代码的功能。人类编写的代码是“好”还是“坏”无关紧要。这根本不是这段代码的用途。此代码用于显示机器代码的作用,并且它正在这样做。

标签: c arm reverse-engineering decompiling


【解决方案1】:

由内而外的工作:

a + 4

a 的值,并在其上加上4,如果适用的话,遵循通常的算术转换。此表达式的等级至少为 int32_t

下一步:

(int32_t *)(a + 4)

意味着您获取这个新的整数值,并将其解释为指向int32_t 的指针。此表达式的类型为 int32_t *

更进一步,您将使用 * 运算符取消引用它:

*(int32_t *)(a + 4)

这会在地址a + 4 处给出int32_t 类型的左值(如典型变量)(此类地址的有效性将取决于实现)。

最后,您将b 中的值分配给此位置:

*(int32_t *)(a + 4) = b;

总而言之,这意味着您将int32_t b 的值作为int32_t 存储到a 值之后的内存位置4 中。

除非a + 4 碰巧指向一个有效的内存位置来存储int32_t(可能在其原始上下文中),否则这可能会导致程序行为异常。充其量,行为是实现定义的。在最坏的情况下,它是未定义的。

【讨论】:

    【解决方案2】:

    问题在于反编译器无法知道变量的类型。它只知道寄存器中有一些东西,堆栈中有一定大小的东西,并且以某种方式使用,所以它认为所有 32 位实体都是int32_t,即使它们在 ARM 上也可能是指针.甚至零扩展字符在寄存器中移动。

    在这种情况下,a 似乎不是整数,而是指向数组中元素的指针,或者可能是指向结构的指针,代码类似于

    int *a = something;
    int b = calculate_something();
    a[1] = b;
    

    或许

    struct foo *a = something;
    int b = calculate_something();
    a->second_member = b;
    

    我们不知道。所以反编译器能想到的最好的就是

    int32_t a = something;
    int32_t b = calculate_something();
    *(int32_t *)(a + 4) = b;
    

    即“哎呀,a + sizeof (int) 中的值现在应该用作指针,并将b 分配给该位置。”


    至于再次编译它 - 甚至不要梦想为任何其他平台而不是代码来源。

    【讨论】:

      【解决方案3】:

      这意味着机器代码的反编译不会产生原始源代码!我们以下面的代码 sn-p 为例。

      int a[5];
      int b;
      void somefunc(void)
      {
          a[1] = b;
      }
      

      它编译成这样的:

      somefunc:
              ldr     r2, =b       # Load the address of b
              ldr     r3, =a       # Load the address of a
              ldr     r2, [r2]     # Load the value in b 
              str     r2, [r3, #4] # Store value in b to a[1] or *(a + 4)
              bx      lr           # return
      

      现在,如果有人在不知道数组和任何其他上下文的情况下尝试将其逐行反编译成 C 代码,结果会像您发布的 sn-p 一样。

      str     r2, [r3, 4]  => *((int32_t *)r3 + 4) = r2
      

      可能还有许多其他的 C 代码 sn-ps 可以编译成完全相同的汇编序列。这就是为什么反编译远不是一门“精确的科学”!

      【讨论】:

        【解决方案4】:
        *(int32_t *)(a + 4) = b;
        

        简单来说,这意味着获取a+4 的值并将其视为int32_t 类型的变量所在的地址。在该地址存储b 的值。

        反编译不能总是产生准确的结果,因为这样的代码应该会崩溃,除非您在a+4int32_t 保留了内存位置。

        另外,我认为这是因为.so 是专门为32 位架构编写的代码的反编译版本,这就是它说类型int32_t 的原因。猜测一下,如果你提供 gcc-m32 标志,它“可能”会起作用,它要求它为 32 位架构编译代码。

        【讨论】:

          【解决方案5】:

          ARM cpu 是一种加载存储架构。它有如下的 store 形式,

          str rN, [rP, #4]
          

          这将获取寄存器rP(一个指针)的值并将其加四。 BUS 将使用寄存器rN 中的值向内存发出存储。你的反编译器似乎很简陋注意下面并将其翻译为,

          int32_t a = `some value`;      /* sets up pointer register `rP` */
          int32_t b = `another value`;   /* Initializes value `rN` */
          *(int32_t *)(a + 4) = b;       /* the instruction `str rN, [rP, #4]` */
          

          如果您查看 wiki,它会注意到编译为二进制会丢失信息。反编译器的目标是,如果你编译结果不变,它应该给出相同的二进制文件。

          由于代码试图复制相同的机器语言,因此代码永远无法移植。


          该工具的部分问题是,

          我已经反编译了一个 .so 文件(来自 Android 应用程序中的 ARM 库)

          编译共享库会生成一些奇怪的代码,以允许多个用户使用它们。使用的寄存器可能是非标准的,这不允许反编译器匹配主可执行文件中的 EABI 常规寄存器使用。

          我看了一眼,该工具似乎没有“-shared-library”反编译选项。我怀疑您正在反编译某种 thunk。即,一个 plt 或 got;见ARM Dynamic linking。这里是a question on shared library for the ARM;如果反编译器有一个-shared-library 选项,它可能需要一个操作系统(和版本)限定符。

          【讨论】:

          • 不是完全相同的二进制文件,但它应该编译成行为相同的代码
          • 如果它是相同的二进制文件,我认为开发人员不会感到不安 :) 但很可能它会像你建议的那样非常相似。实际上编译器(代码/版本)和标志会影响这一点。如果您以编译器的 RTL/etc 为模型,则应该可以生成二进制等效项。即,您知道确切的编译器并为用户提供特定的标志。一些代码将校验和本身或部分代码。产生相同的二进制文件至少是有用的。可能大多数真正的反编译器都无法做到这一点。
          猜你喜欢
          • 2017-02-02
          • 2013-12-16
          • 2014-02-09
          • 2012-06-14
          • 2018-08-06
          • 1970-01-01
          • 2022-11-10
          • 2014-01-16
          • 1970-01-01
          相关资源
          最近更新 更多