DLL与Shellcode完全卸载自身
2015-06-13 11:15
656 查看
搬运自我的百度空间
原创文章,转载请贴出处
远程注入时有一个不痛不痒的问题,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
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
相关文章推荐
- 转变思维--使用Python生成Shell命令,批量执行程序
- 执行shell脚本中declare: not found
- shell学习之函数与库(一)
- shell和bat 监控进程,自动关机
- [apache]用shell分析网站的访问情况
- shell编程中 read export echo 理解
- PowerShell配置Exchange Server集线器配置
- HBase Shell的基本用法
- sourceRelative("xxx.bsh")
- SharePoint自动化系列——通过PowerShell在SharePoint中批量做数据
- linux_shell_拆分文件_多进程脚本
- Microsoft.VisualStudio.Shell.14.0.dll 文件位置
- shell编程之sed和awk
- Shell if语句用法小结
- Linux shell脚本——if运算例子
- shell的if语句
- Linux脚本语言shell基础学习
- android adb shell 命令大全
- HBase的SHELL操作和API
- shell case语句