C++ 通过DEF文件损坏的DLL导出符号似乎已被修改

C++ 通过DEF文件损坏的DLL导出符号似乎已被修改,c++,dll,visual-studio-2012,C++,Dll,Visual Studio 2012,我使用DEF文件创建了一个包装DLL,用于描述其导出 从原始DLL的导出(使用dumpbin生成): 从为my wrapper DLL创建的DEF文件: EXPORTS ??0FooBar@@QAE@ABV0@@Z=__E__0__ @1 使用OllyDbg检查生成的包装器DLL时,我可以看到导出实际上被更改为: Names in FooBarDLL, item 0 Address=0F085685 Section=.text Type=Export Name=??0FooBar 如

我使用DEF文件创建了一个包装DLL,用于描述其
导出

从原始DLL的导出(使用dumpbin生成):

从为my wrapper DLL创建的DEF文件:

EXPORTS
??0FooBar@@QAE@ABV0@@Z=__E__0__ @1
使用OllyDbg检查生成的包装器DLL时,我可以看到导出实际上被更改为:

Names in FooBarDLL, item 0
 Address=0F085685
 Section=.text
 Type=Export
 Name=??0FooBar
如您所见,它缺少额外的garbledegock(
@@QAE@ABV0通常由微软Visual C++生成的此类类/函数/参数组合。

为了确保这不是用户在使用OllyDbg时出现的错误,我还检查了原始DLL的导出,毫不奇怪,这些导出看起来与我所期望的一样:

Names in FooBarDLLOriginal, item 1
 Address=1003A800
 Section=.text
 Type=Export
 Name=??0FooBar@@QAE@ABV0@@Z
因为我需要我的包装器DLL看起来与原始DLL完全一样,所以这个结果显然是不好的

<>强>如何防止VisualC++忽略我的DEF导出定义的片段?< /强> < /P>
我曾尝试使用许多编译器和链接器选项,但最终未能实现我的目标。

我终于让它工作起来了

正如Hans Passant指出的那样,链接器确实以某种方式处理导出定义中的符号,我发现很难理解


我的意见:

  • Mulink C++函数(<代码>?0FoBaar @)QAE@ABV0@@Z
)转发到未混合(
extern“C”
)函数(
\uuu E\uu 0\uuu
>P+>=C++功能弱化在第一个@符号(<代码>?0FoBOAR < /代码>)< /P> < /LI>处裁剪 <> > McLead C++函数(<代码>?0FoBaar @)QAE@ABV0@@Z)转发到损坏的(
extern“C”\uuuu declspec(dllexport)
)函数(
\uuuuuu E\uuu 0\u4

= C++功能函数保持完整< /LI>
这将是我的初始测试用例,现在离我的初始目标已经足够近了

从包装器的DEF文件:

EXPORTS
??0FooBar@@QAE@ABV0@@Z=___E__0__@4 @1
从包装DLL的导出(使用dumpbin生成):

真正让我吃惊的是,当使用OllyDbg读取导出表时,序号1的导出仍然不可见。不过,当加载DLL(
LoadLibrary
)并使用
GetProcAddress(hDLL)解析该函数的地址时,“?”0FooBar@@QAE@ABV0@@Z”)
,一切正常


因此,我现在有一个DLL,它使用功能相同的导出表来包装原始DLL。它现在显示了我的代理实现的额外导出,但这对我来说并没有问题。通过这种方式,可以创建一个代理DLL,它可以截取,并且如果需要,即使在涉及C++的处理时,也可以增强原始的实现。我总觉得我把事情复杂化了一点,但是,嘿,这很有效

对于完全通过的函数,我只需在DEF文件中创建一个转发导出,如下所示:

??BarFoo@WhoCares@@@Z = OriginalDllName.??BarFoo@WhoCares@@@Z
这个问题(以及其他类似的问题)与使用该工具自动创建代理DLL有关。使用Till观察的解决方案是:

始终将包装函数声明为u stdcall,以便使用前导的u和尾随的@0对其进行修饰,然后在.def文件中使用它,以便保留原始函数修饰(如果有)

(当/如果使用实函数替换包装器时,需要记住将调用约定从_stdcall更改为所需的调用约定,以及删除_declspec(裸)、添加参数声明、更改或删除.def文件声明以匹配等。)

包装器.cpp snipet:

// _OriginalFunction@12
extern "C" __declspec(naked) void __stdcall __E__0__()
    {
    __asm
        {
        jmp p[0*4];
        }
    }
.def文件:

EXPORTS
_OriginalFunction@12=___E__0__@0 @1
etc.
我修改了我的WRAPPIT工具版本以自动执行此操作:

165c165
<                       fprintf(fdef,"%s=%s @%u\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u\r\n",v[i].en,v[i].in,v[i].o);
167c167
<                       fprintf(fdef,"%s=%s @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
225c225
<               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,argv[3],v[i].in);
---
>               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,"__stdcall",v[i].in);
165c165
fprintf(fdef,“%s=u2;%s@0@%u\r\n,v[i].en,v[i].in,v[i].o);
167c167
fprintf(fdef,“%s=u2;%s@0@%u非名称\r\n,v[i].en,v[i].in,v[i].o);
225c225
fprintf(fcpp,“/%s\r\n下一个字符串\“C\”\u declspec(裸)无效%s%s()\r\n”,v[i].en,“\u stdcall”,v[i].in);

是的,这不起作用。链接器将@字符视为特殊字符,因为它还用于给出序号。等同于。没有已知的解决方法,没有报价选项。只需用函数的原始名称声明函数,这样它们就会被损坏,并让它们调用您想要的任何替换函数。@HansPassant非常感谢您的评论。我觉得自己很愚蠢,因为我没有尝试使用原始签名来替换我的签名。。。我将尝试看看是否如预期的那样工作。在我尝试更改函数之前,代理DLL工作正常。但是当我将声明更改为
extern“C”double\uu cdecl\uu E\uu 0\uu(double a,double a)
时,我从链接的<代码>错误LNK2001:未解析的外部符号uuu E_uu0_uu@0。知道为什么会发生这种情况吗?您需要将
\uu E\uu 0\uuu
更改为实际函数名并删除.def声明,或者更改.def文件以与包装函数的u cdecl名称匹配。
EXPORTS
_OriginalFunction@12=___E__0__@0 @1
etc.
165c165
<                       fprintf(fdef,"%s=%s @%u\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u\r\n",v[i].en,v[i].in,v[i].o);
167c167
<                       fprintf(fdef,"%s=%s @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
---
>                       fprintf(fdef,"%s=_%s@0 @%u NONAME\r\n",v[i].en,v[i].in,v[i].o);
225c225
<               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,argv[3],v[i].in);
---
>               fprintf(fcpp,"// %s\r\nextern \"C\" __declspec(naked) void %s %s()\r\n",v[i].en,"__stdcall",v[i].in);