程序行为追踪(API TRACING)
2007-08-21 16:30
204 查看
作者:glacier
对木马类程序处理多了,就渐渐觉得静/动态手工分析过程在很大程度上都是重复劳动。总要先花半个钟头了解程序特性,手工分析时还生怕漏掉某项隐蔽的关键操作,导致最终清除不彻底。其实只要在主动安装木马的时候将API调用序列及相应参数做完整记录,就能极大减轻分析和清除木马的工作量。
以前曾写过一个利用 API HOOKING 原理记录可疑程序对文件、注册表、服务和网络操作的小工具。API HOOKING 方式的优点在于,当调用 CreateFile 时可将文件名与句柄关联,等调用 WriteFile 对句柄操作时便能轻易取到文件名,对 hKey、socket 等句柄操作亦是如此。但该方式的缺点也显而易见,首先必须为每个感兴趣的 API 函数编写代码,“体力工作”繁重;其次我们不可能 HOOK 所有的 API 函数,由于缺乏完整的 API 调用序列作参考,在分析日志时很可能漏掉某些小动作。
另一种思路是采用调试技术,在所有被引入的 DLL 的各函数入口处预先设置断点,调试期间再通过堆栈信息获取参数。IDA pro 和 OllyDbg 都可用于动态调试,同时还提供了脚本/插件功能。上周在北京开会的时候,我利用酒醒的时间写了一个简单的 OllyDbg 插件,仅从 CALL 指令处通过 ESP 指针获取8个函数参数,不对函数返回后的 EAX 及堆栈内容进行记录,在对普通(未加壳)程序的测试中效果还算理想。只要先在“Search for -> All intermodular calls”窗口中执行“Set breakpoint on every command”设置断点,再运行插件的“Fast trace”功能即可。日志文件片断如下:
-------------------------------------------------------------------
004099EC: CALL DWORD PTR DS:[<&KERNEL32.GetModuleFileNameA>] (kernel32.GetModuleFileNameA)
-------------------------------------------------------------------
ESP+00 (0012F704): 00000000
ESP+04 (0012F708): 0012F824 ""
ESP+08 (0012F70C): 00000104 00000104 ???
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409A00: CALL DWORD PTR DS:[<&KERNEL32.CopyFileA>] (kernel32.CopyFileA)
-------------------------------------------------------------------
ESP+00 (0012F704): 0012F824 "E:\trojan.exe"
ESP+04 (0012F708): 0012F71C "C:\WINDOWS\system32\trojan.exe"
ESP+08 (0012F70C): 00000000
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409A94: CALL DWORD PTR DS:[<&ADVAPI32.OpenSCManagerA>] (ADVAPI32.OpenSCManagerA)
-------------------------------------------------------------------
ESP+00 (0012F704): 00000000
ESP+04 (0012F708): 00000000
ESP+08 (0012F70C): 000F003F 000F003F ???
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409ACF: CALL DWORD PTR DS:[<&ADVAPI32.CreateServiceA>] (ADVAPI32.CreateServiceA)
-------------------------------------------------------------------
ESP+00 (0012F6DC): 0014F9C0
F8 F9 14 00 98 BA DC FE 00 00 00 00 B4 F9 CC 53 ...............S
82 6C FC 42 BF 8C 55 14 00 44 14 F4 AB AB AB AB .l.B..U..D......
AB AB AB AB EE FE EE FE 00 00 00 00 00 00 00 00 ................
20 00 07 00 09 07 18 00 58 FA C3 77 EF CD AB 89 .......X..w....
00 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 05 00 00 00 01 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
06 00 00 00 0A 00 00 00 00 00 00 00 30 FB 14 00 ............0...
ESP+04 (0012F6E0): 0042008C "trojan"
ESP+08 (0012F6E4): 004200C0 "Back door for testing"
ESP+0C (0012F6E8): 000F01FF 000F01FF ???
ESP+10 (0012F6EC): 00000120 00000120 ???
ESP+14 (0012F6F0): 00000002 00000002 ???
ESP+18 (0012F6F4): 00000001 00000001 ???
ESP+1C (0012F6F8): 0012F71C "C:\WINDOWS\system32\trojan.exe -start"
......
这种简单粗糙的日志对我来说已经够用了。若希望以更友好的形式显示参数信息,就必须有一些数据文件来描述各 API 函数的调用方式、返回值类型、参数个数等内容。比如这样:
int LoadLibraryA([in] char *lpLibFileName);
int LoadLibraryW([in] wchar *lpLibFileName);
void *GetProcAddress([in] int hModule, [in] char *lpProcName);
int GetModuleFileNameA([in] int hModule, [out] char *lpFilename, [in] int nSize);
int GetModuleFileNameW([in] int hModule, [out] wchar *lpFilename, [in] int nSize);
编写一个简单的词法解析模块直接解析 VC 自带的 .h 文件,对使用者来说就更省事了。经过参数类型解析后的输出信息会好看很多:
------------------------------------------------------
004099F2 -> GetModuleFileNameA(
int hModule: 0 (unsigned = 0 / hex = 0),
char* lpFilename: [0012F824] = "",
int nSize: 260 (unsigned = 260 / hex = 104),
13 << results
int hModule: 0 (unsigned = 0 / hex = 0),
char* lpFilename: [0012F824] = "e:\trojan.exe" in stack of Thread,
int nSize: 260 (unsigned = 260 / hex = 104)
);
------------------------------------------------------
00409A06 -> CopyFileA(
char* lpExistingFileName: [0012F824] = "e:\trojan.exe" in stack of Thread,
char* lpNewFileName: [0012F71C] = "C:\WINDOWS\system32\trojan.exe" in stack of Thread,
int bFailIfExists: 0 (unsigned = 0 / hex = 0),
1 << results
char* lpExistingFileName: [0012F824] = "",
char* lpNewFileName: [0012F71C] = "",
int bFailIfExists: 0 (unsigned = 0 / hex = 0)
);
------------------------------------------------------
00409A9A -> OpenSCManagerA(
char* lpMachineName: [00000000] = (null),
char* lpDatabaseName: [00000000] = (null),
int dwDesiredAccess: 983103 (unsigned = 983103 / hex = F003F),
1374656 << results
char* lpMachineName: [00000000] = "",
char* lpDatabaseName: [00000000] = "",
int dwDesiredAccess: 983103 (unsigned = 983103 / hex = F003F)
);
------------------------------------------------------
00409AD5 -> CreateServiceA(
int hSCManager: 1374656 (unsigned = 1374656 / hex = 14F9C0),
char* lpServiceName: [0042008C] = "trojan" in main image (.data),
char* lpDisplayName: [004200C0] = "Back door for testing" in main image (.data),
int dwDesiredAccess: 983551 (unsigned = 983551 / hex = F01FF),
int dwServiceType: 288 (unsigned = 288 / hex = 120),
int dwStartType: 2 (unsigned = 2 / hex = 2),
int dwErrorControl: 1 (unsigned = 1 / hex = 1),
char* lpBinaryPathName: [0012F71C] = "C:\WINDOWS\system32\trojan.exe -start" in stack of Thread,
char* lpLoadOrderGroup: [00000000] = (null),
int* lpdwTagId: 00000000,
char* lpDependencies: [004201C4] = "" in main image (.data),
char* lpServiceStartName: [00000000] = (null),
char* lpPassword: [00000000] = (null),
1370392 << results
int hSCManager: 1374656 (unsigned = 1374656 / hex = 14F9C0),
char* lpServiceName: [0042008C] = "",
char* lpDisplayName: [004200C0] = "",
int dwDesiredAccess: 983551 (unsigned = 983551 / hex = F01FF),
int dwServiceType: 288 (unsigned = 288 / hex = 120),
int dwStartType: 2 (unsigned = 2 / hex = 2),
int dwErrorControl: 1 (unsigned = 1 / hex = 1),
char* lpBinaryPathName: [0012F71C] = "",
char* lpLoadOrderGroup: [00000000] = "",
int* lpdwTagId: 00000000,
char* lpDependencies: [004201C4] = "",
char* lpServiceStartName: [00000000] = "",
char* lpPassword: [00000000] = ""
);
......
dumbug 是一个开源的 API TRACING 工具,但被设计为仅对 trace 文件中定义的 API 调用进行跟踪。要想通过原始的 dumbug 获得完整 API 调用序列,工作量一点也不比 API HOOKING 方式小。而且就分析木马程序来说,我们并不需要记录 kernel32.dll 等系统链接库内部的 API 调用序列,所以还应根据 EXE 和 DLL 的入口地址、代码段长度进行过滤,最大限度减少冗余信息。在 dumbug 中,只要为 Tracer 对象的 ActivateTraces() 方法添加一些代码,并在其他地方也做相应的小修改,就可以输出上面的结果了。
附1 - dumbug 的源代码可以从这里获得:
http://www.phenoelit.de/dumbug/dumbugVegasRelease.zip
附2 - 简单的 ApiTracing-plugin for OllyDbg 源代码:
// ApiTracing.c
#define STRICT // Avoids some type mismatches
#include <windows.h>
#include <stdio.h>
#include <dir.h>
#include "plugin.h"
#define VERSIONHI 1 // High plugin version
#define VERSIONLO 0 // Low plugin version
#define LOG_FILENAME "TraceApi.log" // Log filename
static HINSTANCE hinst; // DLL instance
static BOOL bFastTracing = TRUE;
static BOOL bStartTrace = FALSE;
int Execute(char *text,char *answer);
BOOL WINAPI DllEntryPoint(HINSTANCE hi, DWORD reason, LPVOID reserved)
{
FILE *fLog;
if (reason == DLL_PROCESS_ATTACH) {
hinst = hi; // Mark plugin instance
fLog = fopen(LOG_FILENAME, "w");
if (fLog) {
fprintf(fLog, "API tracing plugin v%i.%02i, written by glacier_at_xfocus.org\n",
VERSIONHI, VERSIONLO);
fclose(fLog);
}
}
return 1; // Report success
}
// Report plugin name and return version of plugin interface.
extc int _export cdecl ODBG_Plugindata(char shortname[32])
{
strcpy(shortname, "API tracing"); // Name of command line plugin
return PLUGIN_VERSION;
}
extc int _export cdecl ODBG_Plugininit(int ollydbgversion, HWND hw, ulong *features)
{
// This plugin uses some newest features,
// check that version of OllyDbg is correct.
if (ollydbgversion < PLUGIN_VERSION)
return -1;
return 0;
}
extc void _export cdecl ODBG_Pluginmainloop(DEBUG_EVENT *debugevent) {
}
// Function adds items to main OllyDbg menu (origin=PM_MAIN).
extc int _export cdecl ODBG_Pluginmenu(int origin, char data[4096], void *item)
{
if (origin != PM_MAIN)
return 0; // No pop-up menus in OllyDbg's windows
strcpy(data, "0 &Fast trace,1 &Slow trace|2 &About");
return 1;
}
// Receives commands from main menu.
extc void _export cdecl ODBG_Pluginaction(int origin, int action, void *item)
{
char szLine[MAX_PATH] = {0};
if (origin != PM_MAIN)
return;
switch (action) {
case 0: // Fast tracing
bFastTracing = TRUE;
bStartTrace = TRUE;
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F9);
break;
case 1: // Slow tracing
bFastTracing = FALSE;
bStartTrace = TRUE;
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F7);
break;
case 2: // "About", displays plugin info
sprintf(szLine, "API tracing plugin v%i.%02i",
VERSIONHI, VERSIONLO);
MessageBox(0, szLine, "API tracing", MB_OK|MB_ICONINFORMATION);
break;
default: break;
}
}
// User opens new or restarts current application.
extc void _export cdecl ODBG_Pluginreset(void)
{
bStartTrace = FALSE;
}
extc int _export cdecl ODBG_Pluginclose(void)
{
return 0;
}
extc void _export cdecl ODBG_Plugindestroy(void)
{
}
// 记录二进制内容
void LogBinToFile(char *szFileName, const char *pBuf, int nSize)
{
FILE *fLog;
int i, j;
unsigned const char *ptr = (unsigned const char *)pBuf;
fLog = fopen(szFileName, "a+");
if (!fLog) return;
if (nSize == 0)
nSize = strlen(pBuf);
for (i=0; i<nSize; i=i+0x10) {
fprintf(fLog, "\t\t");
for (j=i; j<i+0x10 && j<nSize; j++)
fprintf(fLog, "%02X ", ptr[j]);
fprintf(fLog, "\t");
for (j=i; j<i+0x10 && j<nSize; j++) {
if (IsCharAlpha(ptr[j]) || (ptr[j]>=0x20 && ptr[j]<0x7F))
fprintf(fLog, "%c", ptr[j]);
else
fprintf(fLog, "%c", '.');
}
fprintf(fLog, "\n");
}
fclose(fLog);
}
// 格式化记录日志
void LogToFile(char *szFileName, char *szFmt, ...)
{
FILE *fLog;
char buff[1024];
va_list arglist;
va_start(arglist, szFmt);
_vsnprintf(buff, sizeof(buff), szFmt, arglist);
va_end(arglist);
fLog = fopen(szFileName, "a+");
if (!fLog) return;
fprintf(fLog, "%s", buff);
fclose(fLog);
}
// 检查是否为ASCII字符串
BOOL CheckCharAlpha(char *szLine)
{
int i = 0;
while (szLine[i]) {
if (!IsCharAlpha(szLine[i]) && (szLine[i]<0x20 || szLine[i]>=0x7F))
return FALSE;
i++;
}
return TRUE;
}
extc int _export cdecl ODBG_Paused(int reason, t_reg *reg)
{
char szSrcDec[1024] = {0};
char szLine[1024] = {0};
unsigned long uEsp = 0, uAddr = 0, uTemp = 0;
int nSize = 0, i = 0;
if (!bStartTrace) return 0;
if (!reg) {
ShellExecute(0, "open", "notepad.exe", LOG_FILENAME, NULL, SW_SHOW);
return 0;
}
// 读取断点处指令
nSize = Readcommand(reg->ip, szLine);
if (nSize > 0) {
t_disasm disasm;
// 反汇编二进制指令
Disasm(szLine, nSize, reg->ip, szSrcDec, &disasm, DISASM_ALL, 0);
if (strstr(disasm.result, "CALL ")) { // 若为CALL指令
LogToFile(LOG_FILENAME, "\n%s\n",
"------------------------------------------------------");
LogToFile(LOG_FILENAME, "%08X: %s (%s)", reg->ip, disasm.result, disasm.comment);
LogToFile(LOG_FILENAME, "\n%s\n",
"------------------------------------------------------");
uEsp = reg->r[4];
// 由ESP读取8个堆栈参数
uTemp = uEsp;
for (i=0; i<8; i++) {
Readmemory(&uAddr, uTemp, sizeof(uAddr), MM_SILENT);
LogToFile(LOG_FILENAME, "\tESP+%02X (%08X): %08X", i*4, uTemp, uAddr);
if (uAddr == 0) nSize = 0;
else nSize = Decodeascii(uAddr, szLine, sizeof(szLine)-1, DASC_ASCII);
if (nSize > 0 && CheckCharAlpha(szLine)) {
LogToFile(LOG_FILENAME, "\t\t%s\n", szLine);
}
else {
memset(szLine, 0, sizeof(szLine));
nSize = Readmemory(szLine, uAddr, 128, MM_SILENT);
if (nSize > 0) {
LogToFile(LOG_FILENAME, "\n");
LogBinToFile(LOG_FILENAME, szLine, nSize);
}
else
LogToFile(LOG_FILENAME, "\n");
}
uTemp += 4;
}
}
// 继续执行
if (bFastTracing)
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F9);
else {
if (strstr(disasm.result, ".0")) {
Go(0, reg->ip, STEP_IN, 1, 0);
}
else {
Go(0, reg->ip, STEP_OVER, 1, 0);
}
}
}
return 0;
}
对木马类程序处理多了,就渐渐觉得静/动态手工分析过程在很大程度上都是重复劳动。总要先花半个钟头了解程序特性,手工分析时还生怕漏掉某项隐蔽的关键操作,导致最终清除不彻底。其实只要在主动安装木马的时候将API调用序列及相应参数做完整记录,就能极大减轻分析和清除木马的工作量。
以前曾写过一个利用 API HOOKING 原理记录可疑程序对文件、注册表、服务和网络操作的小工具。API HOOKING 方式的优点在于,当调用 CreateFile 时可将文件名与句柄关联,等调用 WriteFile 对句柄操作时便能轻易取到文件名,对 hKey、socket 等句柄操作亦是如此。但该方式的缺点也显而易见,首先必须为每个感兴趣的 API 函数编写代码,“体力工作”繁重;其次我们不可能 HOOK 所有的 API 函数,由于缺乏完整的 API 调用序列作参考,在分析日志时很可能漏掉某些小动作。
另一种思路是采用调试技术,在所有被引入的 DLL 的各函数入口处预先设置断点,调试期间再通过堆栈信息获取参数。IDA pro 和 OllyDbg 都可用于动态调试,同时还提供了脚本/插件功能。上周在北京开会的时候,我利用酒醒的时间写了一个简单的 OllyDbg 插件,仅从 CALL 指令处通过 ESP 指针获取8个函数参数,不对函数返回后的 EAX 及堆栈内容进行记录,在对普通(未加壳)程序的测试中效果还算理想。只要先在“Search for -> All intermodular calls”窗口中执行“Set breakpoint on every command”设置断点,再运行插件的“Fast trace”功能即可。日志文件片断如下:
-------------------------------------------------------------------
004099EC: CALL DWORD PTR DS:[<&KERNEL32.GetModuleFileNameA>] (kernel32.GetModuleFileNameA)
-------------------------------------------------------------------
ESP+00 (0012F704): 00000000
ESP+04 (0012F708): 0012F824 ""
ESP+08 (0012F70C): 00000104 00000104 ???
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409A00: CALL DWORD PTR DS:[<&KERNEL32.CopyFileA>] (kernel32.CopyFileA)
-------------------------------------------------------------------
ESP+00 (0012F704): 0012F824 "E:\trojan.exe"
ESP+04 (0012F708): 0012F71C "C:\WINDOWS\system32\trojan.exe"
ESP+08 (0012F70C): 00000000
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409A94: CALL DWORD PTR DS:[<&ADVAPI32.OpenSCManagerA>] (ADVAPI32.OpenSCManagerA)
-------------------------------------------------------------------
ESP+00 (0012F704): 00000000
ESP+04 (0012F708): 00000000
ESP+08 (0012F70C): 000F003F 000F003F ???
ESP+0C (0012F710): 0012FA6D ""
ESP+10 (0012F714): 00000001 00000001 ???
ESP+14 (0012F718): 00000000
ESP+18 (0012F71C): 575C3A43 575C3A43 ???
ESP+1C (0012F720): 4F444E49 4F444E49 ???
-------------------------------------------------------------------
00409ACF: CALL DWORD PTR DS:[<&ADVAPI32.CreateServiceA>] (ADVAPI32.CreateServiceA)
-------------------------------------------------------------------
ESP+00 (0012F6DC): 0014F9C0
F8 F9 14 00 98 BA DC FE 00 00 00 00 B4 F9 CC 53 ...............S
82 6C FC 42 BF 8C 55 14 00 44 14 F4 AB AB AB AB .l.B..U..D......
AB AB AB AB EE FE EE FE 00 00 00 00 00 00 00 00 ................
20 00 07 00 09 07 18 00 58 FA C3 77 EF CD AB 89 .......X..w....
00 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 05 00 00 00 01 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
06 00 00 00 0A 00 00 00 00 00 00 00 30 FB 14 00 ............0...
ESP+04 (0012F6E0): 0042008C "trojan"
ESP+08 (0012F6E4): 004200C0 "Back door for testing"
ESP+0C (0012F6E8): 000F01FF 000F01FF ???
ESP+10 (0012F6EC): 00000120 00000120 ???
ESP+14 (0012F6F0): 00000002 00000002 ???
ESP+18 (0012F6F4): 00000001 00000001 ???
ESP+1C (0012F6F8): 0012F71C "C:\WINDOWS\system32\trojan.exe -start"
......
这种简单粗糙的日志对我来说已经够用了。若希望以更友好的形式显示参数信息,就必须有一些数据文件来描述各 API 函数的调用方式、返回值类型、参数个数等内容。比如这样:
int LoadLibraryA([in] char *lpLibFileName);
int LoadLibraryW([in] wchar *lpLibFileName);
void *GetProcAddress([in] int hModule, [in] char *lpProcName);
int GetModuleFileNameA([in] int hModule, [out] char *lpFilename, [in] int nSize);
int GetModuleFileNameW([in] int hModule, [out] wchar *lpFilename, [in] int nSize);
编写一个简单的词法解析模块直接解析 VC 自带的 .h 文件,对使用者来说就更省事了。经过参数类型解析后的输出信息会好看很多:
------------------------------------------------------
004099F2 -> GetModuleFileNameA(
int hModule: 0 (unsigned = 0 / hex = 0),
char* lpFilename: [0012F824] = "",
int nSize: 260 (unsigned = 260 / hex = 104),
13 << results
int hModule: 0 (unsigned = 0 / hex = 0),
char* lpFilename: [0012F824] = "e:\trojan.exe" in stack of Thread,
int nSize: 260 (unsigned = 260 / hex = 104)
);
------------------------------------------------------
00409A06 -> CopyFileA(
char* lpExistingFileName: [0012F824] = "e:\trojan.exe" in stack of Thread,
char* lpNewFileName: [0012F71C] = "C:\WINDOWS\system32\trojan.exe" in stack of Thread,
int bFailIfExists: 0 (unsigned = 0 / hex = 0),
1 << results
char* lpExistingFileName: [0012F824] = "",
char* lpNewFileName: [0012F71C] = "",
int bFailIfExists: 0 (unsigned = 0 / hex = 0)
);
------------------------------------------------------
00409A9A -> OpenSCManagerA(
char* lpMachineName: [00000000] = (null),
char* lpDatabaseName: [00000000] = (null),
int dwDesiredAccess: 983103 (unsigned = 983103 / hex = F003F),
1374656 << results
char* lpMachineName: [00000000] = "",
char* lpDatabaseName: [00000000] = "",
int dwDesiredAccess: 983103 (unsigned = 983103 / hex = F003F)
);
------------------------------------------------------
00409AD5 -> CreateServiceA(
int hSCManager: 1374656 (unsigned = 1374656 / hex = 14F9C0),
char* lpServiceName: [0042008C] = "trojan" in main image (.data),
char* lpDisplayName: [004200C0] = "Back door for testing" in main image (.data),
int dwDesiredAccess: 983551 (unsigned = 983551 / hex = F01FF),
int dwServiceType: 288 (unsigned = 288 / hex = 120),
int dwStartType: 2 (unsigned = 2 / hex = 2),
int dwErrorControl: 1 (unsigned = 1 / hex = 1),
char* lpBinaryPathName: [0012F71C] = "C:\WINDOWS\system32\trojan.exe -start" in stack of Thread,
char* lpLoadOrderGroup: [00000000] = (null),
int* lpdwTagId: 00000000,
char* lpDependencies: [004201C4] = "" in main image (.data),
char* lpServiceStartName: [00000000] = (null),
char* lpPassword: [00000000] = (null),
1370392 << results
int hSCManager: 1374656 (unsigned = 1374656 / hex = 14F9C0),
char* lpServiceName: [0042008C] = "",
char* lpDisplayName: [004200C0] = "",
int dwDesiredAccess: 983551 (unsigned = 983551 / hex = F01FF),
int dwServiceType: 288 (unsigned = 288 / hex = 120),
int dwStartType: 2 (unsigned = 2 / hex = 2),
int dwErrorControl: 1 (unsigned = 1 / hex = 1),
char* lpBinaryPathName: [0012F71C] = "",
char* lpLoadOrderGroup: [00000000] = "",
int* lpdwTagId: 00000000,
char* lpDependencies: [004201C4] = "",
char* lpServiceStartName: [00000000] = "",
char* lpPassword: [00000000] = ""
);
......
dumbug 是一个开源的 API TRACING 工具,但被设计为仅对 trace 文件中定义的 API 调用进行跟踪。要想通过原始的 dumbug 获得完整 API 调用序列,工作量一点也不比 API HOOKING 方式小。而且就分析木马程序来说,我们并不需要记录 kernel32.dll 等系统链接库内部的 API 调用序列,所以还应根据 EXE 和 DLL 的入口地址、代码段长度进行过滤,最大限度减少冗余信息。在 dumbug 中,只要为 Tracer 对象的 ActivateTraces() 方法添加一些代码,并在其他地方也做相应的小修改,就可以输出上面的结果了。
附1 - dumbug 的源代码可以从这里获得:
http://www.phenoelit.de/dumbug/dumbugVegasRelease.zip
附2 - 简单的 ApiTracing-plugin for OllyDbg 源代码:
// ApiTracing.c
#define STRICT // Avoids some type mismatches
#include <windows.h>
#include <stdio.h>
#include <dir.h>
#include "plugin.h"
#define VERSIONHI 1 // High plugin version
#define VERSIONLO 0 // Low plugin version
#define LOG_FILENAME "TraceApi.log" // Log filename
static HINSTANCE hinst; // DLL instance
static BOOL bFastTracing = TRUE;
static BOOL bStartTrace = FALSE;
int Execute(char *text,char *answer);
BOOL WINAPI DllEntryPoint(HINSTANCE hi, DWORD reason, LPVOID reserved)
{
FILE *fLog;
if (reason == DLL_PROCESS_ATTACH) {
hinst = hi; // Mark plugin instance
fLog = fopen(LOG_FILENAME, "w");
if (fLog) {
fprintf(fLog, "API tracing plugin v%i.%02i, written by glacier_at_xfocus.org\n",
VERSIONHI, VERSIONLO);
fclose(fLog);
}
}
return 1; // Report success
}
// Report plugin name and return version of plugin interface.
extc int _export cdecl ODBG_Plugindata(char shortname[32])
{
strcpy(shortname, "API tracing"); // Name of command line plugin
return PLUGIN_VERSION;
}
extc int _export cdecl ODBG_Plugininit(int ollydbgversion, HWND hw, ulong *features)
{
// This plugin uses some newest features,
// check that version of OllyDbg is correct.
if (ollydbgversion < PLUGIN_VERSION)
return -1;
return 0;
}
extc void _export cdecl ODBG_Pluginmainloop(DEBUG_EVENT *debugevent) {
}
// Function adds items to main OllyDbg menu (origin=PM_MAIN).
extc int _export cdecl ODBG_Pluginmenu(int origin, char data[4096], void *item)
{
if (origin != PM_MAIN)
return 0; // No pop-up menus in OllyDbg's windows
strcpy(data, "0 &Fast trace,1 &Slow trace|2 &About");
return 1;
}
// Receives commands from main menu.
extc void _export cdecl ODBG_Pluginaction(int origin, int action, void *item)
{
char szLine[MAX_PATH] = {0};
if (origin != PM_MAIN)
return;
switch (action) {
case 0: // Fast tracing
bFastTracing = TRUE;
bStartTrace = TRUE;
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F9);
break;
case 1: // Slow tracing
bFastTracing = FALSE;
bStartTrace = TRUE;
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F7);
break;
case 2: // "About", displays plugin info
sprintf(szLine, "API tracing plugin v%i.%02i",
VERSIONHI, VERSIONLO);
MessageBox(0, szLine, "API tracing", MB_OK|MB_ICONINFORMATION);
break;
default: break;
}
}
// User opens new or restarts current application.
extc void _export cdecl ODBG_Pluginreset(void)
{
bStartTrace = FALSE;
}
extc int _export cdecl ODBG_Pluginclose(void)
{
return 0;
}
extc void _export cdecl ODBG_Plugindestroy(void)
{
}
// 记录二进制内容
void LogBinToFile(char *szFileName, const char *pBuf, int nSize)
{
FILE *fLog;
int i, j;
unsigned const char *ptr = (unsigned const char *)pBuf;
fLog = fopen(szFileName, "a+");
if (!fLog) return;
if (nSize == 0)
nSize = strlen(pBuf);
for (i=0; i<nSize; i=i+0x10) {
fprintf(fLog, "\t\t");
for (j=i; j<i+0x10 && j<nSize; j++)
fprintf(fLog, "%02X ", ptr[j]);
fprintf(fLog, "\t");
for (j=i; j<i+0x10 && j<nSize; j++) {
if (IsCharAlpha(ptr[j]) || (ptr[j]>=0x20 && ptr[j]<0x7F))
fprintf(fLog, "%c", ptr[j]);
else
fprintf(fLog, "%c", '.');
}
fprintf(fLog, "\n");
}
fclose(fLog);
}
// 格式化记录日志
void LogToFile(char *szFileName, char *szFmt, ...)
{
FILE *fLog;
char buff[1024];
va_list arglist;
va_start(arglist, szFmt);
_vsnprintf(buff, sizeof(buff), szFmt, arglist);
va_end(arglist);
fLog = fopen(szFileName, "a+");
if (!fLog) return;
fprintf(fLog, "%s", buff);
fclose(fLog);
}
// 检查是否为ASCII字符串
BOOL CheckCharAlpha(char *szLine)
{
int i = 0;
while (szLine[i]) {
if (!IsCharAlpha(szLine[i]) && (szLine[i]<0x20 || szLine[i]>=0x7F))
return FALSE;
i++;
}
return TRUE;
}
extc int _export cdecl ODBG_Paused(int reason, t_reg *reg)
{
char szSrcDec[1024] = {0};
char szLine[1024] = {0};
unsigned long uEsp = 0, uAddr = 0, uTemp = 0;
int nSize = 0, i = 0;
if (!bStartTrace) return 0;
if (!reg) {
ShellExecute(0, "open", "notepad.exe", LOG_FILENAME, NULL, SW_SHOW);
return 0;
}
// 读取断点处指令
nSize = Readcommand(reg->ip, szLine);
if (nSize > 0) {
t_disasm disasm;
// 反汇编二进制指令
Disasm(szLine, nSize, reg->ip, szSrcDec, &disasm, DISASM_ALL, 0);
if (strstr(disasm.result, "CALL ")) { // 若为CALL指令
LogToFile(LOG_FILENAME, "\n%s\n",
"------------------------------------------------------");
LogToFile(LOG_FILENAME, "%08X: %s (%s)", reg->ip, disasm.result, disasm.comment);
LogToFile(LOG_FILENAME, "\n%s\n",
"------------------------------------------------------");
uEsp = reg->r[4];
// 由ESP读取8个堆栈参数
uTemp = uEsp;
for (i=0; i<8; i++) {
Readmemory(&uAddr, uTemp, sizeof(uAddr), MM_SILENT);
LogToFile(LOG_FILENAME, "\tESP+%02X (%08X): %08X", i*4, uTemp, uAddr);
if (uAddr == 0) nSize = 0;
else nSize = Decodeascii(uAddr, szLine, sizeof(szLine)-1, DASC_ASCII);
if (nSize > 0 && CheckCharAlpha(szLine)) {
LogToFile(LOG_FILENAME, "\t\t%s\n", szLine);
}
else {
memset(szLine, 0, sizeof(szLine));
nSize = Readmemory(szLine, uAddr, 128, MM_SILENT);
if (nSize > 0) {
LogToFile(LOG_FILENAME, "\n");
LogBinToFile(LOG_FILENAME, szLine, nSize);
}
else
LogToFile(LOG_FILENAME, "\n");
}
uTemp += 4;
}
}
// 继续执行
if (bFastTracing)
Sendshortcut(PM_MAIN, 0, WM_KEYDOWN, 0, 0, VK_F9);
else {
if (strstr(disasm.result, ".0")) {
Go(0, reg->ip, STEP_IN, 1, 0);
}
else {
Go(0, reg->ip, STEP_OVER, 1, 0);
}
}
}
return 0;
}
相关文章推荐
- 程序行为追踪(API TRACING)
- 通过程序行为追踪揪出木马病毒 【 大成天下 黄鑫 】
- 微信小程序开发—快速掌握组件及API的方法
- 3D建模和3D渲染技术专题一: 热身篇,光线追踪(path Tracing),环境光阴影(ambient occlusion),焦距效果(effect focus)介绍
- .Net程序调试与追踪的一些方法
- Win32 API 实现系统托盘程序
- ANDROID例子程序(ApiDemo)简单分类整理
- FileGDB API for linux 学习系列之一,编译例子程序
- 如何使用Native Messaging API 打开window程序
- ArcGIS API for Silverlight开发入门(8):在程序中使用Virtual Earth
- [转]微信小程序联盟 跳坑《一百八十一》设置API:wx.openSetting使用说明
- Hadoop实战-使用Eclipse开发Hadoop API程序(四)
- C#用API可以改程序名字
- 第一个Windows API程序
- php程序调试追踪工具[PHPLog]
- KB: 如果通过API读取黑莓程序jad文件里面的属性
- 微信小程序开发 | 把玩系列:各种组件和API实用详解
- 如何编辑WP程序API插件转
- KnockoutJS 3.X API 第七章 其他技术(5) 使用其他事件处理程序
- 寻找黑匣子--程序行为记录与跟踪