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

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

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

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

    unsigned int __stdcall test()
    {
      return 42;
    }
    

    稍后我会:

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

    在这种情况下,我不能让C++ STATICECAST强制工作(它会导致编译器错误)…因此,C型演员,但这是除此之外的要点…我也尝试过使用参考和测试,但没有任何帮助。

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

    0XE9,0XBC,0X18,0X00,0X00,0XCC…

    这显然是错误的,0xe9是一个相对的跳转指令,从0xbc字节开始查找,如下所示:

    0xcc、0xcc、0xcc…

    也就是说,对于未分配或未使用的内存,内存初始化为调试断点操作码。

    正如我所期望的,返回42的函数应该是:

    0x8b、0x2a、0x00、0x00、0x00、0x3

    或者至少有一些MOV的味道,后面跟着一个ret(0xc2、0xc3、0xca或0xcb),再往下一点。

    MSVC++是采取措施阻止我出于安全原因做这类事情,还是我做了一些愚蠢的事情却没有意识到?使用DMC作为编译器,这种方法似乎可以很好地工作…

    我也很难换一种方式(执行字节),但我怀疑根本原因是相同的。

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

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

    我只能猜测,但我很确定您正在检查调试版本。 在调试模式下,MSVC++编译器通过调用跳转存根来替换所有调用。这意味着,每个函数都从跳转到实际函数开始,这正是您所面对的。
    周围的0xcc字节实际上是断点指令,以便在不应该执行代码的情况下触发可能附加的调试器。
    在发布版本中也尝试同样的方法。这应该如预期的那样有效。

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

        2
  •  2
  •   Rob K    15 年前

    你想要的演员:

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

    在Project & Gt中,将调试信息格式从“程序数据库编辑和继续(/ZI)”切换到“程序数据库(/ZI)”。我相信正是这种设置导致编译器插入跳转代码,以便调试器可以在程序运行时重建函数并对其进行热修补。也可能关闭“启用最小重建”。

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

        3
  •  1
  •   Crashworks    15 年前

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

    我不确定定义的行为是什么,用于将函数指针强制转换为字节流——它甚至可能无法正常工作——但另一个可能的困惑是,x86函数的大小都是可变的,而且其结尾也很小。

        4
  •  1
  •   MSN    15 年前

    如果这是在启用增量链接的情况下,那么您看到的是 jmp [destination] . 您可以运行调试器并查看反汇编也要验证什么。