gpt4 book ai didi

c++ - 我可以控制特定的VC++编译器优化吗?

转载 作者:搜寻专家 更新时间:2023-10-31 01:31:50 25 4
gpt4 key购买 nike

我正在一个引用另一个二进制函数的项目中。为了获得上述功能,我需要通过模式匹配/扫描以编程方式找到它们。为了简化此过程,我在一个单独的项目中编译了部分代码,并在反汇编程序中比较两个项目之间的内容,以提取所需的字节信息以扫描功能。

但是,我碰到了VC++的优化正在阻碍某些事情的地步,我想知道是否有一种方法可以禁用每条指令或类似指令的特定优化。

例如,这是目标二进制文件的功能的一部分:

push    ebp
mov ebp, esp
push ecx
push [ebp+arg_4]
push [ebp+arg_0]
call sub_00000000
add esp, 8
cmp dword ptr [eax+4], 3

而功能的一部分来自我个人对代码的编译:

push    ebp
mov ebp, esp
push ecx
push [ebp+arg_4]
push [ebp+arg_0]
call sub_00000000
pop ecx
pop ecx
cmp dword ptr [eax+4], 3

为了获得使代码接近的代码,我不得不使用特定的项目设置,并且以其他任何方式修改它们会产生截然不同的输出。我的问题是由于启用了最少的代码优化,堆栈对齐方式已从“添加”指令转换为“弹出”指令。我无法更改此设置,否则将获得完全不同的输出。

有什么方法可以专门禁用'add esp,??'从转换成适当数量的持久性有机污染物?

最佳答案

I am working on a project that references another binaries functions. In order to obtain said functions, I need to find them programmatically via pattern matching/scanning.



为什么?如果二进制文件足够稳定,可以依靠模式匹配/扫描,那么二进制文件足够稳定,可以仅对地址进行硬编码。当二进制文件重新编译时,这两种方法都可能会更改,因此它们都同样脆弱。只需提前分析二进制文件,找出所需函数的地址,然后直接调用它们即可。它也会更快。

I am wondering if there is a way to disable specific optimizations per-instruction or similar.



不,没有。 The optimization settings的粒度非常宽松。您可以指定是否要优化空间或时间,可以指定最小可用指令集,还可以(在32位版本上)指定是否删除帧指针。基本上就是这样。还有其他一些优化设置(很显然是 according to Ross Ridge,一些我没有很熟悉的未记录选项),但是这里没有一个相关的设置。

从提供的样本反汇编来看,似乎要控制的是在 __cdecl函数调用之后通过一系列 pop或仅通过 add将适当数量的字节分配给堆栈指针来清理堆栈。

根据我的测试,当只有一个或两个参数时,MSVC在优化大小( pop)时会更喜欢 /O1来清理堆栈。如果有两个以上的参数,或者无论参数数量如何,都在优化速度( /O2)时,都将首选 add esp, x。这适用于所有版本的Visual Studio(我明确测试了VS 6,VS 2005,VS 2008,VS 2010,VS 2013和VS 2015)。这实际上是很合理的。 pop是1字节指令,而 add esp, x是3字节指令。因此,只要您执行2个或更少的 pop,即比 add小1个字节。一旦执行了三个 pop,它的大小与 add相同,但是 add会更快,所以会获胜。

现在,对我来说没有意义的是如何让编译器输出问题中所示的汇编代码。我尝试了几种不同的方法对它进行反向工程,将其转换为等效的C代码,但是我从未成功。问题是MSVC永远不会重复使用堆栈上剩余的参数。

查看代码,我们有:

push    ebp
mov ebp, esp


这是标准的序言代码,您必须在不删除框架指针( /Oy- )的情况下进行彻底放弃。

push    ecx
push [ebp+arg_4]
push [ebp+arg_0]
call sub_00000000


如果此时 ecx在函数中有意义,则它可能是C++类的成员函数,该类遵循 __thiscall调用约定,并在 this中传递 ecx指针。由于将 ecx压入堆栈,因此您必须调用一个以类对象指针作为第三个参数的自由函数。就像是:
RetVal sub_00000000(int arg_0, int arg_4, CClass* pClass);

这可以正常工作,并且我可以轻松地生成可生成类似程序集输出的代码。问题是,我永远无法获得以下指示:

add     esp, 8


因为三个参数被压入堆栈。那是12个字节,而不是8个字节,因此此代码应从堆栈中清除12个字节。仅清除8个字节才有意义的唯一方法是,如果您正在调用以 pClass作为其唯一参数的第二个函数。但是,就像我说的那样,即使MSVC知道它们是 const,也不会重复使用已经被压入堆栈的参数。它总是从头开始,重新推送第二个函数调用的参数,因此您将看到类似以下内容:
push    ebp              ; \  prologue
mov ebp, esp ; / code
push esi ; preserve esi
mov esi, ecx ; make copy of ecx
push esi ; \
push [ebp+arg_4] ; | push parameters for 1st function call
push [ebp+arg_0] ; /
call sub_00000000
push esi ; push parameters for 2nd function call
call sub_00000001
add esp, 16 ; clean stack
...

无论如何,如果发布您正在查看的原始C代码,可能会更容易。这将使我更容易尝试在具有不同编译选项的不同版本的编译器上进行编译,以查看是否可以重现一些内容。

这是一个有趣的观察。如果我们有如下代码:
struct Foo
{
bool Caller(int one, int two);
};

struct RetStruct
{
unsigned long lo;
unsigned long hi;
};

RetStruct* Callee(int one, int two, Foo* pFoo);

bool Foo::Caller(int one, int two)
{
return (Callee(one, two, this)->hi == 3);
}

并使用 /O1 /Oy-在VS 2010(或更旧的版本)上对其进行编译(针对大小进行了优化,而没有忽略帧指针),我们得到:
push   ebp
mov ebp, esp
push ecx
push DWORD PTR _two$[ebp]
push DWORD PTR _one$[ebp]
call Callee
xor ecx, ecx
add esp, 12
cmp DWORD PTR [eax+4], 3
...

忽略那些虚假的 xor ecx, ecx,它看起来非常接近您的原始代码。好吧,除了我已经指出的有关如何仅从堆栈中清除8个字节没有任何意义的事情。

/O1切换为 /O2(即优化速度而不是大小),我们得到:
push   ebp
mov ebp, esp
mov eax, DWORD PTR _two$[ebp]
push ecx
mov ecx, DWORD PTR _one$[ebp]
push eax
push ecx
call Callee
xor edx, edx
add esp, 12
cmp DWORD PTR [eax+4], 3
...

发生了什么变化?好吧,再次,忽略了虚假的 xor edx, edx,改变的是值先被加载到寄存器中,然后被推送,而不是被推送到内存操作数。由于某种原因,MSVC认为这样做速度更快,因此值得增加代码大小。

但是,在较新版本的MSVC上会发生什么?在VS 2013和VS 2015上,我们都为 /O1/O2获得相同的输出:
push   ebp
mov ebp, esp
push ecx
push DWORD PTR _two$[ebp]
push DWORD PTR _one$[ebp]
call Callee
add esp, 12
cmp DWORD PTR [eax+4], 3
...

我们还会丢失伪造的 xor。 :-)但真正重要的是,我们现在可以优化速度( /O2),以获得所需的堆栈清洁行为( add而不是 pop + pop),而无需更改将参数压入堆栈的方式。

因此,我想说的是,原始二进制文件可能是使用Visual Studio的最新版本(VS 2013或VS 2015)编译的,相关的选项开关为 /O2 /Oy-。尝试一下,看看它没有满足您的需求。

关于c++ - 我可以控制特定的VC++编译器优化吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44731590/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com