分享
 
 
 

实现DLL注入的另一种方法

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

实现DLL注入的另一种方法

将DLL注入进程技术在实现Api函数的监视程序中不可缺少的一项工作。其中最常见的就是用SetWindowsHookEx函数实现了。不过,该方法的缺点是被监视的目标进程必须有窗口,这样,SetWindowsHookEx才能将DLL注入目标进程中。而且,目标程序已经运行了,那么,在窗口创建之前的Api函数就不能被Hook了。

另外一种方法用Debug方案,就可以实现在程序创建时监视所有的Api了,缺点是必须是目标进程的Debug源,在监视程序终了时,目标进程会无条件终了。最大的缺点就是无法调试注入的DLL。

还有其他多种方案也可以实现DLL的注入,在《Windows核心编程》一书中就介绍了8-9种,其中有一种采用CreateProcess的方法,实现起来比较复杂,但没有上面几种方法的局限性。且可以用其他工具(VC等)调试注入的DLL。下面进行介绍。

原理如下:

1. 用CreateProcess(CREATE_SUSPENDED)启动目标进程。

2. 找到目标进程的入口,用ImageHlp中的函数可以实现。

3. 将目标进程入口的代码保存起来。

4. 在目标进程的入口写入LoadLibrary(MyDll)实现Dll的注入。

5. 用ResumeThread运行目标进程。

6. 目标进程就运行了LoadLibrary(MyDll),实现DLL的注入。

7. 目标进程运行完LoadLibrary(MyDll)后,将原来的代码写回目标进程的入口。

8. 目标进程Jmp至原来的入口,继续运行程序。

从原理上可以看出,DLL的注入在目标进程的开始就运行了,而且不是用Debug的方案,这样,就没有上面方案的局限性了。该方案的关键在6,7,8三步,实现方法需要监视进程和DLL合作。下面,结合代码进行分析。

在监视进程中,创建FileMapping,用来保存目标进程的入口代码,同时保证DLL中可以访问。在第7步实现将原目标代码写回目标进程的入口。

// 监视程序和DLL共用的结构体

#pragma pack (push ,1) // 保证下面的结构体采用BYTE对齐(必须)

typedef struct

{

BYTE int_PUSHAD; // pushad 0x60

BYTE int_PUSH; // push &szDLL 0x68

DWORD push_Value; // &szDLL = "ApiSpy.dll"的path

BYTE int_MOVEAX; // move eax &LoadLibrary 0xB8

DWORD eax_Value; // &LoadLibrary

WORD call_eax; // call eax 0xD0FF(FF D0) (LoadLibrary("ApiSpy.dll");

BYTE jmp_MOVEAX; // move eax &ReplaceOldCode 0xB8

DWORD jmp_Value; // JMP的参数

WORD jmp_eax; // jmp eax 0xE0FF(FF E0) jmp ReplaceOldCode;

char szDLL[MAX_PATH]; // "ApiSpy.dll"的FullPath

}INJECT_LOADLIBRARY_CODE, *LPINJECT_CODE;

#pragma pack (pop , 1)

上面结构体的代码为汇编代码,对应的汇编为:

pushad

push szDll

mov eax, &LoadLibraryA

call eax // 实现调用LoadLibrary(szDll)的代码

mov eax, oldentry

jmp eax // 实现在LoadLibrary运行完后, 跳至目标进程的入口继续运行

// FileMaping的结构体

typedef struct

{

LPBYTE lpEntryPoint; // 目标进程的入口地址

BYTE oldcode[sizeof(INJECT_CODE)]; // 目标进程的代码保存

}SPY_MEM_SHARE, * LPSPY_MEM_SHARE;

准备工作:

第一步:用CreateProcess(CREATE_SUSPENDED)启动目标进程。

CreateProcessA(0, szRunFile, 0, 0, FALSE, CREATE_SUSPENDED

0, NULL, &stInfo,

&m_proInfo) ;

// 用CreateProcess启动一个暂停的目标进程

// 找到目标进程的入口点,函数如下

第二步:找到目标进程的入口,用ImageHlp中的函数可以实现。

pEntryPoint = GetExeEntryPoint(szRunFile);

LPBYTE GetExeEntryPoint(char *filename)

{

PIMAGE_NT_HEADERS pNTHeader;

DWORD pEntryPoint;

PLOADED_IMAGE pImage;

pImage = ImageLoad(filename, NULL);

if(pImage == NULL)

return NULL;

pNTHeader = pImage->FileHeader;

pEntryPoint = pNTHeader->OptionalHeader.AddressOfEntryPoint + pNTHeader->OptionalHeader.ImageBase;

ImageUnload(pImage);

return (LPBYTE)pEntryPoint;

}

// 创建FileMapping

hMap = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL,

PAGE_READWRITE, 0, sizeof(SPY_MEM_SHARE), “MyDllMapView”);

// 保存目标进程的代码

第三步:将目标进程入口的代码保存起来。

LPSPY_MEM_SHARE lpMap = pMapViewOfFile(hMap,

FILE_MAP_ALL_ACCESS,

0, 0, 0);

ReadProcessMemory(m_proInfo.hProcess, pEntryPoint,

&lpMap->oldcode, sizeof(INJECT_CODE),

&cBytesMoved);

lpMap->lpEntryPoint = pEntryPoint;

// 第四步:在目标进程的入口写入LoadLibrary(MyDll)实现Dll的注入。

// 准备注入DLL的代码

INJECT_CODE newCode;

// 写入MyDll―――用全路径

lstrcpy(newCode.szDLL, szMyDll);

// 准备硬代码(汇编代码)

newCode.int_PUSHAD = 0x60;

newCode.int_PUSH = 0x68;

newCode.int_MOVEAX = 0xB8;

newCode.call_eax = 0xD0FF;

newCode.jmp_MOVEAX = 0xB8;

newCode.jmp_eax = 0xE0FF;

newCode.eax_Value = (DWORD)&LoadLibrary;

newCode.push_Value=(pEntryPoint + offsetof(INJECT_CODE,szDLL));

// 将硬代码写入目标进程的入口

// 修改内存属性

DWORD dwNewFlg, dwOldFlg;

dwNewFlg = PAGE_READWRITE;

VirtualProtectEx(m_proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwNewFlg, &dwOldFlg);

WriteProcessMemory(m_proInfo.hProcess, pEntryPoint,

&newCode, sizeof(newCode), NULL);//&dwWrited);

VirtualProtectEx(proInfo.hProcess, (LPVOID)pEntryPoint, sizeof(DWORD), dwOldFlg, &dwNewFlg);

// 释放FileMaping 注意,不是Closehandle(hMap)

UnmapViewOfFile(lpMap);

// 继续目标进程的运行

第五步:用ResumeThread运行目标进程。

ResumeThread(m_proInfo.hThread);

在监视进程中就结束了自己的任务,剩下的第6,7,8步就需要在Dll的DllMain中进行配合。

DLL中用来保存数据的结构体

typedef struct

{

DWORD lpEntryPoint;

DWORD OldAddr;

DWORD OldCode[4];

}JMP_CODE,* LPJMP_CODE;

static JMP_CODE _lpCode;

// 在DllMain的DLL_PROCESS_ATTACH中调用InitApiSpy函数

// 在该函数中实现第6,7,8步

第六步:目标进程就运行了LoadLibrary(MyDll),实现DLL的注入。

int WINAPI DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved)

{

switch(dwReason)

{

case DLL_PROCESS_ATTACH:

return InitApiSpy();

……

// InitApiSpy函数的实现

BOOL WINAPI InitApiSpy()

{

HANDLE hMap;

LPSPY_MEM_SHARE lpMem;

DWORD dwSize;

BOOL rc;

BYTE* lpByte;

// 取得FileMapping的句柄

hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, 0, “MyDllMapView”);

if(hMap)

{

lpMem = (LPSPY_MEM_SHARE)MapViewOfFile(hMap,

FILE_MAP_ALL_ACCESS,

0, 0, 0);

if(lpMem)

{

第七步:目标进程运行完LoadLibrary(MyDll)后,将原来的代码写回目标进程的入口。

// 恢复目标进程的入口代码

// 得到mov eax, value代码的地址

_lpCode.OldAddr = (DWORD)((BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX));

_lpCode.lpEntryPoint = (DWORD)lpMem->lpEntryPoint;

// 保存LoadLibrary()后面的代码

memcpy(&_lpCode.OldCode, (BYTE*)lpMem->oldcode + offsetof(INJECT_CODE, jmp_MOVEAX), 2*sizeof(DWORD));

// 恢复目标进程的入口代码

rc = WriteProcessMemory(GetCurrentProcess(), lpMem->lpEntryPoint, lpMem->oldcode, sizeof(INJECT_CODE), &dwSize);

lpByte = (BYTE*)lpMem->lpEntryPoint + offsetof(INJECT_CODE, jmp_MOVEAX);

UnmapViewOfFile(lpMem);

}

CloseHandle(hMap);

}

// 实现自己Dll的其他功能,如导入表的替换

// ……

// 将LoadLibrary后面的代码写为转入处理程序中

// 指令为:mov eax, objAddress

// jmp eax

{

BYTE* lpMovEax;

DWORD* lpMovEaxValu;

WORD* lpJmp;

DWORD fNew, fOld;

fNew = PAGE_READWRITE;

lpMovEax = lpByte;

VirtualProtect(lpMovEax, 2*sizeof(DWORD), fNew, &fOld);

*lpMovEax = 0xB8;

lpMovEaxValu = (DWORD*)(lpMovEax + 1);

*lpMovEaxValu = (DWORD)&DoJmpEntryPoint;

lpJmp = (WORD*)(lpMovEax + 5);

*lpJmp = 0xE0FF; // (FF E0)

VirtualProtect(lpMovEax, 2*sizeof(DWORD), fOld, &fNew);

}

return TRUE;

}

// 转入处理程序

DWORD* lpMovEax;

DWORD fNew, fOld;

void __declspec(naked) DoJmpEntryPoint ()

{

// 恢复LoadLibrary后面的代码

_gfNew = PAGE_READWRITE;

_glpMovEax = (DWORD*)_lpCode.OldAddr;

VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfNew, &_gfOld);

*_glpMovEax = _lpCode.OldCode[0];

*(_glpMovEax + 1) = _lpCode.OldCode[1];

VirtualProtect(_glpMovEax, 2*sizeof(DWORD), _gfOld, &_gfNew);

第八步:目标进程Jmp至原来的入口,继续运行程序。

// 跳至目标代码的入口

_asm popad

_asm jmp _lpCode.lpEntryPoint

}

这样就实现了原来的目标,将DLL的注入放在目标进程的入口运行,实现了目标进程运行之前运行我们的注入Dll的功能。

下面用图形表示在目标进程中代码发生变化的情况。(示意图)

监视程序

FileMapping

目标进程

第一步

启动目标进程

第二步

找到入口

启动(暂停中)

第三步

保存代码

目标进程的代码

无变化

第四步

写入注入代码

同上

入口代码为:

LoadLibrary(MyDll);

jmp xxxxxxxx

其他原来的代码

第五步

运行目标进程

同上

运行LoadLibrary

第六步

无变化

无变化

LoadLibrary中处理

第七步

无变化

无变化

入口代码被写为原来的代码

第7.5步

无变化

无变化

上面jmp xxxxxxxx的代码写为jmp DoJmpEntryPoint

第7.9步

无变化

无变化

运行完LoadLibrary后,转入DoJmpEntryPoint函数中处理

在该函数中,将jmp DoJmpEntryPoint的指令恢复为原指令。

第八步

无变化

无变化

转入目标进程的入口执行,完成注入目标。

上面代码为了方便分析原理,未作错误和防干扰的处理。本人利用该方案在9X,NT和2000中实现了一个监视API的输入输出的实用程序.

我放在verybigbug.home.chinaren.com上,不过,经常连不上。

我放在CSDN上却一直是未发布,不知道怎么回事。哈哈:)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有