<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    XHtmlTreeTest中的木馬dll分析

    VSole2022-04-17 06:26:04

    概述

    該樣本是來自于VirusShare網站提供,于2022年3月15日在VirusTotal上被首次提交。拿到樣本時只有一個dll文件,查看其資源節,發現有“XHtmlTreeTest.exe”等字符串信息,經網上搜索后懷疑其是在一個叫XHtmlTreeTest的XML和HTML解析工具上附帶的木馬dll。

    詳細分析

    從該木馬dll(因為該dll的原本名字未知,就先怎么稱呼了)的DllMain函數開始分析,其通過sub_1007B390函數獲取“kernel32.dll”、“ntdll.dll”,以及“msvcrt.dll”三個dll庫的基址。該函數訪問進程的ldr鏈表,遍歷其中所有的導入庫,并通過訪問 LDR_DATA_TABLE_ENTRY 結構體中的DllBase可以獲取到dll的基址。

    GetProcAddress2 函數是我自己重命名的,其作用就是通過訪問前面獲得的導入庫基址,然后訪問其對應的導出表獲得目標函數地址。其中函數最后一個參數就是hash化的函數名,上面sub_1007B390函數的dll庫名同樣hash化了。此外,該木馬dll中DllMain函數里調用的幾個自定義函數都加入了大量的花指令混淆,如下圖中的 GetProcAddress2 函數所示:

    花指令的特征:加或減 N*dword_100ABxxx 和 N*dword_100ABxxx << M。

    發現訪問了資源節,用ResourceHack打開看看,發現一個語言未知的項里保存了一個長達0x24000字節的二進制數據。用od調試一下這個dll,該dll獲取到該資源后,申請了一個0x24000的空間,并通過memcpy函數拷貝資源的內容到該空間。發現拷貝的內容就是ResourceHack看到的二進制數據。

    跳過一大堆CreateWindowExW函數后,sub_10072050函數使用密鑰”D#PSqwp>oy@S#7dEfvT1uh?XI5X<$kuIz5qEdsD#GbBoy6I+fTJmEH$sG%kSLP_6m“進行擴展。sub_100707E0函數對該資源數據進行解密,此時會發現內存里多了一個叫”Y.dll“的模塊。

    對sub_100707E0函數去花后,加密代碼如下:

    unsigned int __cdecl sub_100707E0(int a1, int a2, unsigned int a3)
    {
      v11 = 0;
      v7 = 0;
      for ( i = 0; i < a3; ++i )
      {
        v11 = ( + v11 + 1) % 21765;
        v7 = (v7 + *(unsigned __int8 *)(a1 + v11)) % 21765;
        v10 = *(_BYTE *)(a1 + v11);
        *(_BYTE *)(a1 + v11) = *(_BYTE *)(a1 + v7);
        v4 = v7;
        *(_BYTE *)(a1 + v4) = v10;
        v5 = (*(unsigned __int8 *)(a1 + v7) + *(unsigned __int8 *)(a1 + v11)) % 21765;
        v6 = v5;
        v8 = v6;
        LOBYTE(v4) = *(_BYTE *)(a1 + v8);
        *(_BYTE *)(a2 + i) ^= v4;
        result = i + 1;
      }
      return result;
    }
    

    對sub_10070E70函數中僅調用sub_1007BE90函數,sub_1007BE90函數去花后,分析可知其主要是記錄前面獲得的幾個函數地址以及計算出DllRegisterServer函數的地址,其代碼如下:

    DWORD *__cdecl sub_1007BE90(unsigned __int16 *a1, int a2, int (__cdecl *a3)(int, int, int, int, int), void (__cdecl *a4)(int, _DWORD, int, int), int a5, int a6, int a7, int a8)
    {
      v30 = 0;
      if ( !sub_100701B0( a2 , 64) )
        return 0;
      v31 = a1;
      if ( *a1 != 23117)
        return 0;
      if ( !sub_100701B0( a2 , *((_DWORD *)v31 + 15) + 248 ) )
        return 0;
      v22 = (char *)a1 + *((_DWORD *)v31 + 15);
      if ( *(_DWORD *)v22 !=  17744 )
        return 0;
      if ( *((unsigned __int16 *)v22 + 2) != 332 )
        return 0;
      if ( (*((_DWORD *)v22 + 14) & ( 1 != 0 )
        return 0;
      v9 = (int)&v22[ 24 + *((unsigned __int16 *)v22 + 10) ];
      v21 =  v9 ;
      v27 = *((_DWORD *)v22 + 14);
      v32 = 0;
      while ( v32 < (unsigned int)*((unsigned __int16 *)v22 + 3) )
      {
        if ( *(_DWORD *)(v21 + 16) )
          v20 = *(_DWORD *)(v21 + 16) + *(_DWORD *)(v21 + 12) ;
        else
          v20 = v27 + *(_DWORD *)(v21 + 12) ;
        if ( v20 > v30 )
          v30 = v20 ;
        ++v32;
        v21 += 40;
      }
      v10 = &v25[0];
      kernel32_GetNativeSystemInfo(&v10[0]);
      v28 = sub_100703D0( *((_DWORD *)v22 + 20), v26 ) ;
      v11 = sub_100703D0( v30 , v26 );
      if ( v28 != v11 )
        return 0;
      v18 =  4 ;
      v12 =  0x2000 ;
      v29 = VirtualAlloc( *((_DWORD *)v22 + 13) , v28, 4096 ) | v12, v18, a8);
      if ( !v29 )
      {
        v19 = 4 ;
        v13 = 0x2000 ;
        v29 = VirtualAlloc( 0, v28 , 4096 ) | ( v13 ), v19, a8);
        if ( !v29 )
          return 0;
      }
      v14 = kernel32_GetProcessHeap( 8 , 64 );
      v15 = ntdll_RtlAllocateHeap(v14);
      v16 = v15 ;
      v24 = (_DWORD *)( v16 );
      if ( v24 )
      {
        v24[1] = v29;
        v24[5] = ( 0x2000 ) & *((unsigned __int16 *)v22 + 11)) != 0;
        v24[7] = a3;
        v24[8] = VirtualFree;
        v24[9] = a5;
        v24[10] = a6;
        v24[11] = a7;
        v24[13] = a8;
        v24[15] = v26 ;
        if ( sub_100701B0( a2 , *((_DWORD *)v22 + 21) )
          && (v33 = a3( v29, *((_DWORD *)v22 + 21) , 4096 , 4 , a8),
              msvcrt_memcpy(v33,v31,*((_DWORD *)v22 + 21)),*v24 = v33 + *((_DWORD *)v31 + 15),*(_DWORD *)(*v24 + 52) = + v29,
              sub_10076230(a1,a2,v22,&v24[0]))&& ((v23 =*(_DWORD *)(*v24 + 52) - *((_DWORD *)v22 + 13)) == 0 ? (v24[6] = 1) : (v17 = sub_10074F00(&v24[0],v23),v24[6] = v17),
              sub_1006D090(&v24[0])
           && sub_10079BD0(&v24[0])
           && sub_1006E5C0(&v24[0])) )
        {
          if ( *(_DWORD *)(*v24 + 40) )
          {
            if ( v24[5] )
            {
              dword_100AE7E0 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD))(v29);
              v24[4] = 1;
            }
            else
            {
              v24[14] = *(_DWORD *)(*v24 + 40) + v29;
            }
          }
          else
          {
            v24[14] = 0;
          }
          result = v24;
        }
        else
        {
          sub_1006BFA0(v24);
          result = 0;
        }
      }
      else
      {
        VirtualFree(v29,0,0x8000,a8);
        result = 0;
      }
      return result;
    }
    

    該木馬dll只導出了兩個函數,分別是DllRegisterServer函數和DllUnregisterServer函數。該木馬dll的DllRegisterServer函數會去調用Y.dll的DllRegisterServer函數。sub_10073000函數的作用就是尋找Y.dll中DllRegisterServer函數的地址。

    使用ollydump把Y.dll從內存中dump下來,用ida靜態分析一下。Y.dll中導出函數只有一個DllRegisterServer函數,一個導入函數都沒有,看來所有的導入函數都是動態獲得的。DllRegisterServer函數里有兩個函數,分別是sub_10021D63和sub_10004587。sub_10021D63函數明顯是進行了控制流混淆,里面通過各種hash計算,套利多個switch分發。分析了部分分支的邏輯,發現其最后都會通過回調的形式訪問Windows API,匯編中表現為”call eax“。所以,想到對Y.dll模塊中所有的”call eax“指令下斷點。又因為這些分支太多了,一個個下不實際,所以利用ollyscript來下斷點。


    所有分支的底層函數示例如下:


    分享一下od下斷腳本:

    var target
    var add1
    var add2
    var count
    ask "輸入hex,支持??,hex必須用##,比如查找#B8??00000001db#"
    mov target,$RESULT
    mov add1,eip
    start:
    mov add2,add1
    mov $RESULT,target
    find add2,$RESULT
    cmp $RESULT,0
    je over
    add count,1
    mov add1,$RESULT
    add add1,1
    bp $RESULT
    jne start
    over:
    add count,"次下斷"
    msg count
    ret
    

    經過調試發現,其通過 SHFileOperationW 函數在”C:\Windows\SysWOW64“目錄下生成一個基于系統時間隨機生成名字的子目錄,并往該目錄創建一個同樣隨機命名的文件。

    這里說一下 SHFileOperationW 函數的作用,如下:

    其中SHFILEOPSTRUCTA定義如下:

    typedef struct _SHFILEOPSTRUCTA {
      HWND         hwnd;
      UINT         wFunc;
      PCZZSTR      pFrom;
      PCZZSTR      pTo;
      FILEOP_FLAGS fFlags;
      BOOL         fAnyOperationsAborted;
      LPVOID       hNameMappings;
      PCSTR        lpszProgressTitle;
    } SHFILEOPSTRUCTA, *LPSHFILEOPSTRUCTA;
    

    在od調試時斷下,發現有一次 SHFILEOPSTRUCTA結構體中的pFrom指向木馬dll的文件路徑(這里我重命名該木馬dll為virus.dll),pTo則是指向如上圖所示的新路徑。

    通過CreateProcessW函數調用Regsvr32.exe以安靜模式運行將上面創建的文件寫入注冊表。因為Regsvr32常用于dll注冊,那么可以知道其目的就是換個文件名把 該木馬dll注冊到操作系統里。因為該木馬dll是32位的,所以其選擇了”C:\Windows\SysWOW64“目錄。

    image-20220319175122377

    不過我在分析時每次調用 SHFileOperationW 去創建文件的時候都失敗了,以管理員權限運行也還是一樣,不知道是什么原因。進程在反復調用幾次SHFileOperationW 失敗后就會終止。用rundll32去加載運行,用procmon去觀察結果,依然是如此。也嘗試過把后綴改為.exe,其結果是無法運行。因為不想硬肝那個控制流混淆,所以分析就到此為止了。

    IOC

    文件名19c5b13e2635be7d9931a404ccbbce7015ea2414cc12d24fd27a2ab8ad7bbe3bMD501d91a225f23340268641f2a88eddf7fSHA1ec385cf0d5dc90bc2b27d31d217356271368030eSHA25619c5b13e2635be7d9931a404ccbbce7015ea2414cc12d24fd27a2ab8ad7bbe3b文件大小1,020,928 bytes文件類型PE32 executable (DLL) (GUI) Intel 80386, for MS Windows發現時間2022-03-15 02:07:01 UTC

    dll文件dword
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    簡介這次實驗是在WIN7 X86系統上進程,使用的編譯器是VS2017。所謂的DLL注入,其實就是在其他的進程中把我們編寫的DLL加載進去。所以DLL注入的核心就是把要注入的DLL的路徑寫到目標進程中,然后在目標進程中調用LoadLibrary函數,并且指定參數為保存了DLL路徑的地址。要實現DLL注入,首先就要創建一個用來注入的DLL
    DLL(Dynamic Link Library)文件為動態鏈接庫文件,又稱“應用程序拓展”,是軟件文件類型。在Windows中,許多應用程序并不是一個完整的可執行文件,它們被分割成一些相對獨立的動態鏈接庫,即DLL文件
    DLL劫持思路和研究
    2021-10-25 10:13:22
    基礎知識DLL文件為動態鏈接庫文件,又稱“應用程序拓展”,是軟件文件類型。在Windows中,許多應用程序并不是一個完整的可執行文件,它們被分割成一些相對獨立的動態鏈接庫,即DLL文件。在這種利用場景下,偽造的DLL文件不需要存在任何導出函數即可被成功加載,即使加載后進程內部出錯,也是在DLL被成功加載之后的事情。
    初探DLL注入
    2023-02-06 10:35:24
    DLL注入是指向運行中的其它進程強制插入特定的DLL文件。從技術細節來說,DLL注入命令其它進程自行調用LoadLibrary()API,加載用戶指定的DLL文件。從上圖可以看到,test.dll已被強制插入進程。加載到某一進程中的test.dll與已經加載到某一進程中的dll一樣,擁有訪問notepad.exe進程內存的權限。DLL被加載到進程后會自動運行DLLMain()函數,用戶可以把想執行的代碼放到DLLMain()函數,每當加載DLL時,添加的代碼就會得到執行。利用這種特性可以修復程序Bug以及添加新功能
    C:\Users\dyy\AppData\Local\Programs\Python\Python38\Scripts\oci.dll使用 cs 生成惡意 dll,重命名為?后放置到該目錄下手動挖掘Process Monitor 查找可用 dll,設置如下圖所示配置完可以保存導出配置,下次直接導入使用使用?進行測試,運行程序 filter 加載所使用的 dll 文件這里可以看出來,當?文件編寫一個基礎的彈窗 dllJAVA1. // dllmain.cpp : 定義 DLL 應用程序的入口點。CS 上線cs 生成 c 的 payload生成的?填入到下面相應的位置上CPP1
    lib文件在windows下有兩種形式出現,第一種就是普通的靜態庫,第二種是作為dll的導入庫。接下來我來分享一下如何在這兩種lib文件中注入后門代碼,使程序編譯后生成的exe在運行時候自動。執行我們后門,并且不影響正常lib的功能。lib實際上就是一堆Obj文件打包在了一起,當然還有一些額外的信息,這個之后再說。
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    Dll注入
    2021-11-08 14:57:41
    最近太忙啦XDM,又在做一些列的分析復現工作量有點大,更新要慢一點了。一致,也不會覆蓋其他的進程信息。
    在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    全局鉤子注入在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类