代码之家  ›  专栏  ›  技术社区  ›  jheriko

MSVC中检查字节码时出现问题++

  •  2
  • jheriko  · 技术社区  · 16 年前

    我一直在工作中摆弄免费的数字火星编译器(我知道这很淘气),并创建了一些代码来检查编译后的函数,并出于学习目的查看字节码,看看我是否能从编译器构建函数的方式中学到任何有价值的东西。然而,在MSVC++中重新创建相同的方法失败了,我得到的结果非常令人困惑。我有一个这样的函数:

    unsigned int __stdcall test()
    {
      return 42;
    }
    

    然后我做了:

    unsigned char* testCode = (unsigned char*)test;
    

    在这种情况下,我似乎无法让C++static_cast工作(它抛出了一个编译器错误)。..因此有C风格的演员阵容,但这不是重点。..我也尝试过使用参考和;测试,但这无济于事。

    现在,当我检查testCode指向的内存内容时,我感到很困惑,因为我看到的内容甚至看起来都不是有效的代码,甚至有一个调试断点卡在那里。..它看起来像这样(目标是IA-32):

    0xe9、0xbc、0x18、0x00、0x00、0xcc。..

    这显然是错误的,0xe9是一个相对跳转指令,从0xbc字节看,它看起来像这样:

    0xcc,0xcc,0xFC。..

    即,对于未分配或未使用的内存,按照预期将内存初始化为调试断点操作码。

    正如我所期望的,函数返回42的结果如下:

    0x8b、0x2a、0x00、0x00、0x00、0xc3

    或者至少有一些mov的味道,然后再往下一点ret(0xc2、0xc3、0xca或0xcb)

    MSVC++是否出于安全原因采取措施阻止我做这种事情,还是我做了一些愚蠢的事情却没有意识到?使用DMC作为编译器,这种方法似乎运行良好。..

    我也遇到了另一种问题(执行字节),但我怀疑根本原因是一样的。

    任何帮助或提示将不胜感激。

    4 回复  |  直到 16 年前
        1
  •  2
  •   newgre    16 年前

    我只能猜测,但我确信您正在检查调试版本。 在调试模式下,MSVC++编译器将所有调用替换为跳转存根的调用。这意味着,每个函数都从跳转到实际函数开始,这正是你在这里面临的。
    周围的0xCC字节确实是断点指令,以便在执行不应该执行的代码时启动可能附加的调试器。
    在发布版本中尝试同样的操作。这应该像预期的那样起作用。

    编辑: 这实际上受到链接器设置/INCREMENTAL的影响。您所描述的效果在发布版本中没有出现的原因是,如果启用了任何类型的优化,这些跳转存根就会被简单地优化掉(当然,发布版本通常都是这种情况)。

        2
  •  2
  •   Rob K    16 年前

    对于您想要的演员阵容:

    unsigned char* testCode = reinterpret_cast<unsigned char*>( test );
    

    从“程序数据库”切换调试信息格式以进行编辑和;在项目中继续(/ZI)到“程序数据库(/ZI)”->;属性->C/C++->;将军。我相信正是这种设置导致编译器插入跳转代码,以便调试器可以在程序运行时重建函数并对其进行热补丁。可能还会关闭“启用最小重建”。

    在MSVC中检查代码的一种更简单的方法是简单地设置一个断点并检查反汇编(右键单击该行并从弹出菜单中选择“Goto反汇编”)。它用源代码注释反汇编,这样你就可以看到每一行被编译成什么。

        3
  •  1
  •   Crashworks    16 年前

    如果你想查看给定编译函数的汇编和机器代码,那么向编译器提供/FAcs命令行选项并查看随后的.asm文件会更容易。

    我不确定将函数指针转换为字节流的定义行为是什么——它甚至可能无法正常工作——但另一个可能的混淆来源是x86函数都是可变大小和小字节序的。

        4
  •  1
  •   MSN    16 年前

    如果启用了增量链接,那么您看到的是 jmp [destination] 。您也可以运行调试器并查看要验证的反汇编内容。

    推荐文章