采用
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 步实现将原目标代码写回目标进程的入口。
上面结构体的代码为汇编代码,对应的汇编为:
准备工作:
第一步:用 CreateProcess ( CREATE_SUSPENDED )启动目标进程。
// 找到目标进程的入口点,函数如下
第二步:找到目标进程的入口,用 ImageHlp 中的函数可以实现。
// 保存目标进程的代码
第三步:将目标进程入口的代码保存起来。
// 第四步:在目标进程的入口写入 LoadLibrary ( MyDll )实现 Dll 的注入。
// 继续目标进程的运行
第五步:用 ResumeThread 运行目标进程。
在监视进程中就结束了自己的任务,剩下的第 6 , 7 , 8 步就需要在 Dll 的 DllMain 中进行配合。
DLL 中用来保存数据的结构体
// 在 DllMain 的 DLL_PROCESS_ATTACH 中调用 InitApiSpy 函数
// 在该函数中实现第 6 , 7 , 8 步
第六步:目标进程就运行了 LoadLibrary ( MyDll ),实现 DLL 的注入。
第七步:目标进程运行完 LoadLibrary(MyDll) 后,将原来的代码写回目标进程的入口。
第八步:目标进程 Jmp 至原来的入口,继续运行程序。
这样就实现了原来的目标,将 DLL 的注入放在目标进程的入口运行,实现了目标进程运行之前运行 我们的注入 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 )启动目标进程。
- // 用CreateProcess启动一个暂停的目标进程
- CreateProcessA(0, szRunFile, 0, 0, FALSE, CREATE_SUSPENDED
- 0, NULL, &stInfo,
- &m_proInfo) ;
// 找到目标进程的入口点,函数如下
第二步:找到目标进程的入口,用 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 至原来的入口,继续运行程序。
这样就实现了原来的目标,将 DLL 的注入放在目标进程的入口运行,实现了目标进程运行之前运行 我们的注入 Dll 的功能。