远程call

挂起目标线程后

x64进程直接劫持_kthread的trapframe结构的rip即可

对于x86进程也可以劫持 _kthread的trapframe结构的rip,不过这个rip这是返回到64位代码的地址,所以要写64位的shellcode

或者劫持wow64_context结构 https://learn.microsoft.com/zh-cn/windows/win32/api/winnt/ns-winnt-wow64_context 根据文档wow64_context是在_TEB.TlsSlots[1]+4位置(经测试win10 1903也在此处)

利用ntoskrnl中的jmp rcx当跳板跳转到shellcode

!process 0 0
!process [eprocess值]

remotecall.h

#pragma once
#include "../drivercommon.h"
NTSTATUS RemoteCall(HANDLE pid, PVOID shellCode, ULONG shellCodeSize);

remotecall.c

#include "remotecall.h"
#include "../ExportFunc.h"
#include "../SearchCode.h"
#include "../tools.h"
typedef struct _FreeMemoryInfo {
ULONG64 IsExecuteAddr;
ULONG64 freeSize;
ULONG64 baseAddr;
WORK_QUEUE_ITEM workitem;
HANDLE pid;
}FreeMemoryInfo, * PFreeMemoryInfo;

VOID ExFreeMemoryWorkItem(PVOID);

NTSTATUS NTAPI ZwGetNextThread(
__in HANDLE ProcessHandle,
__in HANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in ULONG HandleAttributes,
__in ULONG Flags,
__out PHANDLE NewThreadHandle
) {

typedef NTSTATUS(NTAPI* ZwGetNextThreadPtr)(
__in HANDLE ProcessHandle,
__in HANDLE ThreadHandle,
__in ACCESS_MASK DesiredAccess,
__in ULONG HandleAttributes,
__in ULONG Flags,
__out PHANDLE NewThreadHandle
);
static ZwGetNextThreadPtr ZwGetNextThreadFunc = NULL;
if (!ZwGetNextThreadFunc) {
ZwGetNextThreadFunc = (ZwGetNextThreadPtr)searchCode("ntoskrnl.exe", ".text",
"50 9C 6A ?? 48 ?? ?? ?? ?? ?? ?? 50 B8 F3 ?? ?? ?? E9 ?? ?? ?? ??",
-0x8
);
}
if (ZwGetNextThreadFunc) {
return ZwGetNextThreadFunc(ProcessHandle, ThreadHandle, DesiredAccess,
HandleAttributes, Flags, NewThreadHandle);
}

}

PETHREAD NtGetProcessMainThread(PEPROCESS eprocess) {

PEPROCESS ethread = NULL;
KAPC_STATE apcState = { 0 };
KeStackAttachProcess(eprocess, &apcState);

HANDLE hThread = NULL;
//第二个参数为NULL获取主线程
NTSTATUS status = ZwGetNextThread(NtCurrentProcess(), NULL, THREAD_ALL_ACCESS, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, &hThread);
if (!NT_SUCCESS(status)) {
KeUnstackDetachProcess(&apcState);
return NULL;
}
else {
status = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &ethread, NULL);
NtClose(hThread);
}
KeUnstackDetachProcess(&apcState);
return ethread;

}


NTSTATUS PsSuspendThread(IN PETHREAD Thread, OUT PULONG PreviousSuspendCount OPTIONAL
) {
typedef NTSTATUS(NTAPI* PsSuspendThreadPtr)(IN PETHREAD Thread, OUT PULONG PreviousSuspendCount OPTIONAL);
static PsSuspendThreadPtr PsSuspendThreadFunc = NULL;
if (!PsSuspendThreadFunc) {
PsSuspendThreadFunc = (PsSuspendThreadPtr)searchCode("ntoskrnl.exe", "PAGE",
"65 ?? ?? ?? ?? ?? ?? ?? ?? 48 89 ?? ?? ?? 66 ?? ?? ?? ?? ?? ?? 4C 8D ?? ?? ?? ?? ?? 4C 89 ?? ?? ?? 49 8B CF E8 ?? ?? ?? ?? 84 C0 0F ?? ?? ?? ?? ?? 8B 87 ?? ?? ?? ??",
-0x20
);
}
if (PsSuspendThreadFunc) {
return PsSuspendThreadFunc(Thread, PreviousSuspendCount);
}
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS PsResumeThread(IN PETHREAD Thread, OUT PULONG PreviousSuspendCount OPTIONAL
) {
typedef NTSTATUS(NTAPI* PsResumeThreadPtr)(IN PETHREAD Thread, OUT PULONG PreviousSuspendCount OPTIONAL);
static PsResumeThreadPtr PsResumeThreadFunc = NULL;
if (!PsResumeThreadFunc) {
PsResumeThreadFunc = (PsResumeThreadPtr)searchCode("ntoskrnl.exe", "PAGE",
"48 8B DA 48 8B F9 E8 ?? ?? ?? ?? 65 ?? ?? ?? ?? ?? ?? ?? ?? 8B F0 83 ?? ?? 75 ?? 4C 8B ?? ?? ?? ?? ?? B8 ?? ?? ?? ?? 41 8B ?? ?? ?? ??",
-0xf
);
}
if (PsResumeThreadFunc) {
return PsResumeThreadFunc(Thread, PreviousSuspendCount);
}
return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS RemoteCall(HANDLE pid, PVOID shellCode, ULONG shellCodeSize) {

NTSTATUS status = STATUS_UNSUCCESSFUL;
//获取主线程
PEPROCESS eprocess = NULL;
status = PsLookupProcessByProcessId(pid, &eprocess);
if (!NT_SUCCESS(status) || NULL == eprocess) {
return status;
}
if (PsGetProcessExitStatus(eprocess) != 0x103) {
ObDereferenceObject(eprocess);
return STATUS_UNSUCCESSFUL;
}
//DbgBreakPoint();

PETHREAD ethread = NtGetProcessMainThread(eprocess);
if (NULL == ethread) {
return STATUS_UNSUCCESSFUL;
}
//拷贝到内核缓冲区
PUCHAR kShellcode = ExAllocatePool(PagedPool, shellCodeSize);
memcpy(kShellcode, shellCode, shellCodeSize);

BOOLEAN isWow64 = PsGetProcessWow64Process(eprocess);
//附加到目标进程
KAPC_STATE apcState = { 0 };
KeStackAttachProcess(eprocess, &apcState);
//目标进程缓冲区
PVOID baseAddr = 0;
SIZE_T regionSize = shellCodeSize + PAGE_SIZE;
PUCHAR flagAd = 0;
do {
status = ZwAllocateVirtualMemory(NtCurrentProcess(), &baseAddr, NULL, &regionSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(status) || baseAddr == 0 || regionSize == 0) {
break;
}
memset(baseAddr, 0, regionSize);
PUCHAR shellCodeAd = (PUCHAR)baseAddr + PAGE_SIZE;
flagAd = (PUCHAR)baseAddr + PAGE_SIZE / 2;
memcpy(shellCodeAd, kShellcode, shellCodeSize);
//挂起
status = PsSuspendThread(ethread, NULL);
if (!NT_SUCCESS(status)) {
break;
}
if (isWow64) {
/*
60 pushad
B8 78563412 mov eax, 12345678
83EC 40 sub esp, 40
FFD0 call eax; 测试. < ModuleEntryPoint>
83C4 40 add esp, 40
B8 78563412 mov eax, 12345678
C700 01000000 mov dword ptr ds : [eax] , 1
61 popad
FF25 00000000 jmp dword ptr ds : [0]
0000 add byte ptr ds : [eax] , al
0000 add byte ptr ds : [eax] , al
0000 add byte ptr ds : [eax] , al
0000 add byte ptr ds : [eax] , al
0000 add byte ptr ds : [eax] , al
*/

char bufcode[] =
{
0x60,
0xB8, 0x78, 0x56, 0x34, 0x12,
0x83, 0xEC, 0x40,
0xFF, 0xD0,
0x83, 0xC4, 0x40,
0xB8, 0x78, 0x56, 0x34, 0x12,
0xC7, 0x00, 0x01, 0x00, 0x00,0x00,
0x61,
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
};
//x86进程
//也可以劫持 _kthread的trapframe结构的rip,不过这个rip这是返回到64位代码的地址,所以要写64位的shellcode
PUCHAR teb64 = (PUCHAR)PsGetThreadTeb(ethread);
SIZE_T retProc = NULL;
MmCopyVirtualMemory(eprocess, (PULONG64)(teb64 + 0x1488), eprocess, (PULONG64)(teb64 + 0x1488), 8, UserMode, &retProc);

PUCHAR WowContext = (PUCHAR) * (PULONG64)(teb64 + 0x1488);//ULONGLONG TlsSlots[64]; //0x1480
*(PULONG)&bufcode[2] = shellCodeAd;//shellcode
*(PULONG)&bufcode[15] = (ULONG)(flagAd);//flags
*(PULONG)&bufcode[32] = *(PULONG)(WowContext + 0xbc);//original eip

memcpy(baseAddr, bufcode, sizeof(bufcode));
//修改eip
*(PULONG)(WowContext + 0xbc) = baseAddr;
}
else {
/* 50 push rax
51 push rcx
52 push rdx
53 push rbx
55 push rbp
56 push rsi
57 push rdi
41 50 push r8
41 51 push r9
41 52 push r10
41 53 push r11
41 54 push r12
41 55 push r13
41 56 push r14
41 57 push r15
48 B8 99 89 67 45 23 01 00 00 mov rax,0x0000012345678999
48 81 EC A8 00 00 00 sub rsp,0x00000000000000A8
FF D0 call rax
48 81 C4 A8 00 00 00 add rsp,0x00000000000000A8
41 5F pop r15
41 5E pop r14
41 5D pop r13
41 5C pop r12
41 5B pop r11
41 5A pop r10
41 59 pop r9
41 58 pop r8
5F pop rdi
5E pop rsi
5D pop rbp
5B pop rbx
5A pop rdx
59 pop rcx
48 B8 89 67 45 23 01 00 00 00 mov rax,0x0000000123456789
C7 00 01 00 00 00 mov dword ptr ds:[rax],0x0000000000000001
58 pop rax
FF 25 00 00 00 00 jmp qword ptr ds:[PCHunter64.00000001403ABA27]
00 00 add byte ptr ds:[rax],al
00 00 add byte ptr ds:[rax],al
00 00 add byte ptr ds:[rax],al
00 00 add byte ptr ds:[rax],al
*/
char bufcode[] =
{
0x50, //push rax
0x51, //push rcx
0x52, //push rdx
0x53, //push rbx //
0x55, //
0x56, //
0x57, //
0x41, 0x50, //
0x41, 0x51, //
0x41, 0x52, //
0x41, 0x53, //
0x41, 0x54, //
0x41, 0x55, //
0x41, 0x56, //
0x41, 0x57, //
0x48, 0xB8, 0x99, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,0x00, //
0x48, 0x81, 0xEC, 0xA0, 0x00, 0x00, 0x00, //
0xFF, 0xD0, //
0x48, 0x81, 0xC4, 0xA0, 0x00, 0x00, 0x00, //
0x41, 0x5F, //
0x41, 0x5E, //
0x41, 0x5D, //
0x41, 0x5C, //
0x41, 0x5B, //
0x41, 0x5A, //
0x41, 0x59, //
0x41, 0x58, //
0x5F, //
0x5E, //
0x5D, //
0x5B, //
0x5A, //
0x59, //
0x48, 0xB8, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, //
0xC7, 0x00, 0x01, 0x00, 0x00, 0x00, 0x58, //
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
};
ULONG64 initStackAddr = *(PULONG64)((PUCHAR)ethread + 0x28);
PKTRAP_FRAME ptrap = (PKTRAP_FRAME)(initStackAddr - sizeof(KTRAP_FRAME));
*(PULONG64)&bufcode[25] = (ULONG64)shellCodeAd;
*(PULONG64)&bufcode[73] = (ULONG64)flagAd;
*(PULONG64)&bufcode[94] = ptrap->Rip;

memcpy(baseAddr, bufcode, sizeof(bufcode));
ptrap->Rip = baseAddr;
}
//启动系统工作项检测是否执行完成,并释放内存
PFreeMemoryInfo freeMemInfo = (PFreeMemoryInfo)ExAllocatePool(NonPagedPool, sizeof(FreeMemoryInfo));
freeMemInfo->freeSize = regionSize;
freeMemInfo->baseAddr = baseAddr;
freeMemInfo->pid = pid;
freeMemInfo->IsExecuteAddr = flagAd;
ExInitializeWorkItem(&freeMemInfo->workitem, ExFreeMemoryWorkItem, freeMemInfo);
ExQueueWorkItem(&freeMemInfo->workitem, DelayedWorkQueue);

PsResumeThread(ethread, NULL);

} while (0);
KeUnstackDetachProcess(&apcState);
ObDereferenceObject(eprocess);
ObDereferenceObject(ethread);
ExFreePool(kShellcode);
return status;
}
VOID ExFreeMemoryWorkItem(PVOID info) {

//DbgBreakPoint();
PFreeMemoryInfo freeMemInfo = (PFreeMemoryInfo)info;

PEPROCESS eprocess = NULL;
NTSTATUS status = PsLookupProcessByProcessId(freeMemInfo->pid,&eprocess);
if (!NT_SUCCESS(status)) {
return;
}

if (PsGetProcessExitStatus(eprocess) != 0x103) {
ObDereferenceObject(eprocess);
return;
}

ULONG32 exeValue = 0;
SIZE_T cped = 0;
int count = 0;
BOOLEAN isSuccess = FALSE;

while (1) {
if (count > 10000) break;
status=MmCopyVirtualMemory(eprocess,freeMemInfo->IsExecuteAddr,IoGetCurrentProcess(),&exeValue,sizeof(exeValue), KernelMode,&cped);
if (NT_SUCCESS(status) && exeValue == 1) {
isSuccess = TRUE;
break;
}

KernelSleep(10, FALSE);
count++;

}
KAPC_STATE kApcState = { 0 };
KeStackAttachProcess(eprocess, &kApcState);

if (isSuccess) {
ZwFreeVirtualMemory(NtCurrentProcess(), &freeMemInfo->baseAddr, &freeMemInfo->freeSize, MEM_RELEASE);
}

KeUnstackDetachProcess(&kApcState);
//LOG("ZwFreeVirtualMemory");
ExFreePool(freeMemInfo);
ObDereferenceObject(eprocess);

}