• 自己动手写API监控工具


    需求来源:

    1.长期做木马外挂的逆向分析工作,基本上也就看看API调用情况也就知道大概的原理了,手工一个个地分析无疑浪费时间和精力。

    2.想知道一个感兴趣的应用程序是如何编写的,监控下API调用情况也可以基本了解实现原理。

    现状:

    目前市面上这样的工具还是蛮多的,有AutoDebug,ApiTracing,ApiMonitor,bladeapimonitor,不多有点鱼龙混杂。

    API Monitorhttp://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地址,就容易有漏网之鱼,特别是针对木马病毒这类的程序是个漏洞。

  • 相关阅读:
    简明Python3教程 12.问题解决
    简明Python3教程 11.数据结构
    【SPOJ 694】Distinct Substrings
    【codeforces Manthan, Codefest 17 C】Helga Hufflepuff's Cup
    【CF Manthan, Codefest 17 B】Marvolo Gaunt's Ring
    【CF Manthan, Codefest 17 A】Tom Riddle's Diary
    【SPOJ 220】 PHRASES
    【POJ 3261】Milk Patterns
    【POJ 3294】Life Forms
    【POJ 1226】Substrings
  • 原文地址:https://www.cnblogs.com/daxingxing/p/2308655.html
Copyright © 2020-2023  润新知