assembly 使用Ghidra的x86 ASM,了解内联函数调用的反编译器结果

thigvfpy  于 2023-05-07  发布在  其他
关注(0)|答案(1)|浏览(210)
#include <iostream>

int addition(int a, int b) {
    int funcA = a;
    int funcB = b;
    return funcA + funcB;
};

int main() {
    volatile int count1 = 8;

    volatile int count2 = 9;

    volatile int anotherRandomVar = 0;

    volatile int result = addition(count1, count2);

    std::printf("main func is here");

    return 0;
}
**************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             int __cdecl main(int _Argc, char * * _Argv, char * * _Env)
                               assume GS_OFFSET = 0xff00000000
             int               EAX:4          <RETURN>
             int               ECX:4          _Argc
             char * *          RDX:8          _Argv
             char * *          R8:8           _Env
             undefined4        Stack[0x18]:4  local_res18                             XREF[1]:     14000108b(W)  
             undefined4        Stack[0x10]:4  local_res10                             XREF[2]:     140001074(W), 
                                                                                                   140001097(R)  
             undefined4        Stack[0x8]:4   local_res8                              XREF[2]:     140001083(W), 
                                                                                                   140001093(R)  
                             main                                            XREF[2]:     __scrt_common_main_seh:1400012cb
                                                                                          14000400c(*)  
       140001070 48 83 ec 28     SUB        RSP,0x28
       140001074 c7 44 24        MOV        dword ptr [RSP + local_res10],0x8
                 38 08 00 
                 00 00
       14000107c 48 8d 0d        LEA        _Argc,[s_main_func_is_here]                      = "main func is here"
                 cd 11 00 00
       140001083 c7 44 24        MOV        dword ptr [RSP + local_res8],0x9
                 30 09 00 
                 00 00
       14000108b c7 44 24        MOV        dword ptr [RSP + local_res18],0x0
                 40 00 00 
                 00 00
       140001093 8b 44 24 30     MOV        EAX,dword ptr [RSP + local_res8]
       140001097 8b 44 24 38     MOV        EAX,dword ptr [RSP + local_res10]
       14000109b e8 70 ff        CALL       printf                                           int printf(char * _Format, ...)
                 ff ff
       1400010a0 33 c0           XOR        EAX,EAX
       1400010a2 48 83 c4 28     ADD        RSP,0x28
       1400010a6 c3              RET

这是原始的ASM输出,我已经分配了正确的类型并正确命名了它们,现在有

**************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             int __cdecl main(int _Argc, char * * _Argv, char * * _Env)
                               assume GS_OFFSET = 0xff00000000
             int               EAX:4          <RETURN>
             int               ECX:4          _Argc
             char * *          RDX:8          _Argv
             char * *          R8:8           _Env
             int               Stack[0x18]:4  anotherRandomVar                        XREF[1]:     14000108b(W)  
             int               Stack[0x10]:4  count1                                  XREF[2]:     140001074(W), 
                                                                                                   140001097(R)  
             int               Stack[0x8]:4   count2                                  XREF[2]:     140001083(W), 
                                                                                                   140001093(R)  
                             main                                            XREF[2]:     __scrt_common_main_seh:1400012cb
                                                                                          14000400c(*)  
       140001070 48 83 ec 28     SUB        RSP,0x28
       140001074 c7 44 24        MOV        dword ptr [RSP + count1],0x8
                 38 08 00 
                 00 00
       14000107c 48 8d 0d        LEA        _Argc,[s_main_func_is_here]                      = "main func is here"
                 cd 11 00 00
       140001083 c7 44 24        MOV        dword ptr [RSP + count2],0x9
                 30 09 00 
                 00 00
       14000108b c7 44 24        MOV        dword ptr [RSP + anotherRandomVar],0x0
                 40 00 00 
                 00 00
       140001093 8b 44 24 30     MOV        EAX,dword ptr [RSP + count2]
       140001097 8b 44 24 38     MOV        EAX,dword ptr [RSP + count1]
       14000109b e8 70 ff        CALL       printf                                           int printf(char * _Format, ...)
                 ff ff
       1400010a0 33 c0           XOR        EAX,EAX
       1400010a2 48 83 c4 28     ADD        RSP,0x28
       1400010a6 c3              RET

一旦我正确地应用了一个类型,它就从反编译器中给了我这个,所以我基本上认为反编译器不是很有帮助?

int __cdecl main(int _Argc,char **_Argv,char **_Env)

{
  int count2;
  int count1;
  int anotherRandomVar;
  
  printf("main func is here");
  return 0;
}

因为在我应用类型之前反编译的代码是空的。
我的主要问题是关于这几行:

140001093 8b 44 24 30     MOV        EAX,dword ptr [RSP + count2]
       140001097 8b 44 24 38     MOV        EAX,dword ptr [RSP + count1]

这是两个值 count1count2 被传递到加法函数的地方。基本上,我想知道当它们被传递到EAX时,会发生什么?我最初的想法是,函数在此之后以某种方式调用,并查看EAX寄存器中的前2个条目?基本上,事情的顺序似乎很重要。当这两个值被移到这个寄存器中时,它们会立即被消耗,或者我可能错误地将EAX与堆栈的操作方式混淆了?我假设它只是优化了这里的函数只是做了一个加法,但我想看看这个加法发生在哪里。
结果volatile int result = addition(count1, count2);存储在哪里?

编辑1

我在x86发布模式下编译并构建了EXE,而不是x64,现在ASM似乎是正确的:

**************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             int __cdecl main(int _Argc, char * * _Argv, char * * _Env)
                               assume FS_OFFSET = 0xffdff000
             int               EAX:4          <RETURN>
             int               Stack[0x4]:4   _Argc
             char * *          Stack[0x8]:4   _Argv
             char * *          Stack[0xc]:4   _Env
             undefined4        Stack[-0x8]:4  local_8                                 XREF[2]:     0040104d(W), 
                                                                                                   0040105b(R)  
             undefined4        Stack[-0xc]:4  local_c                                 XREF[2]:     00401046(W), 
                                                                                                   0040105e(R)  
             undefined4        Stack[-0x10]:4 local_10                                XREF[2]:     00401054(W), 
                                                                                                   00401068(W)  
                             _main                                           XREF[1]:     __scrt_common_main_seh:00401241(
                             main
        00401040 55              PUSH       EBP
        00401041 8b ec           MOV        EBP,ESP
        00401043 83 ec 0c        SUB        ESP,0xc
        00401046 c7 45 f8        MOV        dword ptr [EBP + local_c],0x8
                 08 00 00 00
        0040104d c7 45 fc        MOV        dword ptr [EBP + local_8],0x9
                 09 00 00 00
        00401054 c7 45 f4        MOV        dword ptr [EBP + local_10],0x0
                 00 00 00 00
        0040105b 8b 4d fc        MOV        ECX,dword ptr [EBP + local_8]
        0040105e 8b 45 f8        MOV        EAX,dword ptr [EBP + local_c]
        00401061 03 c1           ADD        EAX,ECX
        00401063 68 00 21        PUSH       s_main_func_is_here                              = "main func is here"
                 40 00
        00401068 89 45 f4        MOV        dword ptr [EBP + local_10],EAX
        0040106b e8 a0 ff        CALL       printf                                           int printf(char * _Format, ...)
                 ff ff
        00401070 83 c4 04        ADD        ESP,0x4
        00401073 33 c0           XOR        EAX,EAX
        00401075 8b e5           MOV        ESP,EBP
        00401077 5d              POP        EBP
        00401078 c3              RET

编辑2

int result被修改为volatile时,我似乎也忘记了重新编译,所以现在的反汇编是:

**************************************************************
                             *                          FUNCTION                          *
                             **************************************************************
                             int __cdecl main(int _Argc, char * * _Argv, char * * _Env)
                               assume GS_OFFSET = 0xff00000000
             int               EAX:4          <RETURN>
             int               ECX:4          _Argc
             char * *          RDX:8          _Argv
             char * *          R8:8           _Env
             int               Stack[0x18]:4  local_res18                             XREF[1]:     140001084(W)  
             int               Stack[0x10]:4  local_res10                             XREF[2]:     140001074(W), 
                                                                                                   140001090(R)  
             int               Stack[0x8]:4   local_res8                              XREF[3]:     14000107c(W), 
                                                                                                   14000108c(R), 
                                                                                                   140001096(W)  
                             main                                            XREF[2]:     __scrt_common_main_seh:1400012cb
                                                                                          14000400c(*)  
       140001070 48 83 ec 28     SUB        RSP,0x28
       140001074 c7 44 24        MOV        dword ptr [RSP + local_res10],0x8
                 38 08 00 
                 00 00
       14000107c c7 44 24        MOV        dword ptr [RSP + local_res8],0x9
                 30 09 00 
                 00 00
       140001084 c7 44 24        MOV        dword ptr [RSP + local_res18],0x0
                 40 00 00 
                 00 00
       14000108c 8b 4c 24 30     MOV        _Argc,dword ptr [RSP + local_res8]
       140001090 8b 44 24 38     MOV        EAX,dword ptr [RSP + local_res10]
       140001094 03 c8           ADD        _Argc,EAX
       140001096 89 4c 24 30     MOV        dword ptr [RSP + local_res8],_Argc
       14000109a 48 8d 0d        LEA        _Argc,[s_main_func_is_here]                      = "main func is here"
                 af 11 00 00
       1400010a1 e8 6a ff        CALL       printf                                           int printf(char * _Format, ...)
                 ff ff
       1400010a6 33 c0           XOR        EAX,EAX
       1400010a8 48 83 c4 28     ADD        RSP,0x28
       1400010ac c3              RET

那么,在给出非易失性反汇编的情况下,当反转时,我怎么知道发生了一些加法呢?正如你所说的,它已经优化了,但我该如何去发现它呢?(如果我是没有源代码的逆向工程)也许没有一个直接的答案,它更多的是关于分解所有东西,或者也许有什么东西可以在反汇编器中删除这些优化?

hts6caw3

hts6caw31#

在原始的asm源代码中,没有与volatile int result = addition(count1, count2);中的add或final赋值对应的指令。可能你实际编译的源代码省略了int result上的volatile,所以声明/语句可以优化掉,除了来自两个volatile read的两个MOV加载,它们不使用它们的结果,比如(void)count1; (void)count2;
volatile访问计数为可见的副作用,并将显示在asm中,但其他东西不会,除非编译器需要它来产生其他可见的结果。
我并不感到惊讶Ghidra没有显示局部变量的初始化器,也没有显示它们是volatile的。如果你在调试版本中使用它,你不会希望每个变量都显示为volatile,但是在-O0编译器中,比如gcc/clang/msvc/icc,所有的变量都是spill vars from registers between statements,类似于所有的变量都是volatile。因此,在反编译调试构建时,您会将每个局部变量错误地标识为volatile。反编译器试图识别导致函数外部可见内容的计算;存储/重新加载最终不会以其他方式使用的局部变量只是噪音。
我本来打算编辑你的问题标题来描述你所问的实际情况,比如“Ghidra反编译器无法恢复内联的函数调用”,但我认为任何能够用这些术语提出问题的人都已经明白这是一个答案。:/
即使这项工作没有被优化,它也无法与result = count1 + count2;区分开来。事实上,有一个内联的帮助函数在编译过程中完全丢失了。
https://godbolt.org/z/KaGa9axs1显示了MSVC 19.35 x64优化的代码生成器,用于在result上使用和不使用volatile的代码。它确实使用volatile添加和存储到result
考虑到非易失性拆卸,当反转时,我如何才能发现正在发生一些添加?正如你所说的,它已经优化了,但我该如何去发现它呢?(如果我是逆向工程,没有源代码)

你不会看到添加,它不存在于编译后的程序中,因为它不是原始C程序的可观察行为的一部分。(C定义了可观察行为,包括I/O和volatile访问。)这就是为什么它在编译时被优化了。-

这是有道理的,但是如果不存在于反汇编中,那么像常量值这样的东西是如何可逆的呢?是不是使用consts的工作已经完成,如果不再需要,就把它剥离掉,就像在这个例子中一样?如果是这样的话,看看我如何扭转它们的原点/知道它们的存在开始?-
反编译器无法判断源代码中的常量是如何编写的,例如。mmap(PROT_EXEC|PROT_WRITE|PROT_READ)mmap(0x111)是无法区分的,不管这些常数实际上是什么。一个了解一些系统调用位标志的智能反编译器可能会为您分解内容,就像strace所做的那样。
但在其他情况下,您只会得到像malloc(12)这样的东西,而不是malloc(3*sizeof(int))。同样,除非反编译器有模式可以根据它推断的类型发明一个sizeof。

**编译+反编译会丢失关于程序结构的信息,很明显,不仅仅是变量名和注解。**所有常量在它们被使用的地方都变成了硬编码的数字,所以没有办法分辨两个具有相同值的东西是巧合还是两次使用同一个命名的常量。

相关问题