远程代码注入

来源:互联网 发布:顺德经济发展数据 编辑:程序博客网 时间:2024/06/08 17:21

《计算机病毒揭秘与对抗》5.1.3章节学习笔记


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

1. 远程代码注入介绍

    远程代码注入,一种计算机病毒的惯用技术,指在远程进程中开辟足够大的内存空间,然后将要执行的代码直接写过去,最后使用远程线程的方式运行注入代码。这样可以使其他进程执行病毒代码,又不会在对方进程中产生新的模块,从而使病毒更隐蔽地运行。


1.1 直接注入代码

指将病毒代码以写内存的方式由一个进程空间写入到目标进程空间内,然后在目标进程空间中开启新的线程运行被写入的代码。通常分为如下步骤:
  1. 准备被注入的代码;
  2. 将被注入的代码写入到远程进程空间内;
  3. 开启远程线程运行被注入的代码;
  4. 等待远程线程退出

--> 下面针对如上几个步骤详细讲解直接注入代码的原理 :

1) 准备被注入的代码


    被注入的代码与我们平时本地编写的代码有很大的区别。像我们要完成某种功能必然要调用系统api,然而注入代码对API函数的调用有着特殊的要求,我们先看一个例子:
#include "windows.h"int main(){    MessageBox(0,0,0,0);    return 0;}

MessageBox函数调用的反汇编代码:
    MessageBox(0,0,0,0);00C013EE 8B F4                mov         esi,esp  00C013F0 6A 00                push        0  00C013F2 6A 00                push        0  00C013F4 6A 00                push        0  00C013F6 6A 00                push        0  00C013F8 FF 15 40 93 C0 00    call        dword ptr ds:[0C09340h]  00C013FE 3B F4                cmp         esi,esp  00C01400 E8 36 FD FF FF       call        __RTC_CheckEsp (0C0113Bh)  

    我们看到MessageBox函数的代码不是“ call  绝对地址 ”, 而是一个相对地址。可以知道,平时我们在源码中直接调用API,而编译器在链接过程中将在生成的可执行程序中创建一个导入表,然后把程序中用到的所有相关api函数信息存放于导入表中。导入表保存了函数名称/序号与IAT信息。程序在运行时将加载函数所属dll模块。但是我们将上述代码注入到其他进程中,由于是后期注入的,所以这个代码所使用的IAT与目标进程的IAT很有可能并不相符,从而导致问题。所以对于远程注入的代码,不可以静态直接通过函数名调用API,必须动态获得要调用的函数地址,然后再加以调用。

    

    引发问题:如何动态获得函数地址呢?

使用GetProcAddress可以获得函数地址,前提要求知道所求函数所属句柄模块,这个模块句柄可以通过LoadLibrary获得。然而这两个函数也是API,在注入的代码中不可以直接通过函数名静态调用,也需要动态获得他们的地址。方法一的话,对于NT,2k/xp等系统,所有进程加载Kernel32.dll的模块基址基本都是固定,也意味着上述两个API的地址是一致的,所以我们可以在本地进程中获取其地址,在传给远程进程中。但是这种方法收到系统的限制,地址并不一定可靠。下面说下重点方式二:

    方法二:在远程进程通过解析Kernel32.dll模块获取

Kernel32.dll是系统最基本的模块,几乎所有的进程启动后都会加载。所以我们的思路是:
  • 在目标进程中获取Kernel32.dll模块基址;
  • 解析导出表,获取上述两个API的地址。

① 获取Kernel32.dll模块的加载基址

计算机的CPU除了具有通用寄存器外,还有一个段寄存器FS,Win中在Ring3时FS存储的是当前线程的TEB(线程环境块)。TEB保存的是当前线程各种信息的结构,关于TEB结构定义参阅关于TEB和PEB结构的定义。FS寄存器偏移0x18处就是FS段寄存器在内存中的镜像地址,也就是TEB的地址。其中TEB偏移0x30处也就是Peb成员,指向PEB进程环境块,PEB保存当前进程各种信息。也可以通过FS段寄存器偏移0x30处获得PEB的地址。我们知道进程通常要加载若干个模块信息。每个模块信息使用_LDR_MODULE结构来保存,关于这个结构请看官自行百度查阅。该结构中有链表连接着进程加载的模块信息。关于链表头都被保存在PEB_LDR_DATA结构体中。这样我们就可以不用一个API获得Kernel32.dll模块基址的方法:
#include "windows.h"#include "tchar.h"#include "stdio.h"typedef struct _UNICODE_STRING {    USHORT Length;    USHORT MaximumLength;#ifdef MIDL_PASS    [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;#else // MIDL_PASS    PWSTR  Buffer;#endif // MIDL_PASS} UNICODE_STRING;typedef struct _PEB_LDR_DATA{    ULONG Length; // +0x00    BOOLEAN Initialized; // +0x04    PVOID SsHandle; // +0x08    LIST_ENTRY InLoadOrderModuleList; // +0x0c    LIST_ENTRY InMemoryOrderModuleList; // +0x14    LIST_ENTRY InInitializationOrderModuleList;// +0x1c} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24typedef struct _LDR_MODULE{    LIST_ENTRY InLoadOrderModuleList; //+0x00    LIST_ENTRY InMemoryOrderModuleList; //+0x08    LIST_ENTRY InInitializationOrderModuleList; //0x10    void* BaseAddress; //0x18   //模块基址    void* EntryPoint; //0x1c    ULONG SizeOfImage;    UNICODE_STRING FullDllName;   //模块名称    UNICODE_STRING BaseDllName;    ULONG Flags;    SHORT LoadCount;    SHORT TlsIndex;    HANDLE SectionHandle;    ULONG CheckSum;    ULONG TimeDateStamp;} LDR_MODULE, *PLDR_MODULE;typedef struct _LDR_MODULE2{    LIST_ENTRY InMemoryOrderModuleList; //+0x08    LIST_ENTRY InInitializationOrderModuleList; //0x10    void* BaseAddress; //0x18   //模块基址    void* EntryPoint; //0x1c    ULONG SizeOfImage;    UNICODE_STRING FullDllName;   //模块名称    UNICODE_STRING BaseDllName;    ULONG Flags;    SHORT LoadCount;    SHORT TlsIndex;    HANDLE SectionHandle;    ULONG CheckSum;    ULONG TimeDateStamp;} LDR_MODULE2, *PLDR_MODULE2;typedef struct _PEB{    UCHAR InheritedAddressSpace; // 00h    UCHAR ReadImageFileExecOptions; // 01h    UCHAR BeingDebugged; // 02h    UCHAR Spare; // 03h    PVOID Mutant; // 04h    PVOID ImageBaseAddress; // 08h    PPEB_LDR_DATA Ldr; // 0Ch    //...... 后面的成员忽略} PEB, *PPEB;typedef struct _CLIENT_ID {    HANDLE UniqueProcess;    HANDLE UniqueThread;} CLIENT_ID;typedef struct _NT_TEB{    NT_TIB Tib; // 00h    PVOID EnvironmentPointer; // 1Ch    CLIENT_ID Cid; // 20h    PVOID ActiveRpcInfo; // 28h    PVOID ThreadLocalStoragePointer; // 2Ch    PPEB Peb; // 30h    //...... 后面的成员忽略} NT_TEB, *PNT_TEB;int main(){    // 我们需要查找的DLL模块名    PTCHAR pDllFind = _T("kernel32.dll");    PBYTE pDllBase = NULL;    PNT_TEB pTeb = NULL;    __asm    {        // 获取FS段寄存器在内存中的镜像地址,即TEB的地址        mov eax, fs:[0x18]        mov pTeb, eax    }    // 通过TEB获取PEB的地址    PPEB          pPeb = pTeb->Peb;    PPEB_LDR_DATA pLdrData = pPeb->Ldr;    // 获取链表头, 并保存链表头    PLDR_MODULE pLdrModule = (PLDR_MODULE)(pLdrData->InLoadOrderModuleList.Flink);    PLDR_MODULE pLdrHead = pLdrModule;    PWCHAR wchar = L"\\";    do     {        int i=0, nPos;        while (pLdrModule->FullDllName.Buffer[i])        {            if( pLdrModule->FullDllName.Buffer[i] == *wchar )            {                nPos = i;            }            i++;        }        // 判断该模块名是否是kerne32.dll        i=0;        while (i<=11)        {            if( pLdrModule->FullDllName.Buffer[nPos+i+1] == pDllFind[i] )                i++;            else            {                break;            }        }        if( i==12 )        {            pDllBase = (PBYTE)pLdrModule->BaseAddress;            break;        }        pLdrModule = (PLDR_MODULE)pLdrModule->InLoadOrderModuleList.Flink;    } while (pLdrModule != pLdrHead);    if( pDllBase!=NULL )        printf("kernel32.dll的基址: %x\n\n", pDllBase);    system("pause");}




② 解析Kernel32.dll模块导出表

  • 首先我们要找的API名称在导出函数名称表中的索引;
  • 该索引也就是导出函数序号表对应序号的索引;
  • 通过索引序号表中对应序号的值,该值即为地址表中的索引
按照上面的思路,我们可以补充前文中的代码,获取我们所需API的导出地址:

    PIMAGE_NT_HEADERS pPeHeader = (PIMAGE_NT_HEADERS)(pDllBase+((PIMAGE_DOS_HEADER)pDllBase)->e_lfanew);    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pDllBase+pPeHeader->OptionalHeader.DataDirectory[0].VirtualAddress);    int nNamesNum = pExportDir->NumberOfNames;        // 分别获取序号表、名称表、地址表    PWORD pdwOrdinals = (PWORD)(pDllBase+pExportDir->AddressOfNameOrdinals);    PDWORD pdwNames    = (PDWORD)(pDllBase+pExportDir->AddressOfNames);    PDWORD pdwAddr     = (PDWORD)(pDllBase+pExportDir->AddressOfFunctions);    // 声明两个变量保存两个函数地址    DWORD dwLoadLibrary = 0, dwGetProcAddress = 0;    DWORD dwOrdinal = 0;    PCHAR pLoadLibrary = "LoadLibraryA", pGetProcAddress = "GetProcAddress";    BOOL bLoadLibrary = TRUE, bGetProcAddress = TRUE;    for(int i=0; i<nNamesNum && (bLoadLibrary || bGetProcAddress); i++)    {        char *pFuncName = (char*)pDllBase+pdwNames[i];        int j=0;        while (bLoadLibrary)        {            if( pFuncName[j]!=pLoadLibrary[j]||pLoadLibrary[j]==0 )            {                break;            }            j++;        }        if( bLoadLibrary&&pLoadLibrary[j]==0 )        {            dwOrdinal = pdwOrdinals[i];            dwLoadLibrary = pdwAddr[dwOrdinal];            bLoadLibrary=FALSE;        }        j=0;        while (bGetProcAddress)        {            if( pFuncName[j]!=pGetProcAddress[j]||pGetProcAddress[j]==0 )            {                break;            }            j++;        }        if( bGetProcAddress&&pGetProcAddress[j]==0 )        {            dwOrdinal = pdwOrdinals[i];            dwGetProcAddress = pdwAddr[dwOrdinal];            bGetProcAddress = FALSE;        }    }

使用上述方法得到LoadLibrary和GetProcAddress两个API后,即可动态获得其他任何函数的地址。

后续远程注入代码讲解

首先在远线程中不能直接使用字符串,因为直接使用的字符串位于本进程的全局数据区,而代码注入到远程进程中远程进程的全局数据区并没有这样的字符串。所以我们需要在远程进程申请内存,然后把需要的字符串写入申请的内存。在使用VC++开发环境编写远程注入代码还需注意:
  1. 此函数只能由4K的局部变量,否则会导致远程调用失败,∵超出4K编译器会增加函数实现局部变量,从而导致远程进程调用不存在的函数而导致失败;
  2. 若我们使用switch case结构,则多于3个分支的话要分开写,这与编译器的汇编实现有关,多于3个编译器会构造地址表,而不是一个个cmp  jmp,构造地址表跳转的是绝对地址,这样远程进程会出错;
  3. 在VC6中要将编译选项中的/GZ去掉,因为它会在远程注入函数末尾自动添加检查esp的代码,从而远程代码执行失败。若是vs2005,则把basic RunTime checks选择default。保证不会添加检查esp的代码,实际是/RTC开关。
下面就是将被注入的代码写入远程进程空间内。下面就贴一份《计算机病毒揭秘与对抗》中的源码,虽然本地测试这段代码失败,被注入进程直接就无响应退出了,我也没看出问题在哪,欢迎大家分析原因(问题已解决,源码略有改动):
// InjectCode.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <windows.h>struct _RemoteData {char cLoad[13];                      //保存LoadLibraryA字符串char cGetAddr[15];                   //保存GetProcAddress字符串char cUser32[11];                    //保存user32.dll字符串char cKernel32[13];                  //保存kernel32.dll字符串char cMessageBox[12];                //保存MessageBoxA字符串char cGetModuleFileName[19];         //保存GetModuleFileNameA字符串wchar_t wcKernel32[13];              //保存kernel32.dll宽字节字符串    char cBuffer[128];};typedef int (__stdcall *MESSAGEBOX)(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);typedef DWORD (__stdcall *GETMODULEFILENAME)(HMODULE hModule,LPTSTR lpFilename,DWORD nSize);typedef HMODULE (__stdcall *LOADLIBRARY)(LPCTSTR lpFileName);typedef FARPROC (__stdcall *GETADDRESS)(HMODULE hModule,LPCSTR lpProcName);typedef struct _UNICODE_STRING {    USHORT Length;    USHORT MaximumLength;#ifdef MIDL_PASS    [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;#else // MIDL_PASS    PWSTR  Buffer;#endif // MIDL_PASS} UNICODE_STRING;typedef struct _PEB_LDR_DATA{ULONG Length; // +0x00BOOLEAN Initialized; // +0x04PVOID SsHandle; // +0x08LIST_ENTRY InLoadOrderModuleList; // +0x0cLIST_ENTRY InMemoryOrderModuleList; // +0x14LIST_ENTRY InInitializationOrderModuleList;// +0x1c} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24typedef struct _LDR_MODULE{LIST_ENTRY InLoadOrderModuleList; //+0x00LIST_ENTRY InMemoryOrderModuleList; //+0x08LIST_ENTRY InInitializationOrderModuleList; //0x10void* BaseAddress; //0x18   //模块基址void* EntryPoint; //0x1cULONG SizeOfImage;UNICODE_STRING FullDllName;   //模块名称UNICODE_STRING BaseDllName;ULONG Flags;SHORT LoadCount;SHORT TlsIndex;HANDLE SectionHandle;ULONG CheckSum;ULONG TimeDateStamp;} LDR_MODULE, *PLDR_MODULE;typedef struct _LDR_MODULE2{LIST_ENTRY InMemoryOrderModuleList; //+0x08LIST_ENTRY InInitializationOrderModuleList; //0x10void* BaseAddress; //0x18   //模块基址void* EntryPoint; //0x1cULONG SizeOfImage;UNICODE_STRING FullDllName;   //模块名称UNICODE_STRING BaseDllName;ULONG Flags;SHORT LoadCount;SHORT TlsIndex;HANDLE SectionHandle;ULONG CheckSum;ULONG TimeDateStamp;} LDR_MODULE2, *PLDR_MODULE2;typedef struct _PEB{UCHAR InheritedAddressSpace; // 00hUCHAR ReadImageFileExecOptions; // 01hUCHAR BeingDebugged; // 02hUCHAR Spare; // 03hPVOID Mutant; // 04hPVOID ImageBaseAddress; // 08hPPEB_LDR_DATA Ldr; // 0Ch//...... 后面的成员忽略} PEB, *PPEB;typedef struct _CLIENT_ID {    HANDLE UniqueProcess;    HANDLE UniqueThread;} CLIENT_ID;typedef struct _NT_TEB{NT_TIB Tib; // 00hPVOID EnvironmentPointer; // 1ChCLIENT_ID Cid; // 20hPVOID ActiveRpcInfo; // 28hPVOID ThreadLocalStoragePointer; // 2ChPPEB Peb; // 30h//...... 后面的成员忽略} NT_TEB, *PNT_TEB;static  void  WINAPI RemoteThread(_RemoteData *pParam){PBYTE pKernelBaseMem = NULL;////////PNT_TEB pTeb = NULL; __asm{mov eax,fs:[0x18]    //获得FS段寄存器在内存中的镜像地址,即TEB的地址mov pTeb,eax         }PPEB pPeb = pTeb->Peb;   //通过TEB获得PEB的地址PPEB_LDR_DATA pLdrData = pPeb->Ldr;PLDR_MODULE pLdrModule = (PLDR_MODULE)pLdrData->InLoadOrderModuleList.Flink;PLDR_MODULE pNext = pLdrModule; //获得保存模块信息链表头//遍历链表,查找kernel32.dl模块的基址do {int i=0, iPos;//首先查找模块路径中最后一个"\"的位置while (pNext->FullDllName.Buffer[i]){if (pNext->FullDllName.Buffer[i] == L'\\'){iPos = i;}i++;}i = 0;//判断该模块名称是否为kernel32.dllwhile (i<=11){if (pNext->FullDllName.Buffer[iPos+i+1] == pParam->wcKernel32[i]){i++;}else{break;}}if (i==12){   //如果找到kernel32.dll模块,取得其基址。pKernelBaseMem = (PBYTE)pNext->BaseAddress;break;}pNext = (PLDR_MODULE)pNext->InLoadOrderModuleList.Flink;} while (pLdrModule != pNext); //如果又回到链表头则退出//////////////////////////if (pKernelBaseMem == NULL){return ;}//pKernelBaseMem是Kernel32.dll模块的基址IMAGE_NT_HEADERS *pPeHeader = (IMAGE_NT_HEADERS *)(pKernelBaseMem + ((IMAGE_DOS_HEADER *)pKernelBaseMem)->e_lfanew);//得到Kernel32.dll模块的导出表IMAGE_EXPORT_DIRECTORY *pExportDescriptor =(IMAGE_EXPORT_DIRECTORY *)(pKernelBaseMem + pPeHeader->OptionalHeader.DataDirectory[0].VirtualAddress);//按照名称导出的函数个数int nNamesNum = pExportDescriptor->NumberOfNames;//分别获得导出函数名称序号表、导出函数地址表、导出函数名称地址表。WORD * pNameOrdinalsTable = (WORD *)(pKernelBaseMem+pExportDescriptor->AddressOfNameOrdinals);DWORD * pAddressOfName = (DWORD *)(pKernelBaseMem+pExportDescriptor->AddressOfNames);DWORD * pAddressOfFunctions = (DWORD *)(pKernelBaseMem+pExportDescriptor->AddressOfFunctions);//声明用于保存两个函数地址的变量DWORD dwLoadLibraryAddress = 0, dwGetProcAddress = 0;BOOL bLoadLibrary = TRUE, bGetProcAddress = TRUE;DWORD dwOrdinale = 0; //用于保存导出序号表中对应的序号。for (int i=0; i<nNamesNum&&(bLoadLibrary || bGetProcAddress ); i++){char *pFuncName = (char *)pKernelBaseMem+pAddressOfName[i];int j = 0;while (bLoadLibrary){if (pFuncName[j] != pParam->cLoad[j] || pParam->cLoad[j] == 0){//如果因为pParam->cLoad[j] == 0而退出循环则找到LoadLibraryA函数break;}j++;}if (bLoadLibrary && pParam->cLoad[j] == 0){dwOrdinale = pNameOrdinalsTable[i];dwLoadLibraryAddress = pAddressOfFunctions[dwOrdinale];bLoadLibrary = FALSE;}j=0;while (bGetProcAddress){if (pFuncName[j] != pParam->cGetAddr[j] || pParam->cGetAddr[j] == 0){//如果因为pParam->cLoad[j] == 0而退出循环则找到GetProcAddress函数break;}j++;}if (bGetProcAddress && pParam->cGetAddr[j] == 0){dwOrdinale = pNameOrdinalsTable[i];dwGetProcAddress = pAddressOfFunctions[dwOrdinale];bGetProcAddress = FALSE;}        }LOADLIBRARY myLoadLib = (LOADLIBRARY)(pKernelBaseMem+dwLoadLibraryAddress);GETADDRESS myGetAddr = (GETADDRESS)(pKernelBaseMem+dwGetProcAddress); ((GETMODULEFILENAME)myGetAddr(myLoadLib(pParam->cKernel32), (pParam->cGetModuleFileName)))(NULL, pParam->cBuffer, 128); ((MESSAGEBOX)myGetAddr(myLoadLib(pParam->cUser32), pParam->cMessageBox))(0,pParam->cBuffer,0,0);}static void AfterThread(){}int main(int argc, char* argv[]){void *pFun = NULL, *pParam = NULL;_RemoteData node;HANDLE hThread;strcpy(node.cLoad, "LoadLibraryA");strcpy(node.cGetAddr, "GetProcAddress");strcpy(node.cUser32, "user32.dll");strcpy(node.cMessageBox, "MessageBoxA");strcpy(node.cGetModuleFileName, "GetModuleFileNameA");strcpy(node.cKernel32, "kernel32.dll");wcscpy(node.wcKernel32, L"kernel32.dll");LPBYTE iBack = (LPBYTE) AfterThread;LPBYTE iBof = (LPBYTE) RemoteThread;int cbCodeSize;if (iBof > iBack){cbCodeSize = (iBof - iBack);}else{cbCodeSize = (iBack - iBof);}DWORD dwProcessID;printf("请输入要注入的进程ID:");scanf("%d", &dwProcessID);HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwProcessID);if (hProcess == NULL){printf("该进程无法注入!");return -1;}//在远程进程空间分配内存,从而接受要执行的代码pFun = VirtualAllocEx(hProcess, 0, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );if (pFun == NULL){printf("该进程无法注入!");return -1;}BOOL bSuccess;//拷贝要执行的代码到远程进程空间bSuccess = WriteProcessMemory(hProcess, pFun, RemoteThread, cbCodeSize, NULL);if (!bSuccess){printf("该进程无法注入!");return -1;}pParam = VirtualAllocEx(hProcess, 0, sizeof(_RemoteData), MEM_COMMIT, PAGE_EXECUTE_READWRITE );if (pParam == NULL){printf("该进程无法注入!");return -1;}bSuccess = WriteProcessMemory(hProcess, pParam, &node, sizeof(_RemoteData), NULL);if (!bSuccess){printf("该进程无法注入!");return -1;}//创建远程进程,并执行拷贝过去的代码hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFun, pParam, 0, NULL);//等待远程进程代码执行完毕返回WaitForSingleObject(hThread ,INFINITE);//释放远程进程中分配的内存VirtualFreeEx(hProcess, pParam, 0, MEM_RELEASE);VirtualFreeEx(hProcess, pFun, 0, MEM_RELEASE);//关闭进程句柄CloseHandle(hProcess);//关闭线程句柄CloseHandle(hThread);return 0;}


1.2 隐式加载DLL

上述直接注入代码的方法有些不方便,要动态获取所需函数的地址,并且在变量定义和C语言语句上都有许多限制。这里再介绍一种隐式加载DLL的方法。

关于隐式加载DLL说明:

是将病毒代码写到一个DLL中,在远程进程中并不调用LoadLibrary函数加载这个模块,而是模拟系统加载模块的过程手动架在它,这样就可以在远程进程中并不产生新的模块。

LoadLibrary函数加载一个模块进进程空间内通常分为以下步骤:
  1. PE加载器按照内存对齐粒度的方式将DLL文件加载到内存中;
  2. 若加载基址与建议加载基址不同,PE加载器将遍历重定位表修正重定位表指定的数据;
  3. PE加载器会继续解析导入表,动态获得导入表导入的函数地址填充到IAT中;
  4. 然后就是初始化一些进程内保存模块信息的数据结构从而增加新模块的信息。
实际上远程隐式加载DLL需要如下步骤:
  1. 将DLL文件按照内存对齐粒度的方式读入到本地内存;
  2. 在远程进程中分配内存以存储DLL文件映射到内存后的内容;
  3. 远程进程分配的内存基址就是DLL的实际加载基址,若实际与建议加载基址不同则解析重定位表重定位数据;
  4. 解析导入表,获得导入表函数地址填充IAT;
  5. 执行DLL模块中的目的代码
注意事项:解析导入表,获得导入函数地址填充IAT的过程实际上不应该在本地进程完成,应该重定位数据修复结束的本地DLL内存数据写入远程进程空间内,然后在远程进程中完成IAT的填充过程。这是因为注入的DLL导入表中可能导入了很多模块的函数,而该DLL若要调用这些函数,那么该dll所在进程就必须加载注入dll所导入的函数所属的模块。在远程填充IAT实际是先加载函数所属模块,在获取函数地址填充,这样就保证了注入DLL所要注入的进程中加载了它所依赖的所有模块。
0 0
原创粉丝点击