Visual C ++在函数末尾附加0xCC(int3)字节

时间:2013-08-06 20:55:31

标签: c++ windows visual-studio compiler-construction

这是我的第一次,我真的希望你们可以帮助我,因为我现在已经没有了想法。

我现在已经搜索了几个小时的答案,但找不到真正有效的答案。

我想直接将代码注入正在运行的进程中。是的,你已经读完了。我试图将代码注入另一个应用程序,并且 - 信不信由你 - 这只是为了扩展它的功能。

我在Windows上使用Visual Studio 2012 Express Edition。

我有以下代码:

__declspec(naked) void Foo()
{
    __asm
    {
        // Inline assembly code here
    }
}
__declspec(naked) void FooEnd() {}

int main()
{
    cout << HEX(Foo) << endl;
    cout << HEX(FooEnd) <<  endl;
    cout << (int)FooEnd - (int)Foo << endl;

    // Inject code here using WriteProcessMemory

    return 0;
}

为了保持可读性,大部分代码已被删除,但我可以根据请求发布其他部分代码。

输出如下:

0x010B1000
0x010B1010
16

结果大小实际上是不正确的。函数按正确的顺序编译(确保使用/ ORDER),但编译器在每个扩展它的大小的方法之后添加了一堆0xCC(int 3)字节,因此我无法获得真实(有用)数字包含实际可执行代码的字节数。

在另一个stackoverflow问题中,有人说禁用“编辑并继续”会使这些额外的字节消失,但无论如何,这对我都不起作用。

我也尝试使用Release setup而不是Debug,更改了一堆优化设置,但这些都没有任何效果。您认为解决方案是什么?我可能会遗漏一些明显的东西。

无论如何,这是(在您看来)获取函数长度的最佳方式(可读性,可靠性,易用性)?

我希望我解释了我所需要的一切,以便你能够提供帮助。如果您还有其他问题,请随时发表评论。

感谢您的时间和努力。

3 个答案:

答案 0 :(得分:4)

当您注入代码时,您不需要额外的填充,因此丢弃它们是可以的。复制它们也应该没问题,它只会导致一些额外的复制字节。无论如何,机会是你通过页面对齐的块注入的记忆,所以你通过剥离它并没有真正获得任何东西。

但是如果你真的想要删除它,那么解决问题的一个简单方法就是从下一个函数之前的最后一个字节向后迭代,直到没有更多的0xcc字节。

即:

__declspec(naked) void Foo()
{
   __asm
   {
      _emit 0x4A
      _emit 0x4B
   }
}
__declspec(naked) void FooEnd() {}


int main(int argc, char** argv)
{
   //start at the last byte of the memory-aligned code instead of the first byte of FooEnd
   unsigned char* fooLast = (unsigned char*)FooEnd-1;

   //keep going backwards until we don't have a 0xcc
   while(*fooLast == 0xCC)
      fooLast--;

   //fooLast will now point at the last byte of your function, so you need to add 1
   int length = ((int)fooLast - (int)Foo) + 1;

   //should output 2 for the length of Foo
   std::cout << length;
}

答案 1 :(得分:2)

正如Devolus指出的那样,编译器会在代码之后插入这些额外的字节,以便在合理的(通常可被16整除)起始地址上对齐下一个函数。

编译器实际上是试图帮助你,因为0xCC是断点指令,如果指令指针在执行期间的任何时刻意外地指向函数外,代码将进入调试器(如果附加)。

这些都不会让你担心。您可以将0xCC填充视为函数的一部分。

答案 2 :(得分:1)

编译器插入额外的字节以创建内存对齐,因此您不能丢弃它,因为您正在使用下一个函数作为标记。

另一方面,既然你是在汇编中编写注入的代码,你也可以编写代码,编译它,然后将二进制形式放在一个字节数组中。我就是这样做的,因为那样你就有了确切的长度。