一种躲避运行时代码校验的方法.docx
- 文档编号:7197824
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:7
- 大小:17.16KB
一种躲避运行时代码校验的方法.docx
《一种躲避运行时代码校验的方法.docx》由会员分享,可在线阅读,更多相关《一种躲避运行时代码校验的方法.docx(7页珍藏版)》请在冰豆网上搜索。
一种躲避运行时代码校验的方法
一种躲避运行时代码校验的方法
我们有时候需要对运行中的程序打内存补丁,或者对它的代码挂一些钩子之类的工作。
但是现在相当多软件进行了运行时的代码检测。
一旦发现内存中的代码被修改掉,就会进行处理。
本文介绍了一种比较特别的办法,用于通过这些检测。
首先需要说一下做运行时代码校验的方法。
一般来说,校验者需要取得当前模块的基地址,通过分析PE结构,获得代码节的偏移和大小,然后对内存中的代码进行CRC或者其他的一些校验。
这其中有个很大的问题,校验者默认了通过这种方式取得的代码节就是当前被使用到的代码,但是事实却不一定如此。
一般编译器正常生成的代码,绝大部分跳转和call语句都使用相对地址,因此,我们完全可以把代码节或者整个exe文件映像复制到内存其他地方,并操作进程内的所有线程,使得它执行在新复制的那份代码中。
这样,校验者仍然在扫描旧的地址,新的那份我们就可以随意修改了。
由于一旦进程开始执行,并且创建其他线程之后,我们通过取得线程Context获得的EIP,多半在系统代码中间,所以难以改变。
唯一的方法就是,在exe的EntryPoint被执行前,将EntryPoint重定向到新的代码,并HookCreateThread,将新线程也重新定位。
可以通过下面几个步骤来实现:
1.如果想处理的进程为a.exe,并且a.exe是由b.exe创建的,那么我们需要hook掉b.exe的进程创建函数,一般是CreateProcess。
2.在Hook的CreateProcess中,以CREATE_SUSPENDED标志创建a.exe。
并向a.exe中注入我们的dll,等待这个dll完成处理之后才ResumeThreada.exe的主线程。
3.注入的dll需要完成几件事。
首先要获得主模块的基地址和大小,并分配足够的空间,将原始映像复制过去。
然后Hook掉EntryPoint,并HookCreateThread,最后恢复a.exe主线程。
4.Hook掉的EntryPoint中,恢复被Hook的EntryPoint代码,防止在后面被检查出来,然后jmp到新分配的代码区域即可。
5.Hook的CreateThread中,重新计算代码线程函数地址,并修改后创建。
这样,所有线程就都在新分配的代码中执行了。
下面是注入的dll的实现代码:
pfnCreateThread
g_pCreateThread =:
:
CreateThread;
PBYTE g_pbyNewImage = NULL;
#pragma
pack(push,1)
typedef
struct
_PUSH_RETN
{
BYTE
byOpcodePush;//0×68
DWORD
dwRetnAddr;
BYTE
byOpcodeRetn;//0xC3
}PUSH_RETN,*PPUSH_RETN;
#pragma
pack(pop)
BYTE
g_abyOldEntry[6]={0};
PBYTE
g_pbyOldEntry =0;
PBYTE
g_pbyNewEntry =0;
//这里使用Detours库Hook掉CreateThread。
BOOL
HookThreadCreate()
{
DetourTransactionBegin();
DetourUpdateThread( GetCurrentThread());
if( DetourAttach(&(PVOID&)g_pCreateThread, Hook_CreateThread)!
= NO_ERROR)
{
DebugOut( TEXT( “HookCreateThreadfailrn”));
}
if( DetourTransactionCommit()!
= NO_ERROR)
{
DebugOut( TEXT( “Hookfailrn”));
return
FALSE;
}
else
{
DebugOut( TEXT( “Hookokrn”));
return
TRUE;
}
}
//Hook的CreateThread里面,重新计算lpStartAddress地址,并按这个地址来创建
HANDLE
WINAPI
Hook_CreateThread(
LPSECURITY_ATTRIBUTES
lpThreadAttributes,
SIZE_T
dwStackSize,
LPTHREAD_START_ROUTINE
lpStartAddress,
LPVOID
lpParameter,
DWORD
dwCreationFlags,
LPDWORD
lpThreadId
)
{
PBYTE
pfn =(PBYTE)lpStartAddress;
HMODULE
hMod =:
:
GetModuleHandle( NULL);
pfn = g_pbyNewImage +(pfn -(PBYTE)hMod);
HANDLE
hThread = g_pCreateThread( lpThreadAttributes, dwStackSize,(LPTHREAD_START_ROUTINE)pfn, lpParameter, dwCreationFlags,lpThreadId);
return
hThread;
}
//Hook掉的入口点,恢复旧代码并跳转到新分配的代码空间
__declspec( naked ) VOID
Hook_EntryPoint()
{
//_asmint3
for ( DWORD
i =0; i < sizeof(g_abyOldEntry); i++)
{//恢复旧的代码
g_pbyOldEntry[i]= g_abyOldEntry[i];
}
_asm
jmp
g_pbyNewEntry;
}
//Hook掉入口点
VOID
HookEntryPoint()
{
DebugOut( _T( “InHookEntryPointrn”));
HMODULE
hMod =:
:
GetModuleHandle( NULL);
PIMAGE_DOS_HEADER
pstDosHeader =(PIMAGE_DOS_HEADER)hMod;
PIMAGE_NT_HEADERS
pstHeader =(PIMAGE_NT_HEADERS)((PBYTE)hMod + pstDosHeader->e_lfanew);
DWORD
dwEntryRVA = pstHeader->OptionalHeader.AddressOfEntryPoint;
PBYTE
pbyRVA =(PBYTE)(hMod)+ dwEntryRVA;
PPUSH_RETN
pstHook =(PPUSH_RETN)pbyRVA;
memcpy( g_abyOldEntry, pbyRVA, sizeof(PUSH_RETN));
pstHook->byOpcodePush =0×68;
pstHook->dwRetnAddr =(DWORD)Hook_EntryPoint;
pstHook->byOpcodeRetn =0xC3;
g_pbyOldEntry = pbyRVA;
g_pbyNewEntry = g_pbyNewImage + dwEntryRVA;
DebugOut( _T(“Newimagebase=0x%X,NewEntryPoint=0x%Xrn
Oldimagebase=0x%X,OldEntryPoint=0x%Xrn”), g_pbyNewImage, g_pbyNewEntry, hMod, g_pbyOldEntry);
DebugOut( _T( “HookEntryPointOKrn”));
}
//在DllMain里面ProcessAttach的时候调用
VOID
OnAttachProcess()
{
HMODULE
hMod =:
:
GetModuleHandle( NULL);
DWORD
dwSize = GetImageSize();
g_pbyNewImage = new
BYTE[dwSize];
DWORD
dwOldProtect =0;
VirtualProtect( g_pbyNewImage, dwSize, PAGE_EXECUTE_READWRITE,&dwOldProtect);
VirtualProtect((LPVOID)hMod, dwSize, PAGE_READWRITE,&dwOldProtect);
memcpy( g_pbyNewImage,(LPVOID)hMod, dwSize);
HookEntryPoint();
HookThreadCreate();
}
//获得主模块映像大小
DWORD
GetImageSize()
{
HANDLE
hShot = NULL;
BOOL
blResult = FALSE;
MODULEENTRY32
stInfo ={0};
stInfo.dwSize = sizeof( MODULEENTRY32);
TCHAR
tszTmp[MAX_PATH]={0};
:
:
GetModuleFileName( GetModuleHandle(NULL), tszTmp, MAX_PATH);
_tcslwr( tszTmp);
size_t
s = _tcslen(tszTmp);
for ( DWORD
i =0; i < s; i++)
{
if ( tszTmp[s - i]== ”)
{
s = s - i +1;
break;
}
}
hShot =:
:
CreateToolhelp32Snapshot( TH32CS_SNAPMODULE,:
:
GetCurrentProcessId());
if ( INVALID_HANDLE_VALUE == hShot)
{
DebugOut( _T( “CreateToolhelp32Snapshotfail.Err=%drn”), GetLastError());
return 0;
}
blResult =:
:
Module32First( hShot,&stInfo);
while ( blResult)
{
_tcslwr( stInfo.szModule);
if ( _tcscmp( stInfo.szModule, tszTmp + s)==0)
{
CloseHandle( hShot);
return
stInfo.modBaseSize;
}
blResult =:
:
Module32Next( hShot,&stInfo);
}
CloseHandle( hShot);
return 0;
}
这种方法对加壳之后的exe作用有限,因为Hook的并不是真实的OEP。
这样做常常会造成壳执行过程中,或者跳转到OEP之后迅速崩溃。
本文仅仅是介绍一种思想,有时候巧妙的构思可以使得复杂问题很快得到解决。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 一种 躲避 运行 时代 校验 方法