如何从内存加载动态链接库(DLL)
摘要:如何从内存加载动态链接库(DLL)。
Windows可执行文件-PE格式
首先我们看一下pe的结构
DOS headerDOS stub
PE header
Section header
Section 1
Section 2
. . .
Section n
下面给出的所有结构都可以在头文件winnt.h中找到。
DOS header
DOS header仅用于向后兼容。它位于DOS stub之前。
Microsoft定义DOS标头如下:
typedef struct _IMAGE_DOS_HEADER {// DOS .EXE标头 WORD e_magic; //Magic number 字e_cblp; //文件最后一页上的字节 字e_cp; //文件中的页面 WORD e_crlc; //Relocations 字e_cparhdr; //段落中header的大小 字e_minalloc; //所需的最少额外段落 字e_maxalloc; //所需的最大额外段落数 WORD e_ss; //初始(相对)SS值 WORD e_sp; //初始SP值 WORD e_csum; //校验和 WORD e_ip; //初始IP值 WORD e_cs; //初始(相对)CS值 字e_lfarlc; //重定位表的文件地址 WORD e_ovno; //覆盖数 WORD e_res [4]; //保留字 WORD e_oemid; // OEM标识符(用于e_oeminfo) WORD e_oeminfo; // OEM信息;特定于e_oemid 字e_res2 [10]; //保留字 LONG e_lfanew; //新的exe标头的文件地址 } IMAGE_DOS_HEADER,* PIMAGE_DOS_HEADER;
PE header
PE header包含有关可执行文件中不同部分的信息,这些不同部分用于存储代码和数据或定义从其他库导入或由此库提供的导出。
定义如下:
typedef struct _IMAGE_NT_HEADERS { DWORD签名; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32可选标题; } IMAGE_NT_HEADERS32,* PIMAGE_NT_HEADERS32;
FileHeader中描述的physical 文件的格式,例如目录符号和其他信息:
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 { // //标准字段。 //WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; // // NT其他字段。 // 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_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32,* PIMAGE_OPTIONAL_HEADER32;
DataDirectory目录包含16个(IMAGE_NUMBEROF_DIRECTORY_ENTRIES定义库的逻辑组件)条目:
对于导入DLL,我们仅需要描述导入表和基本重定位表的条目。为了提供对导出功能的访问,需要导出条目。
Section header
段头存储在PE头中的OptionalHeader结构之后。Microsoft提供了宏IMAGE_FIRST_SECTION以基于PE标头获得起始地址。
实际上,节头是文件中每个节的信息列表:
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;
一个部分可以包含代码,数据,重定位信息,资源,导出或导入定义等。
加载库
要模拟PE加载程序,我们必须首先了解必须将文件加载到内存中,并准备从其他程序调用它们的结构。
在对LoadLibrary发出API调用时,Windows基本上执行以下任务:
在以下段落中,将描述每个步骤。
分配内存
由于Windows提供了保护这些内存块的功能,因此必须使用VirtualAlloc来保留/分配该库所需的所有内存。 这对于限制对内存的访问(例如防止对代码或常量数据的写访问)是必要的。
OptionalHeader结构定义该库所需的存储块的大小。如果可能,必须将其保存在ImageBase指定的地址上:
memory = VirtualAlloc((LPVOID)(PEHeader->OptionalHeader.ImageBase), PEHeader->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE);
如果保留的内存与ImageBase中指定的地址不同,则必须执行以下基本重定位。
复制sections
保留内存后,即可将文件内容复制到系统中。 必须评估section header,以确定文件中的位置和内存中的目标区域。
复制数据之前,必须首先提交内存块:
dest = VirtualAlloc(baseAddress + section->VirtualAddress, section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE);
Base relocation
库的代码/数据部分中的所有内存地址都相对于ImageBase在OptionalHeader中定义的地址进行存储。如果无法将库导入此内存地址,则必须调整引用=> relocated。文件格式通过将有关所有这些引用的信息存储在基本重定位表中来帮助实现此目的,该表可在OptionalHeader的DataDirectory的目录条目5中找到。
该表由一系列此类结构组成
typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; } IMAGE_BASE_RELOCATION;
它包含(SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION)/ 2个条目,每个条目为16位。高4位定义重定位的类型,低12位定义与VirtualAddress的偏移量。
似乎在DLL中使用的唯一类型是
IMAGE_REL_BASED_ABSOLUTE
用于填充。
IMAGE_REL_BASED_HIGHLOW
将ImageBase和分配的内存块之间的增量添加到在偏移处找到的32位。
解决导入部分
OptionalHeader中DataDirectory的目录条目1指定要从中导入符号的库列表。此列表中的每个条目定义如下:
typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)} IMAGE_IMPORT_DESCRIPTOR;
名称条目描述了偏移到库的名称(例如,以空字符结尾的字符串KERNEL32.DLL)。此OriginalFirstThunk条目所指向的函数名称的参考列表是从外部库中导入的。FirstThunk指向一个地址列表,其中包含指向导入符号的指针。
解决导入问题时,我们浏览了两个列表,将名称定义的函数导入到第一个列表中,并将指向符号的指针存储在第二个列表中:
nameRef = (DWORD *)(baseAddress + importDesc->OriginalFirstThunk); symbolRef = (DWORD *)(baseAddress + importDesc->FirstThunk);for (; *nameRef; nameRef++, symbolRef++) { PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)(codeBase + *nameRef); *symbolRef = (DWORD)GetProcAddress(handle, (LPCSTR)&thunkData->Name); if (*funcRef == 0) { handleImportError(); return; } }
内存保护
在每个部分的“ 特征”条目中指定了权限标志。这些标志可以是以下标志之一或组合
IMAGE_SCN_MEM_EXECUTE
本节包含可以执行的数据。
IMAGE_SCN_MEM_READ
本节包含可读数据。
IMAGE_SCN_MEM_WRITE
本节包含可写的数据。
这些标志必须映射到保护标志
PAGE_NOACCESS
PAGE_WRITECOPY
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_WRITECOPY
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
现在,您可以使用功能VirtualProtect限制对内存的访问。如果程序尝试以未经授权的方式访问它,则Windows会引发异常。
除了上面的某些标志之外,您还可以添加以下内容:
IMAGE_SCN_MEM_DISCARDABLE
导入后可以释放此部分中的数据。通常,这是为重定位数据指定的。
IMAGE_SCN_MEM_NOT_CACHED
Windows不得缓存此部分中的数据。将位标志PAGE_NOCACHE添加到上面的保护标志中。
Notify library
最后要做的是调用DLL入口点(由AddressOfEntryPoint定义),并因此通知库有关该进程的信息。
入口点的功能定义为
typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved);
所以我们最后需要执行的代码是
DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);
之后,我们可以像使用任何普通库一样使用导出的函数。
导出功能
如果要访问库导出的函数,则需要找到符号的入口点,即要调用的函数的名称。
OptionalHeader中DataDirectory的目录条目包含有关导出函数的信息。定义如下:
typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
首先要做的是将函数名称映射到导出符号的序列号。因此,只需并行遍历AddressOfNames和AddressOfNameOrdinals定义的数组,直到找到所需的名称。
现在,您可以通过评估AddressOfFunctions数组的第n个元素来使用序列号读取地址。
释放
要发布自定义加载的库,请按照下列步骤操作
调用入口点以通知库有关分离的信息:
DllEntryProc entry = (DllEntryProc)(baseAddress + PEHeader->OptionalHeader.AddressOfEntryPoint); (*entry)((HINSTANCE)baseAddress, DLL_PROCESS_ATTACH, 0);
用于解析导入的免费外部库。
释放已分配的内存。
内存模块
MemoryModule是一个C库,可用于从内存中加载DLL。
此接口与加载库的标准方法非常相似:
typedef void *HMEMORYMODULE;HMEMORYMODULE MemoryLoadLibrary(const void *);FARPROC MemoryGetProcAddress(HMEMORYMODULE, const char *);void MemoryFreeLibrary(HMEMORYMODULE);
上一篇:映射攻击者活动技术:LNK文件构建关联用户搜索
下一篇:绕过Android SDK网络安全配置:Frida脚本利用,测试脚本分析脚本的运行机制
人机验证(Captcha)绕过方法:使用Chrome开发者工具在目标网站登录页面上执行简单的元素编辑,以实现Captcha绕过
牛创网络: " 人机身份验证(Captcha)通常显示在网站的注册,登录名和密码重置页面上。 以下是目标网站在登录页面中排列的验证码机制。 从上图可以
2020-01-26 12:44:09 )8872( 亮了
自动发现IDOR(越权)漏洞的方法:使用BurpSuite中的Autozie和Autorepeater插件来检测和识别IDOR漏洞,而无需手动更改每个请求的参数
牛创网络: "自动发现IDOR(越权)漏洞的方法:使用BurpSuite中的Autozie和Autorepeater插件来检测和识别IDOR漏洞,而无需手动更改每个请求的参数
2020-01-30 14:04:47 )6288( 亮了
Grafana CVE-2020-13379漏洞分析:重定向和URL参数注入漏洞的综合利用可以在任何Grafana产品实例中实现未经授权的服务器端请求伪造攻击SSRF
牛创网络: "在Grafana产品实例中,综合利用重定向和URL参数注入漏洞可以实现未经授权的服务器端请求伪造攻击(SSRF)。该漏洞影响Grafana 3 0 1至7 0 1版本。
2020-08-12 14:26:44 )4301( 亮了
Nginx反向代理配置及反向代理泛目录,目录,全站方法
牛创网络: "使用nginx代理dan(sui)是http响应消息写入服务地址或Web绝对路径的情况。 写一个死的服务地址是很少见的,但它偶尔也会发生。 最棘手的是写入web绝对路径,特别是如果绝对路径没有公共前缀
2019-06-17 10:08:58 )3858( 亮了
fortify sca自定义代码安全扫描工具扫描规则(源代码编写、规则定义和扫描结果展示)
牛创网络: "一般安全问题(例如代码注入漏洞),当前fortify sca规则具有很多误报,可通过规则优化来减少误报。自带的扫描规则不能检测到这些问题。 需要自定义扫描规则,合规性角度展示安全风险。
2020-02-12 10:49:07 )3505( 亮了
整理几款2020年流行的漏洞扫描工具
牛创网络: "漏洞扫描器就是确保可以及时准确地检测信息平台基础架构的安全性,确保业务的平稳发展,业务的高效快速发展以及公司,企业和国家 地区的所有信息资产的维护安全。
2020-08-05 14:36:26 )2536( 亮了
微擎安装使用技巧-微擎安装的时候页面显示空白是怎么回事?
牛创网络: "我们在公众号开发中,有时候会用到微擎,那我们来看一下微擎安装的时候页面显示空白是怎么回事吧
2019-06-08 15:34:16 )2261( 亮了
渗透测试:利用前端断点拦截和JS脚本替换对前端加密数据的修改
牛创网络: " 本文介绍的两种方法,虽然断点调试比JS脚本代码替换更容易,但是JS脚本代码替换方法可以实现更强大的功能,测试人员可以根据实际需要选择适当的测试方法
2020-01-07 09:34:42 )1995( 亮了
从工业界到学界盘点SAS与R优缺点比较
牛创网络: "虽然它在业界仍然由SAS主导,但R在学术界广泛使用,因为它的免费开源属性允许用户编写和共享他们自己的应用程序 然而,由于缺乏SAS经验,许多获得数据分析学位的学生很难找到工作。
2019-07-13 22:25:29 )1842( 亮了
41款APP侵犯用户隐私权:QQ,小米,搜狐,新浪,人人均被通报
牛创网络: "随着互联网的不断发展,我们进入了一个时代,每个人都离不开手机。 但是,APP越来越侵犯了用户隐私权。12月19日,工业和信息化部发布了《关于侵犯用户权益的APP(第一批)》的通知。
2019-12-20 11:28:14 )1775( 亮了