从内存中加载DLL指南
- 格式:doc
- 大小:110.00 KB
- 文档页数:12
dll加载原理DLL加载原理概述•DLL(动态链接库)是一种可执行文件格式,用于存储和共享程序代码和数据。
•DLL加载是将DLL文件加载到内存并解析其导出函数的过程。
DLL的分类•内核模式DLL:运行在操作系统内核空间中,提供给操作系统使用。
•用户模式DLL:运行在应用程序进程的用户空间中,为应用程序提供功能支持。
DLL的加载方式1.隐式加载:在应用程序启动时由操作系统自动加载所需的DLL文件。
–应用程序代码中使用函数,操作系统自动在加载应用程序的同时加载其依赖的DLL。
–应用程序代码需要将DLL的路径告知操作系统,操作系统根据路径找到DLL并加载。
2.显式加载:在应用程序运行时手动加载所需的DLL文件。
–应用程序通过调用加载函数(如LoadLibrary函数)手动加载DLL。
–调用GetProcAdress函数获取DLL中函数的入口地址,从而调用DLL中的函数。
DLL的加载过程1.读取DLL文件:–操作系统通过文件系统读取DLL文件的内容。
2.根据DLL文件的导入表(Import Table)解析DLL的依赖:–导入表记录了DLL所依赖的其他DLL,以及导出函数的名称和地址。
3.加载DLL依赖的其他DLL:–递归地加载DLL所依赖的其他DLL文件。
4.解析DLL导出函数:–根据导入表中记录的函数名称,找到导出函数的入口地址。
5.将DLL文件映射到进程空间:–将DLL文件映射到进程的虚拟内存空间中,以便能够访问DLL中的代码和数据。
6.更新进程的导入表:–更新进程的导入表,将DLL中导出函数的地址填入相应的入口地址。
DLL的卸载•当不再需要某个DLL时,可以将其从内存中卸载。
•DLL卸载的条件通常是没有其他模块依赖该DLL,或者由操作系统决定。
总结•DLL加载是将DLL文件加载到内存并解析导出函数的过程。
•DLL可以通过隐式加载或显式加载的方式加载。
•DLL的加载过程包括读取DLL文件、解析依赖、加载其他DLL、解析导出函数等步骤。
从内存中加载DLL指南从内存中加载DLL指南 ..................................................................... ............................................. 1 前言 ..................................................................... ........................................................................ .. 2 内容提纲 ..................................................................... .................................................................. 2 简介 ..................................................................... ........................................................................ .. 2 PE文件格式 ..................................................................... ............................................................. 2 DOS header /DOS stub 格式...................................................................... ................................ 3 PEheader ................................................................. ........................................................ 3 格式Sectionheader ................................................................. .................................................. 6 格式加载LIBRARY ................................................................ . (6)Section ............................................................ ...................................................... 7 拷贝的内容.................................................................... ........................................................ 7 基地址重定位.................................................................... ........................................................ 8 处理导入信息内存访问权限设置 ..................................................................... ............................................... 9 通知library被进程加载...................................................................... ...................................... 9 调用导出函数 ..................................................................... ..................................................... 10 释放library ................................................................ . (10)MEMORYMODULE工具包...................................................................... (11).................................................................... ............................................................. 11 下载位置已知的问题...................................................................... .. (11)License ................................................................ (11)版本移植 ..................................................................... ............................................................ 11 版权声明 ..................................................................... (12)前言本篇指南是讲解如何不借助文件系统的帮助来加载内存中载入DLL的技术。
复制代码复制代码MFC DLL向导虽然能用DLL实现的东西都可以用COM来实现,但DLL的优点确实不少,它更容易创建。
本文将讨论如何利用MFC来创建不同类型的DLL,以及如何使用他们。
一、DLL的不同类型使用MFC可以生成两种类型的DLL:MFC扩展DLL和常规DLL。
常规DLL有可以分为动态连接和静态连接。
Visual C++还可以生成W IN32 DLL,但不是这里讨论的主要对象。
1、MFC扩展DLL每个DLL都有某种类型的接口:变量、指针、函数、客户程序访问的类。
它们的作用是让客户程序使用DLL,MFC扩展DLL可以有C+ +的接口。
也就是它可以导出C++类给客户端。
导出的函数可以使用C ++/MFC数据类型做参数或返回值,导出一个类时客户端能创建类对象或者派生这个类。
同时,在DLL中也可以使用DLL和MFC。
Visual C++使用的MFC类库也是保存在一个DLL中,MFC扩展D LL动态连接到MFC代码库的DLL,客户程序也必须要动态连接到MFC 代码库的DLL。
(这里谈到的两个DLL,一个是我们自己编写的DLL,一个装MFC类库的DLL)现在MFC代码库的DLL也存在多个版本,客户程序和扩展DLL都必须使用相同版本的MFC代码DLL。
所以为了让MFC扩展DLL能很好的工作,扩展DLL和客户程序都必须动态连接到MFC代码库DLL。
而这个DLL必须在客户程序运行的计算机上。
2、常规DLL使用MFC扩展DLL的一个问题就是DLL仅能和MFC客户程序一起工作,如果需要一个使用更广泛的DLL,最好采用常规DLL,因为它不受MFC的某些限制。
常规DLL也有缺点:它不能和客户程序发送指针或MFC派生类和对象的引用。
一句话就是常规DLL和客户程序的接口不能使用MFC,但在DLL和客户程序的内部还是可以使用MFC。
当在常规DLL的内部使用MFC代码库的DLL时,可以是动态连接/静态连接。
如果是动态连接,也就是常规DLL需要的MFC代码没有构建到DLL中,这种情况有点和扩展DLL类似,在DLL运行的计算机上必须要MFC代码库的DLL。
闲着没事整理了一下DLL的N种注入方法,对学习外挂的朋友,应该有用!第一种方法:利用CreateRemoteThread 远程建立线程的方式注入DLL.首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的.function EnableDebugPriv : Boolean;varhToken : THANDLE;tp : TTokenPrivileges;rl : Cardinal;beginresult := false;//打开进程令牌环OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken);//获得进程本地唯一IDif LookupPrivilegeValue(nil, 'SeDebugPrivilege',tp.Privileges[0].Luid) thenbegintp.PrivilegeCount := 1;tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;//调整权限result := AdjustTokenPrivileges(hToken, False, tp,sizeof(tp), nil, rl);end;end;关于OpenProcessToken() 和AdjustTokenPrivileges() 两个API的简单介绍:OpenProcessToken():获得进程访问令牌的句柄.function OpenProcessToken(ProcessHandle: THandle; //要修改访问权限的进程句柄DesiredAccess: DWORD; //指定你要进行的操作类型var TokenHandle: THandle): BOOL; //返回的访问令牌指针AdjustTokenPrivileges() :调整进程的权限.function AdjustTokenPrivileges(TokenHandle: THandle; // 访问令牌的句柄DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限const NewState: TTokenPrivileges; // 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组,数据组的每个项指明了权限的类型和要进行的操作;BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为0var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息var ReturnLength: DWORD //实际PreviousState结构返回的大小) : BOOL;远程注入DLL其实是通过CreateRemoteThread 建立一个远程线程调用LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。
DLL两种加载的详解目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。
静态库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。
比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。
动态库一般会有对应的导入库,方便程序静态载入动态链接库,否则你可能就需要自己LoadLibary调入DLL文件,然后再手工GetProcAddress获得对应函数了。
有了导入库,你只需要链接导入库后按照头文件函数接口的声明调用函数就可以了。
导入库和静态库的区别很大,他们实质是不一样的东西。
静态库本身就包含了实际执行代码、符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。
这也是实际上很多开源代码发布的惯用方式:1. 预编译的开发包:包含一些.dll文件和一些.lib文件。
其中这里的.lib就是导入库,而不要错以为是静态库。
但是引入方式和静态库一样,要在链接路径上添加找到这些.lib的路径。
而.dll则最好放到最后产生的应用程序exe执行文件相同的目录。
这样运行时,就会自动调入动态链接库。
2. 用户自己编译:下载的是源代码,按照readme自己编译。
生成很可能也是.dll + .lib(导入库)的库文件3. 如果你只有dll,并且你知道dll中函数的函数原型,那么你可以直接在自己程序中使用LoadLibary调入DLL文件,GetProcAddressDLL:动态链接库 (DLL) 是作为共享函数库的可执行文件。
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。
函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。
调用DLL,首先需要将DLL文件映像到用户进程的地址空间中,然后才能进行函数调用,这个函数和进程内部一般函数的调用方法相同。
Windows提供了两种将DLL映像到进程地址空间的方法:1. 隐式的加载时链接这种方法需要DLL工程经编译产生的LIB文件,此文件中包含了DLL允许应用程序调用的所有函数的列表,当链接器发现应用程序调用了LIB文件列出的某个函数,就会在应用程序的可执行文件的文件映像中加入一些信息,这些信息指出了包含这个函数的DLL文件的名字。
当这个应用程序运行时,也就是它的可执行文件被操作系统产生映像文件时,系统会查看这个映像文件中关于DLL的信息,然后将这个DLL文件映像到进程的地址空间。
系统通过DLL文件的名称,试图加载这个文件到进程地址空间时,它寻找DLL 文件的路径按照先后顺序如下:·程序运行时的目录,即可执行文件所在的目录;·当前程序工作目录·系统目录:对于Windows95/98来说,可以调用GetSystemDirectory函数来得到,对于WindowsNT/2000 来说,指的是32位Windows的系统目录,也可以调用GetSystemDirectory函数来得到,得到的值为SYSTEM32。
·Windows目录·列在PATH环境变量中的所有目录VC中加载DLL的LIB文件的方法有以下三种:①LIB文件直接加入到工程文件列表中在VC中打开File View一页,选中工程名,单击鼠标右键,然后选中“Add Files to Project”菜单,在弹出的文件对话框中选中要加入DLL的LIB文件即可。
②设置工程的 Project Settings来加载DLL的LIB文件打开工程的 Project Settings菜单,选中Link,然后在Object/library modules 下的文本框中输入DLL的LIB文件。
③通过程序代码的方式加入预编译指令#pragma comment (lib,”*.lib”),这种方法优点是可以利用条件预编译指令链接不同版本的LIB文件。
一、动态链接库什么是动态链接库?DLL三个字母对于你来说一定很熟悉吧,它是Dynamic Link Library 的缩写形式,动态链接库(DLL) 是作为共享函数库的可执行文件。
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。
函数的可执行代码位于一个DLL 中,该DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。
DLL 还有助于共享数据和资源。
多个应用程序可同时访问内存中单个DLL 副本的内容。
和大多数程序员一样,你一定很使用过DLL吧。
也曾感受到它的带给你程序设计和编码上的好错吧今天我想和大家探讨一个主题:如何在C#创建和调用DLL(动态链接库), 其实在很大意义上而讲,DLL让我更灵活的组织编写我们的应用程序,作为软件设计者,可一个根据它来达到很高的代码重用效果。
下面我来介绍一下在C#中如何创建和调用DLL。
二、准备工作我们需要对我们接下来要做的事情做个简单的介绍,在本文我们将利用C#语言创建一个名为MyDLL.DLL的动态链接库,在这个动态链接库文件中我们将提供两个功能一个是对两个参数交换他们的值,另一个功能是求两个参数的最大公约数。
然后创建一个应用程序使用这个DLL。
运行并输出结果。
1、MySwap.csnamespace MyMethods{public class SwapClass{public static bool Swap(ref long i,ref long j){i = i+j;j = i-j;i = i-j;return true;}}}2、MyMaxCD.csnamespace MyMethods{public class MaxCDClass{public static long MaxCD(long i, long j){long a,b,temp;if(i>j){a = i;b = j;}else{b = i;a = j;}temp = a % b;while(temp!=0){a = b;b = temp;temp = a % b;}return b;}}}需要注意的是:我们在制作这两个文件的时候可以用Visual 或者其他的文本编辑器,就算是记事本也可以。
从内存中加载DLL指南从内存中加载DLL指南 (1)前言 (2)内容提纲 (2)简介 (2)PE文件格式 (2)DOS header /DOS stub 格式 (3)PE header 格式 (3)Section header格式 (6)加载L IBRARY (6)拷贝Section的内容 (7)基地址重定位 (7)处理导入信息 (8)内存访问权限设置 (9)通知library被进程加载 (9)调用导出函数 (10)释放library (10)M EMORY M ODULE工具包 (11)下载位置 (11)已知的问题 (11)License (11)版本移植 (11)版权声明 (12)前言本篇指南是讲解如何不借助文件系统的帮助来加载内存中载入DLL的技术。
作者:Joachim Bauch联系方式: mail◎joachim-bauch.de版权:Creative Commons License(by-sa)内容提纲●简介●PE 文件格式⏹DOS header /DOS stub 格式⏹PE header 格式⏹Section header 格式●加载library⏹分配内存空间⏹拷贝Section内容到目标位置⏹基址重定位⏹内存访问权限设置⏹通知library被进程加载●调用导出函数●释放library●MemoryModule工具包⏹下载地址⏹已知的问题⏹License⏹移植版本●版权声明简介Windows平台提供的加载library的API(LoadLibarary, LoadLibraryEx)只能加载文件系统中的Library. 没有体哦那个从内存中加载Dll的功能。
但是,有些情况下又需要这样的功能。
譬如,你不想在发布包中包含很多文件,又或者你想给那些逆向工作者一些苦头吃。
这时一个常见的做法是先把dll文件写到一个临时文件中,然后从临时文件中导入它。
当程序终止时把临时文件删除。
PE文件格式绝大多数包含执行代码的二进制文件(.exe、.dll、.sys)都有相同的文件格式。
这种文件格式包括下面几个部分:下面将简单描述各个组成部分。
(注:在头文件winnt.h中可以找到下面要介绍的所有数据结构)DOS header /DOS stub 格式DOS格式部分有两部分组成。
分别是DOS header和DOS stubDOS头部存在的作用仅仅是为了向后兼容的目的。
跟在DOS header后面的是DOS Stub 数据,DOS stub用来显示一条错误信息来告诉用户该文件不能在DOS模式下运行。
微软对DOS Header的声明如下:typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // Magic numberWORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // File address of new exe header} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;PE header 格式PE Header用来描述和section相关的信息。
可执行文件用section来保存执行代码和数据,以及从其他模块导入的函数的信息和本模块导出的函数信息等。
PE Header的定义如下:typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;FileHeader字段描述了文件的物理格式,如目录信息、Symbols的信息等。
typedef struct _IMAGE_FILE_HEADER {WORD Machine;WORD NumberOfSections;DWORD TimeDateStamp;DWORD PointerToSymbolTable;DWORD NumberOfSymbols;WORD SizeOfOptionalHeader;WORD Characteristics;} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;字段OptionalHeader保存模块的逻辑信息,这些信息包括需要的操作系统版本,需要的内存大小和入口点信息。
typedef struct _IMAGE_OPTIONAL_HEADER {//// Standard fields.//WORD Magic;BYTE MajorLinkerVersion;BYTE MinorLinkerVersion;DWORD SizeOfCode;DWORD SizeOfInitializedData;DWORD SizeOfUninitializedData;DWORD AddressOfEntryPoint;DWORD BaseOfCode;DWORD BaseOfData;//// NT additional fields.//DWORD ImageBase;DWORD SectionAlignment;DWORD FileAlignment;WORD MajorOperatingSystemVersion;WORD MinorOperatingSystemVersion;WORD MajorImageVersion;WORD MinorImageVersion;WORD MajorSubsystemVersion;WORD MinorSubsystemVersion;DWORD Win32VersionValue;DWORD SizeOfImage;DWORD SizeOfHeaders;DWORD CheckSum;WORD Subsystem;WORD DllCharacteristics;DWORD SizeOfStackReserve;DWORD SizeOfStackCommit;DWORD SizeOfHeapReserve;DWORD SizeOfHeapCommit;DWORD LoaderFlags;DWORD NumberOfRvaAndSizes;IMAGE_DATA_DIRECTORYDataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;字段DataDirectory保存着模块中定义的(IMAGE_NUMBEROF_DIRECTORY_ENTRIES)个逻辑项(Conpontents)的入口:对于只是加载DLL的任务来说,我们仅仅用到导入块和基地址重定位表块。
不过为了调用导出的函数,我们还需要用到导出函数块。
Section header格式这一部分数据保存在PE header部分的OPtionalHeader字段后面。
为了方便定位section header的位置,微软提供了一个宏定义IMAGE_FIRST_SECTION,它的值等于SectionHeader 的开始地址相对于PE Header基地址的位移。
Section header实际上是一个存储着文件中每一个Section的信息的列表:typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME];union {DWORD PhysicalAddress;DWORD VirtualSize;} Misc;DWORD VirtualAddress;DWORD SizeOfRawData;DWORD PointerToRawData;DWORD PointerToRelocations;DWORD PointerToLinenumbers;WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics;} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;而一个Section中可以包含代码,数据,重定位信息,资源,导出导入函数等信息。
加载Library为了模拟PE loader的过程,我们首先必须弄清楚从把文件读入内存中开始到准备好可以被程序使用为止,都需要执行那些步骤。
当我们调用LoadLibrary时,Windows主要做了了以下工作:1.打开指定的文件并检查DOS和PE头部。
2.在PEHeader.OptionalHeader.ImageBase处分配PEHeader.Optionalheader.SizeOfImage个字节的内存。
3.分析section header,从IMAGE_SECTION_HEADER结构中的VirtualAddress属性中获取每个section的开始地址相对与ImageBase的位移。