【问题标题】:Visual C++ appends 0xCC (int3) bytes at the end of functionsVisual C++ 在函数末尾追加 0xCC (int3) 字节
【发布时间】:2013-08-08 01:23:45
【问题描述】:

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

我已经搜索了几个小时的答案,但找不到实际可行的答案。

我想直接将代码注入正在运行的进程中。是的,你没看错。我正在尝试将代码注入另一个应用程序,而且 - 不管你信不信 - 这只是为了扩展它的功能。

我在 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 问题中,据说禁用“编辑并继续”会使这些额外的字节消失,但无论如何,这对我不起作用。

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

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

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

感谢您的时间和努力。

【问题讨论】:

    标签: c++ windows assembly visual-c++ machine-code


    【解决方案1】:

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

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

    出于您的目的,这些都不应该让您担心。您可以将 0xCC 填充视为函数的一部分。

    【讨论】:

      【解决方案2】:

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

      但如果你真的想去掉它,解决问题的一个简单方法是从下一个函数之前的最后一个字节向后迭代,直到没有更多的 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;
      }
      

      【讨论】:

      • 尽管机会很低,但我不能确定后面的最后一个 0xCC(就在其他东西之前)不是实际操作码,可以吗?
      • @skreborn,如果这是一个问题,那么请确保您的汇编代码以已知的操作码结尾并丢弃它。
      • @Mark 谢谢,实际上考虑过。原来我并没有我想的那么傻。我现在也只是复制额外的字节,这不是太大的开销。
      【解决方案3】:

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

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

      【讨论】:

      • @Devouls 但其他人使用完全相同的方法。这真的是我无法定制的吗?其他编译器(你知道的)会这样做吗?我曾经注入字节数组,但每次我必须更改某些内容时都会带来很多不必要的麻烦。
      猜你喜欢
      • 2013-03-21
      • 1970-01-01
      • 2023-03-02
      • 2019-05-30
      • 2013-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      相关资源
      最近更新 更多