自己动手写API监控工具
需求来源:
1.长期做木马外挂的逆向分析工作,基本上也就看看API调用情况也就知道大概的原理了,手工一个个地分析无疑浪费时间和精力。
2.想知道一个感兴趣的应用程序是如何编写的,监控下API调用情况也可以基本了解实现原理。
现状:
目前市面上这样的工具还是蛮多的,有AutoDebug,ApiTracing,ApiMonitor,bladeapimonitor,不多有点鱼龙混杂。
API Monitor:http://www.rohitab.com/apimonitor
这一款还是蛮不错的,而且还是免费的。
实现原理:
1.使用Hook的方式修改API的入口代码跳转到自己的处理代码处,分析完了之后再跳回去接着执行。
典型的实现方法是使用微软提供的detours库,本文主要介绍这种方法。
2.逆向分析Api Monitor的实现原理实现,这个后面再说。
一、使用detours库监控API调用。
1.下载detours库并安装:http://research.microsoft.com/en-us/downloads/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/default.aspx
2.主要功能在dll中,首先要实现一个dll。
3.把这个dll注入到待分析的目标进程中,注入方法很多,可以参考我的前面一篇文章:Ring3下Dll注入方法整理汇总 (http://www.cnblogs.com/daxingxing/archive/2011/12/16/2290353.html)。
注意如果使用detour库的DetourCreateProcessWithDll函数实现注入,你的dll需要导出一个名为#1的函数,def文件可以这么写:
void __stdcall NullExport(){}
LIBRARY USEDETOUR
EXPORTS
NullExport @1
hook = NullExport
#1 = NullExport
4.在dll的DllMain里完成Hook操作:
BOOL APIENTRY DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
switch (dwReason) {
case DLL_PROCESS_ATTACH:
{
TCHAR szMyPath[MAX_PATH];
TCHAR szExePath[MAX_PATH];
GetModuleFileName(NULL,szMyPath,MAX_PATH);
GetModuleFileName(NULL,szExePath,MAX_PATH);
char *p1 = strrchr(szMyPath,'\\');
char *p2 = strrchr(szExePath,'\\');
if ( p2 && _tcsicmp(p2+1,"ollyice.exe") ){
Trace("----------------------------------------\n");
Trace("当前进程:%s",szExePath);
DetourRestoreAfterWith();
ProcessAttach(hinst);
}else{
return FALSE;
}
}
break;
case DLL_PROCESS_DETACH:
ProcessDetach(hinst);
Trace("ApiTracer: DLL_PROCESS_DETACH\n");
Trace("----------------------------------------\n");
break;
case DLL_THREAD_ATTACH:
Trace("ApiTracer: DLL_THREAD_ATTACH\n");
ThreadAttach(hinst);
break;
case DLL_THREAD_DETACH:
Trace("ApiTracer: DLL_THREAD_DETACH\n");
ThreadDetach(hinst);
break;
}
return TRUE;
}
BOOL ProcessAttach(HMODULE hDll)
{
s_bLog = FALSE;
s_nTlsIndent = TlsAlloc();
s_nTlsThread = TlsAlloc();
ThreadAttach(hDll);
LONG error = AttachDetours();
if (error != NO_ERROR) {
//Syelog(SYELOG_SEVERITY_FATAL, "### Error attaching detours: %d\n", error);
}
return TRUE;
}
LONG AttachDetours(VOID)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
// For this many APIs, we'll ignore one or two can't be detoured.
DetourSetIgnoreTooSmall(TRUE);
AttachApis();
SetExportApiHook("kernel32.dll");
SetExportApiHook("user32.dll");
SetExportApiHook("shell32.dll");
SetExportApiHook("shlwapi.dll");
SetExportApiHook("gdi32.dll");
SetExportApiHook("advapi32.dll");
SetExportApiHook("urlmon.dll");
SetExportApiHook("wininet.dll");
SetExportApiHook("ws2_32.dll");
if ( DetourTransactionCommit()!=NO_ERROR ) {
OutputDebugStringA("AttachDetours failed on DetourTransactionCommit");
PVOID *ppbFailedPointer = NULL;
LONG error = DetourTransactionCommitEx(&ppbFailedPointer);
Trace("DetourTransactionCommitEx Error: %d (%p/%p)",error, ppbFailedPointer, *ppbFailedPointer);
return error;
}else{
OutputDebugStringA("AttachDetours OK");
}
return 0;
}
AttachDetours的写法参考了detours里面samples的代码,我这里调用AttachApis主要Hook了一些感兴趣的Api,其他的都apis只Hook了几个常见系统dll的导出函数。
举个简单的例子,如果我们关心WinExec的参数,那么可以在AttachApis里单独写出hook代码:
UINT (WINAPI *Real_WinExec)( LPCSTR lpCmdLine, UINT uCmdShow ) = WinExec;
UINT WINAPI Mine_WinExec( LPCSTR lpCmdLine, UINT uCmdShow )
{
Trace("WinExec: %s",lpCmdLine);
return Real_WinExec(lpCmdLine,uCmdShow);
}
像CreateFileA、CreateFileW、CreateProcessA、CreateProcessW以及注册表等等操作都可以按照这种方法写。
剩余的api怎么办?我们列举的”感兴趣api“不全怎么办?遗漏了怎么办?
那就是用通用的办法,为每个系统dll的导出函数使用统一的HOOK代码:
class CApiHooker
{
public:
CApiHooker(){
m_pShellCode = NULL;
}
CApiHooker(LPCSTR lpszFuncName,DWORD dwFuncAddr){
m_pShellCode = NULL;
SetHook(lpszFuncName,(DWORD)dwFuncAddr);
}
CApiHooker(LPCTSTR lpszDllName,LPCSTR lpszFuncName){
m_pShellCode = NULL;
HMODULE hModule = GetModuleHandle(lpszDllName);
if ( hModule==NULL ){
hModule = LoadLibrary(lpszDllName);
}
SetHook(lpszFuncName,(DWORD)GetProcAddress(hModule,lpszFuncName));
}
void SetHook(LPCSTR lpszFuncName,DWORD dwFuncAddr){
if ( lpszFuncName==NULL || dwFuncAddr==0 ){
return;
}
//Trace("%s:%08X",lpszFuncName,dwFuncAddr);
if ( m_pShellCode ){
delete []m_pShellCode;
m_pShellCode = NULL;
}
//分配一块内存
int nLen = (int)strlen(lpszFuncName);
int nSize = SHELLCODESIZE+sizeof(DWORD)*4+nLen+1;
DWORD dwOldProtect = 0;
m_pShellCode = new BYTE[nSize];
if ( m_pShellCode ){
memset(m_pShellCode,0,nSize);
memcpy(m_pShellCode,__SHELLCODEBEGIN,SHELLCODESIZE);
memcpy(m_pShellCode+OFFSET_FUNCNAME,lpszFuncName,nLen);
*(PDWORD)(m_pShellCode+OFFSET_ORIGNFUNC) = dwFuncAddr;
LONG lError = DetourAttach((PVOID*)(m_pShellCode+OFFSET_ORIGNFUNC), m_pShellCode);
if ( lError!=NO_ERROR ){
Trace("DetourAttach: %s Error: %d",lpszFuncName,lError);
}
}else{
Trace("no memory: %s ",lpszFuncName);
}
}
private:
PBYTE m_pShellCode;
};
主要是分配了一块内存(内存我没有管理并释放),我们把这块内存当做是类的内存空间,只不多它很特殊,里面存储的有:
API名、伪原始API地址(为什么说是伪呢?因为实际上是跳转到detours的一个跳转代码,再有这个跳转代码跳回到原始api的代码空间的,我们可以不管这些细节,把他们看透明)、
返回地址、命中次数(命中超过一次不再打印任何信息,防止出现海量信息与分析不利,也就是同一个api无论调用多少次,只处理一次)。
最重要的是这个内存里面还包含有一段可执行代码,就是当api调用时会jmp到的内存代码,这段代码如下:
__declspec(naked) void __SHELLCODEBEGIN(){
__asm{
_start:
//获取this
call _next1;
_next1:
pop eax;
sub eax,offset _next1;
add eax,offset _start;
//如果返回地址太大,说明是从系统dll调进来的,不处理
cmp dword ptr[esp+4],0x70000000;
jg _orign;
#pragma region 命中次数大于1的不再处理,以免产生垃圾信息
push eax;
add eax,OFFSET_HITSCOUNT;
inc dword ptr [eax]; //inc会改变标志寄存器,因此放在cmp前
cmp dword ptr [eax],1;
pop eax;
jg _orign;
#pragma endregion
_analyze:
#pragma region 调用OutputDebugStringA打印函数名
pushad;
add eax,OFFSET_FUNCNAME;
push eax;
call pfnOutputDebugString;
popad;
#pragma endregion
#pragma region 调用自定义的参数分析函数
//pushad;
//push eax; //保存this
//push eax; //dummy,只是让esp+4
//lea eax,[esp+0x2C]; //pushad让esp减小了0x20,两个push eax减小了8,再加上返回地址占的4个
//mov dword ptr[esp+4],eax; //参数1:函数入口时的esp
//pop eax; //恢复this
//add eax,OFFSET_FUNCNAME;
//push eax; //参数2:name
//sub eax,OFFSET_FUNCNAME;
//add eax,OFFSET_FUNCID;
//push dword ptr [eax]; //参数3:id
//call pfnParamAnalyze;
//popad;
#pragma endregion
_orign: //调用原来的函数
//获取this,这里eax没有被改变不用再次获取了
#if 0
call _next2;
_next2:
pop eax;
sub eax,offset _next2;
add eax,offset _start;
#endif
#if 1
add eax,OFFSET_ORIGNFUNC;
jmp dword ptr[eax];
#else
//将原返回地址弹出并保存
add eax,OFFSET_ORIGNRET;
pop dword ptr[eax];
sub eax,OFFSET_ORIGNRET;
//调用原函数
add eax,OFFSET_ORIGNFUNC;
call dword ptr [eax];
push eax; //dummy,只是让esp+4,为返回地址预留的空间
push eax; //保存api的返回值
//#pragma region 返回值分析处理
// pushad;
// push eax; //参数2:返回值
//
////获取this
// call _next4;
//_next4:
// pop eax;
// sub eax,offset _next4;
// add eax,offset _start;
//
//
// add eax,OFFSET_FUNCID;
// push dword ptr[eax];
// call pfnParamAnalyze2;
// popad;
//#pragma endregion
//获取this
call _next3;
_next3:
pop eax;
sub eax,offset _next3;
add eax,offset _start;
//原始返回地址入栈,准备返回
add eax,OFFSET_ORIGNRET;
mov eax,dword ptr[eax];
mov dword ptr[esp+4],eax; //返回地址
//恢复api的返回值
pop eax;
ret;
#endif
}
}
__declspec(naked) void __SHELLCODEEND(){}
这段代码通过重定位找到自身内存地址,取出相对偏移处的信息,例如:函数名,命中次数,伪原始API地址等。
上面我还加了个返回地址判断,简单判断是不是从用户代码领空调进来的。
如果命中次数不大于一,则先打印出这个api的名称,然后进入参数分析调用:pfnParamAnalyze。
其次是调用原始api并把结果传递给pfnParamAnalyze2执行返回值分析。
注意在调用原始api时需要把堆栈的返回地址保存起来,然后弹出堆栈再执行call,分析完返回结果后因为还要返回,需要把刚刚保存的返回地址再压入堆栈,
eax存入原始api的返回值调用ret返回。
如果不进行参数分析和返回值分析的话,代码可以简化:
__declspec(naked) void __SHELLCODEBEGIN(){
__asm{
_start:
//获取this
call _next1;
_next1:
pop eax;
sub eax,offset _next1;
add eax,offset _start;
//如果返回地址太大,说明是从系统dll调进来的,不处理
cmp dword ptr[esp+4],0x70000000;
jg _orign;
#pragma region 命中次数大于1的不再处理,以免产生垃圾信息
push eax;
add eax,OFFSET_HITSCOUNT;
inc dword ptr [eax]; //inc会改变标志寄存器,因此放在cmp前
cmp dword ptr [eax],1;
pop eax;
jg _orign;
#pragma endregion
#pragma region 调用OutputDebugStringA打印函数名
pushad;
add eax,OFFSET_FUNCNAME;
push eax;
call pfnOutputDebugString;
popad;
#pragma endregion
_orign: //调用原来的函数
add eax,OFFSET_ORIGNFUNC;
jmp dword ptr[eax];
#endif
}
}
__declspec(naked) void __SHELLCODEEND(){}
具体实践应用:
监控分析一款恶意程序的行为:
00000009 0.11675199 [592] GetCurrentProcess
00000010 0.11732692 [592] VirtualProtect
00000011 0.11738782 [592] FlushInstructionCache
00000012 0.12691809 [592] WSAGetLastError
00000013 0.12713823 [592] RegOpenKeyExW:Software\Microsoft\Windows NT\CurrentVersion\Diagnostics
00000014 0.12747011 [592] GetModuleFileNameW:C:\WINDOWS\system32\msvcrt.dll
00000015 0.12754218 [592] RegOpenKeyExW:SYSTEM\Setup
00000016 0.19361930 [592] GetModuleFileNameW:C:\WINDOWS\system32\SHELL32.dll
00000017 0.19382073 [592] LoadLibraryW:comctl32.dll
00000018 0.22545603 [592] CreateFileW(C:\WINDOWS\WindowsShell.Manifest,80000000,00000005,00000000,00000003,00000000,00000000)
00000019 0.22575998 [592] RegOpenKeyExW:Control Panel\Desktop
00000020 0.22588485 [592] RegOpenKeyExW:software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
00000021 0.22631508 [592] RegOpenKeyExW:Software\Microsoft\Windows NT\CurrentVersion\LanguagePack
00000022 0.22653298 [592] LoadLibraryW:comctl32.dll
00000023 0.22662266 [592] GetVersionExA
00000024 0.22688806 [592] HeapCreate
00000025 0.22694673 [592] GetModuleHandleA
00000026 0.22699533 [592] InitializeCriticalSectionAndSpinCount
00000027 0.22703835 [592] TlsAlloc
00000028 0.22709143 [592] GetCommandLineA
00000029 0.22717887 [592] GetEnvironmentStringsW
00000030 0.22723000 [592] WideCharToMultiByte
00000031 0.22733755 [592] FreeEnvironmentStringsW
00000032 0.22737974 [592] GetStartupInfoA
00000033 0.22742221 [592] GetStdHandle
00000034 0.22746550 [592] GetFileType
00000035 0.22755072 [592] LockResource
00000036 0.22759373 [592] GetCPInfo
00000037 0.22764011 [592] MultiByteToWideChar
00000038 0.22769095 [592] LCMapStringW
00000039 0.22773927 [592] GetModuleFileNameA
00000040 0.22779627 [592] GetModuleFileNameW:C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe
00000041 0.22786835 [592] DisableThreadLibraryCalls
00000042 0.22792730 [592] GetModuleFileNameW:C:\WINDOWS\system32\comctl32.dll
00000043 0.23956089 [592] CreateActCtxW
00000044 0.23967655 [592] ProcessIdToSessionId
00000045 0.23973075 [592] RegisterClipboardFormatW
00000046 0.23979360 [592] SystemParametersInfoW
00000047 0.23984417 [592] GetSystemMetrics
00000048 0.24003386 [592] RegOpenCurrentUser
00000049 0.24015650 [592] OpenProcessToken
00000050 0.24020930 [592] AllocateAndInitializeSid
00000051 0.24025959 [592] CheckTokenMembership
00000052 0.24030988 [592] FreeSid
00000053 0.24036379 [592] RegOpenKeyExW:Control Panel\Desktop
00000054 0.24041463 [592] RegQueryValueExW
00000055 0.24049397 [592] RegCloseKey
00000056 0.24053727 [592] GetSysColor
00000057 0.24057890 [592] GetSysColorBrush
00000058 0.24066047 [592] GetStockObject
00000059 0.24073562 [592] LoadLibraryW:imm32.dll
00000060 0.24077949 [592] ActivateActCtx
00000061 0.24083062 [592] LoadCursorW
00000062 0.24122563 [592] RegisterClassW
00000063 0.24131754 [592] DeactivateActCtx
00000064 0.24138822 [592] GetModuleFileNameW:C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe
00000065 0.24143180 [592] GetModuleFileNameW:C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe
00000066 0.24148795 [592] GetTempPathA
00000067 0.24153125 [592] DeleteFileA:C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\dsad11.exe
00000068 0.24159327 [592] CopyFileA:C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe -> C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\dsad11.exe
00000069 0.24200618 [592] CreateFileW(C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe,80000000,00000001,00000000,00000003,08200000,00000000)
00000070 0.24209110 [592] CreateFileW(DBWIN_DATA_READY,80000000,00000003,00000000,00000003,08200000,00000000)
00000071 0.24215676 [592] CreateFileW(DBWIN_DATA_READY,80000000,00000001,00000000,00000003,08000000,00000000)
00000072 0.24221683 [592] CreateFileW(DBWIN_DATA_READY,80000000,00000003,00000000,00000003,08000000,00000000)
00000073 0.24227074 [592] LoadLibraryA:Kernel32.dll
00000074 0.24232325 [592] LoadLibraryA:ADVAPI32.dll
00000075 0.24236795 [592] GetSystemDirectoryA
00000076 0.24244115 [592] RegOpenKeyExA
00000077 0.24247187 [592] Sleep
00000078 0.73963284 [592] CreateRemoteThread
00000079 0.73989373 [592] CreateThread
00000080 0.74028265 [592] ApiTracer: DllMain DLL_THREAD_ATTACH
00000081 0.74034411 [592] FindWindowA
00000082 0.74090648 [592] ApiTracer: DllMain DLL_THREAD_ATTACH
00000083 0.77042723 [592] FindResourceA
00000084 0.77049428 [592] LoadResource
00000085 0.77063507 [592] SizeofResource
00000086 0.77072144 [592] CreateFileA(C:\WINDOWS\system32\wksbqizm.tmp,C0000000,00000000,00000000,00000002,00000000,00000000)
00000087 0.77402937 [592] CreateFileW(C:\WINDOWS\system32\wksbqizm.tmp,C0000000,00000000,00000000,00000002,00000000,00000000)
00000088 0.77412099 [592] FreeResource
00000089 0.77417547 [592] CreateFileA(C:\WINDOWS\system32\wksbqizm.tmp,40000000,00000002,00000000,00000003,00000000,00000000)
00000090 0.77419758 [592] CreateFileW(C:\WINDOWS\system32\wksbqizm.tmp,40000000,00000002,00000000,00000003,00000000,00000000)
00000091 0.77429843 [592] GetTickCount
00000092 0.78551579 [592] SetFilePointer
00000093 0.87909049 [592] CreateFileA(C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe,80000000,00000001,00000000,00000003,00000000,00000000)
00000094 0.87923717 [592] CreateFileW(C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe,80000000,00000001,00000000,00000003,00000000,00000000)
00000095 0.87932491 [592] ReadFile
00000096 0.87938887 [592] GetSystemTime
00000097 0.88373411 [592] SystemTimeToFileTime
00000098 0.88379222 [592] CreateFileA(C:\WINDOWS\system32\wksbqizm.tmp,C0000000,00000001,00000000,00000003,00000000,00000000)
00000099 0.88384420 [592] CreateFileW(C:\WINDOWS\system32\wksbqizm.tmp,C0000000,00000001,00000000,00000003,00000000,00000000)
00000100 0.88392186 [592] SetFileTime
00000101 0.88457751 [592] RegOpenKeyA
00000102 0.88471860 [592] MoveFileExA:C:\WINDOWS\system32\wksbqizm.tmp -> C:\WINDOWS\system32\wksbqizm.dll
00000103 0.88485968 [592] ApiTracer: DllMain DLL_THREAD_ATTACH
00000104 0.88863558 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000105 0.88909096 [592] RegCreateKeyA:InProcServer32
00000106 0.88920158 [592] MoveFileExA:C:\WINDOWS\system32\wksbqizm.tmp -> C:\WINDOWS\system32\wksbqizm.dll
00000107 0.89008576 [592] GetFileAttributesA
00000108 0.89016062 [592] MoveFileExA:C:\WINDOWS\system32\wksbqizm.tmp -> C:\WINDOWS\system32\wksbqizm.dll
00000109 0.89029777 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000110 0.89063919 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000111 0.89359456 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000112 0.89365548 [592] SleepEx
00000113 0.89430809 [592] RegQueryValueExA
00000114 0.89451480 [592] RegSetValueExA:wksbqizm.dll,data:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000115 0.89493579 [592] RegSetValueExA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C},data:(null)
00000116 2.89548540 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000117 2.89559031 [592] RegCreateKeyA:InProcServer32
00000118 2.89567256 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000119 2.89587092 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000120 2.89598632 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000121 4.87972498 [592] WinExec:
00000122 4.89684963 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000123 4.89699173 [592] RegCreateKeyA:InProcServer32
00000124 4.89707708 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000125 4.89712191 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000126 4.89718342 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000127 6.89585352 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000128 6.89605188 [592] RegCreateKeyA:InProcServer32
00000129 6.89613724 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000130 6.89618254 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000131 6.89624643 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000132 8.89569187 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000133 8.89588070 [592] RegCreateKeyA:InProcServer32
00000134 8.89596558 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000135 8.89601040 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000136 8.89607334 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000137 10.89583778 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000138 10.89595222 [592] RegCreateKeyA:InProcServer32
00000139 10.89612103 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000140 10.89617062 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000141 10.89623356 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000142 12.89584446 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000143 12.89597225 [592] RegCreateKeyA:InProcServer32
00000144 12.89622688 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000145 12.89627838 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000146 12.89634323 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000147 14.89621544 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000148 14.89632416 [592] RegCreateKeyA:InProcServer32
00000149 14.89640522 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000150 14.89644814 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000151 14.89651489 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000152 16.89616394 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000153 16.89627457 [592] RegCreateKeyA:InProcServer32
00000154 16.89635468 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000155 16.89640045 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000156 16.89646339 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000157 18.89614105 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000158 18.89632607 [592] RegCreateKeyA:InProcServer32
00000159 18.89640999 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000160 18.89645576 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000161 18.89651871 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000162 20.89516830 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000163 20.89536476 [592] RegCreateKeyA:InProcServer32
00000164 20.89544868 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000165 20.89549446 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000166 20.89555550 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000167 22.89520454 [592] RegCreateKeyA:{B38E77C6-E3E1-4b0f-BC51-6A8352868C4C}
00000168 22.89532471 [592] RegCreateKeyA:InProcServer32
00000169 22.89548683 [592] RegSetValueA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000170 22.89553642 [592] RegSetValueExA:(null),data:C:\WINDOWS\system32\wksbqizm.dll
00000171 22.89560127 [592] RegSetValueExA:ThreadingModel,data:Apartment
00000172 24.88182449 [592] GetTempFileNameA
00000173 24.88211632 [592] CreateFileW(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\1.tmp,80000000,00000000,00000000,00000001,00000080,00000000)
00000174 24.88360977 [592] GetModuleFileNameW:C:\Documents and Settings\Administrator\桌面\bigfoot_1.exe
00000175 24.88372803 [592] CreateFileA(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\1.tmp.bat,40000000,00000003,0012F4A0,00000002,00000080,00000000)
00000176 24.88378334 [592] CreateFileW(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\1.tmp.bat,40000000,00000003,0012F4A0,00000002,00000080,00000000)
00000177 24.88984871 [592] WinExec: C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\1.tmp.bat
00000178 24.89008331 [592] CreateFileW(C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\1.tmp.bat,80000000,00000003,00000000,00000003,00000080,00000000)
00000179 24.89018250 [592] TerminateProcess
垃圾信息很少,可以很多很方便地提前出有用信息出来。
----------------------------------------------------------
二、逆向分析Api Monitor的实现原理实现
该工具并不像detours库一样改动真实api的入口代码,它动态获取程序的导入表信息并把导入函数地址由原本执行api的修改为指向自己的shellcode。
程序通过GetProcAddress获取的api函数地址也会被修改,API Monitor会先调用原始GetProcAddress然后返回给进程的是指向自己shellcode的地址。
跟踪了GetProcAddress和CreateProcessA的调用,shellcode分别为:
01420000 51 push ecx
01420001 68 30AE807C push kernel32.GetProcAddress
01420006 68 00004000 push 400000
0142000B 68 68C6E000 push 0E0C668
01420010 E8 6B28BE0E call apimonit.10002880
01420015 C2 0800 retn 8
01420594 51 push ecx
01420595 68 6B23807C push kernel32.CreateProcessA
0142059A 68 00004000 push 400000
0142059F 68 3870E000 push 0E07038
014205A4 E8 D722BE0E call apimonit.10002880
014205A9 C2 2800 retn 28
ecx入栈,这个一般作为类的this指针,保存下来做分析理所应当;
真实api函数地址入栈,分析函数最终要调用这个地址的;
猜测进程映像基址入栈,作用未知;
猜测api信息数据地址入栈,类似于一个lpFunctionInfo的数据,在分析函数中可以直接使用,获取该函数几个参数,各个参数的具体情况如何等等;
调用分析函数;
retn后面的个数就是当前api的具体参数个数乘以四后的数值。
为此我模拟了一段代码:
BYTE g_shellcode[] = {
'\x51',
'\x68',
'\x00',
'\x00',
'\x00',
'\x00',
'\x68',
'\x00',
'\x00',
'\x40',
'\x00',
'\x68',
'\x00',
'\x00',
'\x00',
'\x00',
'\xE8',
'\x00',
'\x00',
'\x00',
'\x00',
'\xC2',
'\x00',
'\x00'
};
#define OFFSET_REALAPIADDR 2
#define OFFSET_IMAGEBASE 7
#define OFFSET_FUNCINFO 12
#define OFFSET_CALLOFFSET 17
#define OFFSET_RETURNNUM 22
void __stdcall myParamAnalyze(DWORD dwFuncId, LPCTSTR lpszFuncName, DWORD dwEsp, DWORD dwEcx)
{
if ( dwFuncId==1 ){
DWORD dwParamNum = 10;
PDWORD pParam = ((PDWORD)&dwEcx+2);
Trace("CreateProcess: app: %s cmd:%s\n",(LPCTSTR)(*pParam),(LPCTSTR)(*(pParam+1)));
}
return;
}
FARPROC (WINAPI * Real_GetProcAddress)(HMODULE hModule,LPCSTR lpProcName) = GetProcAddress;
FARPROC WINAPI Mine_GetProcAddress(HMODULE hModule,LPCSTR lpProcName)
{
DWORD dwFuncId = 0;
DWORD dwParamNum = 0;
Trace("GetProcAddress:%s",lpProcName);
FARPROC pfn = Real_GetProcAddress(hModule,lpProcName);
if ( pfn!=NULL ){
if ( strcmp(lpProcName,"CreateProcessA")==0 ){
//这里测试CreateProcessA,假设检索的函数id为1,检索的函数参数个数为10
dwFuncId = 1;
dwParamNum = 10;
PBYTE pBuff = new BYTE[sizeof(g_shellcode)];
memcpy(pBuff,g_shellcode,sizeof(g_shellcode));
*(DWORD*)(pBuff + OFFSET_REALAPIADDR) = (DWORD)pfn;
*(DWORD*)(pBuff + OFFSET_FUNCINFO) = dwFuncId;
*(DWORD*)(pBuff + OFFSET_CALLOFFSET) = (DWORD)myParamAnalyze - (DWORD)(pBuff+OFFSET_CALLOFFSET) - 4;
*(WORD*)(pBuff + OFFSET_RETURNNUM) = dwParamNum * 4;
//偷梁换柱
pfn = (FARPROC)pBuff;
}
}
return pfn;
}
上面只监控了CreateProcessA,当发现是CreateProcessA时给它指定一个id(这里是1),实际应用中可以从数据库中查询该函数的配置信息,
为其创造一个lpFunctionInfo,在shellcode里面根据lpFunctionInfo里的信息获取函数名和其参数信息,我这里直接根据id来判断的。
测试时写了一个动态获取CreateProcessA并调用的程序,监控到的调用信息为:
[3816] CreateProcess: app: C:\Windows\System32\notepad.exe cmd:(null)
三、总结
1.第一种方法在做参数分析和返回值分析时不是很稳定,会出现崩溃现象,但是如果在shellcode里打印函数名还是很稳定的,而且速度也很快基本不影响程序的执行效率,
在需要打印参数的时候手动实现一份hook代码,但是手动一个个地添加比较费时费力。
API Monitor采用xml配置的方式配置函数名参数信息等,可以通过配置文件添加函数,比较方便。但是在执行效率上比较慢。
2.第一种方法是修改api入口代码,因此只要程序调用了该api就能被拦截,第二种方法虽然修改了输入表和GetProcAddress的返回值,但是如果自己手动打造GetProcAddress这种情况去动态获取api地址,就容易有漏网之鱼,特别是针对木马病毒这类的程序是个漏洞。