zoukankan      html  css  js  c++  java
  • 自己动手写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地址,就容易有漏网之鱼,特别是针对木马病毒这类的程序是个漏洞。

  • 相关阅读:
    latin1字符集的数据转换为utf8字符集
    mysql使用utf8mb4经验吐血总结
    字符集GBK和UTF8的区别说明
    10分钟学会理解和解决MySQL乱码问题
    MySQL大小写敏感总结
    分区表基本类型
    form表单提交的几种方法
    Redis面试题及分布式集群
    ELK
    Linux下的crontab定时执行任务命令详解
  • 原文地址:https://www.cnblogs.com/daxingxing/p/2308655.html
Copyright © 2011-2022 走看看