在 x86 程序集中构建 COM 对象 vtable
我正在使用 NASM 在 x86 程序集中构建 COM 对象。我非常了解 COM,也非常了解 x86 程序集,但是让两者相互配合让我很困惑......(顺便说一句,如果您想尝试阻止我使用 x86 程序集,请不要这样做,我有非常特殊的原因为什么要在 x86 程序集中构建它!)
我试图构建一个 vtable 以在我的 COM 对象中使用,但我不断收到奇怪的指针,而不是指向我的函数的实际指针。 (我认为我正在获得相对偏移量,或者 NASM 正在其中嵌入临时值,并且它们在链接期间不会被实际值替换)
我正在尝试构建的当前接口是 IClassFactory 接口,代码如下:
%define S_OK 0x00000000
%define E_NOINTERFACE 0x80004002
section .text
; All of these have very simple shells rather than implementations, but that is just until I can get the vtable worked out
ClassFactory_QueryInterface:
mov eax, E_NOINTERFACE
retn 12
ClassFactory_AddRef:
mov eax, 1
retn 4
ClassFactory_Release:
mov eax, 1
retn 4
ClassFactory_CreateInstance:
mov eax, E_NOINTERFACE
retn 16
ClassFactory_LockServer:
mov eax, S_OK
retn 8
global ClassFactory_vtable
ClassFactory_vtable dd ClassFactory_QueryInterface, ClassFactory_AddRef, ClassFactory_Release, ClassFactory_CreateInstance, ClassFactory_LockServer
global ClassFactory_object
ClassFactory_object dd ClassFactory_vtable
注意:这不是全部代码,我在不同的文件中有DllGetClassObject、DllMain等。
但是当我组装(使用 NASM:nasm -f win32 comobject.asm
)并链接(使用 MS Link:link /dll /subsystem:windows /out:comobject.dll comobject.obj
),并使用 OllyDbg 检查可执行文件,vtable 出现奇怪的值。例如,在我的上一个版本中,函数的实际地址如下:
- QueryInterface - 0x00381012
- AddRef - 0x0038101A
- Release - 0x00381020
- CreateInstance - 0x00381026
- LockServer - 0x0038102E
但 vtable 带有以下值:
- QueryInterface - 0x00F51012
- AddRef - 0x00 F5101A
- 发布 - 0x00F51020
- CreateInstance - 0x00F51026
- LockServer - 0x00F5102E
这些值看起来非常可疑......几乎就像没有进行重定位一样。此外,vtable 显示为 0x00F5104A,所有这些都是不可访问的内存地址。 (为了提供信息,这些值每次都会不同)
我尝试使用 Visual Studio 2010 Express 在 C++ 中做同样的事情,一切都很好。所以我假设这只是我在程序集中缺少的东西......
谁能向我指出为什么这些值没有正确显示?
I am building a COM object in x86 assembly using NASM. I understand COM quite well and I understand x86 assembly pretty well, but getting the two to mesh is getting me hung up... (by the way, if you're thinking of attempting to dissuade me from using x86 assembly, please refrain, I have very particular reasons why I'm building this in x86 assembly!)
I am trying to build a vtable to use in my COM object, but I keep getting strange pointers, rather than actual pointers to my functions. (I'm thinking that I'm getting relative offsets or that NASM is embedding temporary values in there and they're not being replaced with the real values during linking)
The current interface I'm trying to build is the IClassFactory
interface, with code as follows:
%define S_OK 0x00000000
%define E_NOINTERFACE 0x80004002
section .text
; All of these have very simple shells rather than implementations, but that is just until I can get the vtable worked out
ClassFactory_QueryInterface:
mov eax, E_NOINTERFACE
retn 12
ClassFactory_AddRef:
mov eax, 1
retn 4
ClassFactory_Release:
mov eax, 1
retn 4
ClassFactory_CreateInstance:
mov eax, E_NOINTERFACE
retn 16
ClassFactory_LockServer:
mov eax, S_OK
retn 8
global ClassFactory_vtable
ClassFactory_vtable dd ClassFactory_QueryInterface, ClassFactory_AddRef, ClassFactory_Release, ClassFactory_CreateInstance, ClassFactory_LockServer
global ClassFactory_object
ClassFactory_object dd ClassFactory_vtable
Note: This is not all of the code, I have DllGetClassObject, DllMain, etc. in a different file.
But when I assemble (using NASM: nasm -f win32 comobject.asm
) and link (using MS Link: link /dll /subsystem:windows /out:comobject.dll comobject.obj
), and examine the executable using OllyDbg, the vtable comes out with strange values. For example, in my last build, the actual addresses for the functions are as follows:
- QueryInterface - 0x00381012
- AddRef - 0x0038101A
- Release - 0x00381020
- CreateInstance - 0x00381026
- LockServer - 0x0038102E
But the vtable came out with these values:
- QueryInterface - 0x00F51012
- AddRef - 0x00F5101A
- Release - 0x00F51020
- CreateInstance - 0x00F51026
- LockServer - 0x00F5102E
These values look awfully suspicious... almost like the relocation didn't take. Also, the vtable comes out as 0x00F5104A, all of which are inaccessible memory addresses. (for informational purposes, these values come out different every time)
I tried doing the same thing in C++ using Visual Studio 2010 Express and everything comes out fine. So I'm assuming that it's just something that I'm missing in my assembly...
Can anyone point out to me why these values aren't coming out properly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我必须道歉,这个问题原来是我自己的错......在构建这个东西的所有混乱中,我从链接器调用中删除了
/dll
,导致它被构建为EXE,不是 DLL...让我为下一个遇到这个问题的人更好地解释一下。
所有 Windows 可执行文件都有一个基地址,该地址被假定为可执行文件所在的虚拟地址加载到.在大多数情况下,加载到正在运行的进程中的可执行文件不会加载到“首选”基地址,因为另一个 DLL(或应用程序本身)可能已经占用了该地址。因此,Windows PE 可执行文件使用所谓的重定位表。 重定位表告诉Windows在重定位到可执行文件中的哪些位置需要重写新的基址。
然而,随着虚拟内存的出现,大多数链接器将省略 EXE 中的重定位表作为优化,因为可执行文件将始终在其基地址处加载(除非它与保留的内核地址冲突,在这种情况下它将无法全部加载)。因此,因为我停止编译为 DLL,所以我的可执行文件没有被赋予 重定位表 和结果,将无法正确加载到正在运行的进程的地址空间中。
更新:
默认情况下,MSVC 仅在 DLL 项目中包含重定位表,如 MSDN:
可以通过向链接器提供
/FIXED:NO
开关来更改此行为。非 DLL 项目的默认值是/FIXED
,它告诉链接器目标具有固定基地址并且不需要重定位表。I must apologize, the problem turned out to be my own fault... In all of the scuffle building the thing, I had removed the
/dll
from the linker invocation, causing it to be built as an EXE, not a DLL...Let me explain this a little better for the next person who runs across this.
All Windows executables have a base address which is assumed to be the virtual address that the executable will be loaded into. Executables that are loaded into a running process in most cases will not be loaded at the "preferred" base address, because another DLL (or the application itself) is probably already occupying the address. For this reason, Windows PE executables use what is called a Relocation Table. The Relocation Table tells Windows which locations in the executable need to be rewritten in case of a relocation to a new base address.
However, with the advent of Virtual Memory, most linkers will omit the relocation table from EXEs as an optimization, because the executable will always be loaded at it's base address (unless it conflicts with the reserved kernel addresses, in which case it will fail to load all-together). So because I stopped compiling as a DLL, my executable was not being given a Relocation Table and as a result, would not load properly into running process' address space.
Update:
By default, MSVC only includes relocation tables in DLL projects, as described on MSDN:
This behavior can be changed by supplying the
/FIXED:NO
switch to the linker. The default for non-DLL projects is/FIXED
which tells the linker that the target has a fixed base address and does not require a relocation table.您是否尝试过用 C 语言构建一个存根 COM 接口并反汇编结果?这应该可以让您了解您的实施中出了什么问题。
have you tried to build a stub COM interface in C and disassemble the result? That should give you a clue what is going wrong in your implementation.
您是否尝试过将您的全局变量声明为导出?好久没搞x86了。但阅读 nasm 的文档似乎意味着您需要全局和导出才能使 DLL 重定位修复程序发挥作用。
Have you tried to also declare your globals as export? I haven't done x86 for a long time. But reading nasm's doc seems to imply you need to both global and export for the DLL relocation fixup to work.