当使用结构体时,我将如何将编译器的以下汇编代码翻译为C语言?

3
假设我定义了一个新的struct
struct s {
   int *x;
   struct {
      short sh[2];
      int i;
   } w;
   struct s *next;
};

另外,我编写了一个函数来初始化它:

void init_s(struct s *ss) {
   ss->w.sh[1] = /* Line 1 */;
   ss->x = /* Line 2 */;
   ss->next = /* Line 3 */;
}

编译器为init_s生成了以下汇编代码:
init_s:             # line 1
   movw 8(%rdi), %ax    # line 2
   movw %ax, 10(%rdi)   # line 3
   leaq 12(%rdi), %rax  # line 4
   movq %rax, (%rdi)    # line 5
   movq %rdi, 16(%rdi)  # line 6
   retq                 # line 7

我试图根据汇编代码填写init_s的缺失代码行。我已经弄清楚了第1行和第2行(或者至少我认为是这样)。第1行应该是ss->w.sh[0],第2行应该是&(ss->w.sh[2])。但是,我在第3行遇到了问题。我认为根据汇编代码,它应该是&(ss->x),但我觉得这是不正确的,并且不确定为什么会这样。任何反馈或建议都将非常感谢,有助于我更多地了解汇编和结构体。

我理解你的 init_s 只是一个占位符示例,但也许你应该展示一下你用于生成汇编代码的实际编译代码? - AKX
@AKX:我认为这个想法是对init_s进行逆向工程。 - Nate Eldredge
没错,我没有意识到第一部分是假设的。 - AKX
1个回答

5

第1行应该是ss->w.sh[0]

我同意。

第2行应该是&(ss->x)

地址没问题,但ss->w.sh只有2个元素,所以w.sh[2]越界了。实际上这是指向结构体的下一个成员的指针,即ss->x = &(ss->w.i)。这也可以解释为什么ss->x成员是int *而不是short *

然而,第3行让我感到困惑。根据汇编代码,我认为应该是&(ss->x)

类似的问题:虽然%rdi可能指向ss->x,但将类型为int **&ss->x赋给类型为struct s *ss->next是不合理的。你也可以把%rdi看作是指向结构体*ss本身的指针,这更加合理:ss->next = ss;。这创建了一个只有一个节点的循环链表,其next指向自身。

这里的道理是,在C语言中可以用不同的方式引用相同的地址,所有这些方式都会生成相同的汇编代码,你必须运用常识来推测作者更可能想要哪一种方式。理论上讲,C代码的作者写的可能是ss->next = (struct s *)&(ss->x);作为第三行——我们无法证明他们没有——但ss->next = ss;更加合理,因此更有可能是这样写的。

这就是为什么逆向工程既是一门艺术也是一门科学。


1
谢谢您澄清您的理由。现在把它看作链表就更有意义了。我也没有意识到当我写问题时w.sh的索引超出了范围。对于我这样没有C和指针经验的人来说,结构体有点奇怪。 - John Issacs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接