您的位置:首页 > 运维架构 > Shell

DLL与Shellcode完全卸载自身

2015-06-13 11:15 656 查看



DLL与Shellcode完全卸载自身

搬运自我的百度空间

原创文章,转载请贴出处

远程注入时有一个不痛不痒的问题,DLL与shellcode如何卸载自身,做到无痕清除?

由于我的DLL自卸载用到了Shellcode,所以两个问题都需要解决。

DLL卸载一般使用FreeLibrary,但是如果DLL自己Free自己,会遇到下一条指令(eip/rip)无效的问题(DLL自己的内存空间已经卸载了,而下一条指令仍然指向原来的DLL空间)。微软给出的解决办法是FreeLibraryAndExitThread这个函数,卸载DLL后立马退出。

FreeLibrary只是把DLL的引用计数减1,如果之前多次LoadLibrary,则需要多次FreeLibrary来完全卸载DLL,而FreeLibraryAndExitThread只是调用一次FreeLibrary就退出了,很可能DLL根本没有被卸载。

在DLL外部,以下代码可以完全卸载DLL:

while(ret)
{

ret= FreeLibrary(hmod);
}
但是不能在DLL内部使用。

于是想到可以通过把释放DLL的代码写成Shellcode,这样即使DLL被卸载也不会使eip无效。

通过GlobalAlloc得到缓冲区(不能用malloc,因为它的内存随DLL一同释放了),用VirtualProtect使缓冲区可执行,memcpy复制真正释放dll的代码到缓冲区中,执行代码,就可以完全卸载DLL了(X86代码如下)

一点要注意,我的上一篇文章提到VC需要关闭几个开关才能正常的拷贝函数内存指令

struct myEnvir
{
ptGetProcAddress GetProcAddress;
ptFreeLibrary FreeLibrary;
ptExitThread ExitThread;
ptGlobalFree GlobalFree;
} ;

long__stdcall DoTrulyUnloadDLLAndExitThread(myEnvir* par,HMODULE hmod,DWORD exitcode,void*
pcode)
{

BOOL ret=1;
//ptFreeLibrary PFreeLibrary=(ptFreeLibrary)par->GetProcAddress(par->hmodkernel,par->pfree);
// PExitThread=(ptExitThread)par->GetProcAddress(par->hmodkernel,par->pexit);
while(ret)
{

ret=par->FreeLibrary(hmod);
}

void* pExit=par->ExitThread;
void* pFree=par->GlobalFree;
__asm{
push exitcode
push 0
push pcode
push pExit
jmp pFree
}
return 0;
}

void__stdcall TrulyUnloadDLLAndExitThread(HMODULE hmod,DWORD exitcode)
{

void* p=(BYTE*)GlobalAlloc(GMEM_FIXED,200);
memcpy(p,DoTrulyUnloadDLLAndExitThread,200);
DWORD oldpro;
VirtualProtect(p,100,PAGE_EXECUTE_READWRITE,&oldpro);
typedef
long (__stdcall *ptFun)(myEnvir* par,HMODULE hmod,DWORD exitcode,void* pcode);
ptFun pFun=(ptFun)p;
myEnvir par={GetProcAddress,FreeLibrary,ExitThread,GlobalFree};

pFun(&par,hmod,exitcode,p);
}

要注意所有的API函数地址都被重定位过了,所以我们的shellcode需要调用者提供几个API函数的地址(见结构体myEnvir)

Shellcode仍然需要卸载自己,因为它是由GlobalAlloc分配的,如果这样结束的话就会造成内存泄漏。于是使用了一些堆栈技巧

__asm{
push exitcode
push 0
push pcode
push pExit
jmp pFree
}
Call一个函数会把返回地址push入堆栈,我们自己把exitthread的地址push入堆栈,作为返回地址,然后调用GlobalFree,由于free掉了shellcode后直接到了exitthread,所以不会造成错误。

在x64环境下大同小异,只是调用ExitThread和GlobalFree时用rcx传参数,而且汇编代码需要用shellcode完成

X64代码:

struct myEnvir
{
ptGetProcAddress GetProcAddress;
ptFreeLibrary FreeLibrary;
ptExitThread ExitThread;
ptGlobalFree GlobalFree;
} ;

long__stdcall DoTrulyUnloadDLLAndExitThread(myEnvir* par,HMODULE hmod,DWORD exitcode,void*
pcode)
{

BOOL ret=1;
//ptFreeLibrary PFreeLibrary=(ptFreeLibrary)par->GetProcAddress(par->hmodkernel,par->pfree);
// PExitThread=(ptExitThread)par->GetProcAddress(par->hmodkernel,par->pexit);
while(ret)
{

ret=par->FreeLibrary(hmod);
}
//par->ExitThread(exitcode);
typedef
void (*ptShellcode)(void* pcode,void* pexit,void* pfree);
ptShellcode pFun=(ptShellcode)((char*)pcode+200);
pFun(pcode,par->ExitThread,par->GlobalFree);
/*void* pExit=par->ExitThread;
void* pFree=par->GlobalFree;
__asm{
push exitcode
push 0
push pcode
push pExit
jmp pFree
}*/
return 0;
}

void__stdcall TrulyUnloadDLLAndExitThread(HMODULE hmod,DWORD exitcode)
{

UCHAR shellcode[10]=

"\x48\x83\xEC\x28"//4
"\x52" //5
"\x41\xFF\xE0"; //8

void* p=(BYTE*)GlobalAlloc(GMEM_FIXED,300);
memset(p,0xcc,300);
memcpy(p,DoTrulyUnloadDLLAndExitThread,200);
memcpy((char*)p+200,shellcode,sizeof(shellcode));
DWORD oldpro;
VirtualProtect(p,300,PAGE_EXECUTE_READWRITE,&oldpro);
typedef
long (__stdcall *ptFun)(myEnvir* par,HMODULE hmod,DWORD exitcode,void* pcode);
ptFun pFun=(ptFun)p;
myEnvir par={GetProcAddress,FreeLibrary,ExitThread,GlobalFree};

pFun(&par,hmod,exitcode,p);
}

2014-02-13
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: