Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement simple example of hooking kernel pages via EPT #7

Open
wbenny opened this issue Aug 30, 2018 · 19 comments
Open

Implement simple example of hooking kernel pages via EPT #7

wbenny opened this issue Aug 30, 2018 · 19 comments
Labels
enhancement New feature or request

Comments

@wbenny
Copy link
Owner

wbenny commented Aug 30, 2018

No description provided.

@wbenny wbenny added the enhancement New feature or request label Aug 30, 2018
@wbenny
Copy link
Owner Author

wbenny commented Nov 16, 2018

Hi! I have already experimental version of this in my local branch, but since this project got lots of focus in the game-cheating scene, I decided to wait out a little bit with releasing and let those people figure some basic stuff for themselves. :)

@wbenny wbenny mentioned this issue Nov 16, 2018
@assemblyw0t
Copy link

Hey wbenny! After several years of crawling the hub, this project was the first one that actually inspired me to make an account. Not to beat a dead horse, but I was also wondering if you could maybe give a small tip as to getting this working with kernel pages. Simply allocating and page aligning in the kernel instead of usermode doesn't seem to work as I thought it would. Perhaps you'd be willing to give us an early Christmas present and share some advice on what we can do? I can't speak for others, but I would be thrilled to understand what I am doing wrong.

@DebugBuggin
Copy link

I got this working, but not sure if he wants me to share how, I can say though it's extremely simple.

@rianquinn
Copy link
Contributor

rianquinn commented Dec 5, 2018 via email

@wbenny
Copy link
Owner Author

wbenny commented Dec 5, 2018

@assemblyw0t Hello! There are several ways to do it. The naive approach is to use the exact same steps as for UM hooking - which kinda requires you to port Detours-like behavior to KM - patch the first instruction(s) with jump to your routine and create trampoline to call the original function. Second approach is much easier and is used in DdiMon (pointed out by @rianquinn) - use int3 + redirect RIP in the handler and create "micro-trampoline" (first original instruction, jmp + address). Third approach might be using DR - which, if placed at right position, can give you easily all Nt* calls with single DR register.

@DebugBuggin We're on github, I can't prevent anyone who wants to fork this repo :) Quite the opposite, I highly encourage it.

@assemblyw0t
Copy link

@DebugBuggin Maybe he wouldn't mind if it was in private? Would you mind helping a fellow out? My email is adrianpaza89@gmail.com. I would really appreciate it if I could get in contact with you and have a few minutes of your time.

@DebugBuggin
Copy link

I did the first method wbenny just listed and it works great.

@assemblyw0t
Copy link

That's what I tried to do. I allocate kernel buffers and pass them through the same way as you did in the usermode example, but it just ends up in a kernel security check BSOD. I didn't even actually hook anything yet.

@DebugBuggin
Copy link

DebugBuggin commented Dec 5, 2018

That's what I tried to do. I allocate kernel buffers and pass them through the same way as you did in the usermode example, but it just ends up in a kernel security check BSOD. I didn't even actually hook anything yet.

Well currently I still need to rely on the usermode code(that loops through the cpu and issues vm calls) to then call my kernel code that hides the kernel pages. I don't know why but can't get the kernel equivalent that I borrowed from hyperplatform(and is below) to work without bluescreen yet. It's not that big of deal for me though. Anyway just try your stuff with the usermode program, and first replace these with your kernel buffers

    Data->PageRead = MmGetPhysicalAddress(Context->RdxAsPointer);
        Data->PageExec = MmGetPhysicalAddress(Context->R8AsPointer);

hyperplatform func

NTSTATUS UtilForEachProcessor(NTSTATUS(*callback_routine)(void *), void *context) {
	PAGED_CODE();

	const auto number_of_processors =
		KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
	for (ULONG processor_index = 0; processor_index < number_of_processors;
		processor_index++) {
		PROCESSOR_NUMBER processor_number = {0};
		auto status =
			KeGetProcessorNumberFromIndex(processor_index, &processor_number);
		if (!NT_SUCCESS(status)) {
			return status;
		}

		DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "cpu index %d\n", processor_index);

		// Switch the current processor
		GROUP_AFFINITY affinity = {0};
		affinity.Group = processor_number.Group;
		affinity.Mask = 1ull << processor_number.Number;
		GROUP_AFFINITY previous_affinity = {0};
		KeSetSystemGroupAffinityThread(&affinity, &previous_affinity);

		// Execute callback
		status = callback_routine(context);

		KeRevertToUserGroupAffinityThread(&previous_affinity);
		if (!NT_SUCCESS(status)) {
			return status;
		}
	}
	return STATUS_SUCCESS;
}

@assemblyw0t
Copy link

assemblyw0t commented Dec 5, 2018

Maybe I am just dumb. But I don't see what I am doing wrong.

pfnIofCallDriver IofCallDriveFn = (pfnIofCallDriver)GetSystemFunctionAddress(L"IofCallDriver");

PVOID OriginalFunction = (PVOID)IofCallDriveFn;
PVOID OriginalFunctionAligned = PAGE_ALIGN(OriginalFunction);

PVOID OriginalFunctionBackup = KernelAllocateMem(PAGE_SIZE * 2);
PVOID OriginalFunctionBackupAligned = PAGE_ALIGN((ULONG_PTR)OriginalFunctionBackup + PAGE_SIZE);
memcpy(OriginalFunctionBackupAligned, OriginalFunctionAligned, PAGE_SIZE);

Just as the usermode example does, and then I pass these addresses in the same way as usermode example does.

@DebugBuggin
Copy link

Maybe I am just dumb. But I don't see what I am doing wrong.

pfnIofCallDriver IofCallDriveFn = (pfnIofCallDriver)GetSystemFunctionAddress(L"IofCallDriver");

PVOID OriginalFunction = (PVOID)IofCallDriveFn;
PVOID OriginalFunctionAligned = PAGE_ALIGN(OriginalFunction);

PVOID OriginalFunctionBackup = KernelAllocateMem(PAGE_SIZE * 2);
PVOID OriginalFunctionBackupAligned = PAGE_ALIGN((ULONG_PTR)OriginalFunctionBackup + PAGE_SIZE);
memcpy(OriginalFunctionBackupAligned, OriginalFunctionAligned, PAGE_SIZE);

Just as the usermode example does, and then I pass these addresses in the same way as usermode example does.

looks fine assuming GetSystemFunctionAddress returning proper address. Make sure you don't do those calls in, HvppHandleExecuteVmcall callback, but do them beforehand. thne can look like

Data->PageRead = MmGetPhysicalAddress(OriginalFunctionBackupAligned);
Data->PageExec = MmGetPhysicalAddress(OriginalFunctionAligned);

@assemblyw0t
Copy link

assemblyw0t commented Dec 5, 2018

Addresses seem fine. Debug output:

13:45:31.395 INF #3 4 32 System OriginalFunctionBackupAligned 77e0a000
13:45:31.395 INF #3 4 32 System OriginalFunctionAligned fc4c9000

But once I try to pass them through,

ia32_asm_vmx_vmcall(0xc1, (uint64_t)OriginalFunctionBackupAligned, (uint64_t)OriginalFunctionAligned, 0);

Big fat BSOD.

Edit: I found my problem. As I thought, I am actually dumb. I wasn't properly calling it for each core. Thank you all for your help.

@DebugBuggin
Copy link

literally just replacing

 Data->PageRead = MmGetPhysicalAddress(Context->RdxAsPointer);
  Data->PageExec = MmGetPhysicalAddress(Context->R8AsPointer);

with

  Data->PageRead = MmGetPhysicalAddress(OriginalFunctionBackupAligned);
   Data->PageExec = MmGetPhysicalAddress(OriginalFunctionAligned);

then using the usermod tool, worked fine for me. The example btw only hides 1 page so after you get this working you have to modify the code to support multiple hidden pages, which i'm working on rn.

@assemblyw0t
Copy link

assemblyw0t commented Dec 8, 2018

@wbenny Sorry to bother you again. I ran into a bit of a dilemma and was hoping to get some insight. I decided to go the HyperPlatform method route you recommended with the software breakpoint hooking, but I can't seem to catch the exception? I BSOD at the 0xCC instruction without being able to redirect execution to my detour function. I'm doing as follows:

void vmexit_custom_handler::handle_exception_or_nmi(vcpu_t& vp) noexcept
{

  auto interrupt = vp.exit_interrupt_info();

  switch (interrupt.type())
  {

  case vmx::interrupt_type::software_exception:
    switch (interrupt.vector())
    {
    case exception_vector::breakpoint:

      if (vp.guest_rip() == IofCallDriver_Address)
      {
        vp.guest_rip(reinterpret_cast<uintptr_t>(&Detoured_IofCallDriver));
      }
      break;

    default:
      break;
    }
  default:
    break;
  }

  vp.inject(interrupt);
  vp.suppress_rip_adjust();
}

Is there something obvious that I am missing here?

@wbenny
Copy link
Owner Author

wbenny commented Dec 8, 2018

I see two possible issues:
First, ensure that that breakpoint is set in the exception bitmap:

  // Inside vmexit_handler::setup() method:
  auto exception_bitmap = vmx::exception_bitmap_t{};
  exception_bitmap.breakpoint = true;
  vp.exception_bitmap(exception_bitmap);

Second, you don't probably want to reinject the breakpoint exception, when you handle it your way:

      if (vp.guest_rip() == IofCallDriver_Address)
      {
        vp.guest_rip(reinterpret_cast<uintptr_t>(&Detoured_IofCallDriver));
        vp.suppress_rip_adjust();
        return;
      }
      break;

@assemblyw0t
Copy link

That is some crazy fast response time. :) Thank you! I made the changes you suggested. Unfortunately, now I am freezing immediately into an eventual DPC_WATCHDOG_VIOLATION BSOD. But it has at least given me some more wiggle room to experiment. Much appreciated!

@assemblyw0t
Copy link

I ruled out the problem being in the function I'm attempting to detour into. Seems that regardless of what I change the instruction pointer to I just immediately freeze permanently. Strange...

@nicholaskunes
Copy link

@wbenny Any plans to implement this feature or should we implement it locally? It's been a few months and just wondering if you'll be pushing out an official version or if I should just start implementing a local version.

@ScalletaZ
Copy link

I got this working, but not sure if he wants me to share how, I can say though it's extremely simple.

Hi, I'm work on kernel hook and I just got multi pages, can u help me with kernel hook stuff?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants