从DLL中导出变量学习

本文的目的就是想探究dll文件中的变量是如何导出。借此了解ntoskrnl.exe 的导出到底是怎么实现的。

在前面的《SSDT HOOK》代码段中有这么一句话:

 extern "C"  PSERVICE_DESCRIPTOR_TALBE KeServiceDescriptorTable;

当时是说:这个符号是从ntoskrnl.exe 中导出的。从当时测试的时候改变符号名发现执行错误就可以看出来,这个符号绝对是从ntoskrnl.exe 文件中查找出来的。今天使用Depend walker 查看了一下,有这样的结果。

按键F10(c/c++ 符号形式) 转换,发现都是同一个函数名,因此可以判定这个符号是用C导出的。

这是因为如果使用的是c++符号形式导出,那么由于多态性的原因,其导出格式会发生改变。

为此我又做了一次测试。新建dll文件生成cpp 格式。源码如下:

FINAL.H
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the FINAL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// FINAL_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef FINAL_EXPORTS
#define FINAL_API __declspec(dllexport)
#else
#define FINAL_API __declspec(dllimport)
#endif

// This class is exported from the FINAL.dll
class FINAL_API CFINAL {
public:
    CFINAL(void);
    // TODO: add your methods here.
};

extern FINAL_API int nFINAL;
extern FINAL_API int nTemp ;     //add here,others are default input.

FINAL_API int fnFINAL(void);

--------------
// FINAL.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#include "FINAL.h"

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

// This is an example of an exported variable
FINAL_API int nFINAL=0;
FINAL_API int nTemp = 0x10;        //add here.

// This is an example of an exported function.
FINAL_API int fnFINAL(void)
{
    return 42;
}

// This is the constructor of a class that has been exported.
// see FINAL.h for the class definition
CFINAL::CFINAL()
{
    return;
}

程序很简单,

这时候打开depends 查看一下导出情况。

下图是c++默认的导出符号表(最左侧的C++ 表示是通过C++方式导出)

F10切换后,去掉装饰,不知道这个算不算变为C的方式呢?后期注意这个问题。

虽然用depends 可以切换,但是我更想知道编译器做了什么,用PEView 可以查看(导出表name段在.rdata段)。

由于PEView 不能复制结果出来,在这里我用十六进制查看器(Winhex)查看了一下,结果如下。

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0002CF80   41 45 40 58 5A 00 3F 3F  34 43 46 49 4E 41 4C 40   [email protected] ??4[email protected]
0002CF90   40 51 41 45 41 41 56 30  40 41 42 56 30 40 40 5A   @[email protected]@@Z
0002CFA0   00 3F 66 6E 46 49 4E 41  4C 40 40 59 41 48 58 5A    [email protected]@YAHXZ
0002CFB0   00 3F 6E 46 49 4E 41 4C  40 40 33 48 41 00 3F 6E    [email protected]@3HA ?n
0002CFC0   54 65 6D 70 40 40 33 48  41 00 00 00 00 00 00 00   [email protected]@3HA       

可以看到,编译链接后的文件中存放的变量符号为[email protected]@3HA .

接着改变链接方式为Extern "C" .

FINAL.H 其他不变
//extern FINAL_API int nTemp;
extern "C" FINAL_API int nTemp ;

生成的文件结果是:

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0002CF60   00 00 01 00 02 00 03 00  04 00 46 49 4E 41 4C 2E             FINAL.
0002CF70   64 6C 6C 00 3F 3F 30 43  46 49 4E 41 4C 40 40 51   dll ??0[email protected]@Q
0002CF80   41 45 40 58 5A 00 3F 3F  34 43 46 49 4E 41 4C 40   [email protected] ??4[email protected]
0002CF90   40 51 41 45 41 41 56 30  40 41 42 56 30 40 40 5A   @[email protected]@@Z
0002CFA0   00 3F 66 6E 46 49 4E 41  4C 40 40 59 41 48 58 5A    [email protected]@YAHXZ
0002CFB0   00 3F 6E 46 49 4E 41 4C  40 40 33 48 41 00 6E 54    [email protected]@3HA nT
0002CFC0   65 6D 70 00                                        emp 

可以看到,变量符号名并没有改变nTemp .这就是C声明的意思,不改变原变量名。从上面的dump中可以看出来,函数名C++生成的也不是原有的函数名,那么在C下呢,我又试了一次:

//Final.h   the last line.
//add here
extern "C" FINAL_API int sum(int a,int b);

-----
//FINAL.CPP末尾添加
FINAL_API int sum(int a,int b)
{
    return a + b;
}

生成的dll文件用Depends 查看。

F10一下后

depends E 栏中 c 表示用C的方式生成,C++表示用C++的方式生成。

看到函数声明只剩下了函数名。

但是F10自由切换,还是不知道文件中到底是怎么样的,看一下。

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

0002DFA0   56 30 40 41 42 56 30 40  40 5A 00 3F 66 6E 46 49   [email protected]@@Z ?fnFI
0002DFB0   4E 41 4C 40 40 59 41 48  58 5A 00 3F 6E 46 49 4E   [email protected]@YAHXZ ?nFIN
0002DFC0   41 4C 40 40 33 48 41 00  6E 54 65 6D 70 00 73 75   [email protected]@3HA nTemp su
0002DFD0   6D 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   m
0002DFE0   00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00                   

可以看到就是depends中的sum (因为来回切换都没有变–)

来看下如何导入这些变量

#include "stdafx.h"
#include <stdio.h>
#pragma comment(lib,"FINAL.lib")

extern "C" int _declspec(dllimport) nTemp;
int main(int argc, char* argv[])
{
//  printf("%d",nMydll);

 printf("%d",nTemp);
    return 0;
}

//output: 16Press any key to continue.

如果将extern "C" int _declspec(dllimport) nTemp; 中的C 去掉,会怎么样。

--------------------Configuration: mydll_test - Win32 Debug--------------------
Linking...
mydll_test.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) int nTemp" ([email protected]@3HA)
Debug/mydll_test.exe : fatal error LNK1120: 1 unresolved externals
执行 link.exe 时出

其实这个错误就是说找不到符号,上面实验做完应该知道我们文件是.cpp ,那么就会按照c++的方式去找:

1. 首先,编译器将nTemp 转化为C++下的符号[email protected]@3HA

2. 然后 ,通过PE文件结构找到符号[email protected]@3HA ,接着读出它的RVA。

问题是,我们的DLL文件根本就没有这个符号,用C的方式生成的符号是nTemp 。怎么找都找不到。

而最开始的例子就已经说明我要找的变量是用C方式导出的,让编译器将第一步用C的方式生成nTemp ,这次当然就可以找成功了。

最后来看下反汇编过程。

13:    printf("%d",nTemp);
00401028   mov         eax,[__imp__nTemp (0042a18c)]
0040102D   mov         ecx,dword ptr [eax]
0040102F   push        ecx
00401030   push        offset string "%d" (0042201c)
00401035   call        printf (00401060)
0040103A   add         esp,8

对于__imp__nTemp 我的解释是:首先这个是导入进来的,加前缀__imp__ ,然后加入符号名nTemp 。注意导入表能得到的永远是地址,而不是值。也就是说导入nTemp 得到的是nTemp值的地址,要取值nTemp必须通过从地址中取出dword。否则汇编就变成了mov eax, __imp__nTemp,或者说有&ntemp=__imp__nTemp=42a18c.

同样的,用C++的方式形成的反汇编是:

13:    printf("%d",nTemp);
00401028   mov         eax,[[email protected]@3HA (0042a18c)]
0040102D   mov         ecx,dword ptr [eax]
0040102F   push        ecx
00401030   push        offset string "%d" (0042201c)
00401035   call        printf (00401060)
0040103A   add         esp,8

前缀为__imp_ 少了一个下划线。

研究做完了,现在有几个问题待解决:

***

extern FINAL_API int nFINAL;
//extern FINAL_API int nTemp;
extern int FINAL_API  nTemp ;

这两个声明都能通过,一个int在前,一个int在后,区别是什么?

***

最初的那个问题:

 extern "C"  PSERVICE_DESCRIPTOR_TALBE KeServiceDescriptorTable;

这句声明中没有用__declspec(dllimport) .为什么还可以获得此值呢。

如果普通都可以的话,那么我测试程序改成这样,为什么就不能呢。是因为内核的原因么。

#pragma comment(lib,"FINAL.lib")

extern int  nTemp;         //no __declspec(dllimport) --编译失败
int main(int argc, char* argv[])
{
//  printf("%d",nMydll);

 printf("%d",nTemp);
    return 0;
}

***

最后一点:

dll导出文件FINAL.H中的宏定义
#ifdef FINAL_EXPORTS
#define FINAL_API __declspec(dllexport)
#else
#define FINAL_API __declspec(dllimport)
#endif

在哪里测试是否已经define 了 FFINAL_EXPORTS .绝对是宏定义了,以下为例:

#ifdef FINAL_EXPORTS
#define FINAL_API __declspec(dllex444444444port)
#else
#define FINAL_API __declspec(dllimport)
#endif

修改dllimport 不错,但是一旦修改dllexport 就出错,说明绝对是宏定义了。

那么到底是在哪里宏定义的呢,编译器自动搞的??????????

时间: 2024-10-11 08:59:12

从DLL中导出变量学习的相关文章

[转]从普通DLL中导出C++类 – dllexport和dllimport的使用方法(中英对照、附注解)

这几天写几个小程序练手,在准备将一个类导出时,发现还真不知道如果不用MFC的扩展DLL,是怎么导出的.但我知道dllexport可以导出函数和变量,而且MFC扩展DLL就算是使用了MFC的功能,但能否导出类应该也不是必须用MFC才能够做到,一定是有相应的机制可以实现.于是查了一下MSDN,发现这个机制简单的可怕,原来就和导出函数一样,把dllexport关键字加到类名前就可以了.估计和我一样的同学大有人在,把MSDN的相关文档翻译出来,附上我的注解,希望对大家有用. 评注程序均在Visual S

从dll中导出c++类

简介: 动态库(DLL)从开始就作为windows平台的组成部分而存在.它以独立的模块把c函数封装起来供其他用户使用 .DLL从开始就是以封装C语言的形式而存在,当然现在你也可以封装其他语言,比如c++,而如果要实现跨平台使用DLL,则我们必须回归到C语言. 利用C语言接口并不意味着我们必须丢弃掉面向对象方法.C语言可以实现应用二进制接口(ABI),这样使调用者和被调用着可以遵从统一的标准,但是C++语言没有这个特性,导致从一个编译器生成的binary不能被另一个编译器所识别.这样使得直接导出C

从普通DLL中导出C++类 &lt;一&gt;

Microsoft Specific You can declare C++ classes with the dllimport or dllexport attribute. These forms imply that the entire class is imported or exported. Classes exported this way are called exportable classes.The following example defines an export

从普通DLL中导出C++类 &lt;二&gt;

上一篇文章中,我们介绍了怎么从一个DLL中导出C++类,及选择性导出C++类的成员的方法.那么,整个系统的底层机制是怎么样的?是通过什么途径,使得我们可以在另一个程序中使用一个DLL中导出的类的呢? 我们知道,要使用一个C++类,必要的条件是在编译期能得到这个类的头文件,并在链接期可以找到对应的符号的链接地址(比如成员函数.静态数据成员等).如果这个C++类与你的使用者在同一个工程,那这个条件很好满足:    首先,C++类的头文件很好获得.直接在使用者那里将类的头文件include即可    

C++ DLL中导出函数的声明的方法

定义: TESTDLLEXPORT_API int fnTestDllExport(void); TESTDLLEXPORT_API int fnTestCall(void); TESTDLLEXPORT_API int fnAddInt(int i,int j); TESTDLLEXPORT_API BOOL fnContact(char* a); 建立一个.def文件 LIBRARY TestDllExportEXPORTS fnContact @1fnAddInt @2fnTestDllE

DLL中导出全局变量

1. DEF文件 1 EXPORTS 2 3 g_nTest DATA ;导出全局变量 4 5 GetGlobalVar ;导出函数 2. 调用 1 extern int g_nTest; //声明 2 3 int main(int argc, char *argv[]) 4 { 5 *(int*)g_nTest = 1; //注意前面的转换 6 7 return 0; 8 } 需要注意的是用 extern int g_nTest 声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须

DLL中导出ANSI和UNICODE函数

模仿window中的DLL导出ANSI和UNICODE版本的函数,使用UNICODE宏来控制使用哪个版本: 在函数实际的执行代码UNICODE版本中,在ANSI函数的版本中只做参数的转换,及ANSI字符串转UNICODE字符串,然后调用UNICODE版本的函数.  0.DLL头文件 #include <Windows.h> #ifndef _ICAL_H_ #define _ICAL_H_ #ifdef DLL_EXPORT_IMP #define DLL_EXPORT extern &quo

转:DLL如何导出C++的类

由于DLL的出现是针对C语言的,本身对C++的支持不够好.所以如何从DLL中导出C++的类作为DLL的API的一部分就成了问题. 我试了一下 class __declspec(dllexport) Foo这种做法是可行的.并且DEF文件不支持导出C++类(https://msdn.microsoft.com/en-us/library/d91k01sh.aspx:https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx).感觉还有很多不清楚,所

Qt调用dll中的功能函数

DLL 优点 ------------------------------------- 1.扩展了应用程序的特性: 2.可以用许多种编程语言来编写: 3.简化了软件项目的管理: 4.有助于节省内存: 5.有助于资源共享: 6.有助于应用程序的本地化: 7.有助于解决平台差异: 8.可以用于一些特殊的目的.windows使得某些特性只能为DLL所用. 转载:http://www.cnblogs.com/hicjiajia/archive/2010/08/27/1810239.html Qt调用d