URL: http://hi.baidu.com/zxhouse
Email: LZX*QQ.COM
将dll从进程模块列表中移除并保持正常运行,这玩意想想是挺简单,n久前byshell就用了,
简单的思路就是给当前的dll内存映像做份拷贝,然后跳到那份拷贝的地址空间的代码,回头free掉原来的dll,
然后马上用VirtualAlloc在原基址上申请块同样大小的空间,并将那份拷贝还原回去,再跳回去执行。完。
代码写完后再实际程序中应用后发现问题,一旦调用到malloc或new的话就会崩溃,
于是去看了下byshell的代码,发现他申请内存是用VirtualAlloc,但是我大量的代码都是用new来分配,而且类对象不好用VirtualAlloc来分配吧~
经调式才清楚,原来每个dll,windows都会给其分配一个heap,
在freelibrary时系统会将其完全destroy掉,不怕你内存泄露。
调试时在FreeLibrary后HeapDestroy断点正好触发一次,就是这一次那个heap给销毁了。
解决办法就是在FreeLibrary前修改HeapDestroy的第一条指令为return,FreeLibrary后再修复。
实践证明,稳定......
*/
#include <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <psapi.h>
#pragma comment(lib, "Psapi.lib")
#define __printf printf
void HideLibrary(HMODULE hModule, LPVOID pCallBackAddr, LPVOID lParam);
typedef struct
{
HMODULE lpDllBase;
LPVOID lpNewDllBase;
PTHREAD_START_ROUTINE pAddress;
LPVOID lParam;
}UNLOADLIB_CALLBACK, *PUNLOADLIB_CALLBACK;
typedef
LPVOID WINAPI VIRTUALALLOC(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
typedef
BOOL WINAPI VIRTUALFREE(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD dwFreeType
);
typedef
BOOL WINAPI HEAPDESTROY(
HANDLE hHeap
);
typedef
HMODULE WINAPI LOADLIBRARY(
LPCTSTR lpFileName
);
typedef
HANDLE WINAPI CREATETHREAD(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
typedef void * __cdecl MEMCPY(void *, const void *, size_t);
BOOL incLibraryCount(HMODULE hMe)
{
//FreeLibrary后很多系统dll也会free掉,所以将所有已加载的再load一次以增加计数
HANDLE hModsSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if(INVALID_HANDLE_VALUE == hModsSnap)
{
return FALSE;
}
MODULEENTRY32 meModuleEntry;
meModuleEntry.dwSize = sizeof(MODULEENTRY32);
if(!Module32First(hModsSnap, &meModuleEntry))
{
CloseHandle(hModsSnap);
return FALSE;
}
do
{
if(LoadLibrary(meModuleEntry.szModule) == hMe)
FreeLibrary(hMe);
} while(Module32Next(hModsSnap, &meModuleEntry));
CloseHandle(hModsSnap);
return TRUE;
}
//枚举指定进程的所有线程
DWORD WINAPI EnumAndSetThreadState(LPVOID lParam)
{
HANDLE hThreadSnap = NULL;
THREADENTRY32 te32;
memset(&te32,0,sizeof(te32));
te32.dwSize = sizeof(THREADENTRY32);
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
DWORD myThreadId = GetCurrentThreadId();
DWORD pid = GetCurrentProcessId();
if (Thread32First(hThreadSnap, &te32))
{
do
{
if (pid == te32.th32OwnerProcessID)
{
if(myThreadId != te32.th32ThreadID)
{
HANDLE hThread = OpenThread(
THREAD_SUSPEND_RESUME,
FALSE,
te32.th32ThreadID);
if(hThread != NULL)
{
if((int)lParam)
ResumeThread(hThread);
else
SuspendThread(hThread);
CloseHandle(hThread);
}
}
}
}
while (Thread32Next(hThreadSnap,&te32));
}
CloseHandle( hThreadSnap );
return 0;
}
DWORD WINAPI GotoCallBackAddr(LPVOID lParam)
{
PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;
DWORD dwThreadId;
HANDLE hThread;
if(cbFunc->pAddress)
{
hThread = CreateThread(
NULL,
0,
cbFunc->pAddress,
cbFunc->lParam,
0,
&dwThreadId);
if(hThread)
CloseHandle(hThread);
}
//那份dll的拷贝不需要了,释放~
VirtualFree(cbFunc->lpNewDllBase, 0, MEM_DECOMMIT);
delete cbFunc;
return 0;
}
DWORD WINAPI UnLoadLibrary(LPVOID lParam)
{
//__asm INT 3
__printf("UnLoadLibrary Entry./r/n");
BYTE HeapDestroy_HookCode_bak[4];
BYTE HeapDestroy_HookCode[4] = "/xC2/x04/x00";//RETN 0004
MODULEINFO modinfo;
DWORD oldProtect;
PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;
HMODULE hDllInstance = cbFunc->lpDllBase;
char dllpath_bak[MAX_PATH];
GetModuleFileName(hDllInstance, dllpath_bak, sizeof(dllpath_bak));
GetModuleInformation(GetCurrentProcess(), hDllInstance, &modinfo, sizeof(MODULEINFO));
//给所有dll(除了自己)增加计数,防止FreeLibrary的时候那些dll给系统卸载掉
incLibraryCount(hDllInstance);
//保险起见,挂起其他线程,搞定后再恢复
EnumAndSetThreadState((LPVOID)FALSE);
//FreeLibrary之后原来存放api地址的内存也会被释放,
//但是FreeLibrary之后还有些动作,趁现在还没free,关键API记下来
VIRTUALALLOC *_VirtualAlloc = (VIRTUALALLOC*)
GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualAlloc");
LOADLIBRARY *_LoadLibrary = (LOADLIBRARY*)
GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
CREATETHREAD *_CreateThread = (CREATETHREAD*)
GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateThread");
MEMCPY *_memcpy = (MEMCPY*)
GetProcAddress(GetModuleHandle("ntdll.dll"), "memcpy");
//这个很关键,并不是我要调用,是 FreeLibrary 时系统会调用,我要hook它,
//不能给系统破坏这个heap,否则之后的dll貌似能工作,
//但却不能用new或malloc申请内存, VirtualAlloc可以代替之,
//但如果改写好多代码是划不来的,况且一些代码不好改,如list<T>的push内部的new
HEAPDESTROY *_HeapDestroy = (HEAPDESTROY*)
GetProcAddress(GetModuleHandle("kernel32.dll"), "HeapDestroy");
VirtualProtect(_HeapDestroy, 3, PAGE_EXECUTE_READWRITE, &oldProtect);
//修改第一条指令为直接返回
_memcpy(HeapDestroy_HookCode_bak, _HeapDestroy, 3);
_memcpy(_HeapDestroy, HeapDestroy_HookCode, 3);
//Sleep(100);
//终于到这里了~~~^_^!
FreeLibrary(hDllInstance);//释放
//修复刚hook的函数
_memcpy(_HeapDestroy, HeapDestroy_HookCode_bak, 3);
//_memcpy(_RtlFreeHeap, RtlFreeHeap_HookCode_bak, 3);
//在原来的dll基址申请同样大小的内存,并把之前的那份dll拷贝还原回去
if(_VirtualAlloc(hDllInstance,
modinfo.SizeOfImage,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE) == NULL
)
{
//失败,加载原来dll, 以正常方式工作
//注意,不宜在dllmain中调用HideLibrary,LoadLibrary将导致dllmain再次被调用,导致死循环啦
HMODULE hDll = _LoadLibrary(dllpath_bak);
//重新计算回调函数在hDll地址空间的地址
cbFunc->pAddress = (LPTHREAD_START_ROUTINE)
((DWORD)cbFunc->pAddress - (DWORD)hDllInstance + (DWORD)hDll);
LPTHREAD_START_ROUTINE pFunc1 = (LPTHREAD_START_ROUTINE)
((DWORD)EnumAndSetThreadState - (DWORD)hDllInstance + (DWORD)hDll);
//恢复被挂起的线程
_CreateThread(0, 0, pFunc1, (LPVOID)TRUE, 0, 0);
//调用回调函数
if(cbFunc->pAddress)
_CreateThread(0, 0, cbFunc->pAddress, cbFunc->lParam, 0, 0);
return 0;
}
_memcpy(hDllInstance, cbFunc->lpNewDllBase, modinfo.SizeOfImage);
//恢复被挂起的线程
EnumAndSetThreadState((LPVOID)TRUE);
//跳回原dll地址空间的GotoCallBackAddr,由它来释放这边VirtualAlloc申请的指针
_CreateThread(0, 0, GotoCallBackAddr, cbFunc, 0, 0);
return 0;
}
DWORD WINAPI HideLibrary02(LPVOID lParam)
{
//__asm INT 3
__printf("HideLibrary02 Entry./r/n");
PUNLOADLIB_CALLBACK cbFunc = (PUNLOADLIB_CALLBACK)lParam;
MODULEINFO modinfo;
GetModuleInformation(GetCurrentProcess(), cbFunc->lpDllBase, &modinfo, sizeof(MODULEINFO));
//申请一块和当前dll同样大小的内存
cbFunc->lpNewDllBase = VirtualAlloc(NULL, modinfo.SizeOfImage, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(cbFunc->lpNewDllBase == NULL)
return FALSE;
//给当前dll做份拷贝,复制所有数据到刚申请的内存,
memcpy(cbFunc->lpNewDllBase, modinfo.lpBaseOfDll, modinfo.SizeOfImage);
//计算在copy中UnLoadLibrary的地址,并另起线程到该地址执行
void *pNewUnLoadLibrary = LPVOID((DWORD)cbFunc->lpNewDllBase + (DWORD)UnLoadLibrary - (DWORD)modinfo.lpBaseOfDll);
DWORD ThreadId;
HANDLE hThread = CreateThread(0,0,
(LPTHREAD_START_ROUTINE)pNewUnLoadLibrary, (LPVOID)cbFunc, CREATE_SUSPENDED, &ThreadId);
if(hThread == NULL)
{
VirtualFree(cbFunc->lpNewDllBase, 0, MEM_DECOMMIT);
delete cbFunc;
return FALSE;
}
ResumeThread(hThread);
CloseHandle(hThread);
return TRUE;
}
void HideLibrary(HMODULE hModule, LPVOID pCallBackAddr, LPVOID lParam)
{
__printf("HideLibrary Entry./r/n");
PUNLOADLIB_CALLBACK lparam = new UNLOADLIB_CALLBACK;
lparam->lpDllBase = hModule;
lparam->lpNewDllBase = NULL;
lparam->pAddress = (PTHREAD_START_ROUTINE)pCallBackAddr;
lparam->lParam = lParam;
HANDLE hThread = CreateThread(0,0,
HideLibrary02, (LPVOID)lparam, 0, NULL);
if(hThread == NULL)
{
__printf("CreateThread HideLibrary02 Failed./r/n");
delete lparam;
return;
}
CloseHandle(hThread);
return;
}
///////////////////////////////////////////////////////////
//example
//rundll32 UnloadLibrary.dll,Test
DWORD WINAPI testThread(LPVOID lParam)
{
char *ptest = new char[512];
sprintf(ptest, "My Pid: %d/r/n 用一些软件查看下,找不到这个dll吧?", GetCurrentProcessId());
MessageBox(0, ptest, (char*)lParam, 0);
delete ptest;
return 0;
}
DWORD WINAPI testThread02(LPVOID lParam)
{
HMODULE hModule = (HMODULE)lParam;
char *ptest = new char[512];
sprintf(ptest, "testThread02/r/n");
Sleep(500);
MessageBox(0, ptest, "start", 0);
//不设置回调函数,成功还好,一旦失败则gameover
HideLibrary(hModule, 0, 0);
sprintf(ptest, "My Pid: %d/r/n 用一些软件查看下,找不到这个dll吧?", GetCurrentProcessId());
MessageBox(0, ptest, "ok", 0);
delete ptest;
return 0;
}
HMODULE hDll;
extern "C"
{
__declspec(dllexport) int Test();
}
int Test()
{
//1
//主要线程开始工作后再HideLibrary不是很安全
//CreateThread(0, 0, testThread02, hDll, 0, 0);
//
//2
//free掉dll后再开启主要工作线程妥当些
char *p = strdup("hello");
HideLibrary(hDll, testThread, p);
Sleep(60000);
return 0;
}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
hDll = (HMODULE)hModule;
}
break;
}
return TRUE;
}