#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]
这是两个值 count1 和 count2 被传递到加法函数的地方。基本上,我想知道当它们被传递到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
那么,在给出非易失性反汇编的情况下,当反转时,我怎么知道发生了一些加法呢?正如你所说的,它已经优化了,但我该如何去发现它呢?(如果我是没有源代码的逆向工程)也许没有一个直接的答案,它更多的是关于分解所有东西,或者也许有什么东西可以在反汇编器中删除这些优化?
1条答案
按热度按时间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。**编译+反编译会丢失关于程序结构的信息,很明显,不仅仅是变量名和注解。**所有常量在它们被使用的地方都变成了硬编码的数字,所以没有办法分辨两个具有相同值的东西是巧合还是两次使用同一个命名的常量。