您的位置:首页 > 其它

将dll从进程模块列表中移除并保持正常运行

2007-10-14 13:49 260 查看
将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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: