Repository: dmarov/chamd Branch: master Commit: 5e5ca227cc6d Files: 54 Total size: 397.1 KB Directory structure: gitextract_ndahrtj5/ ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── scripts/ │ ├── builder.js │ ├── context.js │ ├── multibuilder.js │ ├── paths.js │ └── utils.js ├── src/ │ ├── DBKDrvr.c │ ├── DBKDrvr.h │ ├── DBKFunc.c │ ├── DBKFunc.h │ ├── IOPLDispatcher.c │ ├── IOPLDispatcher.h │ ├── amd64/ │ │ ├── dbkfunca.asm │ │ ├── debuggera.asm │ │ ├── noexceptionsa.asm │ │ ├── ultimapa.asm │ │ ├── vmxhelpera.asm │ │ └── vmxoffloada.asm │ ├── debugger.c │ ├── debugger.h │ ├── deepkernel.c │ ├── deepkernel.h │ ├── extradefines.h │ ├── extraimports.h │ ├── interruptHook.c │ ├── interruptHook.h │ ├── memscan.c │ ├── memscan.h │ ├── noexceptions.c │ ├── noexceptions.h │ ├── processlist.c │ ├── processlist.h │ ├── segmentinfo.asm │ ├── threads.c │ ├── threads.h │ ├── ultimap.c │ ├── ultimap.h │ ├── ultimap2/ │ │ ├── apic.c │ │ └── apic.h │ ├── ultimap2.c │ ├── ultimap2.h │ ├── vmxhelper.c │ ├── vmxhelper.h │ ├── vmxoffload.c │ └── vmxoffload.h ├── templates/ │ ├── CMakeLists.txt.tpl │ ├── chamd.inf.tpl │ ├── driver64.dat.tpl │ └── makecat.cdf.tpl └── wdk/ └── FindWDK.cmake ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .gitignore ================================================ build .cache .env node_modules src/CMakeLists.txt dist dists ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021 Dmitry Marov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Chamd (Chameleon DBK64) ## Modified Driver Based on Cheat Engine DBK64 This project provides a modified version of the Cheat Engine DBK64 kernel driver aimed at bypassing detection by anti-cheat systems. It has been tested with EAC and BattlEye, potentially working with other systems as well. Current version is based on Cheat Engine version 7.5 ![screen](images/screen.png "Screen") *As shown below, the driver successfully bypasses EAC detection in Robocraft.* ## 0. Important Warning Even if you manage to use this driver successfully, it does not guarantee that you will not be suspected of cheating. The usage of this software and any actions outlined in this document are entirely at your own risk. Please note that using these instructions **poses a significant security risk** to your system. It is highly recommended to use an isolated computer for this purpose. Additionally, isolating the machine within a local network may further enhance security. ## 1. System Requirements and Preparation - **Operating System**: Windows 11 only. This method will not work on Windows 10. - **Disable Anti-Virus Software**: Turn off Virus and Threat Protection in Windows settings, as well as any other anti-virus or anti-malware programs. These can block the required files - Install **Cheat Engine 7.5** by compiling it from [source](https://github.com/cheat-engine/cheat-engine/releases/tag/7.5), which is preferred method, or using the [installer](https://www.cheatengine.org/downloads.php). ## 2. Obtain your driver 2.1. **Download the Compiled Driver** Visit [GitHub](https://github.com/dmarov/chamd/releases/tag/v1.4) to download the compiled driver, which includes a multibuild file. 2.2. **Extract the Archive** Once downloaded, extract the archive containing over 1000+ drivers. This extensive number of drivers is strategically provided to minimize the chances of multiple users using the same driver instance, thereby reducing detection risk. 2.3. **Select and Copy the Driver** From the extracted files, choose any one random driver. Copy all three associated files to the directory where `cheatengine-x86_64.exe` is located. Why Multiple Drivers? Using a unique driver decreases the chances of your setup being flagged or blacklisted by anti-cheat systems. The way anti-cheat works, is that it develops signatures for popular cheats and flags it in the database. Of cousre there's more to anti-cheat. For better reliability, consider compiling your own driver following the instructions in Section 7. This process creates a custom driver, reducing the chances of detection. ## 3. Bypassing **Digital Signature Enforcement** At this point you have driver signed with untrusted certificate. There are a few options to load it. In this section the method based on bypass of **Patchguard** and **Digital Signature Enforcement** will be explained. Refer to section 8 for some extra method. - [Video tutorial 1](https://www.youtube.com/watch?v=EJGuJp2fqpM) - [Video tutorial 2](https://www.youtube.com/watch?v=zsw3xoG3zgs) 3.1. Create bootable usb drive 3.1.1. Download [EfiGuard](https://github.com/Mattiwatti/EfiGuard/releases/) version >= 1.4. 3.1.2. Mount you usb drive. 2GB drive should be more than enough. 3.1.3. Format your usb drive as `FAT32`. **be carefull to format the correct device**, because it will erase all data on USB drive 3.1.4. Partition your device as bootable `GPT` with `EFI` partition. **be carefull to partition the correct device!!!** Open command prompt as administrator and perform the following commands: ```shell diskpart list disk // detect your USB drive select disk {put number of your USB drive here} // SELECTING CORRECT DISK!!! list disk // make sure correct disk is selected clean // wipe out your drive create partition primary size=512 // create EFI partition list partitions // created partition shoud be displayed select partition 1 // select created partition active // mark partition as active format quick fs=fat32 // create filesystem assign // disk should be mounted exit ``` 3.1.5. Copy files to USB drive Copy `EFI` directory from the extracted archive to the root of created partition. 3.1.6. Rename bootloader Locate `EFI/Boot/Loader.efi` on the USB drive, than rename it to `EFI/Boot/bootx64.efi`. 3.2. Boot your system from USB drive. Restart your computer and enter the UEFI settings (usually by pressing F2, F12, or DEL during startup). Set the first boot option to your USB drive (UEFI). The second option should be your windows drive (UEFI). Ensure Secure Boot is disabled since EFIGuard rootkit isn't signed. 3.3. Copy files for digital signature enforcement bypass 3.3.1. In the directory containing `cheatengine-x86_64.exe`, create a new file named `run.bat` with the following content: ```shell "%~dp0\EfiDSEFix.exe" -d start /d "%~dp0" cheatengine-x86_64.exe timeout /t 20 "%~dp0\EfiDSEFix.exe" -e ``` Before doing that, make sure that file explored is not configured to hide known file extensions. 3.3.2. Copy `EfiDSEFix.exe` from the archive to the same directory as `cheatengine-x86_64.exe`. ## 4. Configure Cheat Engine Make sure that `Edit` > `Settings` > `Extra` > `Query memory region routines` is checked `Edit` > `Settings` > `Extra` > `Read/Write process memory` is checked Click `OK`. It might end up with errors. Close Cheat Engine. ## 5. Run Cheat Engine. ### If you followed section 3: Run `run.bat` as Administrator. Do not close the window that pops out manually!!! Wait for it to close automatically. After the driver has been successfully loaded, you can directly run `cheatengine-x86_64.exe` without needing to execute `run.bat`. ### If you followed section 8: Run `cheatengine-x86_64.exe` ## 6. Congratulations You have successfully loaded the DBK64 driver signed with an untrusted certificate. This allows kernel mode anti-cheat (EAC) to permit starting the game and performing operations on the game memory. Note: The last tested version was confirmed on EAC as of April 19, 2025. ## 7. [Extra] Compile the driver from source Anti-cheat systems may collect signatures of suspicious drivers used by a small number of users to block them. To mitigate this, it is recommended to compile your own version of the driver with a unique signature. #### **Note:** Use PowerShell or Cmder for these steps. [Video Tutorial](https://www.youtube.com/watch?v=7ARwpxZPpE8) 7.1. Clone this repository ```shell git clone https://github.com/dmarov/chamd.git cd chamd ``` Ensure you have Git installed on your system before cloning. 7.2. Install [nodejs](https://nodejs.org/en/). Version `>=19` is required. 7.3. Install packages Run the following command in your terminal: ```shell npm install ``` This will install all required packages for building the driver. 7.4. Copy `.env.tpl` to `.env` Create a copy of the template environment file by running: ```shell cp .env.tpl .env ``` 7.5. Optionaly set `CHAMD_DBK_DRIVER_NAME` in `.env`. Edit the `.env` file and set the `CHAMD_DBK_DRIVER_NAME` variable to a unique name if desired. Example: ```env CHAMD_DBK_DRIVER_NAME=mysupercooldrv ``` 7.6. Install Visual Studio Install **Visual Studio 2022** (Community or Enterprise) with support for C++ development. [Download Visual Studio](https://visualstudio.microsoft.com/downloads/) Ensure you select the following components during installation: - **Desktop Development with C++** - **MSVC v143 (C++ Compiler)** 7.7. Install [Windows SDK and WDK](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk) Carefully follow the instructions from the link. It is important that SDK and WDK have the same version. Correct versions of spectre mitigated libraries should be installed in the process. 7.8. Install openssl. The most straightforward way is to use Chocolatey package manager. ```shell choco install openssl ``` This command has to be performed as Admininstrator. 7.9. Run build ```shell npm run all ``` Note: You can use [EV certificate](https://learn.microsoft.com/en-us/windows-hardware/drivers/dashboard/code-signing-cert-manage) to sign driver. You could skip digital signature enforcement bypass this way. They are costly though and can be revoked when misused. 7.10. Copy all files from the 'dist' directory to directory where `cheatengine-x86_64.exe` is located. 7.11. If you've managed to compile this driver successfully and want to share few randomized copies then run `npm run multibuild 10`. `dist` directory will contain `10` randomized drivers. Then go to section 3. ## 8. [Extra] Another Method of dealing with *Digital Signature Enforcement* If you want to bypass digital signature enforcement temporarily (for testing purposes only), follow these steps: 8.1. Enable Test Signing Mode: ```shell bcdedit /set testsigning on ``` This command modifies the boot configuration to allow loading of unsigned or self-signed drivers. 8.2. Reboot Your System: The changes take effect after a system reboot. 8.3. Proceed with Section 4: Load the driver as outlined in section 4. 8.4. Disable Test Signing Mode: After testing, disable test signing mode to restore normal security settings. ```shell bcdedit /set testsigning off ``` Enabling test signing mode is intended for development and troubleshooting purposes only. It temporarily reduces the system's security by allowing unsigned drivers to load. This method does not work with anti-cheat systems, as they typically enforce strict driver signature requirements. ================================================ FILE: package.json ================================================ { "type": "module", "dependencies": { "dotenv": "^10.0.0", "handlebars": "^4.7.7" }, "scripts": { "all": "node --unhandled-rejections=strict ./scripts/builder.js all", "purge": "node --unhandled-rejections=strict ./scripts/builder.js purge", "compile": "node --unhandled-rejections=strict ./scripts/builder.js compile", "geninf": "node --unhandled-rejections=strict ./scripts/builder.js geninf", "sign": "node --unhandled-rejections=strict ./scripts/builder.js sign", "multibuild": "node --unhandled-rejections=strict ./scripts/multibuilder.js" } } ================================================ FILE: scripts/builder.js ================================================ import dotenv from 'dotenv'; import Context from './context.js'; import generateRandomName from './utils.js'; import path from 'path'; import { __dirname } from './paths.js'; const env = dotenv.config().parsed; const driverName = env.CHAMD_DBK_DRIVER_NAME ?? generateRandomName(); const args = process.argv.slice(2); const command = args[0]; const distDir = path.normalize(__dirname + '\\..\\dist\\'); const context = new Context(driverName, distDir); await Context.purgeDir(distDir); switch (command) { case 'all': await context.all(); break; case 'purge': await context.purge(); break; case 'compile': await context.generateCmakeFile(); await context.compile(); break; case 'geninf': await context.createInfFile(); await context.stampInfFile(); break; case 'sign': await context.signDriver(); await context.createInfFile(); break; } ================================================ FILE: scripts/context.js ================================================ import path from 'path'; import fs from 'fs'; import handlebars from 'handlebars'; import { spawn } from 'child_process'; import { __dirname } from './paths.js'; export default class Context { buildDir = path.normalize(__dirname + '\\..\\build\\'); vcvarsCommunityPath = 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvarsall.bat'; vcvarsEnterprisePath = 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Auxiliary\\Build\\vcvarsall.bat'; vcPath = null; cmakeTplPath = path.normalize(__dirname + '\\..\\templates\\CMakeLists.txt.tpl'); infTplPath = path.normalize(__dirname + '\\..\\templates\\chamd.inf.tpl'); makeCatTplPath = path.normalize(__dirname + '\\..\\templates\\makecat.cdf.tpl'); datTplPath = path.normalize(__dirname + '\\..\\templates\\driver64.dat.tpl'); datPath = `${this.buildDir}driver64.dat`; srcDir = path.normalize(__dirname + '\\..\\src\\'); cmakeConfigPath = this.srcDir + 'CMakeLists.txt'; constructor(driverName, distDir) { this.distDir = distDir; this.driverName = driverName .replace(/[^a-z0-9_]/gi, '') .toLowerCase(); const communityVs = fs.existsSync(this.vcvarsCommunityPath); const enterpriseVs = fs.existsSync(this.vcvarsEnterprisePath); if (communityVs) { this.vcPath = this.vcvarsCommunityPath; } if (enterpriseVs) { this.vcPath = this.vcvarsEnterprisePath; } this.makeCatPath = `${this.buildDir}${this.driverName}.cdf`; } async all() { console.log(`Generating ${this.driverName} driver ...`); await this.purge(); await this.generateCmakeFile(); await this.compile(); await this.createMakeCatFile(); await this.makeCatFile(); await this.signDriver(); await this.createDriverDatFile(); await this.install(); await this.clearBuildDir(); console.log(`Success!!!`); console.log(`Now copy all files from ${this.distDir} to directory where cheatengine.exe is located`); } async purge() { await this.clearBuildDir(); if (!fs.existsSync(this.cmakeConfigPath)) { return; } fs.rmSync(this.cmakeConfigPath); } static async purgeDir(dir) { if (!fs.existsSync(dir)) { return; } fs.rmSync(dir, { recursive: true }) } async generateCmakeFile() { await this.templateToFile( this.cmakeTplPath, this.cmakeConfigPath, { DRIVER_NAME: this.driverName, } ); } async templateToFile(src, dist, vars) { const templateContent = fs.readFileSync(src, 'utf-8'); const template = handlebars.compile(templateContent); const res = template(vars) fs.writeFileSync(dist, res); } async compile() { console.log('Compiling'); if (this.vcPath === null) { throw new Error('Visual studio not found'); } if (!fs.existsSync(this.buildDir)) { fs.mkdirSync(this.buildDir); } const cmd = `"${this.vcPath}" amd64 && cd "${this.buildDir}" && cmake -G "Visual Studio 17 2022" -A x64 -T v143 "${this.srcDir}" && cmake --build . --config Release`; await this.execute(cmd, this.buildDir); } async createMakeCatFile() { console.log('Creating makecat file'); await this.templateToFile( this.makeCatTplPath, this.makeCatPath, { DRIVER_NAME: this.driverName, }, ); } async makeCatFile() { console.log('Makeing cat file'); const cmd = `"${this.vcPath}" amd64 && MakeCat.exe -v .\\${this.driverName}.cdf` await this.execute(cmd, this.buildDir); } async signDriver() { console.log('Signing driver'); const openssl = `openssl req -nodes -newkey rsa:2048 -keyout ${this.driverName}.key -out ${this.driverName}.csr -subj "/CN=${this.driverName}.com/O=${this.driverName} LTD./C=US"` const crt = `openssl x509 -signkey ${this.driverName}.key -in ${this.driverName}.csr -req -days 365 -out ${this.driverName}.crt`; const pfx = `openssl pkcs12 -export -out ${this.driverName}.pfx -inkey ${this.driverName}.key -in ${this.driverName}.crt -password pass:`; const vc = `"${this.vcPath}" amd64`; const signtool = `signtool sign /fd SHA256 -f "./${this.driverName}.pfx" -t "http://timestamp.digicert.com" -v "${this.driverName}.cat"`; const cmd = `${openssl} && ${crt} && ${pfx} && ${vc} && ${signtool}`; await this.execute(cmd, this.buildDir); } async execute(cmd, cwd, params = []) { console.log(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`); console.log(`Executing: ${cmd}`); return new Promise((res, rej) => { const proc = spawn(cmd, params, { cwd, shell: true, }); proc.stderr.setEncoding('utf-8'); proc.stdout.pipe(process.stdout); proc.stderr.pipe(process.stderr); proc.on('close', (code) => { code == 0 ? res() : rej(); }); }); } async createDriverDatFile() { console.log('Creating dat file'); await this.templateToFile( this.datTplPath, this.datPath, { DRIVER_NAME: this.driverName, }, ); } async install() { console.log('Installing'); if (!fs.existsSync(this.distDir)) { fs.mkdirSync(this.distDir, { recursive: true }); } fs.copyFileSync( `${this.buildDir}${this.driverName}.sys`, `${this.distDir}${this.driverName}.sys` ); fs.copyFileSync( `${this.buildDir}${this.driverName}.cat`, `${this.distDir}${this.driverName}.cat` ); fs.copyFileSync( `${this.buildDir}driver64.dat`, `${this.distDir}driver64.dat` ); } async clearBuildDir() { await Context.purgeDir(this.buildDir); } } ================================================ FILE: scripts/multibuilder.js ================================================ import Context from './context.js'; import generateRandomName from './utils.js'; import path from 'path'; import http from 'http'; import { __dirname } from './paths.js'; const args = process.argv.slice(2); const cnt = args[0] ? parseInt(args[0]) : 5; const distDir = path.normalize(__dirname + '\\..\\dist\\'); async function getRandomWord() { return new Promise((res, rej) => { const options = { host: 'random-word-api.herokuapp.com', path: '/word' }; const callback = function(response) { let str = ''; response.on('data', function (chunk) { str += chunk; }); response.on('end', function () { res(str); }); } const req = http.request(options, callback); req.on('error', function (e) { rej(e); }); req.end(); }); } await Context.purgeDir(distDir); const max = 15; const min = 4; for (let i = 0; i < cnt; i++) { const len = min + Math.floor(Math.random() * (max - min)); let name = generateRandomName(len); try { const result = await getRandomWord(); name = JSON.parse(result)[0] .replace(/[^a-z0-9_]/gi, '') .toLowerCase(); } catch(e) { console.log(e); } const context = new Context(name, distDir + name + '\\'); await context.all(); } ================================================ FILE: scripts/paths.js ================================================ import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); export const __dirname = path.dirname(__filename); ================================================ FILE: scripts/utils.js ================================================ export default (length) => { let result = ''; const characters = 'abcdefghijklmnopqrstuvwxyz'; const charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } ================================================ FILE: src/DBKDrvr.c ================================================ #pragma warning( disable: 4100 4101 4103 4189) #include "DBKFunc.h" #include #include #include "DBKDrvr.h" #include "deepkernel.h" #include "processlist.h" #include "memscan.h" #include "threads.h" #include "vmxhelper.h" #include "debugger.h" #include "vmxoffload.h" #include "IOPLDispatcher.h" #include "interruptHook.h" #include "ultimap.h" #include "ultimap2.h" #include "noexceptions.h" #include "ultimap2\apic.h" #if (AMD64 && TOBESIGNED) #include "sigcheck.h" #endif #ifdef CETC #include "cetc.h" #endif void UnloadDriver(PDRIVER_OBJECT DriverObject); NTSTATUS DispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); NTSTATUS DispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); #ifndef AMD64 //no api hooks for x64 //-----NtUserSetWindowsHookEx----- //prevent global hooks typedef ULONG (NTUSERSETWINDOWSHOOKEX)( IN HANDLE hmod, IN PUNICODE_STRING pstrLib OPTIONAL, IN DWORD idThread, IN int nFilterType, IN PVOID pfnFilterProc, IN DWORD dwFlags ); NTUSERSETWINDOWSHOOKEX OldNtUserSetWindowsHookEx; ULONG NtUserSetWindowsHookEx_callnumber; //HHOOK NewNtUserSetWindowsHookEx(IN HANDLE hmod,IN PUNICODE_STRING pstrLib OPTIONAL,IN DWORD idThread,IN int nFilterType, IN PROC pfnFilterProc,IN DWORD dwFlags); typedef NTSTATUS (*ZWSUSPENDPROCESS) ( IN ULONG ProcessHandle // Handle to the process ); ZWSUSPENDPROCESS ZwSuspendProcess; NTSTATUS ZwCreateThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, OUT PCLIENT_ID ClientId, IN PCONTEXT ThreadContext, IN PVOID UserStack, IN BOOLEAN CreateSuspended); //PVOID GetApiEntry(ULONG FunctionNumber); #endif typedef NTSTATUS(*PSRCTNR)(__in PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine); PSRCTNR PsRemoveCreateThreadNotifyRoutine2; typedef NTSTATUS(*PSRLINR)(__in PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine); PSRLINR PsRemoveLoadImageNotifyRoutine2; UNICODE_STRING uszDeviceString; PVOID BufDeviceString=NULL; void hideme(PDRIVER_OBJECT DriverObject) { #ifndef AMD64 typedef struct _MODULE_ENTRY { LIST_ENTRY le_mod; DWORD unknown[4]; DWORD base; DWORD driver_start; DWORD unk1; UNICODE_STRING driver_Path; UNICODE_STRING driver_Name; } MODULE_ENTRY, *PMODULE_ENTRY; PMODULE_ENTRY pm_current; pm_current = *((PMODULE_ENTRY*)((DWORD)DriverObject + 0x14)); //eeeeew *((PDWORD)pm_current->le_mod.Blink) = (DWORD) pm_current->le_mod.Flink; pm_current->le_mod.Flink->Blink = pm_current->le_mod.Blink; HiddenDriver=TRUE; #endif } int testfunction(int p1,int p2) { DbgPrint("Hello\nParam1=%d\nParam2=%d\n",p1,p2); return 0x666; } void* functionlist[1]; char paramsizes[1]; int registered=0; #ifdef DEBUG1 VOID TestPassive(UINT_PTR param) { DbgPrint("passive cpu call for cpu %d\n", KeGetCurrentProcessorNumber()); } VOID TestDPC(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { EFLAGS e=getEflags(); DbgPrint("Defered cpu call for cpu %d (Dpc=%p IF=%d IRQL=%d)\n", KeGetCurrentProcessorNumber(), Dpc, e.IF, KeGetCurrentIrql()); } #endif NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) /*++ Routine Description: This routine is called when the driver is loaded by NT. Arguments: DriverObject - Pointer to driver object created by system. RegistryPath - Pointer to the name of the services node for this driver. Return Value: The function value is the final status from the initialization operation. --*/ { NTSTATUS ntStatus; PVOID BufDriverString = NULL, BufProcessEventString = NULL, BufThreadEventString = NULL; UNICODE_STRING uszDriverString; UNICODE_STRING uszProcessEventString; UNICODE_STRING uszThreadEventString; PDEVICE_OBJECT pDeviceObject; HANDLE reg = 0; OBJECT_ATTRIBUTES oa; UNICODE_STRING temp; char wbuf[100]; WORD this_cs, this_ss, this_ds, this_es, this_fs, this_gs; ULONG cr4reg; criticalSection csTest; HANDLE Ultimap2Handle; KernelCodeStepping = 0; KernelWritesIgnoreWP = 0; this_cs = getCS(); this_ss = getSS(); this_ds = getDS(); this_es = getES(); this_fs = getFS(); this_gs = getGS(); temp.Buffer = (PWCH)wbuf; temp.Length = 0; temp.MaximumLength = 100; //DbgPrint("Loading driver\n"); if (RegistryPath) { //DbgPrint("Registry path = %S\n", RegistryPath->Buffer); InitializeObjectAttributes(&oa, RegistryPath, OBJ_KERNEL_HANDLE, NULL, NULL); ntStatus = ZwOpenKey(®, KEY_QUERY_VALUE, &oa); if (ntStatus == STATUS_SUCCESS) { UNICODE_STRING A, B, C, D; PKEY_VALUE_PARTIAL_INFORMATION bufA, bufB, bufC, bufD; ULONG ActualSize; //DbgPrint("Opened the key\n"); BufDriverString = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100); BufDeviceString = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100); BufProcessEventString = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100); BufThreadEventString = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100); bufA = BufDriverString; bufB = BufDeviceString; bufC = BufProcessEventString; bufD = BufThreadEventString; RtlInitUnicodeString(&A, L"A"); RtlInitUnicodeString(&B, L"B"); RtlInitUnicodeString(&C, L"C"); RtlInitUnicodeString(&D, L"D"); if (ntStatus == STATUS_SUCCESS) ntStatus = ZwQueryValueKey(reg, &A, KeyValuePartialInformation, bufA, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100, &ActualSize); if (ntStatus == STATUS_SUCCESS) ntStatus = ZwQueryValueKey(reg, &B, KeyValuePartialInformation, bufB, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100, &ActualSize); if (ntStatus == STATUS_SUCCESS) ntStatus = ZwQueryValueKey(reg, &C, KeyValuePartialInformation, bufC, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100, &ActualSize); if (ntStatus == STATUS_SUCCESS) ntStatus = ZwQueryValueKey(reg, &D, KeyValuePartialInformation, bufD, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 100, &ActualSize); if (ntStatus == STATUS_SUCCESS) { //DbgPrint("Read ok\n"); RtlInitUnicodeString(&uszDriverString, (PCWSTR)bufA->Data); RtlInitUnicodeString(&uszDeviceString, (PCWSTR)bufB->Data); RtlInitUnicodeString(&uszProcessEventString, (PCWSTR)bufC->Data); RtlInitUnicodeString(&uszThreadEventString, (PCWSTR)bufD->Data); //DbgPrint("DriverString=%S\n", uszDriverString.Buffer); //DbgPrint("DeviceString=%S\n", uszDeviceString.Buffer); //DbgPrint("ProcessEventString=%S\n", uszProcessEventString.Buffer); //DbgPrint("ThreadEventString=%S\n", uszThreadEventString.Buffer); } else { ExFreePool(bufA); ExFreePool(bufB); ExFreePool(bufC); ExFreePool(bufD); //DbgPrint("Failed reading the value\n"); ZwClose(reg); return STATUS_UNSUCCESSFUL;; } } else { //DbgPrint("Failed opening the key\n"); return STATUS_UNSUCCESSFUL;; } } else loadedbydbvm = TRUE; ntStatus = STATUS_SUCCESS; if (!loadedbydbvm) { // Point uszDriverString at the driver name #ifndef CETC // Create and initialize device object ntStatus = IoCreateDevice(DriverObject, 0, &uszDriverString, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (ntStatus != STATUS_SUCCESS) { //DbgPrint("IoCreateDevice failed\n"); ExFreePool(BufDriverString); ExFreePool(BufDeviceString); ExFreePool(BufProcessEventString); ExFreePool(BufThreadEventString); if (reg) ZwClose(reg); return ntStatus; } // Point uszDeviceString at the device name // Create symbolic link to the user-visible name ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString); if (ntStatus != STATUS_SUCCESS) { //DbgPrint("IoCreateSymbolicLink failed: %x\n", ntStatus); // Delete device object if not successful IoDeleteDevice(pDeviceObject); ExFreePool(BufDriverString); ExFreePool(BufDeviceString); ExFreePool(BufProcessEventString); ExFreePool(BufThreadEventString); if (reg) ZwClose(reg); return ntStatus; } #endif } //when loaded by dbvm driver object is 'valid' so store the function addresses //DbgPrint("DriverObject=%p\n", DriverObject); // Load structure to point to IRP handlers... DriverObject->DriverUnload = UnloadDriver; DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose; if (loadedbydbvm) DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)DispatchIoctlDBVM; else DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl; //Processlist init #ifndef CETC ProcessEventCount = 0; ExInitializeResourceLite(&ProcesslistR); #endif CreateProcessNotifyRoutineEnabled = FALSE; //threadlist init ThreadEventCount = 0; processlist = NULL; #ifndef AMD64 //determine if PAE is used cr4reg=(ULONG)getCR4(); if ((cr4reg & 0x20)==0x20) { PTESize=8; //pae PAGE_SIZE_LARGE=0x200000; MAX_PDE_POS=0xC0604000; MAX_PTE_POS=0xC07FFFF8; } else { PTESize=4; PAGE_SIZE_LARGE=0x400000; MAX_PDE_POS=0xC0301000; MAX_PTE_POS=0xC03FFFFC; } #else PTESize = 8; //pae PAGE_SIZE_LARGE = 0x200000; //base was 0xfffff68000000000ULL //to MAX_PTE_POS = 0xFFFFF6FFFFFFFFF8ULL; // base + 0x7FFFFFFFF8 MAX_PDE_POS = 0xFFFFF6FB7FFFFFF8ULL; // base + 0x7B7FFFFFF8 #endif #ifdef CETC DbgPrint("Going to initialice CETC\n"); InitializeCETC(); #endif //hideme(DriverObject); //ok, for those that see this, enabling this WILL fuck up try except routines, even in usermode you'll get a blue sreen DbgPrint("Initializing debugger\n"); debugger_initialize(); // Return success (don't do the devicestring, I need it for unload) DbgPrint("Cleaning up initialization buffers\n"); if (BufDriverString) { ExFreePool(BufDriverString); BufDriverString = NULL; } if (BufProcessEventString) { ExFreePool(BufProcessEventString); BufProcessEventString = NULL; } if (BufThreadEventString) { ExFreePool(BufThreadEventString); BufThreadEventString = NULL; } if (reg) { ZwClose(reg); reg = 0; } //fetch cpu info { DWORD r[4]; DWORD a; __cpuid(r, 0); DbgPrint("cpuid.0: r[1]=%x", r[1]); if (r[1] == 0x756e6547) //GenuineIntel { __cpuid(r, 1); a = r[0]; cpu_stepping = a & 0xf; cpu_model = (a >> 4) & 0xf; cpu_familyID = (a >> 8) & 0xf; cpu_type = (a >> 12) & 0x3; cpu_ext_modelID = (a >> 16) & 0xf; cpu_ext_familyID = (a >> 20) & 0xff; cpu_model = cpu_model + (cpu_ext_modelID << 4); cpu_familyID = cpu_familyID + (cpu_ext_familyID << 4); vmx_init_dovmcall(1); setup_APIC_BASE(); //for ultimap } else { DbgPrint("Not an intel cpu"); if (r[1] == 0x68747541) { DbgPrint("This is an AMD\n"); vmx_init_dovmcall(0); } } } #ifdef DEBUG1 { APIC y; DebugStackState x; DbgPrint("offset of LBR_Count=%d\n", (UINT_PTR)&x.LBR_Count - (UINT_PTR)&x); DbgPrint("Testing forEachCpu(...)\n"); forEachCpu(TestDPC, NULL, NULL, NULL, NULL); DbgPrint("Testing forEachCpuAsync(...)\n"); forEachCpuAsync(TestDPC, NULL, NULL, NULL, NULL); DbgPrint("Testing forEachCpuPassive(...)\n"); forEachCpuPassive(TestPassive, 0); DbgPrint("LVT_Performance_Monitor=%x\n", (UINT_PTR)&y.LVT_Performance_Monitor - (UINT_PTR)&y); } #endif #ifdef DEBUG2 DbgPrint("No exceptions test:"); if (NoExceptions_Enter()) { int o = 45678; int x = 0, r = 0; //r=NoExceptions_CopyMemory(&x, &o, sizeof(x)); r = NoExceptions_CopyMemory(&x, (PVOID)0, sizeof(x)); DbgPrint("o=%d x=%d r=%d", o, x, r); DbgPrint("Leaving NoExceptions mode"); NoExceptions_Leave(); } #endif RtlInitUnicodeString(&temp, L"PsSuspendProcess"); PsSuspendProcess = (PSSUSPENDPROCESS)MmGetSystemRoutineAddress(&temp); RtlInitUnicodeString(&temp, L"PsResumeProcess"); PsResumeProcess = (PSSUSPENDPROCESS)MmGetSystemRoutineAddress(&temp); return STATUS_SUCCESS; } NTSTATUS DispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { // Check for SeDebugPrivilege. (So only processes with admin rights can use it) LUID sedebugprivUID; sedebugprivUID.LowPart=SE_DEBUG_PRIVILEGE; sedebugprivUID.HighPart=0; Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; if (SeSinglePrivilegeCheck(sedebugprivUID, UserMode)) { Irp->IoStatus.Status = STATUS_SUCCESS; #ifdef AMD64 #ifdef TOBESIGNED { NTSTATUS s=SecurityCheck(); Irp->IoStatus.Status = s; } // DbgPrint("Returning %x (and %x)\n", Irp->IoStatus.Status, s); #endif #endif } else { DbgPrint("A process without SeDebugPrivilege tried to open the dbk driver\n"); Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } Irp->IoStatus.Information=0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Irp->IoStatus.Status; } NTSTATUS DispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information=0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Irp->IoStatus.Status; } void UnloadDriver(PDRIVER_OBJECT DriverObject) { cleanupDBVM(); if (!debugger_stopDebugging()) { DbgPrint("Can not unload the driver because of debugger\n"); return; // } debugger_shutdown(); ultimap_disable(); DisableUltimap2(); UnregisterUltimapPMI(); clean_APIC_BASE(); NoExceptions_Cleanup(); if ((CreateProcessNotifyRoutineEnabled) || (ImageNotifyRoutineLoaded)) { PVOID x; UNICODE_STRING temp; RtlInitUnicodeString(&temp, L"PsRemoveCreateThreadNotifyRoutine"); PsRemoveCreateThreadNotifyRoutine2 = (PSRCTNR)MmGetSystemRoutineAddress(&temp); RtlInitUnicodeString(&temp, L"PsRemoveCreateThreadNotifyRoutine"); PsRemoveLoadImageNotifyRoutine2 = (PSRLINR)MmGetSystemRoutineAddress(&temp); RtlInitUnicodeString(&temp, L"ObOpenObjectByName"); x=MmGetSystemRoutineAddress(&temp); DbgPrint("ObOpenObjectByName=%p\n",x); if ((PsRemoveCreateThreadNotifyRoutine2) && (PsRemoveLoadImageNotifyRoutine2)) { DbgPrint("Stopping processwatch\n"); if (CreateProcessNotifyRoutineEnabled) { DbgPrint("Removing process watch"); #if (NTDDI_VERSION >= NTDDI_VISTASP1) PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyRoutineEx,TRUE); #else PsSetCreateProcessNotifyRoutine(CreateProcessNotifyRoutine,TRUE); #endif DbgPrint("Removing thread watch"); PsRemoveCreateThreadNotifyRoutine2(CreateThreadNotifyRoutine); } if (ImageNotifyRoutineLoaded) PsRemoveLoadImageNotifyRoutine2(LoadImageNotifyRoutine); } else return; //leave now!!!!! } DbgPrint("Driver unloading\n"); IoDeleteDevice(DriverObject->DeviceObject); #ifdef CETC #ifndef CETC_RELEASE UnloadCETC(); //not possible in the final build #endif #endif #ifndef CETC_RELEASE DbgPrint("DeviceString=%S\n",uszDeviceString.Buffer); { NTSTATUS r = IoDeleteSymbolicLink(&uszDeviceString); DbgPrint("IoDeleteSymbolicLink: %x\n", r); } ExFreePool(BufDeviceString); #endif CleanProcessList(); ExDeleteResourceLite(&ProcesslistR); RtlZeroMemory(&ProcesslistR, sizeof(ProcesslistR)); #if (NTDDI_VERSION >= NTDDI_VISTA) if (DRMHandle) { DbgPrint("Unregistering DRM handle"); ObUnRegisterCallbacks(DRMHandle); DRMHandle = NULL; } #endif } ================================================ FILE: src/DBKDrvr.h ================================================ #ifndef DBKDRVR_H #define DBKDRVR_H #define dbkversion 2000027 #endif ================================================ FILE: src/DBKFunc.c ================================================ #pragma warning( disable: 4103) #include #include #include "DBKFunc.h" /* #include "vmxhelper.h" #include "interruptHook.h" */ #ifdef TESTCOMPILE //small bypass to make test compiles function (this compiler doesn't have CLI/STI ) void _enable() {} void _disable() {} #endif void forEachCpuPassive(PF f, UINT_PTR param) /* calls a specific function for each cpu that runs in passive mode */ { CCHAR cpunr; KAFFINITY cpus, original; ULONG cpucount; //KeIpiGenericCall is not present in xp //count cpus first KeQueryActiveProcessorCount is not present in xp) cpucount=0; cpus=KeQueryActiveProcessors(); original=cpus; while (cpus) { if (cpus % 2) cpucount++; cpus=cpus / 2; } cpus=KeQueryActiveProcessors(); cpunr=0; while (cpus) { if (cpus % 2) { //bit is set #if (NTDDI_VERSION >= NTDDI_VISTA) KAFFINITY oldaffinity; #endif KAFFINITY newaffinity; //DbgPrint("Calling passive function for cpunr %d\n", cpunr); //set affinity newaffinity=(KAFFINITY)(1 << cpunr); #if (NTDDI_VERSION >= NTDDI_VISTA) oldaffinity=KeSetSystemAffinityThreadEx(newaffinity); #else //XP and earlier (this routine is not called often, only when the user asks explicitly { LARGE_INTEGER delay; delay.QuadPart=-50; //short wait just to be sure... (the docs do not say that a switch happens imeadiatly for the no Ex version) KeSetSystemAffinityThread(newaffinity); KeDelayExecutionThread(UserMode, FALSE, &delay); } #endif //call function f(param); #if (NTDDI_VERSION >= NTDDI_VISTA) KeRevertToUserAffinityThreadEx(oldaffinity); #endif } cpus=cpus / 2; cpunr++; } #if (NTDDI_VERSION < NTDDI_VISTA) KeSetSystemAffinityThread(original); #endif } void forOneCpu(CCHAR cpunr, PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback) { PKDPC dpc; if (preDPCCallback) //if preDPCCallback is set call it which may change the system arguments preDPCCallback(cpunr, dpcfunction, DeferredContext, &SystemArgument1, &SystemArgument2); dpc = ExAllocatePool(NonPagedPool, sizeof(KDPC)); KeInitializeDpc(dpc, dpcfunction, DeferredContext); KeSetTargetProcessorDpc(dpc, cpunr); KeInsertQueueDpc(dpc, SystemArgument1, SystemArgument2); KeFlushQueuedDpcs(); ExFreePool(dpc); } void forEachCpu(PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback) /* calls a specified dpcfunction for each cpu on the system */ { CCHAR cpunr; KAFFINITY cpus; ULONG cpucount; PKDPC dpc; int dpcnr; //KeIpiGenericCall is not present in xp //count cpus first KeQueryActiveProcessorCount is not present in xp) cpucount=0; cpus=KeQueryActiveProcessors(); while (cpus) { if (cpus % 2) cpucount++; cpus=cpus / 2; } dpc=ExAllocatePool(NonPagedPool, sizeof(KDPC)*cpucount); cpus=KeQueryActiveProcessors(); cpunr=0; dpcnr=0; while (cpus) { if (cpus % 2) { //bit is set //DbgPrint("Calling dpc routine for cpunr %d (dpc=%p)\n", cpunr, &dpc[dpcnr]); if (preDPCCallback) preDPCCallback(cpunr, dpcfunction, DeferredContext, &SystemArgument1, &SystemArgument2); KeInitializeDpc(&dpc[dpcnr], dpcfunction, DeferredContext); KeSetTargetProcessorDpc (&dpc[dpcnr], cpunr); KeInsertQueueDpc(&dpc[dpcnr], SystemArgument1, SystemArgument2); KeFlushQueuedDpcs(); dpcnr++; } cpus=cpus / 2; cpunr++; } ExFreePool(dpc); } void forEachCpuAsync(PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback) /* calls a specified dpcfunction for each cpu on the system */ { CCHAR cpunr; KAFFINITY cpus; ULONG cpucount; PKDPC dpc; int dpcnr; //KeIpiGenericCall is not present in xp //count cpus first KeQueryActiveProcessorCount is not present in xp) cpucount = 0; cpus = KeQueryActiveProcessors(); while (cpus) { if (cpus % 2) cpucount++; cpus = cpus / 2; } dpc = ExAllocatePool(NonPagedPool, sizeof(KDPC)*cpucount); cpus = KeQueryActiveProcessors(); cpunr = 0; dpcnr = 0; while (cpus) { if (cpus % 2) { //bit is set //DbgPrint("Calling dpc routine for cpunr %d\n", cpunr); if (preDPCCallback) //if preDPCCallback is set call it which may change the system arguments preDPCCallback(cpunr, dpcfunction, DeferredContext, &SystemArgument1, &SystemArgument2); KeInitializeDpc(&dpc[dpcnr], dpcfunction, DeferredContext); KeSetTargetProcessorDpc(&dpc[dpcnr], cpunr); KeInsertQueueDpc(&dpc[dpcnr], SystemArgument1, SystemArgument2); dpcnr++; } cpus = cpus / 2; cpunr++; } KeFlushQueuedDpcs(); ExFreePool(dpc); } //own critical section implementation for use when the os is pretty much useless (dbvm tech) void spinlock(volatile LONG *lockvar) { while (1) { //it was 0, let's see if we can set it to 1 //race who can set it to 1: if (_InterlockedExchange((volatile LONG *)lockvar, 1)==0) return; //lock aquired, else continue loop _mm_pause(); } } void csEnter(PcriticalSection CS) { EFLAGS oldstate=getEflags(); if ((CS->locked) && (CS->cpunr==cpunr())) { //already locked but the locker is this cpu, so allow, just increase lockcount CS->lockcount++; return; } disableInterrupts(); //disable interrupts to prevent taskswitch in same cpu spinlock(&(CS->locked)); //sets CS->locked to 1 //here so the lock is aquired and locked is 1 CS->lockcount=1; CS->cpunr=cpunr(); CS->oldIFstate=oldstate.IF; } void csLeave(PcriticalSection CS) { if ((CS->locked) && (CS->cpunr==cpunr())) { CS->lockcount--; if (CS->lockcount==0) { //unlock if (CS->oldIFstate) enableInterrupts(); CS->cpunr=-1; //set to an cpunr CS->locked=0; } } } int getCpuCount(void) { KAFFINITY ap=KeQueryActiveProcessors(); int count=0; while (ap>0) { if (ap % 2) count++; ap=ap / 2; } return count; } int isPrefix(unsigned char b) { switch (b) { case 0x26: case 0x2e: case 0x36: case 0x3e: case 0x64: case 0x65: case 0x66: case 0x67: case 0xf0: //lock case 0xf2: //repne case 0xf3: //rep return 1; default: return 0; } } UINT64 getDR7(void) { return __readdr(7); } int cpunr(void) { DWORD x[4]; __cpuid(&x[0],1); return (x[1] >> 24)+1; } EFLAGS getEflags(void) { UINT64 x=__getcallerseflags(); PEFLAGS y = (PEFLAGS)&x; return *y; } UINT64 readMSR(DWORD msr) { return __readmsr(msr); } void setCR0(UINT64 newcr0) { __writecr0(newcr0); } UINT64 getCR0(void) { return __readcr0(); } UINT64 getCR2(void) { return __readcr2(); } void setCR3(UINT64 newCR3) { __writecr3((UINT_PTR)newCR3); } UINT64 getCR3(void) { return __readcr3(); } void setCR4(UINT64 newcr4) { __writecr4(newcr4); } UINT64 getCR4(void) { return __readcr4(); } void GetIDT(PIDT pIdt) { __sidt(pIdt); } void enableInterrupts(void) { #ifdef AMD64 _enable(); #else __asm{sti}; #endif } void disableInterrupts(void) { #ifdef AMD64 _disable(); #else __asm{cli}; #endif } UINT64 getTSC(void) { return __rdtsc(); } #ifndef AMD64 //function declarations that can be done inline without needing an .asm file _declspec( naked ) WORD getSS(void) { __asm { mov ax,ss ret } } _declspec( naked ) WORD getCS(void) { __asm { mov ax,cs ret } } _declspec( naked ) WORD getDS(void) { __asm { mov ax,ds ret } } _declspec( naked ) WORD getES(void) { __asm { mov ax,es ret } } _declspec( naked ) WORD getFS(void) { __asm { mov ax,fs ret } } _declspec( naked ) WORD getGS(void) { __asm { mov ax,gs ret } } _declspec( naked ) ULONG getRSP(void) //... { __asm { mov eax,esp add eax,4 //don't add this call ret } } _declspec( naked ) ULONG getRBP(void) { __asm { mov eax,ebp ret } } _declspec( naked ) ULONG getRAX(void) { __asm { mov eax,eax ret } } _declspec( naked ) ULONG getRBX(void) { __asm { mov eax,ebx ret } } _declspec( naked ) ULONG getRCX(void) { __asm { mov eax,ecx ret } } _declspec( naked ) ULONG getRDX(void) { __asm { mov eax,edx ret } } _declspec( naked ) ULONG getRSI(void) { __asm { mov eax,esi ret } } _declspec( naked ) ULONG getRDI(void) { __asm { mov eax,edi ret } } _declspec( naked ) unsigned short GetTR(void) { __asm{ STR AX ret } } void GetGDT(PGDT pGdt) { __asm { MOV EAX, [pGdt] SGDT [EAX] } } _declspec( naked )WORD GetLDT() { __asm { SLDT ax ret } } #endif ================================================ FILE: src/DBKFunc.h ================================================ #ifndef DBKFUNC_H #define DBKFUNC_H #pragma warning( disable: 4214 ) #include #include #include #include "interruptHook.h" #ifdef RELEASE #define DbgPrint(...) #endif int _fltused; typedef VOID F(UINT_PTR param); typedef F *PF; typedef VOID PREDPC_CALLBACK(CCHAR cpunr, PKDEFERRED_ROUTINE Dpc, PVOID DeferredContext, PVOID *SystemArgument1, PVOID *SystemArgument2); typedef PREDPC_CALLBACK *PPREDPC_CALLBACK; typedef struct _criticalSection { LONG locked; int cpunr; //unique id for a cpu int lockcount; int oldIFstate; } criticalSection, *PcriticalSection; //ntosp.h typedef _Function_class_(KNORMAL_ROUTINE) _IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_min_(PASSIVE_LEVEL) _IRQL_requires_(PASSIVE_LEVEL) _IRQL_requires_same_ VOID KNORMAL_ROUTINE( _In_opt_ PVOID NormalContext, _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2 ); typedef KNORMAL_ROUTINE *PKNORMAL_ROUTINE; typedef _Function_class_(KKERNEL_ROUTINE) _IRQL_requires_max_(APC_LEVEL) _IRQL_requires_min_(APC_LEVEL) _IRQL_requires_(APC_LEVEL) _IRQL_requires_same_ VOID KKERNEL_ROUTINE( _In_ struct _KAPC *Apc, _Inout_ PKNORMAL_ROUTINE *NormalRoutine, _Inout_ PVOID *NormalContext, _Inout_ PVOID *SystemArgument1, _Inout_ PVOID *SystemArgument2 ); typedef KKERNEL_ROUTINE *PKKERNEL_ROUTINE; typedef _Function_class_(KRUNDOWN_ROUTINE) _IRQL_requires_max_(PASSIVE_LEVEL) _IRQL_requires_min_(PASSIVE_LEVEL) _IRQL_requires_(PASSIVE_LEVEL) _IRQL_requires_same_ VOID KRUNDOWN_ROUTINE( _In_ struct _KAPC *Apc ); typedef KRUNDOWN_ROUTINE *PKRUNDOWN_ROUTINE; typedef _IRQL_requires_same_ _Function_class_(KENUM_ROUTINE) VOID KENUM_ROUTINE( _In_reads_(_Inexpressible_(Length)) PVOID Data, _In_ ULONG Length, _In_ PVOID Context ); typedef KENUM_ROUTINE *PKENUM_ROUTINE; typedef enum _KAPC_ENVIRONMENT { OriginalApcEnvironment, AttachedApcEnvironment, CurrentApcEnvironment, InsertApcEnvironment } KAPC_ENVIRONMENT; NTKERNELAPI _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_min_(PASSIVE_LEVEL) _IRQL_requires_same_ VOID KeEnumerateQueueApc( _Inout_ PKTHREAD Thread, _In_ PKENUM_ROUTINE CallbackRoutine, _In_ PVOID Context, _In_opt_ KPROCESSOR_MODE *ApcMode ); NTKERNELAPI _IRQL_requires_same_ _When_(Environment != OriginalApcEnvironment, __drv_reportError("Caution: " "Using an APC environment other than the original environment can lead to " "a system bugcheck if the target thread is attached to a process with APCs " "disabled. APC environments should be used with care.")) VOID KeInitializeApc( _Out_ PRKAPC Apc, _In_ PRKTHREAD Thread, _In_ KAPC_ENVIRONMENT Environment, _In_ PKKERNEL_ROUTINE KernelRoutine, _In_opt_ PKRUNDOWN_ROUTINE RundownRoutine, _In_opt_ PKNORMAL_ROUTINE NormalRoutine, _In_opt_ KPROCESSOR_MODE ProcessorMode, _In_opt_ PVOID NormalContext ); NTKERNELAPI _Must_inspect_result_ _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_requires_min_(PASSIVE_LEVEL) _IRQL_requires_same_ BOOLEAN KeInsertQueueApc( _Inout_ PRKAPC Apc, _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2, _In_ KPRIORITY Increment ); NTSYSAPI NTSTATUS NTAPI ZwOpenThread( OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); struct PTEStruct { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned Reserved : 1; // dirty unsigned PS : 1; // page size (0 = 4-KB page) unsigned G : 1; // global page unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN : 20; // page-frame number }; //typedef struct PTEStruct *PPDPTE; //typedef struct PTEStruct *PPDE; //typedef struct PTEStruct *PPTE; struct PTEStruct64 { unsigned long long P : 1; // present (1 = present) unsigned long long RW : 1; // read/write unsigned long long US : 1; // user/supervisor unsigned long long PWT : 1; // page-level write-through unsigned long long PCD : 1; // page-level cache disabled unsigned long long A : 1; // accessed unsigned long long Reserved : 1; // dirty unsigned long long PS : 1; // page size (0 = 4-KB page) unsigned long long G : 1; // global page unsigned long long A1 : 1; // available 1 aka copy-on-write unsigned long long A2 : 1; // available 2/ is 1 when paged to disk unsigned long long A3 : 1; // available 3 unsigned long long PFN : 52; // page-frame number }; //typedef struct PTEStruct64 *PPDPTE_PAE; //typedef struct PTEStruct64 *PPDE_PAE; //typedef struct PTEStruct64 *PPTE_PAE; typedef struct tagDebugregs { ULONG DR0; ULONG DR1; ULONG DR2; ULONG DR3; ULONG DR5; ULONG DR6; ULONG DR7; } Debugregs; typedef struct { unsigned CF :1; // 0 unsigned reserved1 :1; // 1 unsigned PF :1; // 2 unsigned reserved2 :1; // 3 unsigned AF :1; // 4 unsigned reserved3 :1; // 5 unsigned ZF :1; // 6 unsigned SF :1; // 7 unsigned TF :1; // 8 unsigned IF :1; // 9 unsigned DF :1; // 10 unsigned OF :1; // 11 unsigned IOPL :2; // 12+13 unsigned NT :1; // 14 unsigned reserved4 :1; // 15 unsigned RF :1; // 16 unsigned VM :1; // 17 unsigned AC :1; // 18 unsigned VIF :1; // 19 unsigned VIP :1; // 20 unsigned ID :1; // 21 unsigned reserved5 :10; // 22-31 #ifdef AMD64 unsigned reserved6 :8; unsigned reserved7 :8; unsigned reserved8 :8; unsigned reserved9 :8; #endif } EFLAGS,*PEFLAGS; typedef struct tagDebugReg7 { unsigned L0 :1; // 0 unsigned G0 :1; // 1 unsigned L1 :1; // 2 unsigned G1 :1; // 3 unsigned L2 :1; // 4 unsigned G2 :1; // 5 unsigned L3 :1; // 6 unsigned G3 :1; // 7 unsigned GL :1; // 8 unsigned GE :1; // 9 unsigned undefined_1: 1; //1 10 unsigned RTM : 1; // 11 unsigned undefined_0: 1; //0 12 unsigned GD :1; // 13 unsigned undefined2 :2; // 00 unsigned RW0 :2; unsigned LEN0 :2; unsigned RW1 :2; unsigned LEN1 :2; unsigned RW2 :2; unsigned LEN2 :2; unsigned RW3 :2; unsigned LEN3 :2; #ifdef AMD64 unsigned undefined3 :8; unsigned undefined4 :8; unsigned undefined5 :8; unsigned undefined6 :8; #endif } DebugReg7; typedef struct DebugReg6 { unsigned B0 :1; unsigned B1 :1; unsigned B2 :1; unsigned B3 :1; unsigned undefined1 :9; // 011111111 unsigned BD :1; unsigned BS :1; unsigned BT :1; unsigned RTM : 1; //0=triggered unsigned undefined2 :15; // 111111111111111 #ifdef AMD64 unsigned undefined3 :8; unsigned undefined4 :8; unsigned undefined5 :8; unsigned undefined6 :8; #endif } DebugReg6; #pragma pack(2) //allignment of 2 bytes typedef struct tagGDT { WORD wLimit; PVOID vector; } GDT, *PGDT; #pragma pack() //UCHAR BufferSize; void GetIDT(PIDT pIdt); #ifdef AMD64 extern void _fxsave(volatile void *); extern void GetGDT(PGDT pGdt); extern WORD GetLDT(); extern WORD GetTR(void); #else void GetGDT(PGDT pGdt); WORD GetLDT(); WORD GetTR(void); #endif UINT64 readMSR(DWORD msr); UINT64 getDR7(void); void setCR0(UINT64 newCR0); UINT64 getCR0(void); UINT64 getCR2(void); void setCR3(UINT64 newCR3); UINT64 getCR3(void); UINT64 getCR4(void); void setCR4(UINT64 newcr4); UINT64 getTSC(void); #ifdef AMD64 extern WORD getCS(void); extern WORD getSS(void); extern WORD getDS(void); extern WORD getES(void); extern WORD getFS(void); extern WORD getGS(void); extern UINT64 getRSP(void); extern UINT64 getRBP(void); extern UINT64 getRAX(void); extern UINT64 getRBX(void); extern UINT64 getRCX(void); extern UINT64 getRDX(void); extern UINT64 getRSI(void); extern UINT64 getRDI(void); #else WORD getCS(void); WORD getSS(void); WORD getDS(void); WORD getES(void); WORD getFS(void); WORD getGS(void); ULONG getRSP(void); ULONG getRBP(void); ULONG getRAX(void); ULONG getRBX(void); ULONG getRCX(void); ULONG getRDX(void); ULONG getRSI(void); ULONG getRDI(void); #endif extern UINT64 getR8(void); extern UINT64 getR9(void); extern UINT64 getR10(void); extern UINT64 getR11(void); extern UINT64 getR12(void); extern UINT64 getR13(void); extern UINT64 getR14(void); extern UINT64 getR15(void); extern UINT64 getAccessRights(UINT64 segment); extern UINT64 getSegmentLimit(UINT64 segment); int getCpuCount(void); BOOL loadedbydbvm; int PTESize; UINT_PTR PAGE_SIZE_LARGE; UINT_PTR MAX_PDE_POS; UINT_PTR MAX_PTE_POS; int cpu_stepping; int cpu_model; int cpu_familyID; int cpu_type; int cpu_ext_modelID; int cpu_ext_familyID; int KernelCodeStepping; int KernelWritesIgnoreWP; int isPrefix(unsigned char b); EFLAGS getEflags(void); int cpunr(void); void disableInterrupts(void); void enableInterrupts(void); void csEnter(PcriticalSection CS); void csLeave(PcriticalSection CS); void forOneCpu(CCHAR cpunr, PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback); void forEachCpu(PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback); void forEachCpuAsync(PKDEFERRED_ROUTINE dpcfunction, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2, OPTIONAL PPREDPC_CALLBACK preDPCCallback); void forEachCpuPassive(PF f, UINT_PTR param); #endif; ================================================ FILE: src/IOPLDispatcher.c ================================================ #pragma warning( disable: 4100 4101 4103 4189) #include "IOPLDispatcher.h" #include "DBKFunc.h" #include "DBKDrvr.h" #include "memscan.h" #include "deepkernel.h" #include "processlist.h" #include "threads.h" #include "interruptHook.h" #include "debugger.h" #include "vmxhelper.h" #include "vmxoffload.h" #include "ultimap.h" #include "ultimap2.h" UINT64 PhysicalMemoryRanges=0; //initialized once, and used thereafter. If the user adds/removes ram at runtime, screw him and make him the reload the driver UINT64 PhysicalMemoryRangesListSize=0; #if (NTDDI_VERSION >= NTDDI_VISTA) PVOID DRMHandle = NULL; PEPROCESS DRMProcess = NULL; PEPROCESS DRMProcess2 = NULL; #endif typedef PCHAR (*GET_PROCESS_IMAGE_NAME) (PEPROCESS Process); GET_PROCESS_IMAGE_NAME PsGetProcessImageFileName; /* typedef struct { int listcount; char cpunrs[255]; } CPULISTFILLSTRUCT, *PCPULISTFILLSTRUCT; VOID GetCPUIDS_all(PCPULISTFILLSTRUCT p) { DbgPrint("GetCPUIDS_all(for cpu %d)\n", cpunr()); if (p->listcount<255) { p->cpunrs[p->listcount]=cpunr(); p->listcount++; } } */ NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL); void mykapc2(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) { ULONG_PTR iswow64; ExFreePool(Apc); DbgPrint("My second kernelmode apc!!!!\n"); DbgPrint("SystemArgument1=%x\n",*(PULONG)SystemArgument1); DbgPrint("SystemArgument2=%x\n", *(PULONG)SystemArgument2); if (ZwQueryInformationProcess(ZwCurrentProcess(), ProcessWow64Information, &iswow64, sizeof(iswow64), NULL) == STATUS_SUCCESS) { #if (NTDDI_VERSION >= NTDDI_VISTA) if (iswow64) { DbgPrint("WOW64 apc"); PsWrapApcWow64Thread(NormalContext, (PVOID*)NormalRoutine); } #endif } } void nothing2(PVOID arg1, PVOID arg2, PVOID arg3) { return; } void mykapc(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) { //kernelmode apc, always gets executed PKAPC kApc; LARGE_INTEGER Timeout; kApc = ExAllocatePool(NonPagedPool, sizeof(KAPC)); ExFreePool(Apc); DbgPrint("My kernelmode apc!!!!(irql=%d)\n", KeGetCurrentIrql()); DbgPrint("NormalRoutine=%p\n",*(PUINT_PTR)NormalRoutine); DbgPrint("NormalContext=%p\n",*(PUINT_PTR)NormalContext); DbgPrint("SystemArgument1=%p\n",*(PUINT_PTR)SystemArgument1); DbgPrint("SystemArgument2=%p\n",*(PUINT_PTR)SystemArgument2); KeInitializeApc(kApc, (PKTHREAD)PsGetCurrentThread(), 0, (PKKERNEL_ROUTINE)mykapc2, NULL, (PKNORMAL_ROUTINE)*(PUINT_PTR)SystemArgument1, UserMode, (PVOID)*(PUINT_PTR)NormalContext ); KeInsertQueueApc (kApc, (PVOID)*(PUINT_PTR)SystemArgument1, (PVOID)*(PUINT_PTR)SystemArgument2, 0); //wait in usermode (so interruptable by a usermode apc) Timeout.QuadPart = 0; KeDelayExecutionThread(UserMode, TRUE, &Timeout); return; } void nothing(PVOID arg1, PVOID arg2, PVOID arg3) { return; } void CreateRemoteAPC(ULONG threadid,PVOID addresstoexecute) { PKTHREAD kThread; PKAPC kApc; kApc = ExAllocatePool(NonPagedPool, sizeof(KAPC)); kThread=(PKTHREAD)getPEThread(threadid); DbgPrint("(PVOID)KThread=%p\n",kThread); DbgPrint("addresstoexecute=%p\n", addresstoexecute); KeInitializeApc(kApc, kThread, 0, (PKKERNEL_ROUTINE)mykapc, NULL, (PKNORMAL_ROUTINE)nothing, KernelMode, 0 ); KeInsertQueueApc (kApc, addresstoexecute, addresstoexecute, 0); } #define PROCESS_TERMINATE (0x0001) #define PROCESS_CREATE_THREAD (0x0002) #define PROCESS_SET_SESSIONID (0x0004) #define PROCESS_VM_OPERATION (0x0008) #define PROCESS_VM_READ (0x0010) #define PROCESS_VM_WRITE (0x0020) #define PROCESS_DUP_HANDLE (0x0040) #define PROCESS_CREATE_PROCESS (0x0080) #define PROCESS_SET_QUOTA (0x0100) #define PROCESS_SET_INFORMATION (0x0200) #define PROCESS_QUERY_INFORMATION (0x0400) #define PROCESS_SUSPEND_RESUME (0x0800) #define PROCESS_QUERY_LIMITED_INFORMATION (0x1000) #if (NTDDI_VERSION >= NTDDI_VISTA) OB_PREOP_CALLBACK_STATUS ThreadPreCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { if (DRMProcess == NULL) return OB_PREOP_SUCCESS; if (PsGetCurrentProcess() == DRMProcess) return OB_PREOP_SUCCESS; if (OperationInformation->ObjectType == *PsThreadType) { if ((PsGetProcessId(DRMProcess) == PsGetThreadProcessId(OperationInformation->Object)) || ((DRMProcess2) && (PsGetProcessId(DRMProcess2) == PsGetThreadProcessId(OperationInformation->Object)))) { //probably block it if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) { //create handle ACCESS_MASK da = OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; DbgPrint("PID %d opened a handle to the a CE thread with access mask %x", PsGetCurrentProcessId(), da); da = da & (THREAD_SET_LIMITED_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION); OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;// da; } else if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { //duplicate handle ACCESS_MASK da = OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess; DbgPrint("PID %d duplicated a handle to a CE thread with access mask %x", PsGetCurrentProcessId(), da); da = da & (THREAD_SET_LIMITED_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION); OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess = 0;// da; } } } return OB_PREOP_SUCCESS; } VOID ThreadPostCallback(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation) { //DbgPrint("ProcessPostCallback"); } OB_PREOP_CALLBACK_STATUS ProcessPreCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) { if (DRMProcess == NULL) return OB_PREOP_SUCCESS; //if (PsGetCurrentProcess() == DRMProcess) // return OB_PREOP_SUCCESS; if (OperationInformation->ObjectType == *PsProcessType) { if ((OperationInformation->Object == DRMProcess) || (OperationInformation->Object == DRMProcess2)) { //probably block it if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) { //create handle ACCESS_MASK da = OperationInformation->Parameters->CreateHandleInformation.DesiredAccess; DbgPrint("PID %d(%p) opened a handle to the CE process(%p) with access mask %x", PsGetCurrentProcessId(), PsGetCurrentProcess(), DRMProcess, da); da = da & (PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SUSPEND_RESUME); //da = da & PROCESS_SUSPEND_RESUME; OperationInformation->Parameters->CreateHandleInformation.DesiredAccess = 0;// da; } else if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) { //duplicate handle ACCESS_MASK da = OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess; DbgPrint("PID %d(%p) opened a handle to the CE process(%p) with access mask %x", PsGetCurrentProcessId(), PsGetCurrentProcess(), DRMProcess, da); da = da & (PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SUSPEND_RESUME); //da = da & PROCESS_SUSPEND_RESUME; OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess = 0;// da; } } } return OB_PREOP_SUCCESS; } VOID ProcessPostCallback(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION OperationInformation) { //DbgPrint("ProcessPostCallback"); } #endif BOOL DispatchIoctlDBVM(IN PDEVICE_OBJECT DeviceObject, ULONG IoControlCode, PVOID lpInBuffer, DWORD nInBufferSize, PVOID lpOutBuffer, DWORD nOutBufferSize, PDWORD lpBytesReturned) /* Called if dbvm has loaded the driver. Use this to setup a fake irp */ { //allocate a in and out buffer //setup a fake IRP IRP FakeIRP; BOOL r; PVOID buffer; buffer=ExAllocatePool(PagedPool, max(nInBufferSize, nOutBufferSize)); RtlCopyMemory(buffer, lpInBuffer, nInBufferSize); DbgPrint("DispatchIoctlDBVM\n"); FakeIRP.AssociatedIrp.SystemBuffer=buffer; FakeIRP.Flags=IoControlCode; //(ab)using an unused element r=DispatchIoctl(DeviceObject, &FakeIRP)==STATUS_SUCCESS; RtlCopyMemory(lpOutBuffer, buffer, nOutBufferSize); ExFreePool(buffer); return r; } NTSTATUS DispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { NTSTATUS ntStatus=STATUS_UNSUCCESSFUL; PIO_STACK_LOCATION irpStack=NULL; LUID sedebugprivUID; ULONG IoControlCode; if (!loadedbydbvm) { irpStack=IoGetCurrentIrpStackLocation(Irp); IoControlCode=irpStack->Parameters.DeviceIoControl.IoControlCode; } else IoControlCode=Irp->Flags; //DbgPrint("DispatchIoctl. IoControlCode=%x\n", IoControlCode); #ifdef TOBESIGNED sedebugprivUID.LowPart=SE_DEBUG_PRIVILEGE; sedebugprivUID.HighPart=0; if (SeSinglePrivilegeCheck(sedebugprivUID, UserMode)==FALSE) { DbgPrint("DispatchIoctl called by a process without SeDebugPrivilege"); return STATUS_UNSUCCESSFUL; } #endif switch(IoControlCode) { case IOCTL_CE_READMEMORY: __try { struct input { UINT64 processid; UINT64 startaddress; WORD bytestoread; } *pinp; pinp=Irp->AssociatedIrp.SystemBuffer; ntStatus=ReadProcessMemory((DWORD)pinp->processid,NULL,(PVOID)(UINT_PTR)pinp->startaddress,pinp->bytestoread,pinp) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } __except(1) { ntStatus = STATUS_UNSUCCESSFUL; }; break; case IOCTL_CE_WRITEMEMORY: __try { struct input { UINT64 processid; UINT64 startaddress; WORD bytestowrite; } *pinp,inp; DbgPrint("sizeof(inp)=%d\n",sizeof(inp)); pinp=Irp->AssociatedIrp.SystemBuffer; ntStatus=WriteProcessMemory((DWORD)pinp->processid,NULL,(PVOID)(UINT_PTR)pinp->startaddress,pinp->bytestowrite,(PVOID)((UINT_PTR)pinp+sizeof(inp))) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } __except(1) { //something went wrong and I don't know what ntStatus = STATUS_UNSUCCESSFUL; }; break; case IOCTL_CE_OPENPROCESS: { PEPROCESS selectedprocess = NULL; ULONG processid=*(PULONG)Irp->AssociatedIrp.SystemBuffer; HANDLE ProcessHandle = GetHandleForProcessID((HANDLE)processid); struct out { UINT64 h; BYTE Special; } *POutput = Irp->AssociatedIrp.SystemBuffer; ntStatus = STATUS_SUCCESS; if (ProcessHandle == 0) { POutput->Special = 0; __try { ProcessHandle = 0; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(processid), &selectedprocess) == STATUS_SUCCESS) { //DbgPrint("Calling ObOpenObjectByPointer\n"); ntStatus = ObOpenObjectByPointer( selectedprocess, 0, NULL, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, //UserMode, &ProcessHandle); //DbgPrint("ntStatus=%x",ntStatus); } } __except (1) { ntStatus = STATUS_UNSUCCESSFUL; } } else { //DbgPrint("ProcessHandle=%x", (int)ProcessHandle); POutput->Special = 1; } if (selectedprocess) { ObDereferenceObject(selectedprocess); } POutput->h=(UINT64)ProcessHandle; break; } case IOCTL_CE_OPENTHREAD: { HANDLE ThreadHandle; CLIENT_ID ClientID; OBJECT_ATTRIBUTES ObjectAttributes; RtlZeroMemory(&ObjectAttributes,sizeof(OBJECT_ATTRIBUTES)); ntStatus=STATUS_SUCCESS; ClientID.UniqueProcess=0; ClientID.UniqueThread=(HANDLE)(UINT_PTR)*(PULONG)Irp->AssociatedIrp.SystemBuffer; ThreadHandle=0; __try { ThreadHandle=0; ntStatus=ZwOpenThread(&ThreadHandle,PROCESS_ALL_ACCESS,&ObjectAttributes,&ClientID); } __except(1) { ntStatus=STATUS_UNSUCCESSFUL; } *(PUINT64)Irp->AssociatedIrp.SystemBuffer=(UINT64)ThreadHandle; break; } case IOCTL_CE_MAKEWRITABLE: { #ifdef AMD64 //untill I know how win64 handles paging, not implemented #else struct InputBuf { UINT64 StartAddress; ULONG Size; BYTE CopyOnWrite; } *PInputBuf; PInputBuf=Irp->AssociatedIrp.SystemBuffer; ntStatus=MakeWritable((PVOID)(UINT_PTR)PInputBuf->StartAddress,PInputBuf->Size,(PInputBuf->CopyOnWrite==1)) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; #endif break; } case IOCTL_CE_QUERY_VIRTUAL_MEMORY: { struct InputBuf { UINT64 ProcessID; UINT64 StartAddress; } *PInputBuf; struct OutputBuf { UINT64 length; DWORD protection; } *POutputBuf; UINT_PTR BaseAddress; UINT_PTR length; BOOL ShowResult=0; ntStatus=STATUS_SUCCESS; PInputBuf=Irp->AssociatedIrp.SystemBuffer; POutputBuf=Irp->AssociatedIrp.SystemBuffer; if (PInputBuf->StartAddress==(UINT64)0x12000) ShowResult=1; __try { ntStatus = GetMemoryRegionData((DWORD)PInputBuf->ProcessID, NULL, (PVOID)(UINT_PTR)(PInputBuf->StartAddress), &(POutputBuf->protection), &length, &BaseAddress); } __except(1) { DbgPrint("GetMemoryRegionData error"); ntStatus = STATUS_UNSUCCESSFUL; break; } POutputBuf->length=(UINT64)length; if (ShowResult) { DbgPrint("GetMemoryRegionData returned %x\n",ntStatus); DbgPrint("protection=%x\n",POutputBuf->protection); DbgPrint("length=%p\n",POutputBuf->length); DbgPrint("BaseAddress=%p\n", BaseAddress); } break; } case IOCTL_CE_TEST: //just a test to see it's working { UNICODE_STRING test; PVOID x; QWORD a, b; _disable(); a = __rdtsc(); b = __rdtsc(); _enable(); DbgPrint("%d\n", (int)(b - a)); break; } case IOCTL_CE_GETPETHREAD: { *(PUINT64)Irp->AssociatedIrp.SystemBuffer=(UINT64)getPEThread((UINT_PTR)*(PULONG)Irp->AssociatedIrp.SystemBuffer); ntStatus= STATUS_SUCCESS; break; } case IOCTL_CE_GETPEPROCESS: { DWORD processid=*(PDWORD)Irp->AssociatedIrp.SystemBuffer; PEPROCESS selectedprocess; if (processid==0) { ntStatus=STATUS_UNSUCCESSFUL; } else { if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(processid),&selectedprocess)==STATUS_SUCCESS) { #ifdef AMD64 *(PUINT64)Irp->AssociatedIrp.SystemBuffer=(UINT64)selectedprocess; #else *(PUINT64)Irp->AssociatedIrp.SystemBuffer=(DWORD)selectedprocess; #endif //DbgPrint("PEProcess=%llx\n", *(PUINT64)Irp->AssociatedIrp.SystemBuffer); ObDereferenceObject(selectedprocess); } else *(PUINT64)Irp->AssociatedIrp.SystemBuffer=0; } ntStatus= STATUS_SUCCESS; break; } case IOCTL_CE_READPHYSICALMEMORY: { struct input { UINT64 startaddress; UINT64 bytestoread; } *pinp; pinp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_READPHYSICALMEMORY:pinp->startaddress=%x, pinp->bytestoread=%d", pinp->startaddress, pinp->bytestoread); ntStatus = ReadPhysicalMemory((PVOID)(UINT_PTR)pinp->startaddress, (UINT_PTR)pinp->bytestoread, pinp); break; } case IOCTL_CE_WRITEPHYSICALMEMORY: { HANDLE physmem; UNICODE_STRING physmemString; OBJECT_ATTRIBUTES attributes; WCHAR physmemName[] = L"\\device\\physicalmemory"; UCHAR* memoryview; RtlInitUnicodeString( &physmemString, physmemName ); InitializeObjectAttributes( &attributes, &physmemString, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntStatus=ZwOpenSection( &physmem, SECTION_ALL_ACCESS, &attributes ); if (ntStatus==STATUS_SUCCESS) { //hey look, it didn't kill it struct input { UINT64 startaddress; UINT64 bytestoread; } *pinp; UCHAR* pinp2; SIZE_T length; PHYSICAL_ADDRESS viewBase; UINT_PTR offset; UINT_PTR toread; pinp=Irp->AssociatedIrp.SystemBuffer; pinp2=(UCHAR *)pinp; viewBase.QuadPart = (ULONGLONG)(pinp->startaddress); length=0x2000;//pinp->bytestoread; toread=(UINT_PTR)pinp->bytestoread; memoryview=NULL; ntStatus=ZwMapViewOfSection( physmem, //sectionhandle NtCurrentProcess(), //processhandle &memoryview, //BaseAddress 0L, //ZeroBits length, //CommitSize &viewBase, //SectionOffset &length, //ViewSize ViewShare, 0, PAGE_READWRITE); if (ntStatus==STATUS_SUCCESS) { offset=(UINT_PTR)(pinp->startaddress)-(UINT_PTR)viewBase.QuadPart; RtlCopyMemory(&memoryview[offset],&pinp2[16],toread); ZwUnmapViewOfSection( NtCurrentProcess(), //processhandle memoryview); } ZwClose(physmem); } break; } case IOCTL_CE_GETPHYSICALADDRESS: { struct input { UINT64 ProcessID; UINT64 BaseAddress; } *pinp; PEPROCESS selectedprocess; PHYSICAL_ADDRESS physical; physical.QuadPart = 0; ntStatus=STATUS_SUCCESS; pinp=Irp->AssociatedIrp.SystemBuffer; //DbgPrint("IOCTL_CE_GETPHYSICALADDRESS. ProcessID(%p)=%x BaseAddress(%p)=%x\n",&pinp->ProcessID, pinp->ProcessID, &pinp->BaseAddress, pinp->BaseAddress); __try { //switch to the selected process if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(pinp->ProcessID),&selectedprocess)==STATUS_SUCCESS) { KAPC_STATE apc_state; RtlZeroMemory(&apc_state,sizeof(apc_state)); KeStackAttachProcess((PVOID)selectedprocess,&apc_state); __try { physical=MmGetPhysicalAddress((PVOID)(UINT_PTR)pinp->BaseAddress); } __finally { KeUnstackDetachProcess(&apc_state); } ObDereferenceObject(selectedprocess); } } __except(1) { ntStatus=STATUS_UNSUCCESSFUL; } if (ntStatus==STATUS_SUCCESS) { //DbgPrint("physical.LowPart=%x",physical.LowPart); RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&physical.QuadPart,8); } break; } case IOCTL_CE_GETMEMORYRANGES: { struct output { UINT64 address; UINT64 size; } *poutp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_GETMEMORYRANGES\n"); if (PhysicalMemoryRanges==0) { __try { PPHYSICAL_MEMORY_RANGE mr=MmGetPhysicalMemoryRanges(); if (mr) { //find the end int i; PhysicalMemoryRanges=(UINT64)mr; for (i=0; mr[i].NumberOfBytes.QuadPart || mr[i].BaseAddress.QuadPart; i++); PhysicalMemoryRangesListSize=(UINT64)(&mr[i])-(UINT64)(&mr[0]); } } __except(1) { //just in case this function decides to bug out in the future } } poutp->address=PhysicalMemoryRanges; poutp->size=PhysicalMemoryRangesListSize; ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETSDTADDRESS: { DbgPrint("Obsolete\n"); ntStatus=STATUS_UNSUCCESSFUL; break; } case IOCTL_CE_GETCR0: { *(UINT64*)Irp->AssociatedIrp.SystemBuffer=getCR0(); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETCR4: { //seems CR4 isn't seen as a register... *(UINT64*)Irp->AssociatedIrp.SystemBuffer=(UINT64)getCR4(); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_SETCR4: { //seems CR4 isn't seen as a register... ULONG cr4reg=*(ULONG*)Irp->AssociatedIrp.SystemBuffer; setCR4((UINT64)cr4reg); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETCR3: { UINT_PTR cr3reg=0; PEPROCESS selectedprocess; ntStatus=STATUS_SUCCESS; //switch context to the selected process. (processid is stored in the systembuffer) if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(*(ULONG*)Irp->AssociatedIrp.SystemBuffer),&selectedprocess)==STATUS_SUCCESS) { __try { KAPC_STATE apc_state; RtlZeroMemory(&apc_state,sizeof(apc_state)); KeStackAttachProcess((PVOID)selectedprocess,&apc_state); __try { cr3reg=(UINT_PTR)getCR3(); } __finally { KeUnstackDetachProcess(&apc_state); } } __except(1) { ntStatus=STATUS_UNSUCCESSFUL; break; } ObDereferenceObject(selectedprocess); } DbgPrint("cr3reg=%p\n",cr3reg); *(UINT64*)Irp->AssociatedIrp.SystemBuffer=cr3reg; break; } case IOCTL_CE_GETSDT: { //returns the address of KeServiceDescriptorTable ntStatus = STATUS_UNSUCCESSFUL; break; } case IOCTL_CE_GETIDT: { //returns the address of the IDT of the current CPU IDT idt; RtlZeroMemory(&idt,sizeof(IDT)); GetIDT(&idt); RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer,2+8); //so that the 32-bit version doesn't have to deal with garbage at the end RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&idt,sizeof(IDT)); //copy idt ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETGDT: { //returns the address of the IDT of the current CPU GDT gdt; RtlZeroMemory(&gdt,sizeof(GDT)); GetGDT(&gdt); RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer,2+8); RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&gdt,sizeof(GDT)); //copy gdt ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_LAUNCHDBVM: { struct intput { UINT64 dbvmimgpath; DWORD32 cpuid; } *pinp; pinp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_LAUNCHDBVM\n"); initializeDBVM((PCWSTR)(UINT_PTR)pinp->dbvmimgpath); if (pinp->cpuid == 0xffffffff) { forEachCpu(vmxoffload_dpc, NULL, NULL, NULL, vmxoffload_override); cleanupDBVM(); } else forOneCpu((CCHAR)pinp->cpuid, vmxoffload_dpc, NULL, NULL, NULL, vmxoffload_override); DbgPrint("Returned from vmxoffload()\n"); break; } case IOCTL_CE_HOOKINTS: //hooks the DEBUG interrupts { DbgPrint("IOCTL_CE_HOOKINTS\n"); forEachCpu(debugger_initHookForCurrentCPU_DPC, NULL, NULL, NULL, NULL); ntStatus=STATUS_SUCCESS; /* DbgPrint("IOCTL_CE_HOOKINTS for cpu %d\n", cpunr()); if (debugger_initHookForCurrentCPU()) ntStatus=STATUS_SUCCESS; else ntStatus=STATUS_UNSUCCESSFUL;*/ break; } case IOCTL_CE_USERDEFINEDINTERRUPTHOOK: { struct intput { UINT64 interruptnumber; UINT64 newCS; UINT64 newRIP; UINT64 addressofjumpback; } *pinp; DbgPrint("IOCTL_CE_USERDEFINEDINTERRUPTHOOK\n"); pinp=Irp->AssociatedIrp.SystemBuffer; inthook_HookInterrupt((unsigned char)(pinp->interruptnumber), (int)pinp->newCS, (ULONG_PTR)pinp->newRIP, (PJUMPBACK)(UINT_PTR)(pinp->addressofjumpback)); DbgPrint("After the hook\n"); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_UNHOOKALLINTERRUPTS: { int i; DbgPrint("IOCTL_CE_UNHOOKALLINTERRUPTS for cpu %d\n",cpunr()); for (i=0; i<256; i++) inthook_UnhookInterrupt((unsigned char)i); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_SETGLOBALDEBUGSTATE: { struct intput { BOOL newstate; } *pinp; pinp=Irp->AssociatedIrp.SystemBuffer; debugger_setGlobalDebugState(pinp->newstate); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_DEBUGPROCESS: { struct input { DWORD ProcessID; } *pinp; DbgPrint("IOCTL_CE_DEBUGPROCESS\n"); pinp=Irp->AssociatedIrp.SystemBuffer; debugger_startDebugging(pinp->ProcessID); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_STOPDEBUGGING: { debugger_stopDebugging(); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_STARTPROCESSWATCH: { NTSTATUS r = STATUS_SUCCESS; DbgPrint("IOCTL_CE_STARTPROCESSWATCH\n"); ProcessWatcherOpensHandles = *(char *)Irp->AssociatedIrp.SystemBuffer != 0; if (CreateProcessNotifyRoutineEnabled && WatcherProcess) { ntStatus = STATUS_UNSUCCESSFUL; break; } //still here ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE); ProcessEventCount=0; ExReleaseResourceLite(&ProcesslistR); //DbgPrint("IOCTL_CE_STARTPROCESSWATCH\n"); CleanProcessList(); if ((r == STATUS_SUCCESS) && (CreateProcessNotifyRoutineEnabled == FALSE)) { DbgPrint("calling PsSetCreateProcessNotifyRoutine\n"); #if (NTDDI_VERSION >= NTDDI_VISTASP1) r=PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyRoutineEx, FALSE); CreateProcessNotifyRoutineEnabled = r== STATUS_SUCCESS; #else CreateProcessNotifyRoutineEnabled = (PsSetCreateProcessNotifyRoutine(CreateProcessNotifyRoutine,FALSE)==STATUS_SUCCESS); #endif if (CreateProcessNotifyRoutineEnabled) CreateThreadNotifyRoutineEnabled = (PsSetCreateThreadNotifyRoutine(CreateThreadNotifyRoutine) == STATUS_SUCCESS); } ntStatus=(CreateProcessNotifyRoutineEnabled) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; if (ntStatus==STATUS_SUCCESS) DbgPrint("CreateProcessNotifyRoutineEnabled worked\n"); else DbgPrint("CreateProcessNotifyRoutineEnabled failed (r=%x)\n",r); break; } case IOCTL_CE_GETPROCESSEVENTS: { ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE); *(PUCHAR)Irp->AssociatedIrp.SystemBuffer=ProcessEventCount; RtlCopyMemory((PVOID)((UINT_PTR)Irp->AssociatedIrp.SystemBuffer+1),&ProcessEventdata[0],ProcessEventCount*sizeof(ProcessEventdta)); ProcessEventCount=0; //there's room for new events ExReleaseResourceLite(&ProcesslistR); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETTHREADEVENTS: { ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE); *(PUCHAR)Irp->AssociatedIrp.SystemBuffer=ThreadEventCount; RtlCopyMemory((PVOID)((UINT_PTR)Irp->AssociatedIrp.SystemBuffer+1),&ThreadEventData[0],ThreadEventCount*sizeof(ThreadEventDta)); ThreadEventCount=0; //there's room for new events ExReleaseResourceLite(&ProcesslistR); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_CREATEAPC: { struct input { UINT64 threadid; UINT64 addresstoexecute; } *inp; inp=Irp->AssociatedIrp.SystemBuffer; CreateRemoteAPC((ULONG)inp->threadid,(PVOID)(UINT_PTR)inp->addresstoexecute); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_SUSPENDTHREAD: { struct input { ULONG threadid; } *inp; inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("CE_SUSPENDTHREAD\n"); DBKSuspendThread(inp->threadid); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_RESUMETHREAD: { struct input { ULONG threadid; } *inp; inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("CE_RESUMETHREAD\n"); DBKResumeThread(inp->threadid); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_SUSPENDPROCESS: { struct input { ULONG processid; } *inp; inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_SUSPENDPROCESS\n"); if (PsSuspendProcess) { PEPROCESS selectedprocess; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->processid), &selectedprocess) == STATUS_SUCCESS) { ntStatus = PsSuspendProcess(selectedprocess); ObDereferenceObject(selectedprocess); } else ntStatus = STATUS_NOT_FOUND; } else ntStatus = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_CE_RESUMEPROCESS: { struct input { ULONG processid; } *inp; inp = Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_RESUMEPROCESS\n"); if (PsResumeProcess) { PEPROCESS selectedprocess; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->processid), &selectedprocess) == STATUS_SUCCESS) { ntStatus = PsResumeProcess(selectedprocess); ObDereferenceObject(selectedprocess); } else ntStatus = STATUS_NOT_FOUND; } else ntStatus = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_CE_ALLOCATEMEM: { struct input { UINT64 ProcessID; UINT64 BaseAddress; UINT64 Size; UINT64 AllocationType; UINT64 Protect; } *inp; PEPROCESS selectedprocess; PVOID BaseAddress; SIZE_T RegionSize; inp=Irp->AssociatedIrp.SystemBuffer; BaseAddress=(PVOID)(UINT_PTR)inp->BaseAddress; RegionSize=(SIZE_T)(inp->Size); if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->ProcessID),&selectedprocess)==STATUS_SUCCESS) { __try { KAPC_STATE apc_state; RtlZeroMemory(&apc_state,sizeof(apc_state)); KeAttachProcess((PVOID)selectedprocess); //local process is much more fun!!!! DbgPrint("Switched Process\n"); __try { DbgPrint("Calling ZwAllocateVirtualMemory\n"); DbgPrint("Before call: BaseAddress=%p\n", BaseAddress); DbgPrint("Before call: RegionSize=%x\n", RegionSize); ntStatus=ZwAllocateVirtualMemory((HANDLE)-1, &BaseAddress, 0, &RegionSize, (ULONG)inp->AllocationType, (ULONG)inp->Protect); if ((ntStatus==STATUS_SUCCESS) && (HiddenDriver)) { //initialize the memory with crap so it becomes paged int i; char *x; x=BaseAddress; for (i=0; i < (int)RegionSize;i++) x[i]=(unsigned char)i; } DbgPrint("ntStatus=%x\n", ntStatus); DbgPrint("BaseAddress=%p\n",BaseAddress); DbgPrint("RegionSize=%x\n",RegionSize); *(PUINT64)Irp->AssociatedIrp.SystemBuffer=0; *(PUINT_PTR)Irp->AssociatedIrp.SystemBuffer=(UINT_PTR)BaseAddress; } __finally { KeDetachProcess(); } } __except(1) { ntStatus=STATUS_UNSUCCESSFUL; break; } ObDereferenceObject(selectedprocess); } break; } case IOCTL_CE_ALLOCATEMEM_NONPAGED: { struct input { ULONG Size; } *inp; PVOID address; int size; inp=Irp->AssociatedIrp.SystemBuffer; size=inp->Size; address=ExAllocatePool(NonPagedPool,size); *(PUINT64)Irp->AssociatedIrp.SystemBuffer=0; *(PUINT_PTR)Irp->AssociatedIrp.SystemBuffer=(UINT_PTR)address; if (address==0) ntStatus=STATUS_UNSUCCESSFUL; else { DbgPrint("Alloc success. Cleaning memory... (size=%d)\n",size); DbgPrint("address=%p\n", address); RtlZeroMemory(address, size); ntStatus=STATUS_SUCCESS; } break; } case IOCTL_CE_FREE_NONPAGED: { struct input { UINT64 Address; } *inp; inp = Irp->AssociatedIrp.SystemBuffer; ExFreePool((PVOID)(UINT_PTR)inp->Address); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_MAP_MEMORY: { struct input { UINT64 FromPID; UINT64 ToPID; UINT64 address; DWORD size; } *inp; struct output { UINT64 FromMDL; UINT64 Address; } *outp; KAPC_STATE apc_state; PEPROCESS selectedprocess; PMDL FromMDL=NULL; inp = Irp->AssociatedIrp.SystemBuffer; outp = Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_MAP_MEMORY\n"); DbgPrint("address %x size %d\n", inp->address, inp->size); ntStatus = STATUS_UNSUCCESSFUL; if (inp->FromPID) { //switch DbgPrint("From PID %d\n", inp->FromPID); if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->FromPID), &selectedprocess) == STATUS_SUCCESS) { __try { RtlZeroMemory(&apc_state, sizeof(apc_state)); KeStackAttachProcess((PVOID)selectedprocess, &apc_state); __try { FromMDL=IoAllocateMdl((PVOID)(UINT_PTR)inp->address, inp->size, FALSE, FALSE, NULL); if (FromMDL) MmProbeAndLockPages(FromMDL, KernelMode, IoReadAccess); } __finally { KeUnstackDetachProcess(&apc_state); } } __except (1) { DbgPrint("Exception\n"); ntStatus = STATUS_UNSUCCESSFUL; break; } ObDereferenceObject(selectedprocess); } } else { DbgPrint("From kernel or self\n", inp->FromPID); __try { FromMDL = IoAllocateMdl((PVOID)(UINT_PTR)inp->address, inp->size, FALSE, FALSE, NULL); if (FromMDL) { DbgPrint("IoAllocateMdl success\n"); MmProbeAndLockPages(FromMDL, KernelMode, IoReadAccess); } } __except (1) { DbgPrint("Exception\n"); if (FromMDL) { IoFreeMdl(FromMDL); FromMDL = NULL; } } } if (FromMDL) { DbgPrint("FromMDL is valid\n"); if (inp->ToPID) { //switch DbgPrint("To PID %d\n", inp->ToPID); if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->ToPID), &selectedprocess) == STATUS_SUCCESS) { __try { RtlZeroMemory(&apc_state, sizeof(apc_state)); KeStackAttachProcess((PVOID)selectedprocess, &apc_state); __try { outp->Address = (UINT64)MmMapLockedPagesSpecifyCache(FromMDL, UserMode, MmWriteCombined, NULL, FALSE, NormalPagePriority); outp->FromMDL = (UINT64)FromMDL; ntStatus = STATUS_SUCCESS; } __finally { KeUnstackDetachProcess(&apc_state); } } __except (1) { DbgPrint("Exception part 2\n"); ntStatus = STATUS_UNSUCCESSFUL; break; } ObDereferenceObject(selectedprocess); } } else { DbgPrint("To kernel or self\n", inp->FromPID); __try { outp->Address = (UINT64)MmMapLockedPagesSpecifyCache(FromMDL, UserMode, MmWriteCombined, NULL, FALSE, NormalPagePriority); outp->FromMDL = (UINT64)FromMDL; ntStatus = STATUS_SUCCESS; } __except (1) { DbgPrint("Exception part 2\n"); } } } else DbgPrint("FromMDL==NULL\n"); break; } case IOCTL_CE_UNMAP_MEMORY: { struct output { UINT64 FromMDL; UINT64 Address; } *inp; PMDL mdl; inp = Irp->AssociatedIrp.SystemBuffer; mdl = (PMDL)(UINT_PTR)inp->FromMDL; MmUnmapLockedPages((PMDL)(UINT_PTR)inp->Address, mdl); MmUnlockPages(mdl); IoFreeMdl(mdl); ntStatus = STATUS_SUCCESS; //no BSOD means success ;) break; } case IOCTL_CE_LOCK_MEMORY: { struct { UINT64 ProcessID; UINT64 address; UINT64 size; } *inp; struct { UINT64 mdl; } *outp; KAPC_STATE apc_state; PEPROCESS selectedprocess; DbgPrint("IOCTL_CE_LOCK_MEMORY"); inp = Irp->AssociatedIrp.SystemBuffer; outp = Irp->AssociatedIrp.SystemBuffer; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->ProcessID), &selectedprocess) == STATUS_SUCCESS) { PMDL mdl = NULL; KeStackAttachProcess(selectedprocess, &apc_state); __try { mdl = IoAllocateMdl((PVOID)(UINT_PTR)inp->address, (ULONG)inp->size, FALSE, FALSE, NULL); if (mdl) { __try { MmProbeAndLockPages(mdl, UserMode, IoReadAccess); DbgPrint("MmProbeAndLockPages succeeded"); } __except (1) { DbgPrint("MmProbeAndLockPages failed"); IoFreeMdl(mdl); ntStatus = STATUS_UNSUCCESSFUL; break; } } } __finally { KeUnstackDetachProcess(&apc_state); } outp->mdl = (UINT_PTR)mdl; DbgPrint("Locked the page\n"); ntStatus = STATUS_SUCCESS; } break; } case IOCTL_CE_UNLOCK_MEMORY: { struct { UINT64 mdl; } *inp; DbgPrint("IOCTL_CE_UNLOCK_MEMORY"); inp = Irp->AssociatedIrp.SystemBuffer; MmUnlockPages((PMDL)(UINT_PTR)inp->mdl); IoFreeMdl((PMDL)(UINT_PTR)inp->mdl); break; } case IOCTL_CE_GETPROCADDRESS: { struct input { UINT64 s; } *inp; UNICODE_STRING y; UINT64 result; PVOID x; inp=Irp->AssociatedIrp.SystemBuffer; RtlInitUnicodeString(&y, (PCWSTR)(UINT_PTR)(inp->s)); x=MmGetSystemRoutineAddress(&y); result=(UINT64)x; RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,&result,8); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_GETPROCESSNAMEADDRESS: { struct input { UINT64 PEPROCESS; } *inp; struct output { UINT64 Address; } *outp; UNICODE_STRING temp; inp=Irp->AssociatedIrp.SystemBuffer; outp=Irp->AssociatedIrp.SystemBuffer; RtlInitUnicodeString(&temp, L"PsGetProcessImageFileName"); PsGetProcessImageFileName=(GET_PROCESS_IMAGE_NAME)MmGetSystemRoutineAddress(&temp); if (PsGetProcessImageFileName!=NULL) { outp->Address=(UINT_PTR)PsGetProcessImageFileName((PEPROCESS)((UINT_PTR)(inp->PEPROCESS))); ntStatus=STATUS_SUCCESS; } else { DbgPrint("PsGetProcessImageFileName==NULL"); ntStatus=STATUS_UNSUCCESSFUL; } break; } /*x case IOCTL_CE_MAKEKERNELCOPY: { struct input { ULONG Base; ULONG KernelSize; } *inp; DbgPrint("IOCTL_CE_MAKEKERNELCOPY"); inp=Irp->AssociatedIrp.SystemBuffer; ntStatus=makeKernelCopy(inp->Base, inp->KernelSize); break; } */ case IOCTL_CE_CONTINUEDEBUGEVENT: { struct input { BOOL handled; } *inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_CONTINUEDEBUGEVENT\n"); ntStatus=debugger_continueDebugEvent(inp->handled); break; } case IOCTL_CE_WAITFORDEBUGEVENT: { struct input { ULONG timeout; } *inp=Irp->AssociatedIrp.SystemBuffer; ntStatus=debugger_waitForDebugEvent(inp->timeout); break; } case IOCTL_CE_GETDEBUGGERSTATE: { DbgPrint("IOCTL_CE_GETDEBUGGERSTATE\n"); __try { ntStatus=debugger_getDebuggerState((PDebugStackState)(Irp->AssociatedIrp.SystemBuffer)); } __except(1) { DbgPrint("Exception happened\n"); ntStatus=STATUS_UNSUCCESSFUL; } DbgPrint("ntStatus=%x rax=%x\n",ntStatus, ((PDebugStackState)(Irp->AssociatedIrp.SystemBuffer))->rax); break; } case IOCTL_CE_SETDEBUGGERSTATE: { DbgPrint("IOCTL_CE_SETDEBUGGERSTATE: state->rax=%x\n", ((PDebugStackState)(Irp->AssociatedIrp.SystemBuffer))->rax); __try { ntStatus=debugger_setDebuggerState((PDebugStackState)Irp->AssociatedIrp.SystemBuffer); } __except(1) { DbgPrint("Exception happened\n"); ntStatus=STATUS_UNSUCCESSFUL; } break; } case IOCTL_CE_SETKERNELSTEPABILITY: { struct input { int state; } *inp=Irp->AssociatedIrp.SystemBuffer; KernelCodeStepping=inp->state; ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_WRITESIGNOREWP: { KernelWritesIgnoreWP = *(BYTE*)Irp->AssociatedIrp.SystemBuffer; ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_GD_SETBREAKPOINT: { struct input { BOOL active; int debugregspot; UINT64 address; DWORD breakType; DWORD breakLength; } *inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("sizeof(struct input)=%d\n",sizeof(struct input)); //DbgPrint("address=%llx breakType=%d breakLength=%d\n",inp->address, inp->breakType,inp->breakLength); if (inp->active) { DbgPrint("activating breapoint %d\n", inp->debugregspot); ntStatus=debugger_setGDBreakpoint(inp->debugregspot, (UINT_PTR)inp->address, (BreakType)inp->breakType, (BreakLength)inp->breakLength); } else { DbgPrint("Deactivating breakpoint :%d\n", inp->debugregspot); ntStatus=debugger_unsetGDBreakpoint(inp->debugregspot); } break; } case IOCTL_CE_TOUCHDEBUGREGISTER: //used after setting a global debug breakpoint { debugger_touchDebugRegister(0); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_SETSTORELBR: { BOOL newstate=*(PBOOL)Irp->AssociatedIrp.SystemBuffer; DbgPrint("Calling debugger_setStoreLBR(%d)\n", newstate); debugger_setStoreLBR(newstate); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_EXECUTE_CODE: { typedef NTSTATUS (*PARAMETERLESSFUNCTION)(UINT64 parameters); PARAMETERLESSFUNCTION functiontocall; struct input { UINT64 functionaddress; //function address to call UINT64 parameters; } *inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_EXECUTE_CODE\n"); functiontocall=(PARAMETERLESSFUNCTION)(UINT_PTR)(inp->functionaddress); __try { ntStatus=functiontocall(inp->parameters); DbgPrint("Still alive\n"); ntStatus=STATUS_SUCCESS; } __except(1) { DbgPrint("Exception occured\n"); ntStatus=STATUS_UNSUCCESSFUL; } break; } case IOCTL_CE_GETVERSION: { DbgPrint("IOCTL_CE_GETVERSION. Version=%d\n",dbkversion); *(PULONG)Irp->AssociatedIrp.SystemBuffer=dbkversion; ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_READMSR: { DWORD msr=*(PDWORD)Irp->AssociatedIrp.SystemBuffer; //DbgPrint("IOCTL_CE_READMSR: msr=%x\n", msr); __try { *(PUINT64)Irp->AssociatedIrp.SystemBuffer=__readmsr(msr); //DbgPrint("Output: %llx\n",*(PUINT64)Irp->AssociatedIrp.SystemBuffer); ntStatus=STATUS_SUCCESS; } __except(1) { ntStatus=STATUS_UNSUCCESSFUL; } break; } case IOCTL_CE_WRITEMSR: { struct input { UINT64 msr; UINT64 value; } *inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_WRITEMSR:\n"); DbgPrint("msr=%llx\n", inp->msr); DbgPrint("value=%llx\n", inp->value); __try { __writemsr(inp->msr, inp->value ); ntStatus=STATUS_SUCCESS; } __except(1) { DbgPrint("Error while writing value\n"); ntStatus=STATUS_UNSUCCESSFUL; } break; } case IOCTL_CE_ULTIMAP2: { struct input { UINT32 PID; UINT32 Size; UINT32 RangeCount; UINT32 NoPMI; UINT32 UserMode; UINT32 KernelMode; URANGE Ranges[8]; WCHAR OutputPath[200]; } *inp = Irp->AssociatedIrp.SystemBuffer; int i; DbgPrint("IOCTL_CE_ULTIMAP2"); for (i = 0; i < (int)(inp->RangeCount); i++) DbgPrint("%d=%p -> %p", i, (PVOID)(UINT_PTR)inp->Ranges[i].StartAddress, (PVOID)(UINT_PTR)inp->Ranges[i].EndAddress); SetupUltimap2(inp->PID, inp->Size, inp->OutputPath, inp->RangeCount, inp->Ranges, inp->NoPMI, inp->UserMode, inp->KernelMode); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP2_WAITFORDATA: { ULONG timeout = *(ULONG *)Irp->AssociatedIrp.SystemBuffer; PULTIMAP2DATAEVENT output = Irp->AssociatedIrp.SystemBuffer; output->Address = 0; ntStatus = ultimap2_waitForData(timeout, output); break; } case IOCTL_CE_ULTIMAP2_LOCKFILE: { int cpunr = *(int *)Irp->AssociatedIrp.SystemBuffer; ultimap2_LockFile(cpunr); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP2_RELEASEFILE: { int cpunr = *(int *)Irp->AssociatedIrp.SystemBuffer; ultimap2_ReleaseFile(cpunr); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP2_GETTRACESIZE: { *(UINT64*)Irp->AssociatedIrp.SystemBuffer = ultimap2_GetTraceFileSize(); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP2_RESETTRACESIZE: { ultimap2_ResetTraceFileSize(); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP2_CONTINUE: { int cpunr=*(int*)Irp->AssociatedIrp.SystemBuffer; ntStatus = ultimap2_continue(cpunr); break; } case IOCTL_CE_ULTIMAP2_FLUSH: { ntStatus = ultimap2_flushBuffers(); break; } case IOCTL_CE_ULTIMAP2_PAUSE: { ntStatus = ultimap2_pause(); break; } case IOCTL_CE_ULTIMAP2_RESUME: { ntStatus = ultimap2_resume(); break; } case IOCTL_CE_DISABLEULTIMAP2: { DisableUltimap2(); break; } case IOCTL_CE_ULTIMAP: { #pragma pack(1) struct input { UINT64 targetCR3; UINT64 dbgctl; UINT64 dsareasize; BOOL savetofile; int HandlerCount; WCHAR filename[200]; } *inp=Irp->AssociatedIrp.SystemBuffer; #pragma pack() DbgPrint("IOCTL_CE_ULTIMAP:\n"); DbgPrint("ultimap(%I64x, %I64x, %d):\n", (UINT64)inp->targetCR3, (UINT64)inp->dbgctl, inp->dsareasize); if (inp->savetofile) DbgPrint("filename=%S\n", &inp->filename[0]); ntStatus=ultimap(inp->targetCR3, inp->dbgctl, (int)inp->dsareasize, inp->savetofile, &inp->filename[0], inp->HandlerCount); break; } case IOCTL_CE_ULTIMAP_DISABLE: { ultimap_disable(); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP_WAITFORDATA: { ULONG timeout=*(ULONG *)Irp->AssociatedIrp.SystemBuffer; PULTIMAPDATAEVENT output=Irp->AssociatedIrp.SystemBuffer; ntStatus=ultimap_waitForData(timeout, output); break; } case IOCTL_CE_ULTIMAP_CONTINUE: { PULTIMAPDATAEVENT input=Irp->AssociatedIrp.SystemBuffer; ntStatus=ultimap_continue(input); break; } case IOCTL_CE_ULTIMAP_FLUSH: { ultimap_flushBuffers(); ntStatus=STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP_PAUSE: { ultimap_pause(); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_ULTIMAP_RESUME: { ultimap_resume(); ntStatus = STATUS_SUCCESS; break; } /* case IOCTL_CE_GETCPUIDS: { CPULISTFILLSTRUCT x; forEachCpuPassive(GetCPUIDS_all,&x); }*/ case IOCTL_CE_STARTACCESMONITOR: { //this is used instead of writeProcessMemory for speed reasons (the reading out is still done with readProcessMemory because of easier memory management) struct input { UINT64 ProcessID; } *inp; PEPROCESS selectedprocess; PVOID BaseAddress; SIZE_T RegionSize; inp=Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_STARTACCESMONITOR(%d)\n", inp->ProcessID); ntStatus = STATUS_UNSUCCESSFUL; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->ProcessID), &selectedprocess) == STATUS_SUCCESS) { ntStatus = markAllPagesAsNeverAccessed(selectedprocess); ObDereferenceObject(selectedprocess); } break; } case IOCTL_CE_ENUMACCESSEDMEMORY: { struct input { UINT64 ProcessID; } *inp; PEPROCESS selectedprocess; PVOID BaseAddress; SIZE_T RegionSize; inp = Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_ENUMACCESSEDMEMORY(%d)\n", inp->ProcessID); ntStatus = STATUS_UNSUCCESSFUL; if (PsLookupProcessByProcessId((PVOID)(UINT_PTR)(inp->ProcessID), &selectedprocess) == STATUS_SUCCESS) { *(int *)Irp->AssociatedIrp.SystemBuffer = enumAllAccessedPages(selectedprocess); ObDereferenceObject(selectedprocess); } ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_GETACCESSEDMEMORYLIST: { int ListSizeInBytes = *(int *)Irp->AssociatedIrp.SystemBuffer; PPRANGE List = (PPRANGE)Irp->AssociatedIrp.SystemBuffer; DbgPrint("IOCTL_CE_GETACCESSEDMEMORYLIST\n"); getAccessedPageList(List, ListSizeInBytes); DbgPrint("return from IOCTL_CE_GETACCESSEDMEMORYLIST\n"); ntStatus = STATUS_SUCCESS; break; } case IOCTL_CE_INITIALIZE: { //find the KeServiceDescriptorTableShadow struct input { UINT64 AddressOfWin32K; UINT64 SizeOfWin32K; UINT64 NtUserBuildHwndList_callnumber; UINT64 NtUserQueryWindow_callnumber; UINT64 NtUserFindWindowEx_callnumber; UINT64 NtUserGetForegroundWindow_callnumber; UINT64 ActiveLinkOffset; UINT64 ProcessNameOffset; UINT64 DebugportOffset; UINT64 ProcessEvent; UINT64 ThreadEvent; } *pinp; DbgPrint("IOCTL_CE_INITIALIZE\n"); pinp=Irp->AssociatedIrp.SystemBuffer; ntStatus=STATUS_SUCCESS; //referencing event handles to objects ObReferenceObjectByHandle((HANDLE)(UINT_PTR)pinp->ProcessEvent, EVENT_ALL_ACCESS, NULL,KernelMode, &ProcessEvent, NULL); ObReferenceObjectByHandle((HANDLE)(UINT_PTR)pinp->ThreadEvent, EVENT_ALL_ACCESS, NULL,KernelMode, &ThreadEvent, NULL); *(UINT_PTR*)Irp->AssociatedIrp.SystemBuffer=(UINT_PTR)0; break; } case IOCTL_CE_VMXCONFIG: { #pragma pack(1) struct input { ULONG Virtualization_Enabled; QWORD Password1; ULONG Password2; QWORD Password3; } *pinp; #pragma pack() DbgPrint("IOCTL_CE_VMXCONFIG called\n"); ntStatus=STATUS_SUCCESS; pinp=Irp->AssociatedIrp.SystemBuffer; if (pinp->Virtualization_Enabled) { vmx_password1=pinp->Password1; vmx_password2=pinp->Password2; vmx_password3=pinp->Password3; DbgPrint("new passwords are: %p-%x-%p\n", (void*)vmx_password1, vmx_password2, (void*)vmx_password3); __try { vmx_version=vmx_getversion(); DbgPrint("Still here, so vmx is loaded. vmx_version=%x\n",vmx_version); vmxusable = 1; } __except(1) { DbgPrint("Exception happened. This means no vmx installed, or one of the passwords is wrong\n"); ntStatus = STATUS_UNSUCCESSFUL; vmxusable = 0; }; } else { DbgPrint("Virtualization_Enabled=0\n"); vmxusable=0; } break; } case IOCTL_CE_ENABLE_DRM: { #if (NTDDI_VERSION >= NTDDI_VISTA) struct { QWORD PreferedAltitude; QWORD ProtectedProcess; } *inp = Irp->AssociatedIrp.SystemBuffer; DbgPrint("inp->PreferedAltitude=%p", inp->PreferedAltitude); DbgPrint("inp->PreferedAltitude=%p", inp->ProtectedProcess); if (DRMProcess) { //check if this process has been terminated LARGE_INTEGER timeout; timeout.QuadPart = -500000; ntStatus=KeWaitForSingleObject(DRMProcess, UserRequest, UserMode, FALSE, &timeout); if (ntStatus != STATUS_SUCCESS) break; } DRMProcess = PsGetCurrentProcess(); if (inp->ProtectedProcess) { if (DRMProcess != (PEPROCESS)((UINT_PTR)inp->ProtectedProcess)) DRMProcess2 = (PEPROCESS)((UINT_PTR)inp->ProtectedProcess); } DbgPrint("DRMProcess=%p", DRMProcess); DbgPrint("DRMProcess2=%p", DRMProcess2); if (DRMHandle == NULL) { WCHAR wcAltitude[10]; UNICODE_STRING usAltitude; OB_CALLBACK_REGISTRATION r; LARGE_INTEGER tc; OB_OPERATION_REGISTRATION obr[2]; int RandomVal = (int)(inp->PreferedAltitude); int trycount = 0; if (RandomVal == 0) { tc.QuadPart = 0; KeQueryTickCount(&tc); RandomVal = 1000 + (tc.QuadPart % 50000); } DbgPrint("Activating CE's super advanced DRM"); //yeah right.... DbgPrint("RandomVal=%d", RandomVal); RtlStringCbPrintfW(wcAltitude, sizeof(wcAltitude) - 2, L"%d", RandomVal); DbgPrint("wcAltitude=%S", wcAltitude); RtlInitUnicodeString(&usAltitude, wcAltitude); r.Version = OB_FLT_REGISTRATION_VERSION; r.Altitude = usAltitude; r.RegistrationContext = NULL; obr[0].ObjectType = PsProcessType; obr[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; obr[0].PreOperation = ProcessPreCallback; obr[0].PostOperation = ProcessPostCallback; obr[1].ObjectType = PsThreadType; obr[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; obr[1].PreOperation = ThreadPreCallback; obr[1].PostOperation = ThreadPostCallback; r.OperationRegistration = obr; r.OperationRegistrationCount = 2; ntStatus = ObRegisterCallbacks(&r, &DRMHandle); while ((ntStatus == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION) && (trycount<10)) { RandomVal++; RtlStringCbPrintfW(wcAltitude, sizeof(wcAltitude) - 2, L"%d", RandomVal); RtlInitUnicodeString(&usAltitude, wcAltitude); r.Altitude = usAltitude; trycount++; ntStatus = ObRegisterCallbacks(&r, &DRMHandle); } DbgPrint("ntStatus=%X", ntStatus); } else ntStatus = STATUS_SUCCESS; #else ntStatus = STATUS_NOT_IMPLEMENTED; #endif break; } case IOCTL_CE_GET_PEB: { KAPC_STATE oldstate; PEPROCESS ep = *(PEPROCESS *)Irp->AssociatedIrp.SystemBuffer; //DbgPrint("IOCTL_CE_GET_PEB"); KeStackAttachProcess((PKPROCESS)ep, &oldstate); __try { ULONG r; PROCESS_BASIC_INFORMATION pbi; //DbgPrint("Calling ZwQueryInformationProcess"); ntStatus = ZwQueryInformationProcess(ZwCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &r); if (ntStatus==STATUS_SUCCESS) { //DbgPrint("pbi.UniqueProcessId=%x\n", (int)pbi.UniqueProcessId); //DbgPrint("pbi.PebBaseAddress=%p\n", (PVOID)pbi.PebBaseAddress); *(QWORD *)Irp->AssociatedIrp.SystemBuffer = (QWORD)(pbi.PebBaseAddress); } else DbgPrint("ZwQueryInformationProcess failed"); } __finally { KeUnstackDetachProcess(&oldstate); } break; } case IOCTL_CE_QUERYINFORMATIONPROCESS: { struct { QWORD processid; QWORD ProcessInformationAddress; QWORD ProcessInformationClass; QWORD ProcessInformationLength; } *inp = Irp->AssociatedIrp.SystemBuffer; struct { QWORD result; QWORD returnLength; char data; } *outp = Irp->AssociatedIrp.SystemBuffer; PEPROCESS selectedprocess; DbgPrint("IOCTL_CE_QUERYINFORMATIONPROCESS"); if (inp->processid == 0) { DbgPrint("Still works\n"); ntStatus = STATUS_SUCCESS; break; } __try { if (PsLookupProcessByProcessId((HANDLE)(UINT_PTR)inp->processid, &selectedprocess) == STATUS_SUCCESS) { KAPC_STATE oldstate; KeStackAttachProcess((PKPROCESS)selectedprocess, &oldstate); __try { ULONG returnLength; if (inp->ProcessInformationAddress == 0) { DbgPrint("NULL ProcessInformationAddress"); outp->result = ZwQueryInformationProcess(NtCurrentProcess(), inp->ProcessInformationClass, NULL, (ULONG)inp->ProcessInformationLength, &returnLength); } else outp->result = ZwQueryInformationProcess(NtCurrentProcess(), inp->ProcessInformationClass, &(outp->data), (ULONG)inp->ProcessInformationLength, &returnLength); DbgPrint("outp->result=%x", outp->result); outp->returnLength = returnLength; DbgPrint("outp->returnLength=%x", outp->returnLength); ntStatus = STATUS_SUCCESS; } __finally { KeUnstackDetachProcess(&oldstate); } ObDereferenceObject(selectedprocess); } else { DbgPrint("Failed to find pid %x", inp->processid); ntStatus = STATUS_EXPIRED_HANDLE; } } __except (1) { DbgPrint("Exception"); ntStatus = STATUS_EXPIRED_HANDLE; } break; } case IOCTL_CE_NTPROTECTVIRTUALMEMORY: { break; } case IOCTL_CE_ALLOCATE_MEMORY_FOR_DBVM: { PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes; PMDL mdl; QWORD pagecount = *(QWORD*)Irp->AssociatedIrp.SystemBuffer; PFN_NUMBER *pfnlist; DbgPrint("IOCTL_CE_ALLOCATE_MEMORY_FOR_DBVM(%d)\n", pagecount); if (!vmxusable) { DbgPrint("This only works when DBVM is present\n"); ntStatus = STATUS_INVALID_DEVICE_STATE; break; } LowAddress.QuadPart = 0; HighAddress.QuadPart = 0xffffffffffffffffI64; SkipBytes.QuadPart = 0; mdl = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, (SIZE_T)pagecount * 4096, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED); //do not free this, EVER if (mdl) { int i; PDBVMOffloadMemInfo mi; pagecount = MmGetMdlByteCount(mdl) / 4096; DbgPrint("Allocated %d pages\n", pagecount); pfnlist = MmGetMdlPfnArray(mdl); if (pfnlist) { //convert the pfnlist to a list dbvm understands, and go in blocks of 32 mi = ExAllocatePool(PagedPool, sizeof(DBVMOffloadMemInfo)); if (mi) { mi->List = ExAllocatePool(PagedPool, sizeof(UINT64) * 32); if (mi->List) { mi->Count = 0; for (i = 0; i < pagecount; i++) { mi->List[mi->Count] = pfnlist[i] << 12; mi->Count++; if (mi->Count == 32) { int j; int r = vmx_add_memory(mi->List, mi->Count); DbgPrint("vmx_add_memory for %d pages returned %d\n", mi->Count, r); for (j = 0; j < mi->Count; j++) { DbgPrint("%d : %p\n", j, (void*)((UINT_PTR)mi->List[j])); } mi->Count = 0; } } if (mi->Count) { int r = vmx_add_memory(mi->List, mi->Count); DbgPrint("vmx_add_memory for %d pages returned %d\n", mi->Count, r); } ExFreePool(mi->List); } else DbgPrint("Failure allocating mi->List"); ExFreePool(mi); } else DbgPrint("Failure allocting mi"); } else DbgPrint("Failure getting pfn list"); ExFreePool(mdl); //only free the mdl, the rest belongs to dbvm now ntStatus = STATUS_SUCCESS; } else { DbgPrint("Failure allocating MDL"); ntStatus = STATUS_MEMORY_NOT_ALLOCATED; } break; } default: DbgPrint("Unhandled IO request: %x\n", IoControlCode); break; } Irp->IoStatus.Status = ntStatus; // Set # of bytes to copy back to user-mode... if (irpStack) //only NULL when loaded by dbvm { if (ntStatus == STATUS_SUCCESS) Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength; else Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return ntStatus; } ================================================ FILE: src/IOPLDispatcher.h ================================================ #ifndef IOPLDISPACTCHER_H #define IOPLDISPACTCHER_H #include "DBKfunc.h" #define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN #define IOCTL_CE_READMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_WRITEMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_OPENPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_QUERY_VIRTUAL_MEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_TEST CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0804, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPEPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0805, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_READPHYSICALMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0806, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_WRITEPHYSICALMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0807, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPHYSICALADDRESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0808, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //#define IOCTL_CE_PROTECTME CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0809, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETCR3 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETCR3 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETSDT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_INITIALIZE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_DONTPROTECTME CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETIDT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x080f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_HOOKINTS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0810, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_DEBUGPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0811, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //#define IOCTL_CE_RETRIEVEDEBUGDATA CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0812, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_STARTPROCESSWATCH CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0813, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPROCESSEVENTS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0814, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETTHREADEVENTS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0815, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETVERSION CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0816, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETCR4 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0817, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_OPENTHREAD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0818, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_MAKEWRITABLE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0819, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //obsolete: #define IOCTL_CE_DEBUGPROCESS_CHANGEREG CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_STOPDEBUGGING CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //obsolete: #define IOCTL_CE_STOP_DEBUGPROCESS_CHANGEREG CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //#define IOCTL_CE_USEALTERNATEMETHOD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //#define IOCTL_CE_ISUSINGALTERNATEMETHOD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ALLOCATEMEM CTL_CODE(IOCTL_UNKNOWN_BASE, 0x081f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_CREATEAPC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0820, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPETHREAD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0821, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SUSPENDTHREAD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0822, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_RESUMETHREAD CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0823, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SUSPENDPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0824, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_RESUMEPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0825, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ALLOCATEMEM_NONPAGED CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0826, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPROCADDRESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0827, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) //#define IOCTL_CE_SETSDTADDRESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0828, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETSDTADDRESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0829, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETGDT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETCR4 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETTR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_VMXCONFIG CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETCR0 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_USERDEFINEDINTERRUPTHOOK CTL_CODE(IOCTL_UNKNOWN_BASE, 0x082f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETGLOBALDEBUGSTATE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0830, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_CONTINUEDEBUGEVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0831, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_WAITFORDEBUGEVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0832, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETDEBUGGERSTATE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0833, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETDEBUGGERSTATE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0834, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GD_SETBREAKPOINT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0835, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_TOUCHDEBUGREGISTER CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0836, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_LAUNCHDBVM CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_UNHOOKALLINTERRUPTS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_EXECUTE_CODE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETPROCESSNAMEADDRESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETKERNELSTEPABILITY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_READMSR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x083f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_WRITEMSR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0840, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_SETSTORELBR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0841, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0842, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_DISABLE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0843, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_WAITFORDATA CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0844, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_CONTINUE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0845, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_FLUSH CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0846, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETMEMORYRANGES CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0847, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_STARTACCESMONITOR CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0848, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ENUMACCESSEDMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0849, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GETACCESSEDMEMORYLIST CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_WRITESIGNOREWP CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_FREE_NONPAGED CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_MAP_MEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_UNMAP_MEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x084f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_DISABLEULTIMAP2 CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0850, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_WAITFORDATA CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0851, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_CONTINUE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0852, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_FLUSH CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0853, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_PAUSE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0854, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_RESUME CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0855, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_LOCKFILE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0856, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_RELEASEFILE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0857, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_PAUSE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0858, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP_RESUME CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0859, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_GETTRACESIZE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085a, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ULTIMAP2_RESETTRACESIZE CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ENABLE_DRM CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085c, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_GET_PEB CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085d, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_QUERYINFORMATIONPROCESS CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085e, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_NTPROTECTVIRTUALMEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x085f, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_LOCK_MEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0860, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_UNLOCK_MEMORY CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0861, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CE_ALLOCATE_MEMORY_FOR_DBVM CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0862, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) extern PVOID DRMHandle; #define SYSTEMSERVICE(_function) KeServiceDescriptorTable->ServiceTable[ *(PULONG)((PUCHAR)_function+1)] #define SYSTEMSERVICELINK(_function) KeServiceDescriptorTable->ServiceTable[*((PUCHAR)(*(PULONG)*((PULONG)((PUCHAR)_function+2)))+1)] NTSTATUS DispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); BOOL DispatchIoctlDBVM(IN PDEVICE_OBJECT DeviceObject, ULONG IoControlCode, PVOID lpInBuffer, DWORD nInBufferSize, PVOID lpOutBuffer, DWORD nOutBufferSize, PDWORD lpBytesReturned); #endif ================================================ FILE: src/amd64/dbkfunca.asm ================================================ ;RCX: 1st integer argument ;RDX: 2nd integer argument ;R8: 3rd integer argument ;R9: 4th integer argument ;I should probably start converting to inrinsics _TEXT SEGMENT 'CODE' PUBLIC getCS getCS: mov ax,cs ret PUBLIC getSS getSS: mov ax,ss ret PUBLIC getDS getDS: mov ax,ds ret PUBLIC getES getES: mov ax,es ret PUBLIC getFS getFS: mov ax,fs ret PUBLIC getGS getGS: mov ax,gs ret PUBLIC GetTR GetTR: STR AX ret PUBLIC GetLDT GetLDT: SLDT ax ret PUBLIC GetGDT GetGDT: SGDT [rcx] ret PUBLIC _fxsave _fxsave: fxsave [rcx] ret PUBLIC getRSP getRSP: mov rax,rsp add rax,8 ;undo the call push ret PUBLIC getRBP getRBP: push rbp pop rax ret PUBLIC getRAX getRAX: ret PUBLIC getRBX getRBX: mov rax,rbx ret PUBLIC getRCX getRCX: mov rax,rcx ret PUBLIC getRDX getRDX: mov rax,rdx ret PUBLIC getRSI getRSI: mov rax,rsi ret PUBLIC getRDI getRDI: mov rax,rdi ret PUBLIC getR8 getR8: mov rax,r8 ret PUBLIC getR9 getR9: mov rax,r9 ret PUBLIC getR10 getR10: mov rax,r10 ret PUBLIC getR11 getR11: mov rax,r11 ret PUBLIC getR12 getR12: mov rax,r12 ret PUBLIC getR13 getR13: mov rax,r13 ret PUBLIC getR14 getR14: mov rax,r14 ret PUBLIC getR15 getR15: mov rax,r15 ret PUBLIC getAccessRights getAccessRights: xor rax,rax lar rax,rcx jnz getAccessRights_invalid shr rax,8 and rax,0f0ffh ret getAccessRights_invalid: mov rax,010000h ret PUBLIC getSegmentLimit getSegmentLimit: xor rax,rax lsl rax,rcx ret _TEXT ENDS END ================================================ FILE: src/amd64/debuggera.asm ================================================ ;RCX: 1st integer argument ;RDX: 2nd integer argument ;R8: 3rd integer argument ;R9: 4th integer argument CALLBACK struct A qword ? S qword ? CALLBACK ends ASMENTRY_STACK struct ;keep this 16 byte aligned Scratchspace qword ? Scratchspace2 qword ? Scratchspace3 qword ? Scratchspace4 qword ? Originalmxcsr qword ? OriginalRAX qword ? ;0 OriginalRBX qword ? ;1 OriginalRCX qword ? ;2 OriginalRDX qword ? ;3 OriginalRSI qword ? ;4 OriginalRDI qword ? ;5 OriginalRBP qword ? ;6 OriginalRSP qword ? ;7 not really 'original' OriginalR8 qword ? ;8 OriginalR9 qword ? ;9 OriginalR10 qword ? ;10 OriginalR11 qword ? ;11 OriginalR12 qword ? ;12 OriginalR13 qword ? ;13 OriginalR14 qword ? ;14 OriginalR15 qword ? ;15 OriginalES qword ? ;16 OriginalDS qword ? ;17 OriginalSS qword ? ;18 fxsavespace db 512 dup(?) ;fpu state ;errorcode/returnaddress ;19 ;4096 bytes ;eip ;20 ;cs ;21 ;eflags ;esp ;ss ASMENTRY_STACK ends _TEXT SEGMENT 'CODE' EXTERN interrupt1_centry : proc EXTERN Int1JumpBackLocation : CALLBACK PUBLIC interrupt1_asmentry interrupt1_asmentry: ;save stack position push [Int1JumpBackLocation.A] ;push an errorcode on the stack so the stackindex enum type can stay the same relative to interrupts that do have an errorcode (int 14). Also helps with variable interrupt handlers sub rsp,4096 ;functions like setThreadContext adjust the stackframe entry directly. I can't have that messing up my own stack cld ;stack is aligned at this point sub rsp,SIZEOF ASMENTRY_STACK mov (ASMENTRY_STACK PTR [rsp]).OriginalRBP,rbp lea rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRAX mov (ASMENTRY_STACK PTR [rsp]).OriginalRAX,rax mov (ASMENTRY_STACK PTR [rsp]).OriginalRBX,rbx mov (ASMENTRY_STACK PTR [rsp]).OriginalRCX,rcx mov (ASMENTRY_STACK PTR [rsp]).OriginalRDX,rdx mov (ASMENTRY_STACK PTR [rsp]).OriginalRSI,rsi mov (ASMENTRY_STACK PTR [rsp]).OriginalRDI,rdi mov (ASMENTRY_STACK PTR [rsp]).OriginalRSP,rsp mov (ASMENTRY_STACK PTR [rsp]).OriginalR8,r8 mov (ASMENTRY_STACK PTR [rsp]).OriginalR9,r9 mov (ASMENTRY_STACK PTR [rsp]).OriginalR10,r10 mov (ASMENTRY_STACK PTR [rsp]).OriginalR11,r11 mov (ASMENTRY_STACK PTR [rsp]).OriginalR12,r12 mov (ASMENTRY_STACK PTR [rsp]).OriginalR13,r13 mov (ASMENTRY_STACK PTR [rsp]).OriginalR14,r14 mov (ASMENTRY_STACK PTR [rsp]).OriginalR15,r15 fxsave (ASMENTRY_STACK PTR [rsp]).fxsavespace mov ax,ds mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalDS,ax mov ax,es mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalES,ax mov ax,ss mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalSS,ax mov ax,2bh mov ds,ax mov es,ax mov ax,18h mov ss,ax ; rbp= pointer to OriginalRAX cmp qword ptr [rbp+8*21+512+4096],010h ;check if origin is in kernelmode (check ss) je skipswap1 ;if so, skip the swapgs swapgs ;swap gs with the kernel version skipswap1: stmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).Originalmxcsr mov (ASMENTRY_STACK PTR [rsp]).scratchspace2,1f80h ldmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).scratchspace2 mov rcx,rbp call interrupt1_centry ldmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).Originalmxcsr cmp qword ptr [rbp+8*21+512+4096],10h ;was it a kernelmode interrupt ? je skipswap2 ;if so, skip the swapgs part swapgs ;swap back skipswap2: cmp al,1 ;restore state fxrstor (ASMENTRY_STACK PTR [rsp]).fxsavespace mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalDS mov ds,ax mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalES mov es,ax mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalSS mov ss,ax mov rax,(ASMENTRY_STACK PTR [rsp]).OriginalRAX mov rbx,(ASMENTRY_STACK PTR [rsp]).OriginalRBX mov rcx,(ASMENTRY_STACK PTR [rsp]).OriginalRCX mov rdx,(ASMENTRY_STACK PTR [rsp]).OriginalRDX mov rsi,(ASMENTRY_STACK PTR [rsp]).OriginalRSI mov rdi,(ASMENTRY_STACK PTR [rsp]).OriginalRDI mov r8, (ASMENTRY_STACK PTR [rsp]).OriginalR8 mov r9, (ASMENTRY_STACK PTR [rsp]).OriginalR9 mov r10,(ASMENTRY_STACK PTR [rsp]).OriginalR10 mov r11,(ASMENTRY_STACK PTR [rsp]).OriginalR11 mov r12,(ASMENTRY_STACK PTR [rsp]).OriginalR12 mov r13,(ASMENTRY_STACK PTR [rsp]).OriginalR13 mov r14,(ASMENTRY_STACK PTR [rsp]).OriginalR14 mov r15,(ASMENTRY_STACK PTR [rsp]).OriginalR15 je skip_original_int1 ;stack unwind mov rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRBP add rsp,SIZEOF ASMENTRY_STACK add rsp,4096 ;at this point [rsp] holds the original int1 handler ret ; used to be add rsp,8 ;+8 for the push 0 ;todo: do a jmp [Int1JumpBackLocationCPUNR] and have 256 Int1JumpBackLocationCPUNR's and each cpu goes to it's own interrupt1_asmentry[cpunr] ;jmp [Int1JumpBackLocation.A] ;<-works fine skip_original_int1: ;stack unwind mov rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRBP add rsp,SIZEOF ASMENTRY_STACK add rsp,4096 add rsp,8 ;+8 for the push iretq _TEXT ENDS END ================================================ FILE: src/amd64/noexceptionsa.asm ================================================ _TEXT SEGMENT 'CODE' PUBLIC NoException14 NoException14: ;Security cookies sucks, so getjmp/longjmp are not usable ;So, just falling back to an exceptionless Copy command instead ;or disassemble the instruction RIP points at ;rsp=errorcode ;rsp+8=rip ;rsp+10=cs ;20 ;rsp+18=eflags ;rsp+20=rsp ;rsp+28=ss add rsp,8 ;skip the errorcode push rax ;push rax -state as above again mov rax,ExceptionlessCopy_Exception mov [rsp+8],rax pop rax iretq ;go to the designated return address PUBLIC ExceptionlessCopy_Internal ;rcx=destination ;rdx=source ;r8=size in bytes ExceptionlessCopy_Internal: push rbp mov rbp,rsp ;[rbp] = old rbp value ;[rbp+8] = return address ;[rbp+10h] - [rbp+30h] = scratchspace mov [rbp+10h],rsi mov [rbp+18h],rdi mov rsi,rdx mov rdi,rcx mov rcx,r8 rep movsb ;todo: split this up into movsq, movsd, movsw, movsd, or some of those other string routines ;on exception just exit ExceptionlessCopy_Exception: mov rsi,[rbp+10h] mov rdi,[rbp+18h] sub r8,rcx ;decrease the number of bytes left from the total amount of bytes to get the total bytes written mov rax,r8 pop rbp ret _TEXT ENDS END ================================================ FILE: src/amd64/ultimapa.asm ================================================ ;RCX: 1st integer argument ;RDX: 2nd integer argument ;R8: 3rd integer argument ;R9: 4th integer argument CALLBACK struct A qword ? S qword ? CALLBACK ends ASMENTRY_STACK struct ;keep this 16 byte aligned Scratchspace qword ? ;0 Scratchspace2 qword ? ;8 Scratchspace3 qword ? ;0 Scratchspace4 qword ? ;8 Originalmxcsr qword ? OriginalRAX qword ? OriginalRBX qword ? OriginalRCX qword ? OriginalRDX qword ? OriginalRSI qword ? OriginalRDI qword ? OriginalRBP qword ? OriginalRSP qword ? ;not really 'original' OriginalR8 qword ? OriginalR9 qword ? OriginalR10 qword ? OriginalR11 qword ? OriginalR12 qword ? OriginalR13 qword ? OriginalR14 qword ? OriginalR15 qword ? OriginalES qword ? OriginalDS qword ? OriginalSS qword ? ASMENTRY_STACK ends _TEXT SEGMENT 'CODE' EXTERN perfmon_interrupt_centry : proc EXTERN perfmonJumpBackLocation : CALLBACK PUBLIC perfmon_interrupt perfmon_interrupt: ;save stack position cld push 0 ;push an errorcode on the stack so the stackindex enum type can stay the same relative to interrupts that do have an errorcode (int 14) ;stack is aligned at this point sub rsp, 4096 sub rsp,SIZEOF ASMENTRY_STACK mov (ASMENTRY_STACK PTR [rsp]).OriginalRBP,rbp lea rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRAX ;make rbp point to the start of the structure mov (ASMENTRY_STACK PTR [rsp]).OriginalRAX,rax mov (ASMENTRY_STACK PTR [rsp]).OriginalRBX,rbx mov (ASMENTRY_STACK PTR [rsp]).OriginalRCX,rcx mov (ASMENTRY_STACK PTR [rsp]).OriginalRDX,rdx mov (ASMENTRY_STACK PTR [rsp]).OriginalRSI,rsi mov (ASMENTRY_STACK PTR [rsp]).OriginalRDI,rdi mov (ASMENTRY_STACK PTR [rsp]).OriginalRSP,rsp mov (ASMENTRY_STACK PTR [rsp]).OriginalR8,r8 mov (ASMENTRY_STACK PTR [rsp]).OriginalR9,r9 mov (ASMENTRY_STACK PTR [rsp]).OriginalR10,r10 mov (ASMENTRY_STACK PTR [rsp]).OriginalR11,r11 mov (ASMENTRY_STACK PTR [rsp]).OriginalR12,r12 mov (ASMENTRY_STACK PTR [rsp]).OriginalR13,r13 mov (ASMENTRY_STACK PTR [rsp]).OriginalR14,r14 mov (ASMENTRY_STACK PTR [rsp]).OriginalR15,r15 mov ax,ds mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalDS,ax mov ax,es mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalES,ax mov ax,ss mov word ptr (ASMENTRY_STACK PTR [rsp]).OriginalSS,ax mov ax,2bh mov ds,ax mov es,ax mov ax,18h mov ss,ax cmp qword ptr [rbp+8*21+4096],010h ;check if origin is in kernelmode (check ss) je skipswap1 ;if so, skip the swapgs swapgs ;swap gs with the kernel version (not to self fix when called from inside kernel) skipswap1: stmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).Originalmxcsr mov (ASMENTRY_STACK PTR [rsp]).scratchspace,1f80h ldmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).scratchspace ;mov rcx,rbp call perfmon_interrupt_centry ldmxcsr dword ptr (ASMENTRY_STACK PTR [rsp]).Originalmxcsr cmp qword ptr [rbp+8*21+4096],10h ;was it a kernelmode interrupt ? je skipswap2 ;if so, skip the swapgs part swapgs ;swap back skipswap2: cmp al,1 ;restore state mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalDS mov ds,ax mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalES mov es,ax mov ax,word ptr (ASMENTRY_STACK PTR [rsp]).OriginalSS mov ss,ax mov rax,(ASMENTRY_STACK PTR [rsp]).OriginalRAX mov rbx,(ASMENTRY_STACK PTR [rsp]).OriginalRBX mov rcx,(ASMENTRY_STACK PTR [rsp]).OriginalRCX mov rdx,(ASMENTRY_STACK PTR [rsp]).OriginalRDX mov rsi,(ASMENTRY_STACK PTR [rsp]).OriginalRSI mov rdi,(ASMENTRY_STACK PTR [rsp]).OriginalRDI mov r8,(ASMENTRY_STACK PTR [rsp]).OriginalR8 mov r9,(ASMENTRY_STACK PTR [rsp]).OriginalR9 mov r10,(ASMENTRY_STACK PTR [rsp]).OriginalR10 mov r11,(ASMENTRY_STACK PTR [rsp]).OriginalR11 mov r12,(ASMENTRY_STACK PTR [rsp]).OriginalR12 mov r13,(ASMENTRY_STACK PTR [rsp]).OriginalR13 mov r14,(ASMENTRY_STACK PTR [rsp]).OriginalR14 mov r15,(ASMENTRY_STACK PTR [rsp]).OriginalR15 je skip_original_perfmon jmp skip_original_perfmon ;stack unwind mov rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRBP add rsp,SIZEOF ASMENTRY_STACK ;+8 for the push 0 add rsp,8 add rsp,4096 jmp [perfmonJumpBackLocation.A] ;<-works fine skip_original_perfmon: ;stack unwind mov rbp,(ASMENTRY_STACK PTR [rsp]).OriginalRBP add rsp,SIZEOF ASMENTRY_STACK ;+8 for the push 0 add rsp,4096 add rsp,8 ;jump to when the stack has been completly restored to what it was at interrupt time ;push rax ;push rbx ;unmask the perfmon interrupt ;mov rax,0fffffffffffe0330h ;mov rbx,[rax] ;and rbx,0ffh ;mov [rax],rbx ;End of interrupt ;mov rax,0fffffffffffe00b0h ;xor rbx,rbx ;mov [rax],rbx ;set EOI to 0 ;pop rbx ;pop rax iretq _TEXT ENDS END ================================================ FILE: src/amd64/vmxhelpera.asm ================================================ ;RCX: 1st integer argument ;RDX: 2nd integer argument ;R8: 3rd integer argument ;R9: 4th integer argument ;vmcall: rdx = password1 info(rax)->password(@offset 4)=password 2 ;vmcall(info) extern vmx_password1 : QWORD extern vmx_password3 : QWORD _TEXT SEGMENT 'CODE' PUBLIC dovmcall_intel dovmcall_intel: mov rax,rcx mov rdx,vmx_password1 mov rcx,vmx_password3 vmcall ret PUBLIC dovmcall_amd dovmcall_amd: mov rax,rcx mov rdx,vmx_password1 mov rcx,vmx_password3 vmmcall ret _TEXT ENDS END ================================================ FILE: src/amd64/vmxoffloada.asm ================================================ ;RCX: 1st integer argument ;RDX: 2nd integer argument ;R8: 3rd integer argument ;R9: 4th integer argument GDTDesc STRUCT Limit WORD ? Base QWORD ? GDTDesc ENDS S_ORIGINALSTATE STRUCT _cpucount QWORD ? _originalEFER QWORD ? _originalLME QWORD ? _idtbase QWORD ? _idtlimit QWORD ? _gdtbase QWORD ? _gdtlimit QWORD ? _cr0 QWORD ? _cr2 QWORD ? _cr3 QWORD ? _cr4 QWORD ? _dr7 QWORD ? _rip QWORD ? _rax QWORD ? _rbx QWORD ? _rcx QWORD ? _rdx QWORD ? _rsi QWORD ? _rdi QWORD ? _rbp QWORD ? _rsp QWORD ? _r8 QWORD ? _r9 QWORD ? _r10 QWORD ? _r11 QWORD ? _r12 QWORD ? _r13 QWORD ? _r14 QWORD ? _r15 QWORD ? _rflags QWORD ? _cs QWORD ? _ss QWORD ? _ds QWORD ? _es QWORD ? _fs QWORD ? _gs QWORD ? _tr QWORD ? _ldt QWORD ? _cs_AccessRights QWORD ? _ss_AccessRights QWORD ? _ds_AccessRights QWORD ? _es_AccessRights QWORD ? _fs_AccessRights QWORD ? _gs_AccessRights QWORD ? _cs_Limit QWORD ? _ss_Limit QWORD ? _ds_Limit QWORD ? _es_Limit QWORD ? _fs_Limit QWORD ? _gs_Limit QWORD ? _fsbase QWORD ? _gsbase QWORD ? S_ORIGINALSTATE ENDS PS_ORIGNALSTATE TYPEDEF PTR S_ORIGINALSTATE EXTERN NewGDTDescriptor: GDTDesc EXTERN NewGDTDescriptorVA: QWORD EXTERN DBVMPML4PA: QWORD EXTERN TemporaryPagingSetupPA: QWORD EXTERN enterVMM2PA: QWORD EXTERN originalstatePA: QWORD EXTERN enterVMM2: QWORD EXTERN originalstate: PS_ORIGNALSTATE EXTERN vmmPA: QWORD _TEXT SEGMENT 'CODE' PUBLIC JTAGBP JTAGBP: db 0f1h ret PUBLIC enterVMM enterVMM: begin: ;switch to identity mapped pagetable mov cr3,rdx jmp short weee weee: nop nop ;now jump to the physical address (identity mapped to the same virtual address) mov rax,secondentry mov r8,enterVMM sub rax,r8 add rax,rsi ;add the physical address to the offset location jmp rax secondentry: ;contrary to the 32-bit setup, we don't disable paging to make the switch to 64-bit, we're already there ;we can just set the CR3 value ;----------TEST---------- ; waitforready: ; mov dx,0ec05h ; in al,dx ; and al,20h ; cmp al,20h ; jne waitforready ; ; mov dx,0ec00h ; mov al,'1' ; out dx,al ;^^^^^^^^TEST^^^^^^^^ ;enable PAE and PSE (just to make sure) mov eax,30h mov cr4,rax mov cr3,rcx nop nop jmp short weee2 weee2: nop nop mov rbx,0 mov ds,bx mov es,bx mov fs,bx mov gs,bx mov ss,bx mov rax,cr0 or eax,10000h mov cr0,rax ;enable WP bit nop nop nop nop ;db 0f1h ;jtag nop nop nop jmp fword ptr [vmmjump] ;jmp fword ptr [vmmjump] ;one thing that I don't mind about x64, relative addressing, so no need to change it by me extrastorage: nop nop nop nop nop vmmjump: dd 00400000h dw 50h detectionstring: db 0ceh db 0ceh db 0ceh db 0ceh db 0ceh db 0ceh db 0ceh PUBLIC enterVMMPrologue enterVMMPrologue: cli ;goodbye interrupts push rbx mov rbx,originalstate mov (S_ORIGINALSTATE PTR [rbx])._rax,rax pop rbx mov rax,originalstate mov (S_ORIGINALSTATE PTR [rax])._rbx,rbx mov (S_ORIGINALSTATE PTR [rax])._rcx,rcx mov (S_ORIGINALSTATE PTR [rax])._rdx,rdx mov (S_ORIGINALSTATE PTR [rax])._rsi,rsi mov (S_ORIGINALSTATE PTR [rax])._rdi,rdi mov (S_ORIGINALSTATE PTR [rax])._rbp,rbp mov (S_ORIGINALSTATE PTR [rax])._rsp,rsp mov (S_ORIGINALSTATE PTR [rax])._r8,r8 mov (S_ORIGINALSTATE PTR [rax])._r9,r9 mov (S_ORIGINALSTATE PTR [rax])._r10,r10 mov (S_ORIGINALSTATE PTR [rax])._r11,r11 mov (S_ORIGINALSTATE PTR [rax])._r12,r12 mov (S_ORIGINALSTATE PTR [rax])._r13,r13 mov (S_ORIGINALSTATE PTR [rax])._r14,r14 mov (S_ORIGINALSTATE PTR [rax])._r15,r15 mov rbx,enterVMMEpilogue mov (S_ORIGINALSTATE PTR [rax])._rip,rbx ;jmp enterVMMEpilogue ;test to see if the loader is bugged ;still here, loader didn't crash, start executing the move to the dbvm environment xchg bx,bx ;bochs break mov rbx,NewGDTDescriptorVA lgdt fword ptr [rbx] mov rcx,DBVMPML4PA mov rdx,TemporaryPagingSetupPA mov rsi,enterVMM2PA jmp [enterVMM2] PUBLIC enterVMMEpilogue enterVMMEpilogue: nop nop push rax push rbx push rcx push rdx cpuid pop rdx pop rcx pop rbx pop rax nop nop nop mov r8,originalstate ;mov rbx,(S_ORIGINALSTATE PTR [r8])._tr ;ltr bx mov rbx,(S_ORIGINALSTATE PTR [r8])._ss mov ss,bx mov rbx,(S_ORIGINALSTATE PTR [r8])._ds mov ds,bx mov rbx,(S_ORIGINALSTATE PTR [r8])._es mov es,bx mov rbx,(S_ORIGINALSTATE PTR [r8])._fs mov fs,bx mov rbx,(S_ORIGINALSTATE PTR [r8])._gs mov gs,bx mov rcx,0c0000100h mov rax,(S_ORIGINALSTATE PTR [r8])._fsbase mov rdx,rax shr rdx,32 wrmsr mov rcx,0c0000101h mov rax,(S_ORIGINALSTATE PTR [r8])._gsbase mov rdx,rax shr rdx,32 wrmsr mov rax,originalstate mov rbx,(S_ORIGINALSTATE PTR [rax])._rbx mov rcx,(S_ORIGINALSTATE PTR [rax])._rcx mov rdx,(S_ORIGINALSTATE PTR [rax])._rdx mov rsi,(S_ORIGINALSTATE PTR [rax])._rsi mov rdi,(S_ORIGINALSTATE PTR [rax])._rdi mov rbp,(S_ORIGINALSTATE PTR [rax])._rbp mov rsp,(S_ORIGINALSTATE PTR [rax])._rsp mov r8,(S_ORIGINALSTATE PTR [rax])._r8 mov r9,(S_ORIGINALSTATE PTR [rax])._r9 mov r10,(S_ORIGINALSTATE PTR [rax])._r10 mov r11,(S_ORIGINALSTATE PTR [rax])._r11 mov r12,(S_ORIGINALSTATE PTR [rax])._r12 mov r13,(S_ORIGINALSTATE PTR [rax])._r13 mov r14,(S_ORIGINALSTATE PTR [rax])._r14 mov r15,(S_ORIGINALSTATE PTR [rax])._r15 mov rax,(S_ORIGINALSTATE PTR [rax])._rax ;crashtest ;mov rax,0deadh ;mov [rax],rax ;sti ret nop nop nop _TEXT ENDS END ================================================ FILE: src/debugger.c ================================================ /* debugger.c: This unit will handle all debugging related code, from hooking, to handling interrupts todo: this whole thing can be moved to a few simple lines in dbvm... */ #pragma warning( disable: 4100 4103 4189) #include #include #include "DBKFunc.h" #include "interruptHook.h" #include "debugger.h" #include "vmxhelper.h" #ifdef AMD64 extern void interrupt1_asmentry( void ); //declared in debuggera.asm #else void interrupt1_asmentry( void ); #endif volatile struct { BOOL isDebugging; //TRUE if a process is currently being debugged BOOL stoppingTheDebugger; DWORD debuggedProcessID; //The processID that is currently debugger struct { BOOL active; UINT_PTR address; //Up to 4 addresses to break on BreakType breakType; //What type of breakpoint for each seperate address BreakLength breakLength; //How many bytes does this breakpoint look at } breakpoint[4]; //... BOOL globalDebug; //If set all threads of every process will raise an interrupt on taskswitch //while debugging: UINT_PTR *LastStackPointer; UINT_PTR *LastRealDebugRegisters; HANDLE LastThreadID; BOOL CausedByDBVM; BOOL handledlastevent; //BOOL storeLBR; //int storeLBR_max; //UINT_PTR *LastLBRStack; volatile struct { UINT_PTR DR0; UINT_PTR DR1; UINT_PTR DR2; UINT_PTR DR3; UINT_PTR DR6; UINT_PTR DR7; UINT_PTR reserved; volatile int inEpilogue; //if set the global debug bit does no faking } FakedDebugRegisterState[256]; char b[1]; //volatile BYTE DECLSPEC_ALIGN(16) fxstate[512]; BOOL isSteppingTillClear; //when set the user has entered single stepping mode. This is a one thread only thing, so when it's active and another single step happens, discard it } DebuggerState; KEVENT debugger_event_WaitForContinue; //event for kernelmode. Waits till it's set by usermode (usermode function: DBK_Continue_Debug_Event sets it) KEVENT debugger_event_CanBreak; //event for kernelmode. Waits till a break has been handled so a new one can enter KEVENT debugger_event_WaitForDebugEvent; //event for usermode. Waits till it's set by a debugged event DebugReg7 debugger_dr7_getValue(void); void debugger_dr7_setValue(DebugReg7 value); DebugReg6 debugger_dr6_getValue(void); JUMPBACK Int1JumpBackLocation; typedef struct _SavedStack { BOOL inuse; QWORD stacksnapshot[600]; } SavedStack, *PSavedStack; criticalSection StacksCS; int StackCount; PSavedStack *Stacks; void debugger_dr7_setGD(int state) { DebugReg7 _dr7=debugger_dr7_getValue(); _dr7.GD=state; //usually 1 debugger_dr7_setValue(_dr7); } void debugger_dr0_setValue(UINT_PTR value) { __writedr(0,value); } UINT_PTR debugger_dr0_getValue(void) { return __readdr(0); } void debugger_dr1_setValue(UINT_PTR value) { __writedr(1,value); } UINT_PTR debugger_dr1_getValue(void) { return __readdr(1); } void debugger_dr2_setValue(UINT_PTR value) { __writedr(2,value); } UINT_PTR debugger_dr2_getValue(void) { return __readdr(2); } void debugger_dr3_setValue(UINT_PTR value) { __writedr(3,value); } UINT_PTR debugger_dr3_getValue(void) { return __readdr(3); } void debugger_dr6_setValue(UINT_PTR value) { __writedr(6,value); } void debugger_dr7_setValue(DebugReg7 value) { UINT_PTR temp=*(UINT_PTR *)&value; __writedr(7,temp); } void debugger_dr7_setValueDword(UINT_PTR value) { __writedr(7,value); } UINT_PTR debugger_dr7_getValueDword(void) //I wonder why I couldn't just typecast the DebugReg7 to a dword... { return __readdr(7); } DebugReg7 debugger_dr7_getValue(void) { UINT_PTR temp=debugger_dr7_getValueDword(); return *(DebugReg7 *)&temp; } UINT_PTR debugger_dr6_getValueDword(void) { return __readdr(6); } DebugReg6 debugger_dr6_getValue(void) { UINT_PTR temp=debugger_dr6_getValueDword(); return *(DebugReg6 *)&temp; } void debugger_touchDebugRegister(UINT_PTR param) { //DbgPrint("Touching debug register. inepilogue=\n", DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue); debugger_dr0_setValue(debugger_dr0_getValue()); } void debugger_initialize(void) { //DbgPrint("Initializing debugger events\n"); KeInitializeEvent(&debugger_event_WaitForContinue, SynchronizationEvent, FALSE); KeInitializeEvent(&debugger_event_CanBreak, SynchronizationEvent, TRUE); //true so the first can enter KeInitializeEvent(&debugger_event_WaitForDebugEvent, SynchronizationEvent, FALSE); //DbgPrint("DebuggerState.fxstate=%p\n",DebuggerState.fxstate); StackCount = getCpuCount() * 4; Stacks = (PSavedStack*)ExAllocatePool(NonPagedPool, StackCount*sizeof(PSavedStack)); int i; for (i = 0; i < StackCount; i++) { Stacks[i] = (PSavedStack)ExAllocatePool(NonPagedPool, sizeof(SavedStack)); RtlZeroMemory(Stacks[i], sizeof(SavedStack)); } } void debugger_shutdown(void) { if (Stacks) { int i; for (i = 0; i < StackCount; i++) { if (Stacks[i]) { ExFreePool(Stacks[i]); Stacks[i] = NULL; } } ExFreePool(Stacks); Stacks = NULL; } } void debugger_growstack() //called in passive mode { if (Stacks) { KIRQL oldIRQL=KeRaiseIrqlToDpcLevel(); csEnter(&StacksCS); enableInterrupts(); //csEnter disables it, but we need it int newStackCount = StackCount * 2; int i; PSavedStack *newStacks; newStacks = (PSavedStack*)ExAllocatePool(NonPagedPool, newStackCount * sizeof(PSavedStack)); if (newStacks) { for (i = 0; i < StackCount; i++) newStacks[i] = Stacks[i]; for (i = StackCount; i < newStackCount; i++) { newStacks[i] = (PSavedStack)ExAllocatePool(NonPagedPool, sizeof(SavedStack)); if (newStacks[i]) RtlZeroMemory(newStacks[i], sizeof(SavedStack)); else { ExFreePool(newStacks); csLeave(&StacksCS); KeLowerIrql(oldIRQL); return; } } ExFreePool(Stacks); Stacks = newStacks; } csLeave(&StacksCS); KeLowerIrql(oldIRQL); } } void debugger_setInitialFakeState(void) { //DbgPrint("setInitialFakeState for cpu %d\n",cpunr()); DebuggerState.FakedDebugRegisterState[cpunr()].DR0=debugger_dr0_getValue(); DebuggerState.FakedDebugRegisterState[cpunr()].DR1=debugger_dr1_getValue(); DebuggerState.FakedDebugRegisterState[cpunr()].DR2=debugger_dr2_getValue(); DebuggerState.FakedDebugRegisterState[cpunr()].DR3=debugger_dr3_getValue(); DebuggerState.FakedDebugRegisterState[cpunr()].DR6=debugger_dr6_getValueDword(); DebuggerState.FakedDebugRegisterState[cpunr()].DR7=debugger_dr7_getValueDword(); } VOID debugger_initHookForCurrentCPU_DPC(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { debugger_initHookForCurrentCPU(); } int debugger_removeHookForCurrentCPU(UINT_PTR params) { //DbgPrint("Unhooking int1 for this cpu\n"); return inthook_UnhookInterrupt(1); } int debugger_initHookForCurrentCPU(void) /* Must be called for each cpu */ { int result=TRUE; //DbgPrint("Hooking int1 for cpu %d\n", cpunr()); result=inthook_HookInterrupt(1,getCS() & 0xfff8, (ULONG_PTR)interrupt1_asmentry, &Int1JumpBackLocation); #ifdef AMD64 if (result) { ;//DbgPrint("hooked int1. Int1JumpBackLocation=%x:%llx\n", Int1JumpBackLocation.cs, Int1JumpBackLocation.eip); } else { //DbgPrint("Failed hooking interrupt 1\n"); return result; } #endif if (DebuggerState.globalDebug) { //set the fake state //debugger_setInitialFakeState(); //DbgPrint("Setting GD bit for cpu %d\n",cpunr()); debugger_dr7_setGD(1); //enable the GD flag } /*if (DebuggerState.storeLBR) { //DbgPrint("Enabling LBR logging. IA32_DEBUGCTL was %x\n", __readmsr(0x1d9)); __writemsr(0x1d9, __readmsr(0x1d9) | 1); //DbgPrint("Enabling LBR logging. IA32_DEBUGCTL is %x\n", __readmsr(0x1d9)); }*/ return result; } void debugger_setStoreLBR(BOOL state) { return; //disabled for now /* //if (state) // DbgPrint("Setting storeLBR to true\n"); //else // DbgPrint("Setting storeLBR to false\n"); DebuggerState.storeLBR=state; //it's not THAT crucial to disable/enable it DebuggerState.storeLBR_max=0; switch (cpu_model) { case 0x2a: case 0x1a: case 0x1e: case 0x1f: case 0x2e: case 0x25: case 0x2c: DebuggerState.storeLBR_max=16; break; case 0x17: case 0x1d: case 0x0f: DebuggerState.storeLBR_max=4; break; case 0x1c: DebuggerState.storeLBR_max=8; break; } //DbgPrint("Because your cpu_model=%d I think that your storeLBR_max=%d\n", cpu_model, DebuggerState.storeLBR_max); */ } int debugger_setGlobalDebugState(BOOL state) //call this BEFORE debugging, if already debugging, the user must call this for each cpu { //DbgPrint("debugger_setGlobalDebugState(%d)\n",state); if (state) DebuggerState.globalDebug=state; //on enable set this first if (inthook_isHooked(1)) { int oldEpilogueState=DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue; //DbgPrint("Int 1 is hooked,%ssetting GD\n",(state ? "":"un")); //DbgPrint("oldEpilogueState=%d\n",oldEpilogueState); //debugger_setInitialFakeState(); DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=TRUE; DebuggerState.globalDebug=state; debugger_dr7_setGD(state); DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=oldEpilogueState; DebuggerState.FakedDebugRegisterState[cpunr()].DR7=0x400; debugger_dr7_setValueDword(0x400); } return TRUE; } int debugger_startDebugging(DWORD debuggedProcessID) /* Call this AFTER the interrupts are hooked */ { //DbgPrint("debugger_startDebugging. Processid=%x\n",debuggedProcessID); Int1JumpBackLocation.eip=inthook_getOriginalEIP(1); Int1JumpBackLocation.cs=inthook_getOriginalCS(1); #ifdef AMD64 //DbgPrint("Int1 jump back = %x:%llx\n", Int1JumpBackLocation.cs, Int1JumpBackLocation.eip); #endif DebuggerState.isDebugging=TRUE; DebuggerState.debuggedProcessID=debuggedProcessID; return TRUE; } int debugger_stopDebugging(void) { int i; //DbgPrint("Stopping the debugger if it is running\n"); DebuggerState.stoppingTheDebugger=TRUE; if (DebuggerState.globalDebug) { //touch the global debug for each debug processor //DbgPrint("Touching the debug registers\n"); forEachCpuPassive(debugger_touchDebugRegister, 0); } DebuggerState.globalDebug=FALSE; //stop when possible, saves speed DebuggerState.isDebugging=FALSE; for (i=0; i<4; i++) DebuggerState.breakpoint[i].active=FALSE; //unhook all processors forEachCpuPassive(debugger_removeHookForCurrentCPU, 0); return TRUE; } int debugger_unsetGDBreakpoint(int breakpointnr) { int result=DebuggerState.breakpoint[breakpointnr].active; DebuggerState.breakpoint[breakpointnr].active=FALSE; return result; //returns true if it was active } int debugger_setGDBreakpoint(int breakpointnr, ULONG_PTR Address, BreakType bt, BreakLength bl) /* Will register a specific breakpoint. If global debug is used it'll set this debug register accordingly */ { //DbgPrint("debugger_setGDBreakpoint(%d, %x, %d, %d)\n", breakpointnr, Address, bt, bl); DebuggerState.breakpoint[breakpointnr].active=TRUE; DebuggerState.breakpoint[breakpointnr].address=Address; DebuggerState.breakpoint[breakpointnr].breakType=bt; DebuggerState.breakpoint[breakpointnr].breakLength=bl; return TRUE; } NTSTATUS debugger_waitForDebugEvent(ULONG timeout) { NTSTATUS r; LARGE_INTEGER wait; //DbgPrint("debugger_waitForDebugEvent with timeout of %d\n",timeout); //-10000000LL=1 second //-10000LL should be 1 millisecond //-10000LL wait.QuadPart=-10000LL * timeout; if (timeout==0xffffffff) //infinite wait r=KeWaitForSingleObject(&debugger_event_WaitForDebugEvent, UserRequest, KernelMode, TRUE, NULL); else r=KeWaitForSingleObject(&debugger_event_WaitForDebugEvent, UserRequest, KernelMode, TRUE, &wait); if (r==STATUS_SUCCESS) return r; else return STATUS_UNSUCCESSFUL; } NTSTATUS debugger_continueDebugEvent(BOOL handled) /* Only call this by one thread only, and only when there's actually a debug eevnt in progress */ { //DbgPrint("debugger_continueDebugEvent\n"); DebuggerState.handledlastevent=handled; KeSetEvent(&debugger_event_WaitForContinue, 0,FALSE); return STATUS_SUCCESS; } UINT_PTR *debugger_getLastStackPointer(void) { return DebuggerState.LastStackPointer; } NTSTATUS debugger_getDebuggerState(PDebugStackState state) { //DbgPrint("debugger_getDebuggerState\n"); state->threadid=(UINT64)DebuggerState.LastThreadID; state->causedbydbvm = (UINT64)DebuggerState.CausedByDBVM; if (DebuggerState.LastStackPointer) { state->rflags=(UINT_PTR)DebuggerState.LastStackPointer[si_eflags]; state->rax=DebuggerState.LastStackPointer[si_eax]; state->rbx=DebuggerState.LastStackPointer[si_ebx]; state->rcx=DebuggerState.LastStackPointer[si_ecx]; state->rdx=DebuggerState.LastStackPointer[si_edx]; state->rsi=DebuggerState.LastStackPointer[si_esi]; state->rdi=DebuggerState.LastStackPointer[si_edi]; state->rbp=DebuggerState.LastStackPointer[si_ebp]; #ifdef AMD64 //fill in the extra registers state->r8=DebuggerState.LastStackPointer[si_r8]; state->r9=DebuggerState.LastStackPointer[si_r9]; state->r10=DebuggerState.LastStackPointer[si_r10]; state->r11=DebuggerState.LastStackPointer[si_r11]; state->r12=DebuggerState.LastStackPointer[si_r12]; state->r13=DebuggerState.LastStackPointer[si_r13]; state->r14=DebuggerState.LastStackPointer[si_r14]; state->r15=DebuggerState.LastStackPointer[si_r15]; memcpy(state->fxstate, (void *)&DebuggerState.LastStackPointer[si_xmm], 512); #endif //generally speaking, NOTHING should touch the esp register, but i'll provide it anyhow if ((DebuggerState.LastStackPointer[si_cs] & 3) == 3) //if usermode code segment { //priv level change, so the stack info was pushed as well state->rsp=DebuggerState.LastStackPointer[si_esp]; state->ss=DebuggerState.LastStackPointer[si_ss]; } else { //kernelmode stack, yeah, it's really useless here since changing it here only means certain doom, but hey... state->rsp=(UINT_PTR)(DebuggerState.LastStackPointer)-4; state->ss=getSS();; //unchangeble by the user } state->rip=DebuggerState.LastStackPointer[si_eip]; state->cs=DebuggerState.LastStackPointer[si_cs]; state->ds=DebuggerState.LastStackPointer[si_ds]; state->es=DebuggerState.LastStackPointer[si_es]; #ifdef AMD64 state->fs=0; state->gs=0; #else state->fs=DebuggerState.LastStackPointer[si_fs]; state->gs=DebuggerState.LastStackPointer[si_gs]; #endif state->dr0=DebuggerState.LastRealDebugRegisters[0]; state->dr1=DebuggerState.LastRealDebugRegisters[1]; state->dr2=DebuggerState.LastRealDebugRegisters[2]; state->dr3=DebuggerState.LastRealDebugRegisters[3]; state->dr6=DebuggerState.LastRealDebugRegisters[4]; state->dr7=DebuggerState.LastRealDebugRegisters[5]; /*if (DebuggerState.storeLBR) { //DbgPrint("Copying the LBR stack to usermode\n"); //DbgPrint("storeLBR_max=%d\n", DebuggerState.storeLBR_max); for (state->LBR_Count=0; state->LBR_CountLBR_Count++ ) { //DbgPrint("DebuggerState.LastLBRStack[%d]=%x\n", state->LBR_Count, DebuggerState.LastLBRStack[state->LBR_Count]); state->LBR[state->LBR_Count]=DebuggerState.LastLBRStack[state->LBR_Count]; if (state->LBR[state->LBR_Count]==0) //no need to copy once a 0 has been reached break; } } else*/ state->LBR_Count=0; return STATUS_SUCCESS; } else { //DbgPrint("debugger_getDebuggerState was called while DebuggerState.LastStackPointer was still NULL"); return STATUS_UNSUCCESSFUL; } } NTSTATUS debugger_setDebuggerState(PDebugStackState state) { if (DebuggerState.LastStackPointer) { DebuggerState.LastStackPointer[si_eflags]=(UINT_PTR)state->rflags; //DbgPrint("have set eflags to %x\n",DebuggerState.LastStackPointer[si_eflags]); DebuggerState.LastStackPointer[si_eax]=(UINT_PTR)state->rax; DebuggerState.LastStackPointer[si_ebx]=(UINT_PTR)state->rbx; DebuggerState.LastStackPointer[si_ecx]=(UINT_PTR)state->rcx; DebuggerState.LastStackPointer[si_edx]=(UINT_PTR)state->rdx; DebuggerState.LastStackPointer[si_esi]=(UINT_PTR)state->rsi; DebuggerState.LastStackPointer[si_edi]=(UINT_PTR)state->rdi; DebuggerState.LastStackPointer[si_ebp]=(UINT_PTR)state->rbp; //generally speaking, NOTHING should touch the esp register, but i'll provide it anyhow if ((DebuggerState.LastStackPointer[si_cs] & 3) == 3) //if usermode code segment { //priv level change, so the stack info was pushed as well DebuggerState.LastStackPointer[si_esp]=(UINT_PTR)state->rsp; //don't mess with ss } else { //no change in kernelmode allowed } DebuggerState.LastStackPointer[si_eip]=(UINT_PTR)state->rip; DebuggerState.LastStackPointer[si_cs]=(UINT_PTR)state->cs; DebuggerState.LastStackPointer[si_ds]=(UINT_PTR)state->ds; DebuggerState.LastStackPointer[si_es]=(UINT_PTR)state->es; #ifndef AMD64 DebuggerState.LastStackPointer[si_fs]=(UINT_PTR)state->fs; DebuggerState.LastStackPointer[si_gs]=(UINT_PTR)state->gs; #else //don't touch fs or gs in 64-bit DebuggerState.LastStackPointer[si_r8]=(UINT_PTR)state->r8; DebuggerState.LastStackPointer[si_r9]=(UINT_PTR)state->r9; DebuggerState.LastStackPointer[si_r10]=(UINT_PTR)state->r10; DebuggerState.LastStackPointer[si_r11]=(UINT_PTR)state->r11; DebuggerState.LastStackPointer[si_r12]=(UINT_PTR)state->r12; DebuggerState.LastStackPointer[si_r13]=(UINT_PTR)state->r13; DebuggerState.LastStackPointer[si_r14]=(UINT_PTR)state->r14; DebuggerState.LastStackPointer[si_r15]=(UINT_PTR)state->r15; memcpy((void *)&DebuggerState.LastStackPointer[si_xmm], state->fxstate, 512); #endif if (!DebuggerState.globalDebug) { //no idea why someone would want to use this method, but it's in (for NON globaldebug only) //updating this array too just so the user can see it got executed. (it eases their state of mind...) DebuggerState.LastRealDebugRegisters[0]=(UINT_PTR)state->dr0; DebuggerState.LastRealDebugRegisters[1]=(UINT_PTR)state->dr1; DebuggerState.LastRealDebugRegisters[2]=(UINT_PTR)state->dr2; DebuggerState.LastRealDebugRegisters[3]=(UINT_PTR)state->dr3; DebuggerState.LastRealDebugRegisters[4]=(UINT_PTR)state->dr6; DebuggerState.LastRealDebugRegisters[5]=(UINT_PTR)state->dr7; //no setting of the DebugRegs here } } else { //DbgPrint("debugger_setDebuggerState was called while DebuggerState.LastStackPointer was still NULL"); return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } int breakpointHandler_kernel(UINT_PTR *stackpointer, UINT_PTR *currentdebugregs, UINT_PTR *LBR_Stack, int causedbyDBVM) //Notice: This routine is called when interrupts are enabled and the GD bit has been set if globaL DEBUGGING HAS BEEN USED //Interrupts are enabled and should be at passive level, so taskswitching is possible { NTSTATUS r=STATUS_UNSUCCESSFUL; int handled=0; //0 means let the OS handle it LARGE_INTEGER timeout; timeout.QuadPart=-100000; //DbgPrint("breakpointHandler for kernel breakpoints\n"); #ifdef AMD64 //DbgPrint("cs=%x ss=%x ds=%x es=%x fs=%x gs=%x\n",getCS(), getSS(), getDS(), getES(), getFS(), getGS()); //DbgPrint("fsbase=%llx gsbase=%llx gskernel=%llx\n", readMSR(0xc0000100), readMSR(0xc0000101), readMSR(0xc0000102)); //DbgPrint("rbp=%llx\n", getRBP()); //DbgPrint("gs:188=%llx\n", __readgsqword(0x188)); //DbgPrint("causedbyDBVM=%d\n", causedbyDBVM); #endif if (KeGetCurrentIrql()==0) { //crititical section here if ((stackpointer[si_cs] & 3)==0) { //DbgPrint("Going to wait in a kernelmode routine\n"); } //block other threads from breaking until this one has been handled while (r != STATUS_SUCCESS) { r=KeWaitForSingleObject(&debugger_event_CanBreak,Executive, KernelMode, FALSE, NULL); //check r and handle specific events //DbgPrint("Woke up. r=%x\n",r); } if ((stackpointer[si_cs] & 3)==0) { //DbgPrint("Woke up in a kernelmode routine\n"); } //We're here, let's notify the usermode debugger of our situation //first store the stackpointer so it can be manipulated externally DebuggerState.LastStackPointer=stackpointer; DebuggerState.LastRealDebugRegisters=currentdebugregs; /*DebuggerState.LastLBRStack=LBR_Stack;*/ DebuggerState.LastThreadID=PsGetCurrentThreadId(); DebuggerState.CausedByDBVM = causedbyDBVM; //notify usermode app that this thread has halted due to a debug event KeSetEvent(&debugger_event_WaitForDebugEvent,0,FALSE); //wait for event from usermode that debgu event has been handled //KeWaitForSingleObject(); //continue with state while (1) { //LARGE_INTEGER wt; NTSTATUS s=STATUS_UNSUCCESSFUL; //wt.QuadPart=-10000000LL; //s=KeDelayExecutionThread(KernelMode, FALSE, &wt); //DbgPrint("Waiting...\n"); while (s != STATUS_SUCCESS) { s=KeWaitForSingleObject(&debugger_event_WaitForContinue, Executive, KernelMode, FALSE, NULL); //DbgPrint("KeWaitForSingleObject=%x\n",s); } if (s==STATUS_SUCCESS) { if (DebuggerState.handledlastevent) { //DbgPrint("handledlastevent=TRUE"); handled=1; } else handled=0; break; } } DebuggerState.LastStackPointer=NULL; //NULL the stackpointer so routines know it should not be called //i'm done, let other threads catch it KeSetEvent(&debugger_event_CanBreak, 0, FALSE); //DbgPrint("Returning after a wait. handled=%d and eflags=%x\n",handled, stackpointer[si_eflags]); if ((stackpointer[si_cs] & 3)==0) //check rpl of cs { //DbgPrint("and in kernelmode\n"); } return handled; } else { //DbgPrint("Breakpoint wasn't at passive level. Screw this, i'm not going to break here\n"); return 1; } } int interrupt1_handler(UINT_PTR *stackpointer, UINT_PTR *currentdebugregs) { HANDLE CurrentProcessID=PsGetCurrentProcessId(); UINT_PTR originaldr6=currentdebugregs[4]; DebugReg6 _dr6=*(DebugReg6 *)¤tdebugregs[4]; UINT_PTR LBR_Stack[16]; //max 16 // DebugReg7 _dr7=*(DebugReg7 *)¤tdebugregs[5]; int causedbyDBVM = vmxusable && vmx_causedCurrentDebugBreak(); /* if (cpu_familyID==0x6) { if (DebuggerState.storeLBR) { //fetch the lbr stack int MSR_LASTBRANCH_TOS=0x1c9; int MSR_LASTBRANCH_0=0x40; int i; int count; i=(int)__readmsr(MSR_LASTBRANCH_TOS); count=0; while (countRF = 1; //repeat this instruction and don't break return 2; } //DbgPrint("handler: Setting fake dr6 to %x\n",*(UINT_PTR *)&_dr6); DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = *(UINT_PTR *)&_dr6; for (instructionPointer = 0; instruction[instructionPointer] != 0x0f; instructionPointer++); //find the start of the instruction, skipping prefixes etc... //we now have the start of the instruction. //Find out which instruction it is, and which register is used debugregister = (instruction[instructionPointer + 2] >> 3) & 7; generalpurposeregister = instruction[instructionPointer + 2] & 7; #ifdef AMD64 for (prefixpointer = 0; prefixpointer < instructionPointer; prefixpointer++) { //check for a REX.B prefix (0x40 + 0x1 : 0x41) if ((instruction[prefixpointer] & 0x41) == 0x41) { //rex.b prefix is used, r8 to r15 are being accessed generalpurposeregister += 8; } } #endif //DbgPrint("debugregister=%d, generalpurposeregister=%d\n",debugregister,generalpurposeregister); if (instruction[instructionPointer + 1] == 0x21) { UINT_PTR drvalue = 0; //DbgPrint("read opperation\n"); //21=read switch (debugregister) { case 0: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR0; //DbgPrint("Reading DR0 (returning %x real %x)\n", drvalue, currentdebugregs[0]); break; case 1: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR1; break; case 2: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR2; break; case 3: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR3; break; case 4: case 6: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR6; //DbgPrint("reading dr6 value:%x\n",drvalue); break; case 5: case 7: drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR7; break; default: //DbgPrint("Invalid debugregister\n"); drvalue = 0; break; } switch (generalpurposeregister) { case 0: stackpointer[si_eax] = drvalue; break; case 1: stackpointer[si_ecx] = drvalue; break; case 2: stackpointer[si_edx] = drvalue; break; case 3: stackpointer[si_ebx] = drvalue; break; case 4: if ((stackpointer[si_cs] & 3) == 3) //usermode dr access ? stackpointer[si_esp] = drvalue; else stackpointer[si_stack_esp] = drvalue; break; case 5: stackpointer[si_ebp] = drvalue; break; case 6: stackpointer[si_esi] = drvalue; break; case 7: stackpointer[si_edi] = drvalue; break; #ifdef AMD64 case 8: stackpointer[si_r8] = drvalue; break; case 9: stackpointer[si_r9] = drvalue; break; case 10: stackpointer[si_r10] = drvalue; break; case 11: stackpointer[si_r11] = drvalue; break; case 12: stackpointer[si_r12] = drvalue; break; case 13: stackpointer[si_r13] = drvalue; break; case 14: stackpointer[si_r14] = drvalue; break; case 15: stackpointer[si_r15] = drvalue; break; #endif } } else if (instruction[instructionPointer + 1] == 0x23) { //23=write UINT_PTR gpvalue = 0; //DbgPrint("Write operation\n"); switch (generalpurposeregister) { case 0: gpvalue = stackpointer[si_eax]; break; case 1: gpvalue = stackpointer[si_ecx]; break; case 2: gpvalue = stackpointer[si_edx]; break; case 3: gpvalue = stackpointer[si_ebx]; break; case 4: if ((stackpointer[si_cs] & 3) == 3) gpvalue = stackpointer[si_esp]; break; case 5: gpvalue = stackpointer[si_ebp]; break; case 6: gpvalue = stackpointer[si_esi]; break; case 7: gpvalue = stackpointer[si_edi]; break; #ifdef AMD64 case 8: gpvalue = stackpointer[si_r8]; break; case 9: gpvalue = stackpointer[si_r9]; break; case 10: gpvalue = stackpointer[si_r10]; break; case 11: gpvalue = stackpointer[si_r11]; break; case 12: gpvalue = stackpointer[si_r12]; break; case 13: gpvalue = stackpointer[si_r13]; break; case 14: gpvalue = stackpointer[si_r14]; break; case 15: gpvalue = stackpointer[si_r15]; break; default: //DbgPrint("Invalid register value\n"); break; #endif } //gpvalue now contains the value to set the debug register switch (debugregister) { case 0: //DbgPrint("Writing DR0. Original value=%x new value=%x\n", currentdebugregs[0], gpvalue); debugger_dr0_setValue(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR0 = debugger_dr0_getValue(); break; case 1: debugger_dr1_setValue(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR1 = debugger_dr1_getValue(); break; case 2: debugger_dr2_setValue(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR2 = debugger_dr2_getValue(); break; case 3: debugger_dr3_setValue(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR3 = debugger_dr3_getValue(); break; case 4: case 6: //DbgPrint("Setting dr6 to %x (was %x)\n", gpvalue, DebuggerState.FakedDebugRegisterState[cpunr()].DR6); _dr6 = *(DebugReg6 *)&gpvalue; //if (_dr6.BD) DbgPrint("Some code wants to set the BD flag to 1\n"); debugger_dr6_setValue(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = debugger_dr6_getValueDword(); if (_dr6.BD) { _dr6.BD = 0; debugger_dr6_setValue(gpvalue); } break; case 5: case 7: //make sure it doesn't set the GD flag here //DbgPrint("DR7 write\n"); //if (generalpurposeregister == 15) //{ // while (1); //patchguard //} //if (DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue) //{ // DbgPrint("Was in epilogue\n"); //} //check for invalid bits and raise a GPF if invalid gpvalue = (gpvalue | 0x400) & (~(1 << 13)); //unset the GD value //gpvalue=0xf0401; debugger_dr7_setValueDword(gpvalue); DebuggerState.FakedDebugRegisterState[cpunr()].DR7 = debugger_dr7_getValueDword(); break; } } else { //DbgPrint("Some unknown instruction accessed the debug registers?\n"); //if (CurrentProcessID==(HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID) // DbgPrint("Happened inside the target process\n"); //DbgPrint("interrupt1_handler dr6=%x (original=%x) dr7=%d\n",_dr6, originaldr6, _dr7); //DbgPrint("eip=%x\n",stackpointer[si_eip]); } //adjust eip to after this instruction stackpointer[si_eip] += instructionPointer + 3; //0f xx /r return 1; //don't tell windows about it } else { //DbgPrint("DR6.BD == 1 in USERMODE! WTF\n"); _dr6.BD = 0; debugger_dr6_setValue(*(UINT_PTR *)&_dr6); DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = debugger_dr6_getValueDword(); } } } if (DebuggerState.isSteppingTillClear) //this doesn't really work because when the state comes back to interruptable the system has a critical section lock on the GUI, so yeah... I really need a DBVM display driver for this { if ((((PEFLAGS)&stackpointer[si_eflags])->IF == 0) || (KeGetCurrentIrql() != PASSIVE_LEVEL)) { ((PEFLAGS)&stackpointer[si_eflags])->TF = 1; ((PEFLAGS)&stackpointer[si_eflags])->RF = 1; debugger_dr6_setValue(0xffff0ff0); return 1; } DebuggerState.isSteppingTillClear = FALSE; } if (DebuggerState.isDebugging) { //DbgPrint("DebuggerState.isDebugging\n"); //check if this should break if (CurrentProcessID == (HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID) { UINT_PTR originaldebugregs[6]; UINT64 oldDR7 = getDR7(); if ((((PEFLAGS)&stackpointer[si_eflags])->IF == 0) || (KeGetCurrentIrql() != PASSIVE_LEVEL)) { //There's no way to display the state to the usermode part of CE //DbgPrint("int1 at unstoppable location"); if (!KernelCodeStepping) { ((PEFLAGS)&stackpointer[si_eflags])->TF = 0; //just give up stepping // DbgPrint("Quitting this"); } else { // DbgPrint("Stepping until valid\n"); ((PEFLAGS)&stackpointer[si_eflags])->TF = 1; //keep going until a valid state DebuggerState.isSteppingTillClear = TRUE; //Just in case a taskswitch happens right after enabling passive level with interrupts } ((PEFLAGS)&stackpointer[si_eflags])->RF = 1; debugger_dr6_setValue(0xffff0ff0); return 1; } DebuggerState.isSteppingTillClear = FALSE; //DbgPrint("CurrentProcessID==(HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID\n"); if (DebuggerState.globalDebug) { originaldebugregs[0] = DebuggerState.FakedDebugRegisterState[cpunr()].DR0; originaldebugregs[1] = DebuggerState.FakedDebugRegisterState[cpunr()].DR1; originaldebugregs[2] = DebuggerState.FakedDebugRegisterState[cpunr()].DR2; originaldebugregs[3] = DebuggerState.FakedDebugRegisterState[cpunr()].DR3; originaldebugregs[4] = DebuggerState.FakedDebugRegisterState[cpunr()].DR6; originaldebugregs[5] = DebuggerState.FakedDebugRegisterState[cpunr()].DR7; } //DbgPrint("BP in target process\n"); //no extra checks if it's caused by the debugger or not. That is now done in the usermode part //if (*(PEFLAGS)(&stackpointer[si_eflags]).IF) /* if (((PEFLAGS)&stackpointer[si_eflags])->IF==0) { //DbgPrint("Breakpoint while interrupts are disabled: %x\n",stackpointer[si_eip]); //An breakpoint happened while IF was 0. Step through the code untill IF is 1 ((PEFLAGS)&stackpointer[si_eflags])->RF=1; ((PEFLAGS)&stackpointer[si_eflags])->TF=1; //keep going until IF=1 DbgPrint("IF==0\n"); return 1; //don't handle it but also don't tell windows }*/ //set the real debug registers to what it is according to the guest (so taskswitches take over these values) .... shouldn't be needed as global debug is on which fakes that read... if (DebuggerState.globalDebug) { //enable the GD flag for taskswitches that will occur as soon as interrupts are enabled //this also means: DO NOT EDIT THE DEBUG REGISTERS IN GLOBAL DEBUG MODE at this point. Only in the epilogue if (!DebuggerState.stoppingTheDebugger) //This is set when the driver is unloading. So do NOT set it back then debugger_dr7_setGD(DebuggerState.globalDebug); } else { //unset ALL debug registers before enabling taskswitching. Just re-enable it when back when interrupts are disabled again debugger_dr7_setValueDword(0x400); debugger_dr0_setValue(0); debugger_dr1_setValue(0); debugger_dr2_setValue(0); debugger_dr3_setValue(0); debugger_dr6_setValue(0xffff0ff0); } //start the windows taskswitching mode //if (1) return 1; //save the state of the thread to a place that won't get overwritten //todo: breaks 32-bit //int i; BOOL NeedsToGrowStackList = FALSE; PSavedStack SelectedStackEntry = NULL; /* csEnter(&StacksCS); for (i = 0; i < StackCount; i++) { if (Stacks[i]->inuse == FALSE) { SelectedStackEntry = Stacks[i]; SelectedStackEntry->inuse = TRUE; RtlCopyMemory(SelectedStackEntry->stacksnapshot, stackpointer, 600 * 8); if (i > StackCount / 2) NeedsToGrowStackList = TRUE; break; } } csLeave(&StacksCS); enableInterrupts(); //grow stack if needed if (NeedsToGrowStackList) debugger_growstack(); */ { int rs=1; //DbgPrint("calling breakpointHandler_kernel\n"); if (SelectedStackEntry == NULL) //fuck rs = breakpointHandler_kernel(stackpointer, currentdebugregs, LBR_Stack, causedbyDBVM); else rs = breakpointHandler_kernel((UINT_PTR *)(SelectedStackEntry->stacksnapshot), currentdebugregs, LBR_Stack, causedbyDBVM); //DbgPrint("After handler\n"); /* if (SelectedStackEntry) //restore the stack { RtlCopyMemory(stackpointer, SelectedStackEntry->stacksnapshot, 600 * 8); SelectedStackEntry->inuse = FALSE; } */ //DbgPrint("rs=%d\n",rs); disableInterrupts(); //restore the //we might be on a different CPU now if (DebuggerState.globalDebug) { DebuggerState.FakedDebugRegisterState[cpunr()].DR0=originaldebugregs[0]; DebuggerState.FakedDebugRegisterState[cpunr()].DR1=originaldebugregs[1]; DebuggerState.FakedDebugRegisterState[cpunr()].DR2=originaldebugregs[2]; DebuggerState.FakedDebugRegisterState[cpunr()].DR3=originaldebugregs[3]; DebuggerState.FakedDebugRegisterState[cpunr()].DR6=originaldebugregs[4]; DebuggerState.FakedDebugRegisterState[cpunr()].DR7=originaldebugregs[5]; } else { /*if (getDR7() != oldDR7) { DbgPrint("Something changed DR7. old=%llx new=%llx\n",oldDR7, getDR7()); }*/ //set the debugregisters to what they where set to before taskswitching was enable //with global debug this is done elsewhere debugger_dr0_setValue(currentdebugregs[0]); debugger_dr1_setValue(currentdebugregs[1]); debugger_dr2_setValue(currentdebugregs[2]); debugger_dr3_setValue(currentdebugregs[3]); debugger_dr6_setValue(currentdebugregs[4]); if ((currentdebugregs[5] >> 13) & 1) { // DbgPrint("WTF? GD is 1 in currentdebugregs[5]: %llx\n", currentdebugregs[5]); } else debugger_dr7_setValue(*(DebugReg7 *)¤tdebugregs[5]); } return rs; } } else { //DbgPrint("Not the debugged process (%x != %x)\n",CurrentProcessID,DebuggerState.debuggedProcessID ); //check if this break is due to a breakpoint ce has set. (during global debug threadsurfing)) //do that by checking if the breakpoint condition exists in the FAKE dr7 registers //if so, let windows handle it, if not, it is caused by ce, which then means, skip (so execute normally) if (DebuggerState.globalDebug) { DebugReg6 dr6=debugger_dr6_getValue(); DebugReg7 dr7=*(DebugReg7 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR7; //real dr6 //fake dr7 if ((dr6.B0) && (!(dr7.L0 || dr7.G0))) { /*DbgPrint("setting RF because of B0\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } //break caused by DR0 and not expected by the current process, ignore this bp and continue if ((dr6.B1) && (!(dr7.L1 || dr7.G1))) { /*DbgPrint("setting RF because of B1\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR1 ... if ((dr6.B2) && (!(dr7.L2 || dr7.G2))) { /*DbgPrint("setting RF because of B2\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR2 ... if ((dr6.B3) && (!(dr7.L3 || dr7.G3))) { /*DbgPrint("setting RF because of B3\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR3 ... } if (causedbyDBVM) return 1; //correct PA, bad PID, ignore BP if (DebuggerState.isSteppingTillClear) //shouldn't happen often { //DbgPrint("That thing that shouldn\'t happen often happened\n"); ((PEFLAGS)&stackpointer[si_eflags])->TF = 0; DebuggerState.isSteppingTillClear = 0; return 1; //ignore } //DbgPrint("Returning unhandled. DR6=%x", debugger_dr6_getValueDword()); return 0; //still here, so let windows handle it } } else return 0; //Let windows handle it //get the current processid //is it being debugged //if yes, check if the breakpoint is something done by me //if no, exit } int interrupt1_centry(UINT_PTR *stackpointer) //code segment 8 has a 32-bit stackpointer { UINT_PTR before;//,after; UINT_PTR currentdebugregs[6]; //used for determining if the current bp is caused by the debugger or not int handled=0; //if 0 at return, the interupt will be passed down to the operating system QWORD naddress; //DbgPrint("interrupt1_centry cpunr=%d esp=%x\n",cpunr(), getRSP()); //bsod crashfix, but also disables kernelmode stepping IDT idt; GetIDT(&idt); naddress = idt.vector[1].wLowOffset + (idt.vector[1].wHighOffset << 16); #ifdef AMD64 naddress += ((UINT64)idt.vector[1].TopOffset << 32); #endif stackpointer[si_errorcode] = (UINT_PTR)naddress; //the errorcode is used as address to call the original function if needed /* if (Int1JumpBackLocation.eip != naddress) //no, just fucking no (patchguard will replace all inthandlers with invalid ones and then touch dr7) { //todo: the usual, but make sure not to use dbgprint or anything that could trigger a software int if (DebuggerState.globalDebug) { debugger_dr7_setGD(DebuggerState.globalDebug); stackpointer[si_eip] += 4; return 1; } } */ before=getRSP(); //Fetch current debug registers currentdebugregs[0]=debugger_dr0_getValue(); currentdebugregs[1]=debugger_dr1_getValue(); currentdebugregs[2]=debugger_dr2_getValue(); currentdebugregs[3]=debugger_dr3_getValue(); currentdebugregs[4]=debugger_dr6_getValueDword(); currentdebugregs[5]=debugger_dr7_getValueDword(); handled=interrupt1_handler(stackpointer, currentdebugregs); //epilogue: //At the end when returning: // //-------------------------------------------------------------------------- //--------------EPILOGUE (AFTER HAVING HANDLED THE BREAKPOINT)-------------- //-------------------------------------------------------------------------- // disableInterrupts(); //just making sure.. DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1; debugger_dr7_setGD(0); //make sure the GD bit is disabled (int1 within int1, oooh the fun..., and yes, THIS itself will cause one too) DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1; //just be sure... //if (inthook_isDBVMHook(1)) //{ //update the int1 return address, could have been changed //DbgPrint("This was a dbvm hook. Changing if the interrupt return address is still valid\n"); // Int1JumpBackLocation.cs=idt.vector[1].wSelector; // naddress=idt.vector[1].wLowOffset+(idt.vector[1].wHighOffset << 16); #ifdef AMD64 // naddress+=((UINT64)idt.vector[1].TopOffset << 32); #endif //} if (DebuggerState.globalDebug) //DR's are only accesses when there are DR's(no idea how it handles breakpoints in a different process...), so set them in each thread even those that don't belong original: && (PsGetCurrentProcessId()==(HANDLE)DebuggerState.debuggedProcessID)) { //set the breakpoint in this thread. DebugReg6 dr6=debugger_dr6_getValue(); //DebugReg7 dr7=debugger_dr7_getValue(); DebugReg6 _dr6=*(DebugReg6 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR6; DebugReg7 _dr7=*(DebugReg7 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR7; int debugregister=0, breakpoint=0; //first clear the DR6 bits caused by the debugger if (!handled) { //it's going to get sent to windows if (dr6.BD && _dr7.GD) _dr6.BD=1; //should already have been done, but what the heck... if (dr6.B0 && (_dr7.L0 || _dr7.G0)) _dr6.B0=1; if (dr6.B1 && (_dr7.L1 || _dr7.G1)) _dr6.B1=1; if (dr6.B2 && (_dr7.L2 || _dr7.G2)) _dr6.B2=1; if (dr6.B3 && (_dr7.L3 || _dr7.G3)) _dr6.B3=1; _dr6.BS=dr6.BS; _dr6.BT=dr6.BT; //DbgPrint("epilogue: Setting fake dr6 to %x (fake=%x)\n",*(DWORD *)&dr6, *(DWORD *)&_dr6); } debugger_dr6_setValue(0xffff0ff0); //set the debug registers of active breakpoints. Doesn't have to be in the specified order. Just find an unused debug registers //check DebuggerState.FakedDebugRegisterState[cpunumber].DR7 for unused breakpoints //set state to what the guest thinks it is debugger_dr0_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR0); debugger_dr1_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR1); debugger_dr2_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR2); debugger_dr3_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR3); debugger_dr6_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR6); for (breakpoint=0; breakpoint<4; breakpoint++) { if (DebuggerState.breakpoint[breakpoint].active) { int foundone=0; // DbgPrint("Want to set breakpoint %d\n",breakpoint); //find a usable debugregister while ((debugregister<4) && (foundone==0)) { if (DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue==0) { DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1; } //check if this debugregister is usable if (((DebuggerState.FakedDebugRegisterState[cpunr()].DR7 >> (debugregister*2)) & 3)==0) //DR7.Gx and DR7.Lx are 0 { // DbgPrint("debugregister %d is free to be used\n",debugregister); foundone=1; //set address switch (debugregister) { case 0: debugger_dr0_setValue(DebuggerState.breakpoint[breakpoint].address); _dr7.L0=1; _dr7.LEN0=DebuggerState.breakpoint[breakpoint].breakLength; _dr7.RW0=DebuggerState.breakpoint[breakpoint].breakType; break; case 1: debugger_dr1_setValue(DebuggerState.breakpoint[breakpoint].address); _dr7.L1=1; _dr7.LEN1=DebuggerState.breakpoint[breakpoint].breakLength; _dr7.RW1=DebuggerState.breakpoint[breakpoint].breakType; break; case 2: debugger_dr2_setValue(DebuggerState.breakpoint[breakpoint].address); _dr7.L2=1; _dr7.LEN2=DebuggerState.breakpoint[breakpoint].breakLength; _dr7.RW2=DebuggerState.breakpoint[breakpoint].breakType; break; case 3: debugger_dr3_setValue(DebuggerState.breakpoint[breakpoint].address); _dr7.L3=1; _dr7.LEN3=DebuggerState.breakpoint[breakpoint].breakLength; _dr7.RW3=DebuggerState.breakpoint[breakpoint].breakType; break; } } debugregister++; } } } debugger_dr7_setValue(_dr7); //DbgPrint("after:\n"); //DbgPrint("after fake DR0=%x real DR0=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR0, debugger_dr0_getValue()); //DbgPrint("after fake DR1=%x real DR1=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR1, debugger_dr1_getValue()); //DbgPrint("after fake DR2=%x real DR2=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR2, debugger_dr2_getValue()); //DbgPrint("after fake DR3=%x real DR3=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR3, debugger_dr3_getValue()); //DbgPrint("after fake DR6=%x real DR6=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR6, debugger_dr6_getValueDword()); //DbgPrint("after fake DR7=%x real DR7=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR7, debugger_dr7_getValueDword()); } else { //not global debug, just clear all flags and be done with it if (handled) debugger_dr6_setValue(0xffff0ff0); } disableInterrupts(); if (handled == 2) { //DbgPrint("handled==2\n"); handled = 1; //epilogue = 1 Dr handler } else { //not handled by the epilogue set DR0, so the actual epilogue //DbgPrint("handled==1\n"); if (DebuggerState.globalDebug) { DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=0; if (!DebuggerState.stoppingTheDebugger) debugger_dr7_setGD(DebuggerState.globalDebug); //set it back to 1, if not unloading } } //after=getRSP(); //DbgPrint("before=%llx after=%llx\n",before,after); //DbgPrint("end of interrupt1_centry. eflags=%x", stackpointer[si_eflags]); //if branch tracing set lbr back on (get's disabled on debug interrupts) /* if (DebuggerState.storeLBR) __writemsr(0x1d9, __readmsr(0x1d9) | 1); */ return handled; } #ifndef AMD64 _declspec( naked ) void interrupt1_asmentry( void ) //This routine is called upon an interrupt 1, even before windows gets it { __asm{ //change the start of the stack so that instructions like setthreadcontext do not affect the stack it when it's frozen and waiting for input //meaning the setting of debug registers will have to be done with the changestate call //sub esp,4096 //push [esp+4096+0+16] //optional ss //push [esp+4096+4+12] //optional esp //push [esp+4096+8+8] //eflags //push [esp+4096+12+4] //cs //push [esp+4096+16+0] //eip cld //reset the direction flag //save stack position push 0 //push an errorcode on the stack so the stackindex can stay the same push ebp mov ebp,esp //save state pushad xor eax,eax mov ax,ds push eax mov ax,es push eax mov ax,fs push eax mov ax,gs push eax //save fpu state //save sse state mov ax,0x23 //0x10 should work too, but even windows itself is using 0x23 mov ds,ax mov es,ax mov gs,ax mov ax,0x30 mov fs,ax push ebp call interrupt1_centry cmp eax,1 //set flag //restore state pop gs pop fs pop es pop ds popad pop ebp je skip_original_int1 add esp,4 //undo errorcode push (add effects eflags, so set it at both locations) jmp far [Int1JumpBackLocation] skip_original_int1: add esp,4 //undo errorcode push iretd } } #endif ================================================ FILE: src/debugger.h ================================================ #ifndef DEBUGGER_H #define DEBUGGER_H #include #include #pragma pack(4) typedef struct { UINT64 threadid; // UINT64 causedbydbvm; UINT64 rflags; UINT64 rax;// UINT64 rbx; UINT64 rcx;// UINT64 rdx; UINT64 rsi;// UINT64 rdi; UINT64 rbp;// UINT64 rsp; UINT64 rip;// UINT64 r8; UINT64 r9;// UINT64 r10; UINT64 r11;// UINT64 r12; UINT64 r13;// UINT64 r14; UINT64 r15;// UINT64 cs; UINT64 ds;// UINT64 es; UINT64 fs;// UINT64 gs; UINT64 ss;// UINT64 dr0; UINT64 dr1;// UINT64 dr2; UINT64 dr3;// UINT64 dr6; UINT64 dr7;// BYTE fxstate[512]; UINT64 LBR_Count; UINT64 LBR[16]; } DebugStackState, *PDebugStackState; #pragma pack() //stack index typedef enum {bt_OnInstruction=0,bt_OnWrites=1, bt_OnIOAccess=2, bt_OnReadsAndWrites=3} BreakType; typedef enum {bl_1byte=0, bl_2byte=1, bl_8byte=2/*Only when in 64-bit*/, bl_4byte=3} BreakLength; void debugger_initialize(void); void debugger_shutdown(void); int debugger_initHookForCurrentCPU(void); int debugger_setGlobalDebugState(BOOL state); void debugger_setStoreLBR(BOOL state); int debugger_startDebugging(DWORD debuggedProcessID); int debugger_setGDBreakpoint(int breakpointnr, ULONG_PTR Address, BreakType bt, BreakLength bl); int debugger_unsetGDBreakpoint(int breakpointnr); void debugger_touchDebugRegister(UINT_PTR param); int debugger_stopDebugging(void); NTSTATUS debugger_waitForDebugEvent(ULONG timeout); NTSTATUS debugger_continueDebugEvent(BOOL handled); UINT_PTR *debugger_getLastStackPointer(void); NTSTATUS debugger_getDebuggerState(PDebugStackState state); NTSTATUS debugger_setDebuggerState(PDebugStackState state); void GetDebuggerInfo(void); VOID debugger_initHookForCurrentCPU_DPC(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2); #endif ================================================ FILE: src/deepkernel.c ================================================ #pragma warning( disable: 4100 4103) #include "deepkernel.h" #include "DBKFunc.h" #include #include "vmxhelper.h" BOOLEAN MakeWritableKM(PVOID StartAddress,UINT_PTR size) { #ifndef AMD64 UINT_PTR PTE,PDE; struct PTEStruct *x; UINT_PTR CurrentAddress=(UINT_PTR)StartAddress; while (CurrentAddress<((UINT_PTR)StartAddress+size)) { //find the PTE or PDE of the selected address PTE=(UINT_PTR)CurrentAddress; PTE=PTE/0x1000*PTESize+0xc0000000; PTE=(UINT_PTR)StartAddress; PTE=PTE/0x1000*PTESize+0xc0000000; //now check if the address in PTE is valid by checking the page table directory at 0xc0300000 (same location as CR3 btw) PDE=PTE/0x1000*PTESize+0xc0000000; //same formula x=(PVOID)PDE; if ((x->P==0) && (x->A2==0)) { CurrentAddress+=PAGE_SIZE_LARGE; continue; } if (x->PS==1) { //big page, no pte x->RW=1; CurrentAddress+=PAGE_SIZE_LARGE; continue; } CurrentAddress+=0x1000; x=(PVOID)PTE; if ((x->P==0) && (x->A2==0)) continue; //see for explenation the part of the PDE x->RW=1; } return TRUE; #else return FALSE; #endif } BOOLEAN MakeWritable(PVOID StartAddress,UINT_PTR size,BOOLEAN usecopyonwrite) { #ifndef AMD64 struct PTEStruct *x; unsigned char y; UINT_PTR CurrentAddress=(UINT_PTR)StartAddress; //Makes usermode <0x80000000 writable if (((UINT_PTR)StartAddress>=0x80000000) || ((UINT_PTR)StartAddress+size>=0x80000000)) return MakeWritableKM(StartAddress,size); //safety check: don't do kernelmemory with this routine //4kb pages (assumption, I know, but thats the system i'm working with) //PTE/0x1000*4+0xc0000000; while (CurrentAddress<((UINT_PTR)StartAddress+size)) { __try { y=*(PCHAR)CurrentAddress; //page it in if it wasn't loaded already (BSOD if kernelmode address) x=(PVOID)(CurrentAddress/0x1000*PTESize+0xc0000000); if (x->RW==0) //if it's read only then { if (usecopyonwrite) x->A1=1; //set the copy-on-write bit to 1 else x->RW=1; //just writable } } __except(1) { //ignore and continue } CurrentAddress+=0x1000; } return TRUE; #else return FALSE; #endif } //this unit will contain the functions and other crap used by the hider function BOOLEAN CheckImageName(IN PUNICODE_STRING FullImageName, IN char* List,int listsize) { #ifndef AMD64 /* pre:List has been initialized and all entries are UPPERCASE. Each entry is seperated by a 0-marker so just setting the pointer ro the start and doing a compare will work */ ANSI_STRING tempstring; int i; DbgPrint("Checking this image name...\n"); RtlZeroMemory(&tempstring,sizeof(ANSI_STRING)); if (RtlUnicodeStringToAnsiString(&tempstring,FullImageName,TRUE)== STATUS_SUCCESS) { char *p; INT_PTR modulesize; __try { RtlUpperString(&tempstring,&tempstring); p=List; for (i=0;i=0) { DbgPrint("Checking %s with %s\n",&tempstring.Buffer[tempstring.Length-modulesize],p); if ((tempstring.Length>=modulesize) && (strcmp(p,&tempstring.Buffer[tempstring.Length-modulesize])==0)) { //we have a match!!! DbgPrint("It's a match with %s\n",p); return TRUE; } } p=&List[i+1]; } } } __finally { RtlFreeAnsiString(&tempstring); } } DbgPrint("No match\n"); #endif return FALSE; } VOID LoadImageNotifyRoutine(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo) { } ================================================ FILE: src/deepkernel.h ================================================ #include #include VOID LoadImageNotifyRoutine(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo); BOOLEAN MakeWritable(PVOID StartAddress,UINT_PTR size,BOOLEAN usecopyonwrite); BOOLEAN ImageNotifyRoutineLoaded; char* ModuleList; int ModuleListSize; ULONG Size; UINT_PTR ActiveLinkOffset; UINT_PTR ProcessNameOffset; UINT_PTR DebugportOffset; UINT_PTR PIDOffset; ================================================ FILE: src/extradefines.h ================================================ /*#define HDESK ULONG #define HWND ULONG #define DWORD ULONG #define WORD USHORT #define BYTE UCHAR #define UINT ULONG #define FILE_DEVICE_UNKNOWN 0x00000022 #define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN*/ NTKERNELAPI NTSTATUS ObOpenObjectByName( IN POBJECT_ATTRIBUTES ObjectAttributes, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, IN OUT PACCESS_STATE PassedAccessState OPTIONAL, IN ACCESS_MASK DesiredAccess OPTIONAL, IN OUT PVOID ParseContext OPTIONAL, OUT PHANDLE Handle ); ================================================ FILE: src/extraimports.h ================================================ NTSYSAPI BOOLEAN KeAddSystemServiceTable( IN PULONG_PTR Base, IN PULONG Count OPTIONAL, IN ULONG Limit, IN PUCHAR Number, IN ULONG Index ); ================================================ FILE: src/interruptHook.c ================================================ #pragma warning( disable: 4103) #include "ntifs.h" #include #include "DBKFunc.h" #include "vmxhelper.h" #include "interruptHook.h" //this sourcefile only so no need to worry about it being modified by out of context code struct { int hooked; int dbvmInterruptEmulation; //if used, originalCS and originalEIP are ignored and the current IDT data is used to resume the interrupt, currently only for interrupt 1 and 14 WORD originalCS; ULONG_PTR originalEIP; } InterruptHook[256]; WORD inthook_getOriginalCS(unsigned char intnr) { return InterruptHook[intnr].originalCS; } ULONG_PTR inthook_getOriginalEIP(unsigned char intnr) { return InterruptHook[intnr].originalEIP; } int inthook_isHooked(unsigned char intnr) { if (InterruptHook[intnr].hooked) { //todo: add a check to see if the hook is still present. if not, return false and update the hooked value return TRUE; } else return FALSE; } int inthook_isDBVMHook(unsigned char intnr) { return InterruptHook[intnr].dbvmInterruptEmulation; } int inthook_UnhookInterrupt(unsigned char intnr) { if (InterruptHook[intnr].hooked) { //it's hooked, try to unhook DbgPrint("cpu %d : interrupt %d is hooked\n",cpunr(),intnr); if (InterruptHook[intnr].dbvmInterruptEmulation) { if (intnr==1) vmx_redirect_interrupt1(virt_differentInterrupt, 1, 0, 0); else if (intnr==3) vmx_redirect_interrupt3(virt_differentInterrupt, 3, 0, 0); else vmx_redirect_interrupt14(virt_differentInterrupt, 14, 0, 0); return TRUE; //that's all we need } //still here so not a dbvm hook, unhook the old way and hope nothing has interfered { INT_VECTOR newVector; //newVector.bUnused=0; /* newVector.gatetype=6; //interrupt gate newVector.gatesize=1; //32-bit newVector.zero=0; newVector.DPL=0; newVector.P=1; */ newVector.wHighOffset=(WORD)((DWORD)(InterruptHook[intnr].originalEIP >> 16)); newVector.wLowOffset=(WORD)InterruptHook[intnr].originalEIP; newVector.wSelector=(WORD)InterruptHook[intnr].originalCS; #ifdef AMD64 newVector.TopOffset=(InterruptHook[intnr].originalEIP >> 32); newVector.Reserved=0; #endif { IDT idt; GetIDT(&idt); newVector.bAccessFlags=idt.vector[intnr].bAccessFlags; disableInterrupts(); idt.vector[intnr]=newVector; enableInterrupts(); } DbgPrint("Restored\n"); } } return TRUE; } int inthook_HookInterrupt(unsigned char intnr, int newCS, ULONG_PTR newEIP, PJUMPBACK jumpback) { IDT idt; GetIDT(&idt); DbgPrint("inthook_HookInterrupt for cpu %d (vmxusable=%d)\n",cpunr(), vmxusable); #ifdef AMD64 DbgPrint("interrupt %d newCS=%x newEIP=%llx jumpbacklocation=%p\n",intnr, newCS, newEIP, jumpback); #else DbgPrint("interrupt %d newCS=%x newEIP=%x jumpbacklocation=%p\n",intnr, newCS, newEIP, jumpback); #endif DbgPrint("InterruptHook[%d].hooked=%d\n", intnr, InterruptHook[intnr].hooked); if (!InterruptHook[intnr].hooked) { //new hook, so save the originals InterruptHook[intnr].originalCS=idt.vector[intnr].wSelector; InterruptHook[intnr].originalEIP=idt.vector[intnr].wLowOffset+(idt.vector[intnr].wHighOffset << 16); #ifdef AMD64 InterruptHook[intnr].originalEIP|=(UINT64)((UINT64)idt.vector[intnr].TopOffset << 32); #endif } if (jumpback) { jumpback->cs=InterruptHook[intnr].originalCS; jumpback->eip=InterruptHook[intnr].originalEIP; } DbgPrint("vmxusable=%d\n", vmxusable); if (vmxusable && ((intnr==1) || (intnr==3) || (intnr==14)) ) { DbgPrint("VMX Hook path\n"); switch (intnr) { case 1: vmx_redirect_interrupt1(virt_emulateInterrupt, 0, newCS, newEIP); break; case 3: vmx_redirect_interrupt3(virt_emulateInterrupt, 0, newCS, newEIP); break; case 14: vmx_redirect_interrupt14(virt_emulateInterrupt, 0, newCS, newEIP); break; } InterruptHook[intnr].dbvmInterruptEmulation=1; } else { //old fashioned hook INT_VECTOR newVector; #ifdef AMD64 if (intnr<32) { DbgPrint("64-bit: DBVM is not loaded and a non dbvm hookable interrupt is being hooked that falls below 32\n"); return FALSE; } #endif DbgPrint("sizeof newVector=%d\n",sizeof(INT_VECTOR)); newVector.wHighOffset=(WORD)((DWORD)(newEIP >> 16)); newVector.wLowOffset=(WORD)newEIP; newVector.wSelector=(WORD)newCS; newVector.bUnused=0; newVector.bAccessFlags=idt.vector[intnr].bAccessFlags; //don't touch accessflag, the default settings are good (e.g: int3,4 and 8 have dpl=3) #ifdef AMD64 newVector.TopOffset=(newEIP >> 32); newVector.Reserved=0; #endif disableInterrupts(); //no kernelmode taskswitches please idt.vector[intnr]=newVector; enableInterrupts(); InterruptHook[intnr].dbvmInterruptEmulation=0; DbgPrint("int %d will now go to %x:%p\n",intnr, newCS, newEIP); } InterruptHook[intnr].hooked=1; return TRUE; } ================================================ FILE: src/interruptHook.h ================================================ #ifndef INTERRUPTHOOK_H #define INTERRUPTHOOK_H #include //assuming the standard interrupt hook routine is used this stackindex will be valid #ifdef AMD64 typedef enum {si_eax=0, si_ebx=1, si_ecx=2, si_edx=3, si_esi=4, si_edi=5, si_ebp=6, si_stack_esp=7, si_r8=8, si_r9=9, si_r10=10, si_r11=11, si_r12=12, si_r13=13, si_r14=14, si_r15=15, si_es=16, si_ds=17, si_stack_ss=18, si_xmm=19, si_errorcode=19+(4096/8)+(512/8), si_eip=20+(4096/8) + (512 / 8), si_cs=21+(4096/8) + (512 / 8), si_eflags=22+(4096/8) + (512 / 8), si_esp=23+(4096/8) + (512 / 8), si_ss=24+(4096/8) + (512 / 8)} stackindex; #else typedef enum {si_gs=-12, si_fs=-11, si_es=-10, si_ds=-9, si_edi=-8, si_esi=-7, si_stack_ebp=-6, si_stack_esp=-5, si_ebx=-4, si_edx=-3, si_ecx=-2, si_eax=-1, si_ebp=0, si_errorcode=1, si_eip=2, si_cs=3, si_eflags=4, si_esp=5, si_ss=6} stackindex; #endif #pragma pack(1) //allignment of 1 byte typedef struct tagINT_VECTOR { WORD wLowOffset; WORD wSelector; BYTE bUnused; BYTE bAccessFlags; /* unsigned gatetype : 3; //101=Task, 110=interrupt, 111=trap unsigned gatesize : 1; //1=32bit, 0=16bit unsigned zero : 1; unsigned DPL : 2; unsigned P : 1; */ WORD wHighOffset; #ifdef AMD64 DWORD TopOffset; DWORD Reserved; #endif } INT_VECTOR, *PINT_VECTOR; #pragma pack() #pragma pack(2) //allignemnt of 2 byte typedef struct tagIDT { WORD wLimit; PINT_VECTOR vector; } IDT, *PIDT; #pragma pack() #ifdef AMD64 typedef #pragma pack(1) //allignemnt of 1 byte struct { UINT64 eip; WORD cs; } JUMPBACK, *PJUMPBACK; #pragma pack() #else typedef #pragma pack(1) //allignemnt of 1 byte struct { DWORD eip; WORD cs; } JUMPBACK, *PJUMPBACK; #pragma pack() #endif int inthook_HookInterrupt(unsigned char intnr, int newCS, ULONG_PTR newEIP, PJUMPBACK jumpback); int inthook_UnhookInterrupt(unsigned char intnr); int inthook_isHooked(unsigned char intnr); int inthook_isDBVMHook(unsigned char intnr); ULONG_PTR inthook_getOriginalEIP(unsigned char intnr); WORD inthook_getOriginalCS(unsigned char intnr); #endif ================================================ FILE: src/memscan.c ================================================ #pragma warning( disable: 4100 4103 4146 4213) #include "ntifs.h" #include #ifdef CETC #include "tdiwrapper.h" #include "kfiles.h" #endif #include "memscan.h" #include "DBKFunc.h" #include "vmxhelper.h" #include "vmxoffload.h" //PTE structs #include "noexceptions.h" /*#include "deepkernel.h" */ UINT_PTR KnownPageTableBase = 0; void VirtualAddressToIndexes(QWORD address, int *pml4index, int *pagedirptrindex, int *pagedirindex, int *pagetableindex) /* * Returns the indexes for the given address (ia32e) */ { if (PTESize == 8) { *pml4index = (address >> 39) & 0x1ff; *pagedirptrindex = (address >> 30) & 0x1ff; *pagedirindex = (address >> 21) & 0x1ff; *pagetableindex = (address >> 12) & 0x1ff; } else { *pml4index = 0; *pagedirptrindex = 0; *pagedirindex = (address >> 22) & 0x3ff; *pagetableindex = (address >> 12) & 0x3ff; } } QWORD IndexesToVirtualAddress(int pml4index, int pagedirptrindex, int pagedirindex, int pagetableindex, int offset) { QWORD r; #ifndef AMD64 if (PTESize == 8) { #endif r = ((QWORD)pml4index & 0x1ff) << 39; if (pml4index >= 256) r = r | 0xFFFF000000000000ULL; r |= ((QWORD)pagedirptrindex & 0x1ff) << 30; r |= ((QWORD)pagedirindex & 0x1ff) << 21; r |= ((QWORD)pagetableindex & 0x1ff) << 12; r |= offset & 0xfff; #ifndef AMD64 } else { r |= (pagedirindex & 0x3ff) << 22; r |= (pagetableindex & 0x3ff) << 12; r |= offset & 0xfff; } #endif return r; } #ifndef AMD64 void VirtualAddressToPageEntries32(DWORD address, PPDE *pagedirentry, PPTE *pagetableentry) { DWORD PTE = address; UINT_PTR PTB = (DWORD)getPageTableBase(); PTE = PTE >> 12; PTE = PTE * 4; PTE = PTE + PTB; *pagetableentry = (PPTE)PTE; UINT_PTR PDE = PTE; PDE = PDE & 0x0000ffffffffffffULL; PDE = PDE >> 12; PDE = PDE * 4; PDE = PDE + PTB; *pagedirentry = (PPDE)PDE; } #endif void VirtualAddressToPageEntries64(QWORD address, PPDPTE_PAE *pml4entry, PPDPTE_PAE *pagedirpointerentry, PPDE_PAE *pagedirentry, PPTE_PAE *pagetableentry) { QWORD PTE = address; QWORD PTB=getPageTableBase(); PTE = PTE & 0x0000ffffffffffffULL; PTE = PTE >> 12; PTE = PTE * 8; PTE = PTE + PTB; *pagetableentry = (PPTE_PAE)(UINT_PTR)PTE; //*pagetableentry = (PPTE_PAE)((((QWORD)address & 0x0000ffffffffffffull) >> 12)*8) + 0xfffff80000000000ULL; QWORD PDE = PTE; PDE = PDE & 0x0000ffffffffffffULL; PDE = PDE >> 12; PDE = PDE * 8; PDE = PDE + PTB; *pagedirentry = (PPDE_PAE)(UINT_PTR)PDE; //*pagedirentry = (PPDE_PAE)((((QWORD)*pagetableentry & 0x0000ffffffffffffull )>> 12)*8) + 0xfffff80000000000ULL; QWORD PDPTR = PDE; PDPTR = PDPTR & 0x0000ffffffffffffULL; PDPTR = PDPTR >> 12; PDPTR = PDPTR * 8; PDPTR = PDPTR + PTB; *pagedirpointerentry = (PPDPTE_PAE)(UINT_PTR)PDPTR; //*pagedirpointerentry = (PPDPTE_PAE)((((QWORD)*pagedirentry & 0x0000ffffffffffffull )>> 12)*8) + 0xfffff80000000000ULL; #ifdef AMD64 QWORD PML4 = PDPTR; PML4 = PML4 & 0x0000ffffffffffffULL; PML4 = PML4 >> 12; PML4 = PML4 * 8; PML4 = PML4 + PTB; *pml4entry = (PPDPTE_PAE)PML4; #else *pml4entry = NULL; #endif } BOOLEAN IsAddressSafe(UINT_PTR StartAddress) { #ifdef AMD64 //cannonical check. Bits 48 to 63 must match bit 47 UINT_PTR toppart=(StartAddress >> 47); if (toppart & 1) { //toppart must be 0x1ffff if (toppart != 0x1ffff) return FALSE; } else { //toppart must be 0 if (toppart != 0) return FALSE; } #endif //return TRUE; if (loadedbydbvm) { BYTE x=0; UINT_PTR lasterror; disableInterrupts(); vmx_disable_dataPageFaults(); x=*(volatile BYTE *)StartAddress; vmx_enable_dataPageFaults(); lasterror=vmx_getLastSkippedPageFault(); enableInterrupts(); DbgPrint("IsAddressSafe dbvm-mode: lastError=%p\n", lasterror); if (lasterror) return FALSE; } { #ifdef AMD64 UINT_PTR kernelbase=0x7fffffffffffffffULL; if (StartAddressP==0) && (x->A2==0)) { //Not present or paged, and since paging in this area isn't such a smart thing to do just skip it //perhaps this is only for the 4 mb pages, but those should never be paged out, so it should be 1 //bah, I've got no idea what this is used for return FALSE; } if (x->PS==1) { //This is a 4 MB page (no pte list) //so, (startaddress/0x400000*0x400000) till ((startaddress/0x400000*0x400000)+(0x400000-1) ) ) is specified by this page } else //if it's not a 4 MB page then check the PTE { //still here so the page table directory agreed that it is a usable page table entry x=(PVOID)PTE; if ((x->P==0) && (x->A2==0)) return FALSE; //see for explenation the part of the PDE } return TRUE; } #endif } } UINT_PTR getPEThread(UINT_PTR threadid) { //UINT_PTR *threadid; PETHREAD selectedthread; UINT_PTR result=0; if (PsLookupThreadByThreadId((PVOID)(UINT_PTR)threadid,&selectedthread)==STATUS_SUCCESS) { result=(UINT_PTR)selectedthread; ObDereferenceObject(selectedthread); } return result; } BOOLEAN WriteProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer) { PEPROCESS selectedprocess=PEProcess; KAPC_STATE apc_state; NTSTATUS ntStatus=STATUS_UNSUCCESSFUL; if (selectedprocess==NULL) { //DbgPrint("WriteProcessMemory:Getting PEPROCESS\n"); if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess))) return FALSE; //couldn't get the PID //DbgPrint("Retrieved peprocess"); } //selectedprocess now holds a valid peprocess value __try { RtlZeroMemory(&apc_state,sizeof(apc_state)); KeAttachProcess((PEPROCESS)selectedprocess); __try { char* target; char* source; unsigned int i; //DbgPrint("Checking safety of memory\n"); if ((IsAddressSafe((UINT_PTR)Address)) && (IsAddressSafe((UINT_PTR)Address+Size-1))) { //still here, then I gues it's safe to read. (But I can't be 100% sure though, it's still the users problem if he accesses memory that doesn't exist) BOOL disabledWP = FALSE; target=Address; source=Buffer; if ((loadedbydbvm) || (KernelWritesIgnoreWP)) //add a extra security around it as the PF will not be handled { disableInterrupts(); if (loadedbydbvm) vmx_disable_dataPageFaults(); if (KernelWritesIgnoreWP) { DbgPrint("Disabling CR0.WP"); setCR0(getCR0() & (~(1 << 16))); //disable the WP bit disabledWP = TRUE; } } if ((!loadedbydbvm) && ((KernelWritesIgnoreWP) || ((UINT_PTR)target >= 0x8000000000000000ULL))) { i = NoExceptions_CopyMemory(target, source, Size); if (i != (int)Size) ntStatus = STATUS_UNSUCCESSFUL; else ntStatus = STATUS_SUCCESS; } else { RtlCopyMemory(target, source, Size); ntStatus = STATUS_SUCCESS; } if ((loadedbydbvm) || (disabledWP)) { UINT_PTR lastError=0; if (disabledWP) { setCR0(getCR0() | (1 << 16)); DbgPrint("Enabled CR0.WP"); } if (loadedbydbvm) { lastError = vmx_getLastSkippedPageFault(); vmx_enable_dataPageFaults(); } enableInterrupts(); DbgPrint("lastError=%p\n", lastError); if (lastError) ntStatus=STATUS_UNSUCCESSFUL; } } } __finally { KeDetachProcess(); } } __except(1) { //DbgPrint("Error while writing\n"); ntStatus = STATUS_UNSUCCESSFUL; } if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference ObDereferenceObject(selectedprocess); return NT_SUCCESS(ntStatus); } BOOLEAN ReadProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer) { PEPROCESS selectedprocess=PEProcess; //KAPC_STATE apc_state; NTSTATUS ntStatus=STATUS_UNSUCCESSFUL; if (PEProcess==NULL) { if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess))) return FALSE; //couldn't get the PID } //selectedprocess now holds a valid peprocess value __try { KeAttachProcess((PEPROCESS)selectedprocess); __try { char* target; char* source; int i; if ((IsAddressSafe((UINT_PTR)Address)) && (IsAddressSafe((UINT_PTR)Address+Size-1))) { target=Buffer; source=Address; if (loadedbydbvm) //add a extra security around it { disableInterrupts(); vmx_disable_dataPageFaults(); } if ((loadedbydbvm) || ((UINT_PTR)source < 0x8000000000000000ULL)) { RtlCopyMemory(target, source, Size); ntStatus = STATUS_SUCCESS; } else { i=NoExceptions_CopyMemory(target, source, Size); if (i != (int)Size) ntStatus = STATUS_UNSUCCESSFUL; else ntStatus = STATUS_SUCCESS; } if (loadedbydbvm) { UINT_PTR lastError; lastError=vmx_getLastSkippedPageFault(); vmx_enable_dataPageFaults(); enableInterrupts(); DbgPrint("lastError=%p\n", lastError); if (lastError) ntStatus=STATUS_UNSUCCESSFUL; } } } __finally { KeDetachProcess(); } } __except(1) { //DbgPrint("Error while reading: ReadProcessMemory(%x,%p, %p, %d, %p\n", PID, PEProcess, Address, Size, Buffer); ntStatus = STATUS_UNSUCCESSFUL; } if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference ObDereferenceObject(selectedprocess); return NT_SUCCESS(ntStatus); } UINT64 maxPhysAddress = 0; UINT64 getMaxPhysAddress(void) { if (maxPhysAddress==0) { int physicalbits; DWORD r[4]; __cpuid(r, 0x80000008); //get max physical address physicalbits = r[0] & 0xff; maxPhysAddress = 0xFFFFFFFFFFFFFFFFULL; maxPhysAddress = maxPhysAddress >> physicalbits; //if physicalbits==36 then maxPhysAddress=0x000000000fffffff maxPhysAddress = ~(maxPhysAddress << physicalbits); //<< 36 = 0xfffffff000000000 . after inverse : 0x0000000fffffffff } return maxPhysAddress; } NTSTATUS ReadPhysicalMemory(char *startaddress, UINT_PTR bytestoread, void *output) { HANDLE physmem; UNICODE_STRING physmemString; OBJECT_ATTRIBUTES attributes; WCHAR physmemName[] = L"\\device\\physicalmemory"; UCHAR* memoryview; NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; PMDL outputMDL; DbgPrint("ReadPhysicalMemory(%p, %d, %p)", startaddress, bytestoread, output); if (((UINT64)startaddress > getMaxPhysAddress()) || ((UINT64)startaddress + bytestoread > getMaxPhysAddress())) { DbgPrint("Invalid physical address\n"); return ntStatus; } outputMDL = IoAllocateMdl(output, (ULONG)bytestoread, FALSE, FALSE, NULL); __try { MmProbeAndLockPages(outputMDL, KernelMode, IoWriteAccess); } __except (1) { IoFreeMdl(outputMDL); return STATUS_UNSUCCESSFUL; } __try { RtlInitUnicodeString( &physmemString, physmemName ); InitializeObjectAttributes( &attributes, &physmemString, OBJ_CASE_INSENSITIVE, NULL, NULL ); ntStatus=ZwOpenSection( &physmem, SECTION_ALL_ACCESS, &attributes ); if (ntStatus==STATUS_SUCCESS) { //hey look, it didn't kill it SIZE_T length; PHYSICAL_ADDRESS viewBase; UINT_PTR offset; UINT_PTR toread; viewBase.QuadPart = (ULONGLONG)(startaddress); length=0x2000;//pinp->bytestoread; //in case of a overlapping region toread=bytestoread; memoryview=NULL; DbgPrint("ReadPhysicalMemory:viewBase.QuadPart=%x", viewBase.QuadPart); ntStatus=ZwMapViewOfSection( physmem, //sectionhandle NtCurrentProcess(), //processhandle (should be -1) &memoryview, //BaseAddress 0L, //ZeroBits length, //CommitSize &viewBase, //SectionOffset &length, //ViewSize ViewShare, 0, PAGE_READWRITE); if ((ntStatus == STATUS_SUCCESS) && (memoryview!=NULL)) { if (toread > length) toread = length; if (toread) { __try { offset = (UINT_PTR)(startaddress)-(UINT_PTR)viewBase.QuadPart; if (offset + toread > length) { DbgPrint("Too small map"); } else { RtlCopyMemory(output, &memoryview[offset], toread); } ZwUnmapViewOfSection(NtCurrentProcess(), memoryview); } __except (1) { DbgPrint("Failure mapping physical memory"); } } } else { DbgPrint("ReadPhysicalMemory error:ntStatus=%x", ntStatus); } ZwClose(physmem); }; } __except(1) { DbgPrint("Error while reading physical memory\n"); } MmUnlockPages(outputMDL); IoFreeMdl(outputMDL); return ntStatus; } UINT_PTR SignExtend(UINT_PTR a) { #ifdef AMD64 if ((a >> 47)==1) return a | 0xFFFF000000000000ULL; //add sign extended bits else return a; #else return a; #endif } UINT_PTR getPageTableBase() { #if (NTDDI_VERSION >= NTDDI_VISTA) if (KnownPageTableBase==0) { RTL_OSVERSIONINFOW v; v.dwOSVersionInfoSize = sizeof(v); if (RtlGetVersion(&v)) { DbgPrint("RtlGetVersion failed"); return 0; } if ((v.dwMajorVersion >= 10) && (v.dwBuildNumber >= 14393)) { PHYSICAL_ADDRESS a; PVOID r; a.QuadPart = getCR3() & 0xFFFFFFFFFFFFF000ULL; r = MmGetVirtualForPhysical(a); //if this stops working, look for CR3 in the pml4 table KnownPageTableBase = ((UINT_PTR)r) & 0xFFFFFF8000000000ULL; MAX_PTE_POS = (UINT_PTR)((QWORD)KnownPageTableBase + 0x7FFFFFFFF8ULL); MAX_PDE_POS = (UINT_PTR)((QWORD)KnownPageTableBase + 0x7B7FFFFFF8ULL); } else KnownPageTableBase=PAGETABLEBASE; DbgPrint("PageTableBase at %p\n", KnownPageTableBase); } return KnownPageTableBase; #else return PAGETABLEBASE; #endif } typedef void PRESENTPAGECALLBACK(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry); BOOL walkPagingLayout(PEPROCESS PEProcess, UINT_PTR MaxAddress, PRESENTPAGECALLBACK OnPresentPage) { #ifdef AMD64 UINT_PTR pagebase = getPageTableBase(); #else UINT_PTR pagebase = PAGETABLEBASE; #endif if (pagebase == 0) return FALSE; if (OnPresentPage == NULL) return FALSE; __try { KeAttachProcess((PEPROCESS)PEProcess); __try { UINT_PTR currentAddress = 0; //start from address 0 UINT_PTR lastAddress = 0; struct PTEStruct *PPTE, *PPDE, *PPDPE, *PPML4E; while ((currentAddress < MaxAddress) && (lastAddress<=currentAddress) ) { //DbgPrint("currentAddress=%p\n", currentAddress); lastAddress = currentAddress; (UINT_PTR)PPTE = (UINT_PTR)(((currentAddress & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase); (UINT_PTR)PPDE = (UINT_PTR)((((UINT_PTR)PPTE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase; (UINT_PTR)PPDPE = (UINT_PTR)((((UINT_PTR)PPDE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase; (UINT_PTR)PPML4E = (UINT_PTR)((((UINT_PTR)PPDPE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase; if (PTESize == 8) (UINT_PTR)PPDPE = ((((UINT_PTR)PPDE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase; else (UINT_PTR)PPDPE = 0; #ifdef AMD64 (UINT_PTR)PPML4E = ((((UINT_PTR)PPDPE) & 0xFFFFFFFFFFFFULL) >> 12) *PTESize + pagebase; #else (UINT_PTR)PPML4E = 0; #endif #ifdef AMD64 if ((PPML4E) && (PPML4E->P == 0)) { currentAddress &= 0xffffff8000000000ULL; currentAddress += 0x8000000000ULL; continue; } #endif if ((PPDPE) && (PPDPE->P == 0)) { currentAddress &= 0xffffffffc0000000ULL; currentAddress += 0x40000000; continue; } if (PPDPE->PS) //some systems have 1GB page support. But not sure windows uses these { DbgPrint("----->%llx is a 1GB range", currentAddress); OnPresentPage(currentAddress, currentAddress + 0x40000000 - 1, PPDPE); currentAddress += 0x40000000; continue; } if (PPDE->P == 0) { if (PAGE_SIZE_LARGE == 0x200000) currentAddress &= 0xffffffffffe00000ULL; else currentAddress &= 0xffffffffffc00000ULL; currentAddress += PAGE_SIZE_LARGE; continue; } if (PPDE->PS) { OnPresentPage(currentAddress, currentAddress + PAGE_SIZE_LARGE-1, PPDE); currentAddress += PAGE_SIZE_LARGE; continue; } if (PPTE->P == 0) { currentAddress &= 0xfffffffffffff000ULL; currentAddress += 0x1000; continue; } OnPresentPage(currentAddress, currentAddress + 0xfff, PPTE); currentAddress += 0x1000; } } __finally { KeDetachProcess(); } } __except (1) { DbgPrint("Excepion while walking the paging layout\n"); return FALSE; } return TRUE; } PPENTRY AccessedList = NULL; int AccessedListSize; void CleanAccessedList() { PPENTRY e = AccessedList; PPENTRY previous; //DbgPrint("Cleaning list"); while (e) { previous = e; e = e->Next; ExFreePool(previous); } AccessedList = NULL; AccessedListSize = 0; } void StoreAccessedRanges(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry) { if (pageEntry->A) { if ((AccessedList) && (AccessedList->Range.EndAddress == StartAddress - 1)) //group AccessedList->Range.EndAddress = EndAddress; else { //insert PPENTRY e; e = ExAllocatePool(PagedPool, sizeof(PENTRY)); e->Range.StartAddress = StartAddress; e->Range.EndAddress = EndAddress; e->Next = AccessedList; AccessedList = e; AccessedListSize++; } } } int enumAllAccessedPages(PEPROCESS PEProcess) { #ifdef AMD64 UINT_PTR MaxAddress = 0x80000000000ULL; #else UINT_PTR MaxAddress = 0x80000000; #endif CleanAccessedList(); if (walkPagingLayout(PEProcess, MaxAddress, StoreAccessedRanges)) { //DbgPrint("AccessedListSize=%d\n", AccessedListSize); return AccessedListSize*sizeof(PRANGE); } else return 0; } int getAccessedPageList(PPRANGE List, int ListSizeInBytes) { PPENTRY e = AccessedList; int maxcount = ListSizeInBytes / sizeof(PRANGE); int i = 0; // DbgPrint("getAccessedPageList\n"); while (e) { if (i >= maxcount) { //DbgPrint("%d>=%d", i, maxcount); break; } //DbgPrint("i=%d (%p -> %p)\n", i, e->Range.StartAddress, e->Range.EndAddress); List[i] = e->Range; e = e->Next; i++; } CleanAccessedList(); return i*sizeof(PRANGE); } void MarkPageAsNotAccessed(UINT_PTR StartAddress, UINT_PTR EndAddress, struct PTEStruct *pageEntry) { pageEntry->A = 0; } NTSTATUS markAllPagesAsNeverAccessed(PEPROCESS PEProcess) { #ifdef AMD64 UINT_PTR MaxAddress = 0x80000000000ULL; #else UINT_PTR MaxAddress = 0x80000000; #endif if (walkPagingLayout(PEProcess, MaxAddress, MarkPageAsNotAccessed)) return STATUS_SUCCESS; else return STATUS_UNSUCCESSFUL; } UINT_PTR FindFirstDifferentAddress(QWORD address, DWORD *protection) //scans the pagetable system for the first fully present entry //returns 0 when there is no other present address { int i,ri; int pml4index, pagedirpointerindex, pagedirindex, pagetableindex; VirtualAddressToIndexes(address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex); #ifndef AMD64 if (PTESize == 8) #endif { PPDPTE_PAE pml4entry, pagedirpointerentry; PPDE_PAE pagedirentry; PPTE_PAE pagetableentry; *protection = 0; //scan till present or end of memory while (1) { VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry); VirtualAddressToIndexes(address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex); if (*protection == 0) { //get the initial protection if ((((pml4entry) && (pml4entry->P)) || (pml4entry == NULL)) && (pagedirpointerentry->P) && (pagedirentry->P) && ((pagedirentry->PS) || (pagetableentry->P))) { if (pagedirentry->PS) { if (pagedirentry->RW) *protection = PAGE_EXECUTE_READWRITE; else *protection = PAGE_EXECUTE_READ; } else { if (pagetableentry->RW) *protection = PAGE_EXECUTE_READWRITE; else *protection = PAGE_EXECUTE_READ; } } else *protection = PAGE_NOACCESS; } #ifdef AMD64 if (pml4entry->P == 0) { //unreadable at PML4 level if (*protection != PAGE_NOACCESS) return address; pagedirpointerindex = 0; pagedirindex = 0; pagetableindex = 0; for (ri=1, i = pml4index + 1; i < 512; i++, ri++) { if ((ri >= 512) || (ri < 0)) DbgBreakPointWithStatus(ri); if (pml4entry[ri].P) { //found a valid PML4 entry //scan for a valid pagedirpointerentry pml4index = i; address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry); break; } } } if (pml4entry->P == 0) { //nothing readable found return 0; } #endif if (pagedirpointerentry->P == 0) { if (*protection != PAGE_NOACCESS) return (UINT_PTR)address; pagedirindex = 0; pagetableindex = 0; for (ri=1, i = pagedirpointerindex + 1; i < 512; i++, ri++) { if ((ri >= 512) || (ri < 0)) DbgBreakPointWithStatus(ri); if (pagedirpointerentry[ri].P) { //found a valid pagedirpointerentry pagedirpointerindex = i; address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry); break; } } if (pagedirpointerentry->P == 0) { #ifdef AMD64 pagedirpointerindex = 0; pml4index++; if (pml4index >= 512) return 0; //end of the list address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); continue; //try the next PML4 entry #else //nothing readable found return 0; #endif } } if (pagedirentry->P == 0) { if (*protection != PAGE_NOACCESS) return (UINT_PTR)address; pagetableindex = 0; for (ri=1, i = pagedirindex + 1; i < 512; i++, ri++) { if ((ri >= 512) || (ri < 0)) DbgBreakPointWithStatus(ri); if (pagedirentry[ri].P) { //found a valid pagedirentry pagedirindex = i; address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry); break; } } if (pagedirentry->P == 0) { pagedirindex = 0; pagedirpointerindex++; if (pagedirpointerindex >= 512) { pagedirpointerindex = 0; pml4index++; if (pml4index >= 512) return 0; //end of the list } address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); continue; //try the next pagedirpointer entry } } if (pagedirentry->PS) { if (*protection == PAGE_NOACCESS) return (UINT_PTR)address; if ((pagedirentry->RW) && (*protection != PAGE_EXECUTE_READWRITE)) return (UINT_PTR)address; //go to the next one pagedirindex++; if (pagedirindex >= 512) { pagedirindex = 0; pagedirpointerindex++; if (pagedirpointerindex >= 512) { pagedirpointerindex = 0; pml4index++; if (pml4index >= 512) return 0; //end of the list } } address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); continue; } if (pagetableentry->P == 0) { if (*protection != PAGE_NOACCESS) return (UINT_PTR)address; for (ri=1, i = pagetableindex + 1; i < 512; i++, ri++) { if ((ri >= 512) || (ri < 0)) DbgBreakPointWithStatus(ri); if (pagetableentry[ri].P) { //found a valid pagetable entry pagetableindex = i; address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); VirtualAddressToPageEntries64(address, &pml4entry, &pagedirpointerentry, &pagedirentry, &pagetableentry); break; } } if (pagetableentry->P == 0) { pagetableindex = 0; pagedirindex++; if (pagedirindex >= 512) { pagedirindex = 0; pagedirpointerindex++; if (pagedirpointerindex >= 512) { pagedirpointerindex = 0; pml4index++; if (pml4index >= 512) return 0; //end of the list } } address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); continue; //try the next pagedir entry } } //still here so a present page if (*protection == PAGE_NOACCESS) return (UINT_PTR)address; if ((pagetableentry->RW) && (*protection != PAGE_EXECUTE_READWRITE)) return (UINT_PTR)address; //next entry pagetableindex++; if (pagetableindex >= 512) { pagetableindex = 0; pagedirindex++; if (pagedirindex >= 512) { pagedirindex = 0; pagedirpointerindex++; if (pagedirpointerindex >= 512) { pagedirpointerindex = 0; pml4index++; if (pml4index >= 512) return 0; } } } address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); } } #ifndef AMD64 else { PPDE pagedirentry; PPTE pagetableentry; while (1) { VirtualAddressToPageEntries32((UINT_PTR)address, &pagedirentry, &pagetableentry); VirtualAddressToIndexes((UINT_PTR)address, &pml4index, &pagedirpointerindex, &pagedirindex, &pagetableindex); if (*protection == 0) { //get the initial protection if ((pagedirentry->P) && ((pagedirentry->PS) || (pagetableentry->P))) { if (pagedirentry->PS) { if (pagedirentry->RW) *protection = PAGE_EXECUTE_READWRITE; else *protection = PAGE_EXECUTE_READ; } else { if (pagetableentry->RW) *protection = PAGE_EXECUTE_READWRITE; else *protection = PAGE_EXECUTE_READ; } } else *protection = PAGE_NOACCESS; } if (pagedirentry->P == 0) { if (*protection != PAGE_NOACCESS) return (UINT_PTR)address; pagetableindex = 0; for (ri=1, i = pagedirindex + 1; i < 1024; i++, ri++) { if (pagedirentry[ri].P) { //found a valid pagedirentry pagedirindex = i; address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0); VirtualAddressToPageEntries32((UINT_PTR)address, &pagedirentry, &pagetableentry); break; } } if (pagedirentry->P == 0) return 0; //end of the list } if (pagedirentry->PS) { if (*protection == PAGE_NOACCESS) return (UINT_PTR)address; if ((pagedirentry->RW) && (*protection != PAGE_EXECUTE_READWRITE)) return (UINT_PTR)address; //go to the next one pagedirindex++; if (pagedirindex >= 1024) return 0; address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0); } if (pagetableentry->P == 0) { if (*protection != PAGE_NOACCESS) return (UINT_PTR)address; for (ri=1, i = pagetableindex + 1; i < 1024; i++, ri++) { if (pagetableentry[ri].P) { //found a valid pagetable entry pagetableindex = i; address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0); break; } } if (pagetableentry->P == 0) { pagetableindex = 0; pagedirindex++; if (pagedirindex >= 1024) return 0; address = IndexesToVirtualAddress(0, 0, pagedirindex, pagetableindex, 0); continue; //try the next pagedir entry } } //still here so a present page if (*protection == PAGE_NOACCESS) return (UINT_PTR)address; if ((pagetableentry->RW) && (*protection != PAGE_EXECUTE_READWRITE)) return (UINT_PTR)address; //next entry pagetableindex++; if (pagetableindex >= 1024) { pagetableindex = 0; pagedirindex++; if (pagedirindex >= 1024) return 0; } address = IndexesToVirtualAddress(pml4index, pagedirpointerindex, pagedirindex, pagetableindex, 0); } } #endif } BOOLEAN GetMemoryRegionData(DWORD PID,PEPROCESS PEProcess, PVOID mempointer,ULONG *regiontype, UINT_PTR *memorysize,UINT_PTR *baseaddress) { UINT_PTR CurrentAddress; KAPC_STATE apc_state; PEPROCESS selectedprocess=PEProcess; if (getPageTableBase() == 0) { DbgPrint("GetMemoryRegionData failed because pagebase == 0"); return FALSE; } if (PEProcess==NULL) { if (!NT_SUCCESS(PsLookupProcessByProcessId((PVOID)(UINT_PTR)PID,&selectedprocess))) return FALSE; //couldn't get the PID } *baseaddress=(UINT_PTR)mempointer & (UINT_PTR)(~0xfff); *memorysize=0; *regiontype=0; //switch context to the target process RtlZeroMemory(&apc_state,sizeof(apc_state)); __try { KeAttachProcess((PEPROCESS)selectedprocess); __try { CurrentAddress=FindFirstDifferentAddress(*baseaddress, regiontype); *memorysize = CurrentAddress-*baseaddress; } __finally { KeDetachProcess(); if (PEProcess==NULL) //no valid peprocess was given so I made a reference, so lets also dereference ObDereferenceObject(selectedprocess); } } __except(1) { DbgPrint("Exception in GetMemoryRegionData\n"); DbgPrint("mempointer=%p",mempointer); } return 0; } ================================================ FILE: src/memscan.h ================================================ #include #ifdef AMD64 #define PAGETABLEBASE 0xfffff68000000000ULL //win10 1607 it's random #else #define PAGETABLEBASE 0xc0000000 #endif typedef struct _ADDRESSENTRY { DWORD Address; BYTE size; BOOLEAN frozen; PVOID frozendata; } ADDRESSENTRY; typedef struct _MEMREGION //only holds regions that are allowed { DWORD BaseAddress; DWORD Size; } MEMREGION; typedef struct _MEMSCANOPTIONS { BYTE ShowAsSigned; //obsolete (clientside now) BYTE BinariesAsDecimal; //obsolete (clientside now) WORD max; DWORD buffersize; BYTE skip_page_no_cache; //hmmmm BYTE UseDebugRegs; BYTE UseDBKQueryMemoryRegion; //always true BYTE UseDBKReadWriteMemory; //always true BYTE UseDBKOpenProcess; //always true } MEMSCANOPTIONS; MEMSCANOPTIONS MemscanOptions; typedef struct _SCANDATA { PEPROCESS process; DWORD Start; DWORD Stop; BYTE Vartype; BYTE Scantype; BYTE ScanOptions; BYTE scanvaluelength; char *scanvalue; BOOLEAN scanning; BOOLEAN ThreadActive; } SCANDATA; SCANDATA CurrentScan; typedef struct { UINT64 StartAddress; UINT64 EndAddress; } PRANGE, *PPRANGE; typedef struct { PRANGE Range; void *Next; } PENTRY, *PPENTRY; #ifdef CETC BOOLEAN FirstScan(PEPROCESS ActivePEPROCESS, DWORD start,DWORD stop,BYTE vartype,BYTE scantype,BYTE scanvaluesize,char *scanvalue,BYTE ScanOptions); #endif NTSTATUS ReadPhysicalMemory(char *startaddress, UINT_PTR bytestoread, void *output); BOOLEAN ReadProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer); BOOLEAN WriteProcessMemory(DWORD PID,PEPROCESS PEProcess,PVOID Address,DWORD Size, PVOID Buffer); BOOLEAN IsAddressSafe(UINT_PTR StartAddress); BOOLEAN GetMemoryRegionData(DWORD PID,PEPROCESS PEProcess, PVOID mempointer,ULONG *regiontype, UINT_PTR *memorysize,UINT_PTR *baseaddress); NTSTATUS markAllPagesAsNeverAccessed(PEPROCESS PEProcess); int enumAllAccessedPages(PEPROCESS PEProcess); int getAccessedPageList(PPRANGE List, int ListSizeInBytes); UINT_PTR getPageTableBase(); UINT_PTR getPEThread(UINT_PTR threadid); ADDRESSENTRY *AddressList; unsigned int AddressListSize; unsigned int AddressListEntries; KSPIN_LOCK AddressListSpinlock; PVOID FrozenData; //holds the buffer of all frozen data records int FrozenDataSize; LARGE_INTEGER FreezeInterval; HANDLE addressfile; HANDLE valuefile; BOOLEAN HiddenDriver; //scanoptions #define SO_FASTSCAN (0x1) #define SO_HEXADECIMAL (0x2) #define SO_READONLY (0x4) #define SO_FINDONLYONE (0x8) #define SO_ISBINARY (0x10) #define SO_UNICODE (0x20) //scantype #define ST_Exact_value 0 #define ST_Increased_value 1 #define ST_Increased_value_by 2 #define ST_Decreased_value 3 #define ST_Decreased_value_by 4 #define ST_Changed_value 5 #define ST_Unchanged_value 6 #define ST_Advanced_Scan 7 #define ST_String_Scan 8 #define ST_SmallerThan 9 #define ST_BiggerThan 10 #define ST_Userdefined 11 //scanerrors #define SE_IncorrectType -1 #define SE_NotSupported -2 #define SE_NoMemoryFound -3 ================================================ FILE: src/noexceptions.c ================================================ #include "noexceptions.h" int MaxCPUCount; PCPUSTATE cpustate = NULL; #ifdef AMD64 extern void NoException14(void); //declared in debuggera.asm extern int ExceptionlessCopy_Internal(PVOID destination, PVOID source, int size); #else extern void __cdecl NoException14(void); //declared in debuggera.asm extern int __cdecl ExceptionlessCopy_Internal(PVOID destination, PVOID source, int size); #endif #if (NTDDI_VERSION < NTDDI_VISTA) int KeQueryActiveProcessorCount(PVOID x) { int cpucount=0; KAFFINITY cpus = KeQueryActiveProcessors(); while (cpus) { if (cpus % 2) cpucount++; cpus = cpus / 2; } return cpucount; } #endif BOOL NoExceptions_Enter() { KIRQL old; int i; int cpunr; //DbgPrint("NoExceptions_Enter"); __try { if (cpustate == NULL) { //initialize the list MaxCPUCount = (int)KeQueryActiveProcessorCount(NULL); cpustate = ExAllocatePool(NonPagedPool, MaxCPUCount*sizeof(CPUSTATE)); if (cpustate) { RtlZeroMemory(cpustate, MaxCPUCount*sizeof(CPUSTATE)); for (i = 0; i < MaxCPUCount; i++) { cpustate[i].NoExceptionVectorList = ExAllocatePool(NonPagedPool, 256 * sizeof(INT_VECTOR)); if (cpustate[i].NoExceptionVectorList) { RtlZeroMemory(cpustate[i].NoExceptionVectorList, 256 * sizeof(INT_VECTOR)); } else { //alloc failed, cleanup and quit int j; for (j = i - 1; j >= 0; j--) ExFreePool(cpustate[j].NoExceptionVectorList); cpustate = NULL; return FALSE; } } } else return FALSE; } //DbgPrint("cpustate setup here"); KeRaiseIrql(HIGH_LEVEL, &old); cpunr = KeGetCurrentProcessorNumber(); cpustate[cpunr].entryIRQL = old; if (cpustate[cpunr].OriginalIDT.wLimit == 0) { //initialize this UINT_PTR newAddress; __sidt(&cpustate[cpunr].OriginalIDT); RtlCopyMemory(cpustate[cpunr].NoExceptionVectorList, cpustate[cpunr].OriginalIDT.vector, cpustate[cpunr].OriginalIDT.wLimit + 1); //DbgPrint("idt. Limit=%d Vector=%p", (int)cpustate[cpunr].OriginalIDT.wLimit, cpustate[cpunr].OriginalIDT.vector); //hook cpustate[cpunr].NoExceptionVectorList[0-15] //DbgPrint("") newAddress = (UINT_PTR)NoException14; cpustate[cpunr].NoExceptionVectorList[14].wHighOffset = (WORD)((DWORD)(newAddress >> 16)); cpustate[cpunr].NoExceptionVectorList[14].wLowOffset = (WORD)newAddress; #ifdef AMD64 cpustate[cpunr].NoExceptionVectorList[14].TopOffset = (newAddress >> 32); cpustate[cpunr].NoExceptionVectorList[14].Reserved = 0; #endif /* newVector.wHighOffset=(WORD)((DWORD)(newEIP >> 16)); newVector.wLowOffset=(WORD)newEIP; newVector.wSelector=(WORD)newCS; newVector.bUnused=0; newVector.bAccessFlags=idt.vector[intnr].bAccessFlags; //don't touch accessflag, the default settings are good (e.g: int3,4 and 8 have dpl=3) #ifdef AMD64 newVector.TopOffset=(newEIP >> 32); newVector.Reserved=0; #endif */ cpustate[cpunr].ModdedIDT = cpustate[cpunr].OriginalIDT; cpustate[cpunr].ModdedIDT.vector = cpustate[cpunr].NoExceptionVectorList; }; #ifdef AMD64 _disable(); #else __asm{ cli } #endif __lidt(&cpustate[cpunr].ModdedIDT); return TRUE; } __except (1) { DbgPrint("Exception during NoExceptions_Enter. Figures"); } //KeGetCurrentProcessorNumber() //hadError = FALSE; return FALSE; } int NoExceptions_CopyMemory(PVOID Destination, PVOID Source, int size) { BOOL EnteredNoExceptions = FALSE; int r; if (KeGetCurrentIrql() <= DISPATCH_LEVEL) { //DbgPrint("calling NoExceptions_Enter"); EnteredNoExceptions = NoExceptions_Enter(); if (EnteredNoExceptions == FALSE) return 0; } r = ExceptionlessCopy_Internal(Destination, Source, size); if (EnteredNoExceptions) { //DbgPrint("calling NoExceptions_Leave"); NoExceptions_Leave(); } return r; } void NoExceptions_Leave() { int cpunr = KeGetCurrentProcessorNumber(); //restore the IDT __lidt(&cpustate[cpunr].OriginalIDT); #ifdef AMD64 _enable(); #else __asm{ sti } #endif KeLowerIrql(cpustate[cpunr].entryIRQL); } void NoExceptions_Cleanup() { if (cpustate) { int i; for (i = 0; i < MaxCPUCount; i++) { if (cpustate[i].NoExceptionVectorList) { ExFreePool(cpustate[i].NoExceptionVectorList); cpustate[i].NoExceptionVectorList = NULL; } } ExFreePool(cpustate); } } ================================================ FILE: src/noexceptions.h ================================================ #ifndef NOEXCEPTIONS_H #define NOEXCEPTIONS_H /* Will be responsible for temporarily switching out the IDT of the curent CPU with one that doesn't call windows functions on errors */ #include #include #include #include "interruptHook.h" #include "dbkfunc.h" typedef struct { KIRQL entryIRQL; PINT_VECTOR NoExceptionVectorList; //list pointing to an idt table with hooked ints IDT OriginalIDT; IDT ModdedIDT; } CPUSTATE, *PCPUSTATE; BOOL NoExceptions_Enter(); int NoExceptions_CopyMemory(PVOID Destination, PVOID Source, int size); void NoExceptions_Leave(); void NoExceptions_Cleanup(); void NoException14_ErrorHandler(); #endif ================================================ FILE: src/processlist.c ================================================ #pragma warning( disable: 4100 4103 4706) #include "ntifs.h" #include "processlist.h" #include "threads.h" #include "memscan.h" #include "ultimap2.h" PRTL_GENERIC_TABLE InternalProcessList = NULL; PEPROCESS WatcherProcess = NULL; BOOLEAN ProcessWatcherOpensHandles = TRUE; RTL_GENERIC_COMPARE_RESULTS NTAPI ProcessListCompare(__in struct _RTL_GENERIC_TABLE *Table, __in PProcessListData FirstStruct, __in PProcessListData SecondStruct) { //DbgPrint("ProcessListCompate"); if (FirstStruct->ProcessID == SecondStruct->ProcessID) return GenericEqual; else { if (SecondStruct->ProcessID < FirstStruct->ProcessID) return GenericLessThan; else return GenericGreaterThan; } } PVOID NTAPI ProcessListAlloc(__in struct _RTL_GENERIC_TABLE *Table, __in CLONG ByteSize) { PVOID r=ExAllocatePool(PagedPool, ByteSize); RtlZeroMemory(r, ByteSize); //DbgPrint("ProcessListAlloc %d",(int)ByteSize); return r; } VOID NTAPI ProcessListDealloc(__in struct _RTL_GENERIC_TABLE *Table, __in __drv_freesMem(Mem) __post_invalid PVOID Buffer) { //DbgPrint("ProcessListDealloc"); ExFreePool(Buffer); } VOID GetThreadData(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context) { struct ThreadData *tempThreadEntry; PETHREAD selectedthread; HANDLE tid; LARGE_INTEGER Timeout; PKAPC AP; tempThreadEntry=Context; DbgPrint("Gathering PEThread thread\n"); Timeout.QuadPart = -1; KeDelayExecutionThread(KernelMode, TRUE, &Timeout); selectedthread=NULL; if (ExAcquireResourceSharedLite(&ProcesslistR, TRUE)) { tid = tempThreadEntry->ThreadID; AP = &tempThreadEntry->SuspendApc; PsLookupThreadByThreadId((PVOID)tid, &selectedthread); if (selectedthread) { DbgPrint("PEThread=%p\n", selectedthread); KeInitializeApc(AP, (PKTHREAD)selectedthread, 0, (PKKERNEL_ROUTINE)Ignore, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)SuspendThreadAPCRoutine, KernelMode, NULL); ObDereferenceObject(selectedthread); } else { DbgPrint("Failed getting the pethread.\n"); } } ExReleaseResourceLite(&ProcesslistR); } VOID CreateThreadNotifyRoutine(IN HANDLE ProcessId,IN HANDLE ThreadId,IN BOOLEAN Create) { if (KeGetCurrentIrql()==PASSIVE_LEVEL) { /*if (DebuggedProcessID==(ULONG)ProcessId) { // PsSetContextThread (bah, xp only) }*/ if (ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE)) { if (ThreadEventCount < 50) { ThreadEventData[ThreadEventCount].Created = Create; ThreadEventData[ThreadEventCount].ProcessID = (UINT_PTR)ProcessId; ThreadEventData[ThreadEventCount].ThreadID = (UINT_PTR)ThreadId; /* if (Create) DbgPrint("Create ProcessID=%x\nThreadID=%x\n",(UINT_PTR)ProcessId,(UINT_PTR)ThreadId); else DbgPrint("Destroy ProcessID=%x\nThreadID=%x\n",(UINT_PTR)ProcessId,(UINT_PTR)ThreadId); */ ThreadEventCount++; } } ExReleaseResourceLite(&ProcesslistR); KeSetEvent(ThreadEvent, 0, FALSE); KeClearEvent(ThreadEvent); } } VOID CreateProcessNotifyRoutine(IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create) { PEPROCESS CurrentProcess = NULL; HANDLE ProcessHandle = 0; /* if (PsSuspendProcess) { DbgPrint("Suspending process %d", PsGetCurrentThreadId()); PsSuspendProcess(PsGetCurrentProcess()); DbgPrint("After PsGetCurrentProcess()"); } */ if (KeGetCurrentIrql()==PASSIVE_LEVEL) { struct ProcessData *tempProcessEntry; //aquire a spinlock if (ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE)) { if (PsLookupProcessByProcessId((PVOID)ProcessId, &CurrentProcess) != STATUS_SUCCESS) { ExReleaseResourceLite(&ProcesslistR); return; } if ((ProcessWatcherOpensHandles) && (WatcherProcess)) { if (Create) { //Open a handle to this process /* HANDLE ph = 0; NTSTATUS r = ObOpenObjectByPointer(CurrentProcess, 0, NULL, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, &ph); DbgPrint("CreateProcessNotifyRoutine: ObOpenObjectByPointer=%x ph=%x", r, ph); r = ZwDuplicateObject(ZwCurrentProcess(), ph, WatcherHandle, &ProcessHandle, PROCESS_ALL_ACCESS, 0, DUPLICATE_CLOSE_SOURCE); DbgPrint("CreateProcessNotifyRoutine: ZwDuplicateObject=%x (handle=%x)", r, ProcessHandle); */ KAPC_STATE oldstate; KeStackAttachProcess((PKPROCESS)WatcherProcess, &oldstate); __try { __try { ObOpenObjectByPointer(CurrentProcess, 0, NULL, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, &ProcessHandle); } __except (1) { DbgPrint("Exception during ObOpenObjectByPointer"); } } __finally { KeUnstackDetachProcess(&oldstate); } } if (InternalProcessList == NULL) { InternalProcessList = ExAllocatePool(PagedPool, sizeof(RTL_GENERIC_TABLE)); if (InternalProcessList) RtlInitializeGenericTable(InternalProcessList, ProcessListCompare, ProcessListAlloc, ProcessListDealloc, NULL); } if (InternalProcessList) { ProcessListData d, *r; d.ProcessID = ProcessId; d.PEProcess = CurrentProcess; d.ProcessHandle = ProcessHandle; r = RtlLookupElementGenericTable(InternalProcessList, &d); if (Create) { //add it to the list BOOLEAN newElement = FALSE; if (r) //weird { DbgPrint("Duplicate PID detected..."); RtlDeleteElementGenericTable(InternalProcessList, r); } r = RtlInsertElementGenericTable(InternalProcessList, &d, sizeof(d), &newElement); DbgPrint("Added handle %x for pid %d to the list (newElement=%d r=%p)", (int)(UINT_PTR)d.ProcessHandle, (int)(UINT_PTR)d.ProcessID, newElement, r); } else { //remove it from the list (if it's there) DbgPrint("Process %d destruction. r=%p", (int)(UINT_PTR)d.ProcessID, r); if (r) { DbgPrint("Process that was in the list has been closed"); //if (r->ProcessHandle) // ZwClose(r->ProcessHandle); //RtlDeleteElementGenericTable(InternalProcessList, r); r->Deleted = 1; } if (CurrentProcess == WatcherProcess) { DbgPrint("CE Closed"); //ZwClose(WatcherHandle); CleanProcessList(); //CE closed WatcherProcess = 0; } } } } //fill in a processcreateblock with data if (ProcessEventCount < 50) { ProcessEventdata[ProcessEventCount].Created = Create; ProcessEventdata[ProcessEventCount].ProcessID = (UINT_PTR)ProcessId; ProcessEventdata[ProcessEventCount].PEProcess = (UINT_PTR)CurrentProcess; ProcessEventCount++; } //if (!HiddenDriver) if (FALSE) //moved till next version { if (Create) { //allocate a block of memory for the processlist tempProcessEntry = ExAllocatePool(PagedPool, sizeof(struct ProcessData)); tempProcessEntry->ProcessID = ProcessId; tempProcessEntry->PEProcess = CurrentProcess; tempProcessEntry->Threads = NULL; DbgPrint("Allocated a process at:%p\n", tempProcessEntry); if (!processlist) { processlist = tempProcessEntry; processlist->next = NULL; processlist->previous = NULL; } else { tempProcessEntry->next = processlist; tempProcessEntry->previous = NULL; processlist->previous = tempProcessEntry; processlist = tempProcessEntry; } } else { //find this process and delete it tempProcessEntry = processlist; while (tempProcessEntry) { if (tempProcessEntry->ProcessID == ProcessId) { int i; if (tempProcessEntry->next) tempProcessEntry->next->previous = tempProcessEntry->previous; if (tempProcessEntry->previous) tempProcessEntry->previous->next = tempProcessEntry->next; else processlist = tempProcessEntry->next; //it had no previous entry, so it's the root /* if (tempProcessEntry->Threads) { struct ThreadData *tempthread,*tempthread2; KIRQL OldIrql2; tempthread=tempProcessEntry->Threads; tempthread2=tempthread; DbgPrint("Process ended. Freeing threads\n"); while (tempthread) { tempthread=tempthread->next; DbgPrint("Free thread %p (next thread=%p)\n",tempthread2,tempthread); ExFreePool(tempthread2); tempthread2=tempthread; } } ExFreePool(tempProcessEntry);*/ i = 0; tempProcessEntry = processlist; while (tempProcessEntry) { i++; tempProcessEntry = tempProcessEntry->next; } DbgPrint("There are %d processes in the list\n", i); break; } tempProcessEntry = tempProcessEntry->next; } } } } ExReleaseResourceLite(&ProcesslistR); if (CurrentProcess!=NULL) ObDereferenceObject(CurrentProcess); //signal process event (if there's one waiting for a signal) if (ProcessEvent) { KeSetEvent(ProcessEvent, 0, FALSE); KeClearEvent(ProcessEvent); } } } VOID CreateProcessNotifyRoutineEx(IN HANDLE ParentId, IN HANDLE ProcessId, __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo) { DbgPrint("CreateProcessNotifyRoutineEx"); CreateProcessNotifyRoutine(ParentId, ProcessId, CreateInfo!=NULL); } HANDLE GetHandleForProcessID(IN HANDLE ProcessID) { if (InternalProcessList) { ProcessListData d, *r; d.ProcessID = ProcessID; r = RtlLookupElementGenericTable(InternalProcessList, &d); if (r) { DbgPrint("Found a handle for PID %d (%x)", (int)(UINT_PTR)ProcessID, (int)(UINT_PTR)r->ProcessHandle); return r->ProcessHandle; // r->ProcessHandle; } } return 0; } VOID CleanProcessList() { if (InternalProcessList) { PProcessListData li; if (ExAcquireResourceExclusiveLite(&ProcesslistR, TRUE)) { KAPC_STATE oldstate; BOOLEAN ChangedContext; if ((WatcherProcess) && (WatcherProcess != PsGetCurrentProcess())) { KeStackAttachProcess((PKPROCESS)WatcherProcess, &oldstate); ChangedContext = TRUE; } while (li = RtlGetElementGenericTable(InternalProcessList, 0)) { if ((li->ProcessHandle) && (WatcherProcess)) ZwClose(li->ProcessHandle); RtlDeleteElementGenericTable(InternalProcessList, li); } ExFreePool(InternalProcessList); InternalProcessList = NULL; } ExReleaseResourceLite(&ProcesslistR); } } ================================================ FILE: src/processlist.h ================================================ #include #include "extradefines.h" #include "extraimports.h" VOID CreateProcessNotifyRoutine(IN HANDLE ParentId, IN HANDLE ProcessId, IN BOOLEAN Create); VOID CreateProcessNotifyRoutineEx(IN HANDLE ParentId, IN HANDLE ProcessId, __in_opt PPS_CREATE_NOTIFY_INFO CreateInfo); struct ThreadData { HANDLE ThreadID; PETHREAD PEThread; KAPC SuspendApc; KSEMAPHORE SuspendSemaphore; //why not mutex? int suspendcount; struct ThreadData *previous; struct ThreadData *next; }; typedef struct { HANDLE ProcessID; PEPROCESS PEProcess; HANDLE ProcessHandle; BOOLEAN Deleted; } ProcessListData, *PProcessListData; struct ProcessData { HANDLE ProcessID; PEPROCESS PEProcess; struct ThreadData *Threads; struct ProcessData *previous; struct ProcessData *next; } *processlist; typedef struct tagProcessEventData { UINT64 Created; UINT64 ProcessID; UINT64 PEProcess; } ProcessEventdta; ProcessEventdta ProcessEventdata[50]; UCHAR ProcessEventCount; PKEVENT ProcessEvent; //HANDLE ProcessEventHandle; BOOLEAN CreateProcessNotifyRoutineEnabled; ERESOURCE ProcesslistR; VOID CreateThreadNotifyRoutine(IN HANDLE ProcessId, IN HANDLE ThreadId, IN BOOLEAN Create); typedef struct tagThreadEventData { BOOLEAN Created; UINT64 ProcessID; UINT64 ThreadID; } ThreadEventDta; ThreadEventDta ThreadEventData[50]; UCHAR ThreadEventCount; PKEVENT ThreadEvent; //HANDLE ThreadEventHandle; extern HANDLE WatcherHandle; extern PEPROCESS WatcherProcess; extern BOOLEAN ProcessWatcherOpensHandles; BOOLEAN CreateThreadNotifyRoutineEnabled; VOID CleanProcessList(); HANDLE GetHandleForProcessID(IN HANDLE ProcessID); ================================================ FILE: src/segmentinfo.asm ================================================ _TEXT SEGMENT 'CODE' PUBLIC _getAccessRights@8 _getAccessRights@8: push ebp mov ebp,esp push ecx mov ecx,[ebp+8] ;0=old ebp, 4=return address, 8=param1 lar eax,ecx jnz getAccessRights_invalid shr eax,8 and eax,0f0ffh jmp getAccessRights_exit getAccessRights_invalid: mov eax,010000h getAccessRights_exit: pop ecx pop ebp ret PUBLIC _getSegmentLimit@8 _getSegmentLimit@8: push ebp mov ebp,esp push ecx mov ecx,[ebp+8] xor eax,eax lsl eax,ecx pop ecx pop ebp ret _TEXT ENDS END ================================================ FILE: src/threads.c ================================================ #pragma warning( disable: 4100 4103) #include "threads.h" #include "processlist.h" #include "memscan.h" /* NTSTATUS NTAPI PsGetContextThread(IN PETHREAD Thread, IN OUT PCONTEXT ThreadContext, IN KPROCESSOR_MODE PreviousMode); NTSTATUS NTAPI PsSetContextThread(IN PETHREAD Thread, IN OUT PCONTEXT ThreadContext, IN KPROCESSOR_MODE PreviousMode); NTSTATUS NTAPI DBKGetContextThread(IN PETHREAD Thread, IN OUT PCONTEXT ThreadContext) { return PsGetContextThread(Thread, ThreadContext, KernelMode); }*/ struct ThreadData* GetThreaddata(ULONG threadid) { struct ProcessData *tempProcessData; struct ThreadData *tempThreadData; //PRE: Lock the list before calling this routine tempProcessData=processlist; while (tempProcessData) { tempThreadData=tempProcessData->Threads; while (tempThreadData) { if (tempThreadData->ThreadID==(HANDLE)(UINT_PTR)threadid) return tempThreadData; tempThreadData=tempThreadData->next; } tempProcessData=tempProcessData->next; } return NULL; } void Ignore(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) { //ignore return; } void SuspendThreadAPCRoutine(PVOID arg1, PVOID arg2, PVOID arg3) { LARGE_INTEGER Timeout; struct ThreadData *x; //DbgPrint("Inside SuspendThreadAPCRoutine\n"); x=arg1; //DbgPrint("x=%p",x); DbgPrint("Waiting...\n"); Timeout.QuadPart = -999999999999999; KeWaitForSingleObject(&(x->SuspendSemaphore), Suspended, KernelMode, FALSE, NULL); //KeDelayExecutionThread(KernelMode, FALSE, &Timeout); DbgPrint("Resuming...\n"); } void DBKSuspendThread(ULONG ThreadID) { struct ThreadData *t_data; if (ExAcquireResourceSharedLite(&ProcesslistR, TRUE)) { DbgPrint("Going to suspend this thread\n"); //find the thread in the threadlist //find the threadid in the processlist t_data = GetThreaddata(ThreadID); if (t_data) { DbgPrint("Suspending thread....\n"); if (!t_data->PEThread) { //not yet initialized t_data->PEThread = (PETHREAD)getPEThread(ThreadID); KeInitializeApc(&t_data->SuspendApc, (PKTHREAD)t_data->PEThread, 0, (PKKERNEL_ROUTINE)Ignore, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)SuspendThreadAPCRoutine, KernelMode, t_data); } DbgPrint("x should be %p", t_data); t_data->suspendcount++; if (t_data->suspendcount == 1) //not yet suspended so suspend it KeInsertQueueApc(&t_data->SuspendApc, t_data, t_data, 0); } else DbgPrint("Thread not found in the list\n"); } ExReleaseResourceLite(&ProcesslistR); } void DBKResumeThread(ULONG ThreadID) { struct ThreadData *t_data; if (ExAcquireResourceSharedLite(&ProcesslistR, TRUE)) { DbgPrint("Going to resume this thread\n"); //find the thread in the threadlist //find the threadid in the processlist t_data = GetThreaddata(ThreadID); if (t_data) { if (t_data->suspendcount) { t_data->suspendcount--; if (!t_data->suspendcount) //suspendcount=0 so resume KeReleaseSemaphore(&t_data->SuspendSemaphore, 0, 1, FALSE); } } else DbgPrint("Thread not found in the list\n"); } ExReleaseResourceLite(&ProcesslistR); } void DBKSuspendProcess(ULONG ProcessID) { struct ThreadData *t_data=NULL; struct ProcessData *tempProcessData=NULL; if (ExAcquireResourceSharedLite(&ProcesslistR, TRUE)) { DbgPrint("Going to suspend this process\n"); //find the process in the threadlist tempProcessData = processlist; while (tempProcessData) { if (tempProcessData->ProcessID == (HANDLE)(UINT_PTR)ProcessID) { t_data = tempProcessData->Threads; break; } tempProcessData = tempProcessData->next; } if (!t_data) { DbgPrint("This process was not found\n"); ExReleaseResourceLite(&ProcesslistR); return; //no process found } while (t_data) { DbgPrint("Suspending thread....\n"); if (!t_data->PEThread) { //not yet initialized t_data->PEThread = (PETHREAD)getPEThread((UINT_PTR)t_data->ThreadID); KeInitializeApc(&t_data->SuspendApc, (PKTHREAD)t_data->PEThread, 0, (PKKERNEL_ROUTINE)Ignore, (PKRUNDOWN_ROUTINE)NULL, (PKNORMAL_ROUTINE)SuspendThreadAPCRoutine, KernelMode, t_data); } DbgPrint("x should be %p", t_data); t_data->suspendcount++; if (t_data->suspendcount == 1) //not yet suspended so suspend it KeInsertQueueApc(&t_data->SuspendApc, t_data, t_data, 0); t_data = t_data->next; //next thread } } ExReleaseResourceLite(&ProcesslistR); } void DBKResumeProcess(ULONG ProcessID) { struct ThreadData *t_data=NULL; struct ProcessData *tempProcessData=NULL; if (ExAcquireResourceSharedLite(&ProcesslistR, TRUE)) { DbgPrint("Going to suspend this process\n"); //find the process in the threadlist tempProcessData = processlist; while (tempProcessData) { if (tempProcessData->ProcessID == (HANDLE)(UINT_PTR)ProcessID) { t_data = tempProcessData->Threads; break; } tempProcessData = tempProcessData->next; } if (!t_data) { DbgPrint("This process was not found\n"); ExReleaseResourceLite(&ProcesslistR); return; //no process found } while (t_data) { DbgPrint("Resuming thread....\n"); if (t_data->suspendcount) { t_data->suspendcount--; if (!t_data->suspendcount) //suspendcount=0 so resume KeReleaseSemaphore(&t_data->SuspendSemaphore, 0, 1, FALSE); } t_data = t_data->next; //next thread } } ExReleaseResourceLite(&ProcesslistR); } ================================================ FILE: src/threads.h ================================================ #ifndef THREADS_H #define THREADS_H #include #include #include "DBKFunc.h" void Ignore(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2); void SuspendThreadAPCRoutine(PVOID arg1, PVOID arg2, PVOID arg3); void DBKSuspendThread(ULONG ThreadID); void DBKResumeThread(ULONG ThreadID); void DBKResumeProcess(ULONG ProcessID); void DBKSuspendProcess(ULONG ProcessID); //NTSTATUS NTAPI DBKGetContextThread(IN PETHREAD Thread, IN OUT PCONTEXT ThreadContext); #endif ================================================ FILE: src/ultimap.c ================================================ /* Ultimap implements the recording of all the branches in the target process Requires dbvm for process selection */ #pragma warning( disable: 4100 4101 4213) #include #include "ultimap.h" #include "vmxhelper.h" #include "DBKFunc.h" #include #include "ultimap2\apic.h" JUMPBACK perfmonJumpBackLocation; /* #ifdef AMD64 volatile PAPIC APIC_BASE=0; //(PAPIC)0xfffffffffffe0000; #else volatile PAPIC APIC_BASE=0; //(PAPIC)0xfffe0000; #endif */ BOOL SaveToFile; //If set it will save the results to a file instead of sending a message to the usermode app that is watching the data HANDLE FileHandle; int MaxDataBlocks=1; KSEMAPHORE DataBlockSemaphore; //governs how many events can be active at a time FAST_MUTEX DataBlockMutex; //when a thread passes the semaphore this is used to pick a DataBlock typedef struct { BOOL Available; PBTS Data; int DataSize; int CpuID; KEVENT DataReady; } _DataBlock; _DataBlock *DataBlock; PVOID *DataReadyPointerList; int perfmon_interrupt_centry(void); /* use apic.* now #define MSR_IA32_APICBASE 0x0000001b void setup_APIC_BASE(void) { PHYSICAL_ADDRESS Physical_APIC_BASE; DbgPrint("Fetching the APIC base\n"); Physical_APIC_BASE.QuadPart=readMSR(MSR_IA32_APICBASE) & 0xFFFFFFFFFFFFF000ULL; DbgPrint("Physical_APIC_BASE=%p\n", Physical_APIC_BASE.QuadPart); APIC_BASE = (PAPIC)MmMapIoSpace(Physical_APIC_BASE, sizeof(APIC), MmNonCached); DbgPrint("APIC_BASE at %p\n", APIC_BASE); } void clean_APIC_BASE(void) { if (APIC_BASE) MmUnmapIoSpace((PVOID)APIC_BASE, sizeof(APIC)); }*/ void ultimap_flushBuffers_all(UINT_PTR param) { DbgPrint("Calling perfmon_interrupt_centry() manually\n"); if (DS_AREA[cpunr()]) //don't call if ultimap has been disabled { perfmon_interrupt_centry(); enableInterrupts(); //the handler disables it on exit so re-enable it } } void ultimap_flushBuffers(void) { //call this when the buffer of the current cpu needs to be flushed and handled int i; int count; DbgPrint("ultimap_flushBuffers\n"); //what it does: //for each cpu emulate a "buffer filled" event. //the handler then copies all the current data to a temporary buffer and signals the worker thread to deal with it. If there is no available worker thread it waits forEachCpuPassive(ultimap_flushBuffers_all,0); DbgPrint("ultimap_flushBuffers_all has returned\n"); //it returned and all worker thread are currently working on this data (it only returns when it has send a worker to work) //now wait for all workers to finish //do this by aquiring all semaphore slots and waiting for them to return again //forEachCpuPassive(ultimap_flushBuffers_all,0); //DbgPrint("ultimap_flushBuffers_all has returned a second time\n"); //this means that the previous blocks have been dealt with //actually... no, this is no guarantee. Now that the buffers are empty handling is so fast that while block 2,3,4,5 and 6 are still being handled block 1 can become available multiple times } NTSTATUS ultimap_continue(PULTIMAPDATAEVENT data) /* Called from usermode to signal that the data has been handled */ { DbgPrint("ultimap_continue\n"); MmUnmapLockedPages((PVOID)(UINT_PTR)data->Address, (PMDL)(UINT_PTR)data->Mdl); IoFreeMdl((PMDL)(UINT_PTR)data->Mdl); ExFreePool((PVOID)(UINT_PTR)data->KernelAddress); //this memory is not needed anymore if (DataBlock) DataBlock[data->Block].Available=TRUE; KeReleaseSemaphore(&DataBlockSemaphore, 1, 1, FALSE); //Let the next block go through DbgPrint("Released semaphore\n"); return STATUS_SUCCESS; } NTSTATUS ultimap_waitForData(ULONG timeout, PULTIMAPDATAEVENT data) /* Called from usermode to wait for data */ { NTSTATUS r; LARGE_INTEGER wait; PKWAIT_BLOCK waitblock; if (DataBlock) { waitblock=ExAllocatePool(NonPagedPool, MaxDataBlocks*sizeof(KWAIT_BLOCK)); wait.QuadPart=-10000LL * timeout; //Wait for the events in the list //If an event is triggered find out which one is triggered, then map that block into the usermode space and return the address and block //That block will be needed to continue if (timeout==0xffffffff) //infinite wait r=KeWaitForMultipleObjects(MaxDataBlocks, DataReadyPointerList, WaitAny, UserRequest, UserMode, TRUE, NULL, waitblock); else r=KeWaitForMultipleObjects(MaxDataBlocks, DataReadyPointerList, WaitAny, UserRequest, UserMode, TRUE, &wait, waitblock); ExFreePool(waitblock); data->Block=r-STATUS_WAIT_0; if (data->Block <= MaxDataBlocks) { //Map this block to usermode ExAcquireFastMutex(&DataBlockMutex); if (DataBlock) { data->KernelAddress=(UINT64)DataBlock[data->Block].Data; data->Mdl=(UINT64)IoAllocateMdl(DataBlock[data->Block].Data, DataBlock[data->Block].DataSize, FALSE, FALSE, NULL); if (data->Mdl) { MmBuildMdlForNonPagedPool((PMDL)(UINT_PTR)data->Mdl); data->Address=(UINT_PTR)MmMapLockedPagesSpecifyCache((PMDL)(UINT_PTR)data->Mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); if (data->Address) { data->Size=DataBlock[data->Block].DataSize; data->CpuID=DataBlock[data->Block].CpuID; r=STATUS_SUCCESS; } else r=STATUS_UNSUCCESSFUL; } else r=STATUS_UNSUCCESSFUL; } else r=STATUS_UNSUCCESSFUL; ExReleaseFastMutex(&DataBlockMutex); return r; } else return STATUS_UNSUCCESSFUL; } else return STATUS_UNSUCCESSFUL; } /* void apic_clearPerfmon() { APIC_BASE->LVT_Performance_Monitor.a = APIC_BASE->LVT_Performance_Monitor.a & 0xff; APIC_BASE->EOI.a = 0; } */ void ultimap_cleanstate() { apic_clearPerfmon(); } int perfmon_interrupt_centry(void) { KIRQL old = PASSIVE_LEVEL; int changedIRQL = 0; void *temp; int causedbyme=(DS_AREA[cpunr()]->BTS_IndexBaseAddress>=DS_AREA[cpunr()]->BTS_InterruptThresholdAddress); UINT_PTR blocksize; DbgPrint("perfmon_interrupt_centry\n", cpunr()); if (causedbyme) ultimap_cleanstate(); blocksize=(UINT_PTR)(DS_AREA[cpunr()]->BTS_IndexBaseAddress-DS_AREA[cpunr()]->BTS_BufferBaseAddress); { if (KeGetCurrentIrql() < DISPATCH_LEVEL) { //When called by the pre-emptive caller changedIRQL = 1; old = KeRaiseIrqlToDpcLevel(); } DbgPrint("Entry cpunr=%d\n", cpunr()); DbgPrint("Entry threadid=%d\n", PsGetCurrentThreadId()); temp=ExAllocatePool(NonPagedPool, blocksize); if (temp) { RtlCopyMemory(temp, (PVOID *)(UINT_PTR)DS_AREA[cpunr()]->BTS_BufferBaseAddress, blocksize); DbgPrint("temp=%p\n", temp); DS_AREA[cpunr()]->BTS_IndexBaseAddress=DS_AREA[cpunr()]->BTS_BufferBaseAddress; //don't reset on alloc error } else { DbgPrint("ExAllocatePool has failed\n"); KeLowerIrql(old); disableInterrupts(); return causedbyme; } if (changedIRQL) KeLowerIrql(old); //should be passive mode, taskswitches and cpu switches will happen now (When this returns, I may not be on the same interrupt as I was when I started) if (SaveToFile) { IO_STATUS_BLOCK iosb; NTSTATUS r; //Instead of sending the data to a usermode app it was chosen to store the data to a file for later usage DbgPrint("Writing buffer to disk\n"); r=ZwWriteFile(FileHandle, NULL, NULL, NULL, &iosb, temp, (ULONG)blocksize, NULL, NULL); DbgPrint("Done Writing. Result=%x\n", r); } else { DbgPrint("Waiting till there is a block free\n"); //When all workers are busy do not continue if ((DataBlock) && (KeWaitForSingleObject(&DataBlockSemaphore, Executive, KernelMode, FALSE, NULL) == STATUS_SUCCESS)) { int currentblock; int i; //Enter a critical section and choose a block DbgPrint("Acquired semaphore. Now picking a usable datablock\n"); ExAcquireFastMutex(&DataBlockMutex); DbgPrint("Acquired mutex. Looking for a Datablock that can be used\n"); if (DataBlock) { currentblock=-1; for (i=0; i< MaxDataBlocks; i++) { if (DataBlock[i].Available) //look for a block that is set as available { currentblock=i; DataBlock[currentblock].Available=FALSE; //not available anymore break; } } } else currentblock=-1; if (currentblock>=0) { DbgPrint("Using datablock %d\n", currentblock); DataBlock[currentblock].Data=temp; DataBlock[currentblock].DataSize=(int)blocksize; DataBlock[currentblock].CpuID=cpunr(); DbgPrint("Calling KeSetEvent/KeWaitForSingleObject\n"); KeSetEvent(&DataBlock[currentblock].DataReady, 1, FALSE); //Trigger a worker thread to start working } ExReleaseFastMutex(&DataBlockMutex); //DbgPrint("Released mutex\n"); } else { DbgPrint("if ((DataBlock) && (KeWaitForSingleObject(&DataBlockSemaphore, Executive, KernelMode, FALSE, NULL) == STATUS_SUCCESS)) failed\n"); } } //and return to the caller process disableInterrupts(); return causedbyme; } } #ifdef AMD64 extern void perfmon_interrupt(); #else _declspec( naked ) void perfmon_interrupt( void ) { __asm{ cld push ebp mov ebp,esp //store state pushad xor eax,eax mov ax,ds push eax mov ax,es push eax mov ax,fs push eax mov ax,gs push eax mov ax,0x23 //0x10 should work too, but even windows itself is using 0x23 mov ds,ax mov es,ax mov gs,ax mov ax,0x30 mov fs,ax call perfmon_interrupt_centry cmp eax,1 //set flag //restore state pop gs pop fs pop es pop ds popad pop ebp je skip_original_perfmon jmp far [perfmonJumpBackLocation] skip_original_perfmon: // commented out: I don't think a APIC interrupt has an errorcode.... add esp,4 //undo errorcode push iretd } } #endif VOID ultimap_pause_dpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgumen1, IN PVOID SystemArgument2) { vmx_ultimap_pause(); } void ultimap_pause(void) { forEachCpu(ultimap_pause_dpc, NULL, NULL, NULL, NULL); } VOID ultimap_resume_dpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgumen1, IN PVOID SystemArgument2) { vmx_ultimap_resume(); } void ultimap_resume(void) { forEachCpu(ultimap_resume_dpc, NULL, NULL, NULL, NULL); } VOID ultimap_disable_dpc(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgumen1, IN PVOID SystemArgument2) { int i; //DbgPrint("ultimap_disable_dpc()\n"); if (vmxusable) { i=vmx_ultimap_disable(); if (DS_AREA[cpunr()]) { ExFreePool(DS_AREA[cpunr()]); DS_AREA[cpunr()]=NULL; } } } void ultimap_disable(void) { void *clear = NULL; if (DataBlock) { int i; forEachCpu(ultimap_disable_dpc, NULL, NULL, NULL, NULL); if (SaveToFile && FileHandle) { ZwClose(FileHandle); FileHandle=NULL; } //all logging should have stopped now //Trigger all events waking up each thread that was waiting for the events ExAcquireFastMutex(&DataBlockMutex); for (i=0; iDS_AREA_SIZE; if (DS_AREA_SIZE == 0) { DbgPrint("DS_AREA_SIZE==0\n"); return; } DbgPrint("ultimap(%I64x, %I64x, %d)", (UINT64)params->cr3, (UINT64)params->dbgctl_msr, params->DS_AREA_SIZE); DS_AREA[cpunr()]=NULL; if (params->DS_AREA_SIZE) { DS_AREA[cpunr()]=ExAllocatePool(NonPagedPool, params->DS_AREA_SIZE); if (DS_AREA[cpunr()] == NULL) { DbgPrint("ExAllocatePool failed\n"); return; } RtlZeroMemory(DS_AREA[cpunr()], params->DS_AREA_SIZE); DbgPrint("DS_AREA[%d]=%p", cpunr(), DS_AREA[cpunr()]); //Initialize the DS_AREA DS_AREA[cpunr()]->BTS_BufferBaseAddress=(QWORD)(UINT_PTR)DS_AREA[cpunr()]+sizeof(DS_AREA_MANAGEMENT); DS_AREA[cpunr()]->BTS_BufferBaseAddress+=sizeof(BTS); DS_AREA[cpunr()]->BTS_BufferBaseAddress-=DS_AREA[cpunr()]->BTS_BufferBaseAddress % sizeof(BTS); DS_AREA[cpunr()]->BTS_IndexBaseAddress=DS_AREA[cpunr()]->BTS_BufferBaseAddress; DS_AREA[cpunr()]->BTS_AbsoluteMaxAddress=(QWORD)(UINT_PTR)DS_AREA[cpunr()]+params->DS_AREA_SIZE-sizeof(BTS); DS_AREA[cpunr()]->BTS_AbsoluteMaxAddress-=DS_AREA[cpunr()]->BTS_AbsoluteMaxAddress % sizeof(BTS); DS_AREA[cpunr()]->BTS_AbsoluteMaxAddress++; DS_AREA[cpunr()]->BTS_InterruptThresholdAddress=(DS_AREA[cpunr()]->BTS_AbsoluteMaxAddress-1) - 4*sizeof(BTS); } if (params->dbgctl_msr & (1 << 8)) { //hook the perfmon interrupt. First get the interrupt assigned (usually 0xfe, but let's be sure and read it from the apic) int perfmonIVT=(APIC_BASE->LVT_Performance_Monitor.a) & 0xff; DbgPrint("APIC_BASE->LVT_Performance_Monitor.a=%x\n", APIC_BASE->LVT_Performance_Monitor.a); if (perfmonIVT==0) //if not setup at all then set it up now perfmonIVT=0xfe; APIC_BASE->LVT_Performance_Monitor.a=perfmonIVT; //clear mask flag if it was set DbgPrint("APIC_BASE->LVT_Performance_Monitor.a=%x\n", APIC_BASE->LVT_Performance_Monitor.a); /* if (inthook_HookInterrupt((unsigned char)perfmonIVT, getCS(), (ULONG_PTR)perfmon_interrupt, &perfmonJumpBackLocation)) DbgPrint("Interrupt hooked\n"); else DbgPrint("Failed to hook interrupt\n"); */ } //and finally activate the mapping if (vmxusable) { vmx_ultimap((UINT_PTR)params->cr3, params->dbgctl_msr, DS_AREA[cpunr()]); } else { DbgPrint("vmxusable is false. So no ultimap for you!!!\n"); } } void ultimapapc(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine, PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) { EFLAGS e = getEflags(); DbgPrint("ultimapapc call for cpu %d ( IF=%d IRQL=%d)\n", KeGetCurrentProcessorNumber(), e.IF, KeGetCurrentIrql()); DbgPrint("SystemArgument1=%x\n", *(PULONG)SystemArgument1); DbgPrint("tid=%x\n", PsGetCurrentThreadId()); DbgPrint("Apc=%p\n", Apc); } void ultimapapcnormal(PVOID arg1, PVOID arg2, PVOID arg3) { EFLAGS e = getEflags(); DbgPrint("ultimapapcnormal call for cpu %d ( IF=%d IRQL=%d)\n", KeGetCurrentProcessorNumber(), e.IF, KeGetCurrentIrql()); DbgPrint("tid=%x\n", PsGetCurrentThreadId()); ultimap_flushBuffers(); return; } KAPC kApc[128]; volatile LONG apcnr = 0; void perfmon_hook(__in struct _KINTERRUPT *Interrupt, __in PVOID ServiceContext) { int i = InterlockedIncrement(&apcnr) % 128; EFLAGS e = getEflags(); DbgPrint("permon_hook call for cpu %d ( IF=%d IRQL=%d)\n", KeGetCurrentProcessorNumber(), e.IF, KeGetCurrentIrql()); DbgPrint("kApc=%p\n", &kApc); //switch buffer pointers //call DPC for ultimap for this cpu //todo: if this is buggy use a dpc instead to create the apc. (slower) KeInitializeApc(&kApc[i], (PKTHREAD)PsGetCurrentThread(), 0, (PKKERNEL_ROUTINE)ultimapapc, NULL, (PKNORMAL_ROUTINE)ultimapapcnormal, KernelMode, 0 ); KeInsertQueueApc(&kApc[i], NULL, NULL, 0); DbgPrint("after KeInsertQueueApc"); //perfmon_interrupt_centry(); ultimap_cleanstate(); DbgPrint("permon_return"); } void *pperfmon_hook = (void*)perfmon_hook; NTSTATUS ultimap(UINT64 cr3, UINT64 dbgctl_msr, int _DS_AREA_SIZE, BOOL savetofile, WCHAR *filename, int handlerCount) { struct { UINT64 cr3; UINT64 dbgctl_msr; int DS_AREA_SIZE; } params; int i; if (handlerCount>64) return STATUS_UNSUCCESSFUL; //create file SaveToFile=savetofile; if (SaveToFile) { UNICODE_STRING usFile; OBJECT_ATTRIBUTES oaFile; IO_STATUS_BLOCK iosb; NTSTATUS r; RtlInitUnicodeString(&usFile, filename); InitializeObjectAttributes(&oaFile,&usFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL,NULL); DbgPrint("Creating file %S", usFile.Buffer); FileHandle=0; r=ZwCreateFile(&FileHandle,SYNCHRONIZE|FILE_READ_DATA|FILE_APPEND_DATA | GENERIC_ALL,&oaFile,&iosb,0,FILE_ATTRIBUTE_NORMAL,0,FILE_SUPERSEDE, FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,NULL,0); DbgPrint("ZwCreateFile=%x\n", r); } MaxDataBlocks=handlerCount; KeInitializeSemaphore(&DataBlockSemaphore, MaxDataBlocks, MaxDataBlocks); ExInitializeFastMutex(&DataBlockMutex); //Datablock inits DataBlock=ExAllocatePool(NonPagedPool, sizeof(_DataBlock) * MaxDataBlocks); DataReadyPointerList=ExAllocatePool(NonPagedPool, sizeof(PVOID) * MaxDataBlocks); RtlZeroMemory(DataBlock, sizeof(_DataBlock) * MaxDataBlocks); RtlZeroMemory(DataReadyPointerList, sizeof(PVOID) * MaxDataBlocks); if ((DataBlock) && (DataReadyPointerList)) { NTSTATUS r; for (i=0; i< MaxDataBlocks; i++) { //DataBlock[i]-> DataBlock[i].Data=NULL; KeInitializeEvent(&DataBlock[i].DataReady, SynchronizationEvent , FALSE); DataBlock[i].Available=TRUE; DataReadyPointerList[i]=&DataBlock[i].DataReady; } params.cr3=cr3; params.dbgctl_msr=dbgctl_msr; params.DS_AREA_SIZE=_DS_AREA_SIZE; r=HalSetSystemInformation(HalProfileSourceInterruptHandler, sizeof(PVOID*), &pperfmon_hook); //hook the perfmon interrupt DbgPrint("HalSetSystemInformation returned %x\n", r); forEachCpu(ultimap_setup_dpc, ¶ms, NULL, NULL, NULL); return STATUS_SUCCESS; } else { DbgPrint("Failure allocating DataBlock and DataReadyPointerList\n"); return STATUS_MEMORY_NOT_ALLOCATED; } } ================================================ FILE: src/ultimap.h ================================================ #ifndef ULTIMAP_H #define ULTIMAP_H #include #include typedef UINT64 QWORD; #pragma pack(push) #pragma pack(1) /* typedef struct { DWORD BTS_BufferBaseAddress; DWORD BTS_IndexBaseAddress; DWORD BTS_AbsoluteMaxAddress; DWORD BTS_InterruptThresholdAddress; DWORD PEBS_BufferBaseAddress; DWORD PEBS_IndexBaseAddress; DWORD PEBS_AbsoluteMaxAddress; DWORD PEBS_InterruptThresholdAddress; QWORD PEBS_CounterReset; QWORD Reserved; DWORD Reserved2; } DS_AREA_MANAGEMENT32,*PDS_AREA_MANAGEMENT32; */ typedef struct { QWORD BTS_BufferBaseAddress; QWORD BTS_IndexBaseAddress; QWORD BTS_AbsoluteMaxAddress; QWORD BTS_InterruptThresholdAddress; QWORD PEBS_BufferBaseAddress; QWORD PEBS_IndexBaseAddress; QWORD PEBS_AbsoluteMaxAddress; QWORD PEBS_InterruptThresholdAddress; QWORD PEBS_CounterReset; QWORD Reserved; QWORD Reserved2; } DS_AREA_MANAGEMENT64,*PDS_AREA_MANAGEMENT64; typedef struct { QWORD LastBranchFrom; QWORD LastBranchTo; QWORD Extra; } BTS,*PBTS; //#ifdef AMD64 typedef DS_AREA_MANAGEMENT64 DS_AREA_MANAGEMENT; typedef PDS_AREA_MANAGEMENT64 PDS_AREA_MANAGEMENT; //#else //typedef DS_AREA_MANAGEMENT32 DS_AREA_MANAGEMENT; //typedef PDS_AREA_MANAGEMENT32 PDS_AREA_MANAGEMENT; //#endif typedef struct { UINT64 DataReadyEvent; UINT64 DataHandledEvent; } ULTIMAPEVENT, *PULTIMAPEVENT; typedef struct { UINT64 Address; UINT64 Size; UINT64 Block; UINT64 CpuID; UINT64 KernelAddress; UINT64 Mdl; //go ahead, scream } ULTIMAPDATAEVENT, *PULTIMAPDATAEVENT; NTSTATUS ultimap_continue(PULTIMAPDATAEVENT data); NTSTATUS ultimap_waitForData(ULONG timeout, PULTIMAPDATAEVENT data); NTSTATUS ultimap(UINT64 cr3, UINT64 dbgctl_msr, int _DS_AREA_SIZE, BOOL savetofile, WCHAR *filename, int handlerCount); void ultimap_pause(); void ultimap_resume(); void ultimap_disable(void); void ultimap_flushBuffers(void); PDS_AREA_MANAGEMENT DS_AREA[256]; //used to store the addresses. (reading the msr that holds the DS_AREA is impossible with dbvm active) int DS_AREA_SIZE; #pragma pack(pop) #endif ================================================ FILE: src/ultimap2/apic.c ================================================ #include #include "apic.h" #include #include "..\DBKFunc.h" #define MSR_IA32_APICBASE 0x0000001b #define MSR_IA32_X2APIC_EOI 0x80b #define MSR_IA32_X2APIC_LVT_PMI 0x834 volatile PAPIC APIC_BASE; BOOL x2APICMode = FALSE; void apic_clearPerfmon() { if (x2APICMode) { __writemsr(MSR_IA32_X2APIC_LVT_PMI, (__readmsr(MSR_IA32_X2APIC_LVT_PMI) & (UINT64)0xff)); __writemsr(MSR_IA32_X2APIC_EOI, 0); } else { //DbgPrint("Clear perfmon (APIC_BASE at %llx)\n", APIC_BASE); //DbgPrint("APIC_BASE->LVT_Performance_Monitor.a at %p\n", &APIC_BASE->LVT_Performance_Monitor.a); //DbgPrint("APIC_BASE->EOI.a at %p\n", &APIC_BASE->EOI.a); //DbgPrint("APIC_BASE->LVT_Performance_Monitor.a had value %x\n", APIC_BASE->LVT_Performance_Monitor.a); //DbgPrint("APIC_BASE->LVT_Performance_Monitor.b had value %x\n", APIC_BASE->LVT_Performance_Monitor.b); // DbgPrint("APIC_BASE->LVT_Performance_Monitor.c had value %x\n", APIC_BASE->LVT_Performance_Monitor.c); // DbgPrint("APIC_BASE->LVT_Performance_Monitor.d had value %x\n", APIC_BASE->LVT_Performance_Monitor.d); APIC_BASE->LVT_Performance_Monitor.a = APIC_BASE->LVT_Performance_Monitor.a & 0xff; APIC_BASE->EOI.a = 0; } } void setup_APIC_BASE(void) { PHYSICAL_ADDRESS Physical_APIC_BASE; UINT64 APIC_BASE_VALUE = readMSR(MSR_IA32_APICBASE); DbgPrint("Fetching the APIC base\n"); Physical_APIC_BASE.QuadPart = APIC_BASE_VALUE & 0xFFFFFFFFFFFFF000ULL; DbgPrint("Physical_APIC_BASE=%p\n", Physical_APIC_BASE.QuadPart); APIC_BASE = (PAPIC)MmMapIoSpace(Physical_APIC_BASE, sizeof(APIC), MmNonCached); DbgPrint("APIC_BASE at %p\n", APIC_BASE); x2APICMode = (APIC_BASE_VALUE & (1 << 10))!=0; DbgPrint("x2APICMode=%d\n", x2APICMode); } void clean_APIC_BASE(void) { if (APIC_BASE) MmUnmapIoSpace((PVOID)APIC_BASE, sizeof(APIC)); } ================================================ FILE: src/ultimap2/apic.h ================================================ #ifndef apic_h #define apic_h #include #include typedef struct { DWORD a; DWORD b; DWORD c; DWORD d; } UINT128; typedef volatile struct { UINT128 Reserved1; UINT128 Reserved2; UINT128 LocalAPIC_ID; UINT128 LocalAPIC_Version; UINT128 Reserved3; UINT128 Reserved4; UINT128 Reserved5; UINT128 Reserved6; UINT128 Task_Priority; UINT128 Arbritation_Priority; UINT128 Processor_Priority; UINT128 EOI; UINT128 Reserved7; UINT128 Logical_Destination; UINT128 Destination_Format; UINT128 Spurious_Interrupt_Vector; UINT128 In_Service[8]; UINT128 Trigger_Mode[8]; UINT128 Interrupt_Request[8]; UINT128 Error_Status; UINT128 Reserved8[7]; UINT128 Interrupt_Command_Low32Bit; UINT128 Interrupt_Command_High32Bit; UINT128 LVT_Timer; UINT128 LVT_Thermal_Sensor; UINT128 LVT_Performance_Monitor; UINT128 LVT_LINT0; UINT128 LVT_LINT1; UINT128 LVT_Error; UINT128 Initial_Count; UINT128 Current_Count; UINT128 Reserved9[4]; UINT128 Divide_Configuration; UINT128 Reserved10; } APIC, *PAPIC; extern volatile PAPIC APIC_BASE; void apic_clearPerfmon(); void setup_APIC_BASE(void); void clean_APIC_BASE(void); #endif ================================================ FILE: src/ultimap2.c ================================================ #pragma warning( disable: 4100 4706) #include #include #include #include #include #include "Ntstrsafe.h" #include "DBKFunc.h" #include "ultimap2\apic.h" #include "ultimap2.h" PSSUSPENDPROCESS PsSuspendProcess; PSSUSPENDPROCESS PsResumeProcess; KDPC RTID_DPC; BOOL LogKernelMode; BOOL LogUserMode; PEPROCESS CurrentTarget; UINT64 CurrentCR3; HANDLE Ultimap2Handle; volatile BOOLEAN UltimapActive = FALSE; volatile BOOLEAN isSuspended = FALSE; volatile BOOLEAN flushallbuffers = FALSE; //set to TRUE if all the data should be flushed KEVENT FlushData; BOOL SaveToFile; WCHAR OutputPath[200]; int Ultimap2RangeCount; PURANGE Ultimap2Ranges = NULL; PVOID *Ultimap2_DataReady; #if (NTDDI_VERSION < NTDDI_VISTA) //implement this function for XP unsigned int KeQueryMaximumProcessorCount() { CCHAR cpunr; KAFFINITY cpus, original; ULONG cpucount; cpucount = 0; cpus = KeQueryActiveProcessors(); original = cpus; while (cpus) { if (cpus % 2) cpucount++; cpus = cpus / 2; } return cpucount; } #endif typedef struct { PToPA_ENTRY ToPAHeader; PToPA_ENTRY ToPAHeader2; PVOID ToPABuffer; PVOID ToPABuffer2; PMDL ToPABufferMDL; PMDL ToPABuffer2MDL; PRTL_GENERIC_TABLE ToPALookupTable; PRTL_GENERIC_TABLE ToPALookupTable2; KEVENT Buffer2ReadyForSwap; KEVENT InitiateSave; KEVENT DataReady; KEVENT DataProcessed; UINT64 CurrentOutputBase; UINT64 CurrentSaveOutputBase; UINT64 CurrentSaveOutputMask; UINT64 MappedAddress; //set by WaitForData , use with continue UINT64 Buffer2FlushSize; //used by WaitForData KDPC OwnDPC; HANDLE WriterThreadHandle; //for saveToFile mode HANDLE FileHandle; KEVENT FileAccess; UINT64 TraceFileSize; volatile BOOL Interrupted; } ProcessorInfo, *PProcessorInfo; volatile PProcessorInfo *PInfo; int Ultimap2CpuCount; KMUTEX SuspendMutex; KEVENT SuspendEvent; HANDLE SuspendThreadHandle; volatile int suspendCount; BOOL ultimapEnabled = FALSE; BOOL singleToPASystem = FALSE; BOOL NoPMIMode = FALSE; void suspendThread(PVOID StartContext) /* Thread responsible for suspending the target process when the buffer is getting full */ { NTSTATUS wr; __try { while (UltimapActive) { wr = KeWaitForSingleObject(&SuspendEvent, Executive, KernelMode, FALSE, NULL); if (!UltimapActive) return; DbgPrint("suspendThread event triggered"); KeWaitForSingleObject(&SuspendMutex, Executive, KernelMode, FALSE, NULL); if (!isSuspended) { if (CurrentTarget == 0) { if (PsSuspendProcess(CurrentTarget) == 0) isSuspended = TRUE; else DbgPrint("Failed to suspend target\n"); } } KeReleaseMutex(&SuspendMutex, FALSE); } } __except (1) { DbgPrint("Exception in suspendThread thread\n"); } } NTSTATUS ultimap2_continue(int cpunr) { NTSTATUS r = STATUS_UNSUCCESSFUL; if ((cpunr < 0) || (cpunr >= Ultimap2CpuCount)) { DbgPrint("ultimap2_continue(%d)", cpunr); return STATUS_UNSUCCESSFUL; } if (PInfo) { PProcessorInfo pi = PInfo[cpunr]; if (pi->MappedAddress) { MmUnmapLockedPages((PVOID)(UINT_PTR)pi->MappedAddress, pi->ToPABuffer2MDL); //unmap this memory pi->MappedAddress = 0; r = STATUS_SUCCESS; } else DbgPrint("MappedAddress was 0"); DbgPrint("%d DataProcessed", cpunr); KeSetEvent(&pi->DataProcessed, 0, FALSE); //let the next swap happen if needed } return r; } NTSTATUS ultimap2_waitForData(ULONG timeout, PULTIMAP2DATAEVENT data) { NTSTATUS r=STATUS_UNSUCCESSFUL; //Wait for the events in the list //If an event is triggered find out which one is triggered, then map that block into the usermode space and return the address and block //That block will be needed to continue if (UltimapActive) { NTSTATUS wr = STATUS_UNSUCCESSFUL; LARGE_INTEGER wait; PKWAIT_BLOCK waitblock; int cpunr; waitblock = ExAllocatePool(NonPagedPool, Ultimap2CpuCount*sizeof(KWAIT_BLOCK)); wait.QuadPart = -10000LL * timeout; if (timeout == 0xffffffff) //infinite wait wr = KeWaitForMultipleObjects(Ultimap2CpuCount, Ultimap2_DataReady, WaitAny, UserRequest, UserMode, TRUE, NULL, waitblock); else wr = KeWaitForMultipleObjects(Ultimap2CpuCount, Ultimap2_DataReady, WaitAny, UserRequest, UserMode, TRUE, &wait, waitblock); ExFreePool(waitblock); DbgPrint("ultimap2_waitForData wait returned %x", wr); cpunr = wr - STATUS_WAIT_0; if ((cpunr < Ultimap2CpuCount) && (cpunr>=0)) { PProcessorInfo pi = PInfo[cpunr]; if (pi->Buffer2FlushSize) { if (pi->ToPABuffer2MDL) { __try { data->Address = (UINT64)MmMapLockedPagesSpecifyCache(pi->ToPABuffer2MDL, UserMode, MmCached, NULL, FALSE, NormalPagePriority); DbgPrint("MmMapLockedPagesSpecifyCache returned address %p\n", data->Address); if (data->Address) { data->Size = pi->Buffer2FlushSize; data->CpuID = cpunr; pi->MappedAddress = data->Address; r = STATUS_SUCCESS; } } __except (1) { DbgPrint("ultimap2_waitForData: Failure mapping memory into waiter process. Count=%d", (int)MmGetMdlByteCount(pi->ToPABuffer2MDL)); } } else { DbgPrint("ToPABuffer2MDL is NULL. Not even gonna try"); } } else { DbgPrint("ultimap2_waitForData flushsize was 0"); } } } DbgPrint("ultimap2_waitForData returned %x\n", r); return r; } void createUltimap2OutputFile(int cpunr) { NTSTATUS r; PProcessorInfo pi = PInfo[cpunr]; UNICODE_STRING usFile; OBJECT_ATTRIBUTES oaFile; IO_STATUS_BLOCK iosb; WCHAR Buffer[200]; #ifdef AMD64 DbgPrint("OutputPath=%S", OutputPath); swprintf_s(Buffer, 200, L"%sCPU%d.trace", OutputPath, cpunr); #else RtlStringCbPrintfW(Buffer, 200, L"%sCPU%d.trace", OutputPath, cpunr); #endif DbgPrint("Buffer=%S", Buffer); RtlInitUnicodeString(&usFile, Buffer); InitializeObjectAttributes(&oaFile, &usFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); DbgPrint("Creating file %S", usFile.Buffer); pi->FileHandle = 0; ZwDeleteFile(&oaFile); r = ZwCreateFile(&pi->FileHandle, SYNCHRONIZE | FILE_READ_DATA | FILE_APPEND_DATA | GENERIC_ALL, &oaFile, &iosb, 0, FILE_ATTRIBUTE_NORMAL, 0, FILE_SUPERSEDE, FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); DbgPrint("%d: ZwCreateFile=%x\n", (int)cpunr, r); } void WriteThreadForSpecificCPU(PVOID StartContext) { int cpunr = (int)(UINT_PTR)StartContext; PProcessorInfo pi = PInfo[cpunr]; IO_STATUS_BLOCK iosb; NTSTATUS r = STATUS_UNSUCCESSFUL; //DbgPrint("WriteThreadForSpecificCPU %d alive", (int)StartContext); if (SaveToFile) { if (KeWaitForSingleObject(&pi->FileAccess, Executive, KernelMode, FALSE, NULL) == STATUS_SUCCESS) { createUltimap2OutputFile(cpunr); KeSetEvent(&pi->FileAccess, 0, FALSE); } else createUltimap2OutputFile(cpunr); } KeSetSystemAffinityThread((KAFFINITY)(1 << cpunr)); while (UltimapActive) { NTSTATUS wr = KeWaitForSingleObject(&pi->InitiateSave, Executive, KernelMode, FALSE, NULL); //DbgPrint("WriteThreadForSpecificCPU %d: wr=%x", (int)StartContext, wr); if (!UltimapActive) break; if (wr == STATUS_SUCCESS) { UINT64 Size; ToPA_LOOKUP tl; PToPA_LOOKUP result; //DbgPrint("%d: writing buffer", (int)StartContext); //figure out the size tl.PhysicalAddress = pi->CurrentSaveOutputBase; tl.index = 0; result = RtlLookupElementGenericTable(pi->ToPALookupTable2, &tl); if (result) { //write... //DbgPrint("%d: result->index=%d CurrentSaveOutputMask=%p", (int)StartContext, result->index, pi->CurrentSaveOutputMask); if (singleToPASystem) Size = pi->CurrentSaveOutputMask >> 32; else Size = ((result->index * 511) + ((pi->CurrentSaveOutputMask & 0xffffffff) >> 7)) * 4096 + (pi->CurrentSaveOutputMask >> 32); if (Size > 0) { if (SaveToFile) { wr = KeWaitForSingleObject(&pi->FileAccess, Executive, KernelMode, FALSE, NULL); if (wr==STATUS_SUCCESS) { if (pi->FileHandle==0) //a usermode flush has happened createUltimap2OutputFile(cpunr); r = ZwWriteFile(pi->FileHandle, NULL, NULL, NULL, &iosb, pi->ToPABuffer2, (ULONG)Size, NULL, NULL); pi->TraceFileSize += Size; //DbgPrint("%d: ZwCreateFile(%p, %d)=%x\n", (int)StartContext, pi->ToPABuffer2, (ULONG)Size, r); KeSetEvent(&pi->FileAccess, 0, FALSE); } } else { //map ToPABuffer2 into the CE process //wake up a worker thread pi->Buffer2FlushSize = Size; DbgPrint("%d: WorkerThread(%p, %d)=%x\n", (int)(UINT_PTR)StartContext, pi->ToPABuffer2, (ULONG)Size, r); KeSetEvent(&pi->DataReady, 0, TRUE); //a ce thread waiting in ultimap2_waitForData should now wake and process the data //and wait for it to finish r=KeWaitForSingleObject(&pi->DataProcessed, Executive, KernelMode, FALSE, NULL); DbgPrint("KeWaitForSingleObject(DataProcessed)=%x", r); } //DbgPrint("%d: Writing %x bytes\n", (int)StartContext, Size); } } else DbgPrint("Unexpected physical address while writing results for cpu %d (%p)", (int)(UINT_PTR)StartContext, pi->CurrentSaveOutputBase); KeSetEvent(&pi->Buffer2ReadyForSwap, 0, FALSE); } } KeSetSystemAffinityThread(KeQueryActiveProcessors()); if (pi->FileHandle) ZwClose(pi->FileHandle); KeSetEvent(&pi->Buffer2ReadyForSwap, 0, FALSE); } void ultimap2_LockFile(int cpunr) { if ((cpunr < 0) || (cpunr >= Ultimap2CpuCount)) return; if (PInfo) { NTSTATUS wr; PProcessorInfo pi = PInfo[cpunr]; //DbgPrint("AcquireUltimap2File()"); wr = KeWaitForSingleObject(&pi->FileAccess, Executive, KernelMode, FALSE, NULL); if (wr == STATUS_SUCCESS) { //DbgPrint("Acquired"); if (pi->FileHandle) { ZwClose(pi->FileHandle); pi->FileHandle = 0; } } } } void ultimap2_ReleaseFile(int cpunr) { if ((cpunr < 0) || (cpunr >= Ultimap2CpuCount)) return; if (PInfo) { PProcessorInfo pi = PInfo[cpunr]; KeSetEvent(&pi->FileAccess, 0, FALSE); //DbgPrint("Released"); } } UINT64 ultimap2_GetTraceFileSize() //Gets an aproximation of the filesize. Don't take this too exact { UINT64 size = 0; if (PInfo) { int i; for (i = 0; i < Ultimap2CpuCount; i++) size += PInfo[i]->TraceFileSize; } return size; } void ultimap2_ResetTraceFileSize() { if (PInfo) { int i; for (i = 0; i < Ultimap2CpuCount; i++) PInfo[i]->TraceFileSize = 0; } } void SwitchToPABuffer(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) /* DPC routine that switches the Buffer pointer and marks buffer2 that it's ready for data saving Only called when buffer2 is ready for flushing */ { //write the contents of the current cpu buffer PProcessorInfo pi = PInfo[KeGetCurrentProcessorNumber()]; //DbgPrint("SwitchToPABuffer for cpu %d\n", KeGetCurrentProcessorNumber()); if (pi) { UINT64 CTL = __readmsr(IA32_RTIT_CTL); UINT64 Status = __readmsr(IA32_RTIT_STATUS); PVOID temp; if ((Status >> 5) & 1) //Stopped DbgPrint("%d Not all data recorded\n", KeGetCurrentProcessorNumber()); if ((Status >> 4) & 1) DbgPrint("ALL LOST"); //only if the buffer is bigger than 2 pages. That you can check in IA32_RTIT_OUTPUT_MASK_PTRS and IA32_RTIT_OUTPUT_BASE //if (KeGetCurrentProcessorNumber() == 0) // DbgPrint("%d: pi->CurrentOutputBase=%p __readmsr(IA32_RTIT_OUTPUT_BASE)=%p __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)=%p", KeGetCurrentProcessorNumber(), pi->CurrentOutputBase, __readmsr(IA32_RTIT_OUTPUT_BASE), __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); if (pi->Interrupted == FALSE) { //return; //debug test. remove me when released if (!singleToPASystem) { if ((!flushallbuffers) && (((__readmsr(IA32_RTIT_OUTPUT_MASK_PTRS) & 0xffffffff) >> 7) < 2)) return; //don't flush yet } else { INT64 offset = __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS); /*if (KeGetCurrentProcessorNumber() == 0) { DbgPrint("pi->CurrentOutputBase=%p", pi->CurrentOutputBase); DbgPrint("offset=%p", offset); }*/ offset = offset >> 32; //if (KeGetCurrentProcessorNumber() == 0) // DbgPrint("offset=%p", offset); if ((!flushallbuffers) && (((pi->CurrentOutputBase == 0) || (offset < 8192)))) return; //don't flush yet } } else { DbgPrint("%d:Flushing because of interrupt", KeGetCurrentProcessorNumber()); } DbgPrint("%d: Flush this data (%p)", KeGetCurrentProcessorNumber(), __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); //DbgPrint("%d: pi->CurrentOutputBase=%p __readmsr(IA32_RTIT_OUTPUT_BASE)=%p __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)=%p", KeGetCurrentProcessorNumber(), pi->CurrentOutputBase, __readmsr(IA32_RTIT_OUTPUT_BASE), __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); __writemsr(IA32_RTIT_CTL, 0); //disable packet generation __writemsr(IA32_RTIT_STATUS, 0); //DbgPrint("%d: pi->CurrentOutputBase=%p __readmsr(IA32_RTIT_OUTPUT_BASE)=%p __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)=%p", KeGetCurrentProcessorNumber(), pi->CurrentOutputBase, __readmsr(IA32_RTIT_OUTPUT_BASE), __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); //switch the pointer to the secondary buffers KeClearEvent(&pi->Buffer2ReadyForSwap); //swap the buffer temp = pi->ToPABuffer; pi->ToPABuffer = pi->ToPABuffer2; pi->ToPABuffer2 = temp; //swap the MDL that describes it temp = pi->ToPABufferMDL; pi->ToPABufferMDL = pi->ToPABuffer2MDL; pi->ToPABuffer2MDL = temp; //swap the header temp = pi->ToPAHeader; pi->ToPAHeader = pi->ToPAHeader2; pi->ToPAHeader2 = temp; //swap the lookup table temp = pi->ToPALookupTable; pi->ToPALookupTable = pi->ToPALookupTable2; pi->ToPALookupTable2 = temp; //lookup which entry it's pointing at pi->CurrentSaveOutputBase = __readmsr(IA32_RTIT_OUTPUT_BASE); pi->CurrentSaveOutputMask = __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS); KeSetEvent(&pi->InitiateSave,0,FALSE); pi->Interrupted = FALSE; //reactivate packet generation pi->CurrentOutputBase = MmGetPhysicalAddress(pi->ToPAHeader).QuadPart; __writemsr(IA32_RTIT_OUTPUT_BASE, pi->CurrentOutputBase); __writemsr(IA32_RTIT_OUTPUT_MASK_PTRS, 0); __writemsr(IA32_RTIT_CTL, CTL); } } void WaitForWriteToFinishAndSwapWriteBuffers(BOOL interruptedOnly) { int i; for (i = 0; i < Ultimap2CpuCount; i++) { PProcessorInfo pi = PInfo[i]; if ((pi->ToPABuffer2) && ((pi->Interrupted) || (!interruptedOnly))) { KeWaitForSingleObject(&pi->Buffer2ReadyForSwap, Executive, KernelMode, FALSE, NULL); if (!UltimapActive) return; KeInsertQueueDpc(&pi->OwnDPC, NULL, NULL); } } KeFlushQueuedDpcs(); } void bufferWriterThread(PVOID StartContext) { //passive mode //wait for event LARGE_INTEGER Timeout; NTSTATUS wr; DbgPrint("bufferWriterThread active"); while (UltimapActive) { if (NoPMIMode) Timeout.QuadPart = -1000LL; //- 10000LL=1 millisecond //-100000000LL = 10 seconds -1000000LL= 0.1 second else Timeout.QuadPart = -10000LL; //- 10000LL=1 millisecond //-100000000LL = 10 seconds -1000000LL= 0.1 second //DbgPrint("%d : Wait for FlushData", cpunr()); wr = KeWaitForSingleObject(&FlushData, Executive, KernelMode, FALSE, &Timeout); //DbgPrint("%d : After wait for FlushData", cpunr()); //wr = KeWaitForSingleObject(&FlushData, Executive, KernelMode, FALSE, NULL); //DbgPrint("bufferWriterThread: Alive (wr==%x)", wr); if (!UltimapActive) { DbgPrint("bufferWriterThread: Terminating"); return; } //if (wr != STATUS_SUCCESS) continue; //DEBUG code so PMI's get triggered if ((wr == STATUS_SUCCESS) || (wr == STATUS_TIMEOUT)) { if ((wr == STATUS_SUCCESS) && (!isSuspended)) { //woken up by a dpc DbgPrint("FlushData event set and not suspended. Suspending target process\n"); KeWaitForSingleObject(&SuspendMutex, Executive, KernelMode, FALSE, NULL); if (!isSuspended) { DbgPrint("Still going to suspend target process"); if (PsSuspendProcess(CurrentTarget)==0) isSuspended = TRUE; } KeReleaseMutex(&SuspendMutex, FALSE); DbgPrint("After the target has been suspended (isSuspended=%d)\n", isSuspended); } if (wr == STATUS_SUCCESS) //the filled cpu's must take preference { unsigned int i; BOOL found = TRUE; //DbgPrint("bufferWriterThread: Suspended"); //first flush the CPU's that complained their buffers are full DbgPrint("Flushing full CPU\'s"); while (found) { WaitForWriteToFinishAndSwapWriteBuffers(TRUE); if (!UltimapActive) return; //check if no interrupt has been triggered while this was busy ('could' happen as useless info like core ratio is still recorded) found = FALSE; for (i = 0; i < KeQueryMaximumProcessorCount(); i++) { if (PInfo[i]->Interrupted) { DbgPrint("PInfo[%d]->Interrupted\n", PInfo[i]->Interrupted); found = TRUE; break; } } } } //wait till the previous buffers are done writing //DbgPrint("%d: Normal flush", cpunr()); WaitForWriteToFinishAndSwapWriteBuffers(FALSE); //DbgPrint("%d : after flush", cpunr()); if (isSuspended) { KeWaitForSingleObject(&SuspendMutex, Executive, KernelMode, FALSE, NULL); if (isSuspended) { DbgPrint("Resuming target process"); PsResumeProcess(CurrentTarget); isSuspended = FALSE; } KeReleaseMutex(&SuspendMutex, FALSE); } //an interrupt could have fired while WaitForWriteToFinishAndSwapWriteBuffers was busy, pausing the process. If that happened, then the next KeWaitForSingleObject will exit instantly due to it being signaled } else DbgPrint("Unexpected wait result"); } } NTSTATUS ultimap2_flushBuffers() { if (!UltimapActive) return STATUS_UNSUCCESSFUL; DbgPrint("ultimap2_flushBuffers"); KeWaitForSingleObject(&SuspendMutex, Executive, KernelMode, FALSE, NULL); if (CurrentTarget) { if (!isSuspended) { PsSuspendProcess(CurrentTarget); isSuspended = TRUE; } } KeReleaseMutex(&SuspendMutex, FALSE); flushallbuffers = TRUE; DbgPrint("wait1"); WaitForWriteToFinishAndSwapWriteBuffers(FALSE); //write the last saved buffer DbgPrint("wait2"); WaitForWriteToFinishAndSwapWriteBuffers(FALSE); //write the current buffer flushallbuffers = FALSE; DbgPrint("after wait"); KeWaitForSingleObject(&SuspendMutex, Executive, KernelMode, FALSE, NULL); if (CurrentTarget) { if (isSuspended) { PsResumeProcess(CurrentTarget); isSuspended = FALSE; } } KeReleaseMutex(&SuspendMutex, FALSE); DbgPrint("ultimap2_flushBuffers exit"); return STATUS_SUCCESS; } void RTIT_DPC_Handler(__in struct _KDPC *Dpc, __in_opt PVOID DeferredContext, __in_opt PVOID SystemArgument1,__in_opt PVOID SystemArgument2) { //Signal the bufferWriterThread KeSetEvent(&SuspendEvent, 0, FALSE); KeSetEvent(&FlushData, 0, FALSE); } void PMI(__in struct _KINTERRUPT *Interrupt, __in PVOID ServiceContext) { //check if caused by me, if so defer to dpc DbgPrint("PMI"); __try { if ((__readmsr(IA32_PERF_GLOBAL_STATUS) >> 55) & 1) { UINT64 Status = __readmsr(IA32_RTIT_STATUS); DbgPrint("PMI: caused by me"); __writemsr(IA32_PERF_GLOBAL_OVF_CTRL, (UINT64)1 << 55); //clear ToPA full status if ((__readmsr(IA32_PERF_GLOBAL_STATUS) >> 55) & 1) { DbgPrint("PMI: Failed to clear the status\n"); } DbgPrint("PMI: IA32_RTIT_OUTPUT_MASK_PTRS=%p\n", __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); DbgPrint("PMI: IA32_RTIT_STATUS=%p\n", Status); if ((Status >> 5) & 1) //Stopped DbgPrint("PMI %d: Not all data recorded (AT THE PMI!)\n", KeGetCurrentProcessorNumber()); DbgPrint("PMI: IA32_RTIT_OUTPUT_MASK_PTRS %p\n", __readmsr(IA32_RTIT_OUTPUT_MASK_PTRS)); PInfo[KeGetCurrentProcessorNumber()]->Interrupted = TRUE; KeInsertQueueDpc(&RTID_DPC, NULL, NULL); //clear apic state apic_clearPerfmon(); } else { DbgPrint("Unexpected PMI"); } } __except (0) { DbgPrint("PMI exception"); } } void *pperfmon_hook2 = (void *)PMI; void ultimap2_disable_dpc(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { DbgPrint("ultimap2_disable_dpc for cpu %d\n", KeGetCurrentProcessorNumber()); __try { if (DeferredContext) //only pause { RTIT_CTL ctl; DbgPrint("temp disable\n"); ctl.Value = __readmsr(IA32_RTIT_CTL); ctl.Bits.TraceEn = 0; __writemsr(IA32_RTIT_CTL, ctl.Value); } else { DbgPrint("%d: disable all\n", KeGetCurrentProcessorNumber()); __writemsr(IA32_RTIT_CTL, 0); __writemsr(IA32_RTIT_STATUS, 0); __writemsr(IA32_RTIT_CR3_MATCH, 0); __writemsr(IA32_RTIT_OUTPUT_BASE, 0); __writemsr(IA32_RTIT_OUTPUT_MASK_PTRS, 0); } } __except (1) { DbgPrint("ultimap2_disable_dpc exception"); } } void ultimap2_setup_dpc(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { RTIT_CTL ctl; RTIT_STATUS s; int i = -1; __try { ctl.Value = __readmsr(IA32_RTIT_CTL); } __except (1) { DbgPrint("ultimap2_setup_dpc: IA32_RTIT_CTL in unreadable"); return; } ctl.Bits.TraceEn = 1; if (LogKernelMode) ctl.Bits.OS = 1; else ctl.Bits.OS = 0; if (LogUserMode) ctl.Bits.USER = 1; else ctl.Bits.USER = 0; if (CurrentCR3) ctl.Bits.CR3Filter = 1; else ctl.Bits.CR3Filter = 0; ctl.Bits.ToPA = 1; ctl.Bits.TSCEn = 0; ctl.Bits.DisRETC = 0; ctl.Bits.BranchEn = 1; if (PInfo == NULL) return; if (PInfo[KeGetCurrentProcessorNumber()]->ToPABuffer == NULL) { DbgPrint("ToPA for cpu %d not setup\n", KeGetCurrentProcessorNumber()); return; } __try { int cpunr = KeGetCurrentProcessorNumber(); i = 0; PInfo[cpunr]->CurrentOutputBase = MmGetPhysicalAddress(PInfo[cpunr]->ToPAHeader).QuadPart; __writemsr(IA32_RTIT_OUTPUT_BASE, PInfo[cpunr]->CurrentOutputBase); i = 1; __writemsr(IA32_RTIT_OUTPUT_MASK_PTRS, 0); i = 2; __try { __writemsr(IA32_RTIT_CR3_MATCH, CurrentCR3); } __except (1) { CurrentCR3 = CurrentCR3 & 0xfffffffffffff000ULL; DbgPrint("Failed to set the actual CR3. Using a sanitized CR3: %llx\n", CurrentCR3); } i = 3; //ranges if (Ultimap2Ranges && Ultimap2RangeCount) { for (i = 0; i < Ultimap2RangeCount; i++) { ULONG msr_start = IA32_RTIT_ADDR0_A + (2 * i); ULONG msr_stop = IA32_RTIT_ADDR0_B + (2 * i); UINT64 bit = 32 + (i * 4); DbgPrint("Range %d: (%p -> %p)", i, (PVOID)(UINT_PTR)(Ultimap2Ranges[i].StartAddress), (PVOID)(UINT_PTR)(Ultimap2Ranges[i].EndAddress)); DbgPrint("Writing range %d to msr %x and %x", i, msr_start, msr_stop); __writemsr(msr_start, Ultimap2Ranges[i].StartAddress); __writemsr(msr_stop, Ultimap2Ranges[i].EndAddress); DbgPrint("bit=%d", bit); DbgPrint("Value before=%llx", ctl.Value); if (Ultimap2Ranges[i].IsStopAddress) ctl.Value |= (UINT64)2ULL << bit; //TraceStop This stops all tracing on this cpu. Doesn't get reactivated else ctl.Value |= (UINT64)1ULL << bit; //FilterEn //not supported in the latest windows build DbgPrint("Value after=%llx", ctl.Value); } } i = 4; __writemsr(IA32_RTIT_STATUS, 0); i = 5; //if (KeGetCurrentProcessorNumber() == 0) __writemsr(IA32_RTIT_CTL, ctl.Value); i = 6; s.Value=__readmsr(IA32_RTIT_STATUS); if (s.Bits.Error) DbgPrint("Setup for cpu %d failed", KeGetCurrentProcessorNumber()); else DbgPrint("Setup for cpu %d succesful", KeGetCurrentProcessorNumber()); } __except (1) { DbgPrint("Error in ultimap2_setup_dpc. i=%d",i); DbgPrint("ctl.Value=%p\n", ctl.Value); DbgPrint("CR3=%p\n", CurrentCR3); //DbgPrint("OutputBase=%p", __readmsr(IA32_RTIT_OUTPUT_BASE)); } } int getToPAHeaderCount(ULONG _BufferSize) { return 1 + (_BufferSize / 4096) / 511; } int getToPAHeaderSize(ULONG _BufferSize) { //511 entries per ToPA header (4096*511=2093056 bytes per ToPA header) //BufferSize / 2093056 = Number of ToPA headers needed return getToPAHeaderCount(_BufferSize) * 4096; } RTL_GENERIC_COMPARE_RESULTS NTAPI ToPACompare(__in struct _RTL_GENERIC_TABLE *Table, __in PToPA_LOOKUP FirstStruct, __in PToPA_LOOKUP SecondStruct) { //DbgPrint("Comparing %p with %p", FirstStruct->PhysicalAddress, FirstStruct->PhysicalAddress); if (FirstStruct->PhysicalAddress == SecondStruct->PhysicalAddress) return GenericEqual; else { if (SecondStruct->PhysicalAddress < FirstStruct->PhysicalAddress) return GenericLessThan; else return GenericGreaterThan; } } PVOID NTAPI ToPAAlloc(__in struct _RTL_GENERIC_TABLE *Table, __in CLONG ByteSize) { return ExAllocatePool(NonPagedPool, ByteSize); } VOID NTAPI ToPADealloc(__in struct _RTL_GENERIC_TABLE *Table, __in __drv_freesMem(Mem) __post_invalid PVOID Buffer) { ExFreePool(Buffer); } void* setupToPA(PToPA_ENTRY *Header, PVOID *OutputBuffer, PMDL *BufferMDL, PRTL_GENERIC_TABLE *gt, ULONG _BufferSize, int NoPMI) { ToPA_LOOKUP tl; PToPA_ENTRY r; UINT_PTR Output, Stop; ULONG ToPAIndex = 0; int PABlockSize = 0; int BlockSize; PRTL_GENERIC_TABLE x; int i; if (singleToPASystem) { PHYSICAL_ADDRESS la,ha, boundary; ULONG newsize; BlockSize = _BufferSize; //yup, only 1 single entry //get the closest possible if (BlockSize > 64 * 1024 * 1024) { PABlockSize = 15; BlockSize = 128 * 1024 * 1024; } else if (BlockSize > 32 * 1024 * 1024) { PABlockSize = 14; BlockSize = 64 * 1024 * 1024; } else if (BlockSize > 16 * 1024 * 1024) { PABlockSize = 13; BlockSize = 32 * 1024 * 1024; } else if (BlockSize > 8 * 1024 * 1024) { PABlockSize = 12; BlockSize = 16 * 1024 * 1024; } else if (BlockSize > 4 * 1024 * 1024) { PABlockSize = 11; BlockSize = 8 * 1024 * 1024; } else if (BlockSize > 2 * 1024 * 1024) { PABlockSize = 10; BlockSize = 4 * 1024 * 1024; } else if (BlockSize > 1 * 1024 * 1024) { PABlockSize = 9; BlockSize = 2 * 1024 * 1024; } else if (BlockSize > 512 * 1024) { PABlockSize = 8; BlockSize = 1 * 1024 * 1024; } else if (BlockSize > 256 * 1024) { PABlockSize = 7; BlockSize = 512 * 1024; } else if (BlockSize > 128 * 1024) { PABlockSize = 6; BlockSize = 256 * 1024; } else if (BlockSize > 64 * 1024) { PABlockSize = 5; BlockSize = 128 * 1024; } else if (BlockSize > 32 * 1024) { PABlockSize = 4; BlockSize = 64 * 1024; } else if (BlockSize > 16*1024) { PABlockSize = 3; BlockSize = 32 * 1024; } else if (BlockSize > 8 * 1024) { PABlockSize = 2; BlockSize = 16 * 1024; } else if (BlockSize > 4 * 1024) { PABlockSize = 1; BlockSize = 8 * 1024; } else { PABlockSize = 0; BlockSize = 4096; } //adjust the buffersize so it is dividable by the blocksize newsize = BlockSize; DbgPrint("BufferSize=%x\n", _BufferSize); DbgPrint("BlockSize=%x (PABlockSize=%d)\n", BlockSize, PABlockSize); DbgPrint("newsize=%x\n", newsize); la.QuadPart = 0; ha.QuadPart = 0xFFFFFFFFFFFFFFFFULL; boundary.QuadPart = BlockSize; *OutputBuffer=MmAllocateContiguousMemorySpecifyCache(newsize, la, ha, boundary, MmCached); //*OutputBuffer=MmAllocateContiguousMemory(newsize, ha); DbgPrint("Allocated OutputBuffer at %p", MmGetPhysicalAddress(*OutputBuffer).QuadPart); _BufferSize = newsize; if (*OutputBuffer == NULL) { DbgPrint("setupToPA (Single ToPA System): Failure allocating output buffer"); return NULL; } r = ExAllocatePool(NonPagedPool, 4096); if (r == NULL) { MmFreeContiguousMemory(*OutputBuffer); *OutputBuffer = NULL; DbgPrint("setupToPA (Single ToPA System): Failure allocating header for buffer"); return NULL; } } else { //Not a single ToPA system BlockSize = 4096; *OutputBuffer = ExAllocatePool(NonPagedPool, _BufferSize); if (*OutputBuffer == NULL) { DbgPrint("setupToPA: Failure allocating output buffer"); return NULL; } r = ExAllocatePool(NonPagedPool, getToPAHeaderSize(_BufferSize)); if (r == NULL) { ExFreePool(*OutputBuffer); *OutputBuffer = NULL; DbgPrint("setupToPA: Failure allocating header for buffer"); return NULL; } } *Header = r; *gt=ExAllocatePool(NonPagedPool, sizeof(RTL_GENERIC_TABLE)); if (*gt == NULL) { DbgPrint("Failure allocating table"); if (singleToPASystem) MmFreeContiguousMemory(*OutputBuffer); else ExFreePool(*OutputBuffer); *OutputBuffer = NULL; ExFreePool(*Header); *Header = NULL; return NULL; } x = *gt; RtlInitializeGenericTable(x, ToPACompare, ToPAAlloc, ToPADealloc, NULL); tl.index = 0; tl.PhysicalAddress = MmGetPhysicalAddress(&r[0]).QuadPart; RtlInsertElementGenericTable(x, &tl, sizeof(tl), NULL); Output = (UINT_PTR)*OutputBuffer; Stop = Output+_BufferSize; *BufferMDL = IoAllocateMdl(*OutputBuffer, _BufferSize, FALSE, FALSE, NULL); MmBuildMdlForNonPagedPool(*BufferMDL); if (singleToPASystem) { r[0].Value = (UINT64)MmGetPhysicalAddress((PVOID)Output).QuadPart; r[0].Bits.Size = PABlockSize; if (NoPMI) r[0].Bits.INT = 0; else r[0].Bits.INT = 1; r[0].Bits.STOP = 1; r[1].Value = MmGetPhysicalAddress(&r[0]).QuadPart; r[1].Bits.END = 1; } else { while (Output < Stop) { //fill in the topa entries pointing to eachother if ((ToPAIndex + 1) % 512 == 0) { //point it to the next ToPA table r[ToPAIndex].Value = MmGetPhysicalAddress(&r[ToPAIndex + 1]).QuadPart; r[ToPAIndex].Bits.END = 1; tl.index = tl.index++; tl.PhysicalAddress = MmGetPhysicalAddress(&r[ToPAIndex + 1]).QuadPart; RtlInsertElementGenericTable(x, &tl, sizeof(tl), NULL); } else { r[ToPAIndex].Value = (UINT64)MmGetPhysicalAddress((PVOID)Output).QuadPart; r[ToPAIndex].Bits.Size = 0; Output += 4096; } ToPAIndex++; } ToPAIndex--; r[ToPAIndex].Bits.STOP = 1; i = (ToPAIndex * 90) / 100; //90% if ((i == (int)ToPAIndex) && (i > 0)) //don't interrupt on the very last entry (if possible) i--; if ((i > 0) && ((i + 1) % 512 == 0)) i--; DbgPrint("Interrupt at index %d", i); if (NoPMI) r[i].Bits.INT = 0; else r[i].Bits.INT = 1; //Interrupt after filling this entry //and every 2nd page after this. (in case of a rare situation where resume is called right after suspend) if (ToPAIndex > 0) { while (i < (int)(ToPAIndex - 1)) { if (((i + 1) % 512) && (NoPMI==0)) //anything but 0 r[i].Bits.INT = 1; i += 2; } } } return (void *)r; } NTSTATUS ultimap2_pause() { if (ultimapEnabled) { forEachCpu(ultimap2_disable_dpc, (PVOID)1, NULL, NULL, NULL); if (UltimapActive) { flushallbuffers = TRUE; WaitForWriteToFinishAndSwapWriteBuffers(FALSE); //write the last saved buffer WaitForWriteToFinishAndSwapWriteBuffers(FALSE); //write the current buffer flushallbuffers = FALSE; } } return STATUS_SUCCESS; } NTSTATUS ultimap2_resume() { if ((ultimapEnabled) && (PInfo)) forEachCpu(ultimap2_setup_dpc, NULL, NULL, NULL, NULL); return STATUS_SUCCESS; } void *clear = NULL; BOOL RegisteredProfilerInterruptHandler; void SetupUltimap2(UINT32 PID, UINT32 BufferSize, WCHAR *Path, int rangeCount, PURANGE Ranges, int NoPMI, int UserMode, int KernelMode) { //for each cpu setup tracing //add the PMI interupt int i; NTSTATUS r= STATUS_UNSUCCESSFUL; int cpuid_r[4]; if (Path) DbgPrint("SetupUltimap2(%x, %x, %S, %d, %p,%d,%d,%d\n", PID, BufferSize, Path, rangeCount, Ranges, NoPMI, UserMode, KernelMode); else DbgPrint("SetupUltimap2(%x, %x, %d, %p,%d,%d,%d\n", PID, BufferSize, rangeCount, Ranges, NoPMI, UserMode, KernelMode); __cpuidex(cpuid_r, 0x14, 0); if ((cpuid_r[2] & 2) == 0) { DbgPrint("Single ToPA System"); singleToPASystem = TRUE; } NoPMIMode = NoPMI; LogKernelMode = KernelMode; LogUserMode = UserMode; DbgPrint("Path[0]=%d\n", Path[0]); SaveToFile = (Path[0] != 0); if (SaveToFile) { wcsncpy(OutputPath, Path, 199); OutputPath[199] = 0; DbgPrint("Ultimap2: SaveToFile==TRUE: OutputPath=%S",OutputPath); } else { DbgPrint("Ultimap2: Runtime processing"); } if (rangeCount) { if (Ultimap2Ranges) { ExFreePool(Ultimap2Ranges); Ultimap2Ranges = NULL; } Ultimap2Ranges = ExAllocatePool(NonPagedPool, rangeCount*sizeof(URANGE)); for (i = 0; i < rangeCount; i++) Ultimap2Ranges[i] = Ranges[i]; Ultimap2RangeCount = rangeCount; } else Ultimap2RangeCount = 0; //get the EProcess and CR3 for this PID if (PID) { if (PsLookupProcessByProcessId((PVOID)PID, &CurrentTarget) == STATUS_SUCCESS) { //todo add specific windows version checks and hardcode offsets/ or use scans if (getCR3() & 0xfff) { DbgPrint("Split kernel/usermode pages\n"); //uses supervisor/usermode pagemaps CurrentCR3 = *(UINT64 *)((UINT_PTR)CurrentTarget + 0x278); if ((CurrentCR3 & 0xfffffffffffff000ULL) == 0) { DbgPrint("No usermode CR3\n"); CurrentCR3 = *(UINT64 *)((UINT_PTR)CurrentTarget + 0x28); } DbgPrint("CurrentCR3=%llx\n", CurrentCR3); } else { KAPC_STATE apc_state; RtlZeroMemory(&apc_state, sizeof(apc_state)); __try { KeStackAttachProcess((PVOID)CurrentTarget, &apc_state); CurrentCR3 = getCR3(); KeUnstackDetachProcess(&apc_state); } __except (1) { DbgPrint("Failure getting CR3 for this process"); return; } } } else { DbgPrint("Failure getting the EProcess for pid %d", PID); return; } } else { CurrentTarget = 0; CurrentCR3 = 0; } DbgPrint("CurrentCR3=%llx\n", CurrentCR3); if ((PsSuspendProcess == NULL) || (PsResumeProcess == NULL)) { DbgPrint("No Suspend/Resume support"); return; } KeInitializeDpc(&RTID_DPC, RTIT_DPC_Handler, NULL); KeInitializeEvent(&FlushData, SynchronizationEvent, FALSE); KeInitializeEvent(&SuspendEvent, SynchronizationEvent, FALSE); KeInitializeMutex(&SuspendMutex, 0); Ultimap2CpuCount = KeQueryMaximumProcessorCount(); PInfo = ExAllocatePool(NonPagedPool, Ultimap2CpuCount*sizeof(PProcessorInfo)); Ultimap2_DataReady = ExAllocatePool(NonPagedPool, Ultimap2CpuCount*sizeof(PVOID)); if (PInfo == NULL) { DbgPrint("PInfo alloc failed"); return; } if (Ultimap2_DataReady == NULL) { DbgPrint("Ultimap2_DataReady alloc failed"); return; } for (i = 0; i < Ultimap2CpuCount; i++) { PInfo[i] = ExAllocatePool(NonPagedPool, sizeof(ProcessorInfo)); RtlZeroMemory(PInfo[i], sizeof(ProcessorInfo)); KeInitializeEvent(&PInfo[i]->InitiateSave, SynchronizationEvent, FALSE); KeInitializeEvent(&PInfo[i]->Buffer2ReadyForSwap, NotificationEvent, TRUE); setupToPA(&PInfo[i]->ToPAHeader, &PInfo[i]->ToPABuffer, &PInfo[i]->ToPABufferMDL, &PInfo[i]->ToPALookupTable, BufferSize, NoPMI); setupToPA(&PInfo[i]->ToPAHeader2, &PInfo[i]->ToPABuffer2, &PInfo[i]->ToPABuffer2MDL, &PInfo[i]->ToPALookupTable2, BufferSize, NoPMI); DbgPrint("cpu %d:", i); DbgPrint("ToPAHeader=%p ToPABuffer=%p Size=%x", PInfo[i]->ToPAHeader, PInfo[i]->ToPABuffer, BufferSize); DbgPrint("ToPAHeader2=%p ToPABuffer2=%p Size=%x", PInfo[i]->ToPAHeader2, PInfo[i]->ToPABuffer2, BufferSize); KeInitializeEvent(&PInfo[i]->DataReady, SynchronizationEvent, FALSE); KeInitializeEvent(&PInfo[i]->DataProcessed, SynchronizationEvent, FALSE); KeInitializeEvent(&PInfo[i]->FileAccess, SynchronizationEvent, TRUE); Ultimap2_DataReady[i] = &PInfo[i]->DataReady; KeInitializeDpc(&PInfo[i]->OwnDPC, SwitchToPABuffer, NULL); KeSetTargetProcessorDpc(&PInfo[i]->OwnDPC, (CCHAR)i); } UltimapActive = TRUE; ultimapEnabled = TRUE; PsCreateSystemThread(&SuspendThreadHandle, 0, NULL, 0, NULL, suspendThread, NULL); PsCreateSystemThread(&Ultimap2Handle, 0, NULL, 0, NULL, bufferWriterThread, NULL); for (i = 0; i < Ultimap2CpuCount; i++) PsCreateSystemThread(&PInfo[i]->WriterThreadHandle, 0, NULL, 0, NULL, WriteThreadForSpecificCPU, (PVOID)i); if ((NoPMI == FALSE) && (RegisteredProfilerInterruptHandler == FALSE)) { DbgPrint("Registering PMI handler\n"); pperfmon_hook2 = (void *)PMI; r = HalSetSystemInformation(HalProfileSourceInterruptHandler, sizeof(PVOID*), &pperfmon_hook2); //hook the perfmon interrupt if (r == STATUS_SUCCESS) RegisteredProfilerInterruptHandler = TRUE; DbgPrint("HalSetSystemInformation returned %x\n", r); if (r != STATUS_SUCCESS) DbgPrint("Failure hooking the permon interrupt. Ultimap2 will not be able to use interrupts until you reboot (This can happen when the perfmon interrupt is hooked more than once. It has no restore/undo hook)\n"); } forEachCpu(ultimap2_setup_dpc, NULL, NULL, NULL, NULL); } void UnregisterUltimapPMI() { NTSTATUS r; DbgPrint("UnregisterUltimapPMI()\n"); if (RegisteredProfilerInterruptHandler) { pperfmon_hook2 = NULL; r = HalSetSystemInformation(HalProfileSourceInterruptHandler, sizeof(PVOID*), &pperfmon_hook2); DbgPrint("1: HalSetSystemInformation to disable returned %x\n", r); if (r == STATUS_SUCCESS) return; r = HalSetSystemInformation(HalProfileSourceInterruptHandler, sizeof(PVOID*), &clear); //unhook the perfmon interrupt DbgPrint("2: HalSetSystemInformation to disable returned %x\n", r); if (r == STATUS_SUCCESS) return; r = HalSetSystemInformation(HalProfileSourceInterruptHandler, sizeof(PVOID*), 0); DbgPrint("3: HalSetSystemInformation to disable returned %x\n", r); } else DbgPrint("UnregisterUltimapPMI() not needed\n"); } void DisableUltimap2(void) { int i; DbgPrint("-------------------->DisableUltimap2<------------------"); if (!ultimapEnabled) return; DbgPrint("-------------------->DisableUltimap2:Stage 1<------------------"); forEachCpuAsync(ultimap2_disable_dpc, NULL, NULL, NULL, NULL); UltimapActive = FALSE; if (SuspendThreadHandle) { DbgPrint("Waiting for SuspendThreadHandle"); KeSetEvent(&SuspendEvent, 0, FALSE); ZwWaitForSingleObject(SuspendThreadHandle, FALSE, NULL); ZwClose(SuspendThreadHandle); SuspendThreadHandle = NULL; } if (PInfo) { for (i = 0; i < Ultimap2CpuCount; i++) { KeSetEvent(&PInfo[i]->DataProcessed, 0, FALSE); KeSetEvent(&PInfo[i]->DataReady, 0, FALSE); } } if (Ultimap2Handle) { DbgPrint("Waiting for Ultimap2Handle"); KeSetEvent(&FlushData, 0, FALSE); ZwWaitForSingleObject(Ultimap2Handle, FALSE, NULL); ZwClose(Ultimap2Handle); Ultimap2Handle = NULL; } if (PInfo) { DbgPrint("going to deal with the PInfo data"); for (i = 0; i < Ultimap2CpuCount; i++) { if (PInfo[i]) { PToPA_LOOKUP li; KeSetEvent(&PInfo[i]->Buffer2ReadyForSwap, 0, FALSE); KeSetEvent(&PInfo[i]->InitiateSave, 0, FALSE); DbgPrint("Waiting for WriterThreadHandle[%d]",i); ZwWaitForSingleObject(PInfo[i]->WriterThreadHandle, FALSE, NULL); ZwClose(PInfo[i]->WriterThreadHandle); PInfo[i]->WriterThreadHandle = NULL; if (PInfo[i]->ToPABufferMDL) { IoFreeMdl(PInfo[i]->ToPABufferMDL); PInfo[i]->ToPABufferMDL = NULL; } if (PInfo[i]->ToPABuffer) { if (singleToPASystem) MmFreeContiguousMemory(PInfo[i]->ToPABuffer); else ExFreePool(PInfo[i]->ToPABuffer); PInfo[i]->ToPABuffer = NULL; } if (PInfo[i]->ToPABuffer2MDL) { IoFreeMdl(PInfo[i]->ToPABuffer2MDL); PInfo[i]->ToPABufferMDL = NULL; } if (PInfo[i]->ToPABuffer2) { if (singleToPASystem) MmFreeContiguousMemory(PInfo[i]->ToPABuffer2); else ExFreePool(PInfo[i]->ToPABuffer2); PInfo[i]->ToPABuffer2 = NULL; } if (PInfo[i]->ToPAHeader) { ExFreePool(PInfo[i]->ToPAHeader); PInfo[i]->ToPAHeader = NULL; } if (PInfo[i]->ToPAHeader2) { ExFreePool(PInfo[i]->ToPAHeader2); PInfo[i]->ToPAHeader2 = NULL; } while (li = RtlGetElementGenericTable(PInfo[i]->ToPALookupTable, 0)) RtlDeleteElementGenericTable(PInfo[i]->ToPALookupTable, li); ExFreePool(PInfo[i]->ToPALookupTable); PInfo[i]->ToPALookupTable = NULL; while (li = RtlGetElementGenericTable(PInfo[i]->ToPALookupTable2, 0)) RtlDeleteElementGenericTable(PInfo[i]->ToPALookupTable2, li); ExFreePool(PInfo[i]->ToPALookupTable2); PInfo[i]->ToPALookupTable2 = NULL; ExFreePool(PInfo[i]); PInfo[i] = NULL; } } ExFreePool(PInfo); ExFreePool(Ultimap2_DataReady); PInfo = NULL; DbgPrint("Finished terminating ultimap2"); } if (Ultimap2Ranges) { ExFreePool(Ultimap2Ranges); Ultimap2Ranges = NULL; Ultimap2RangeCount = 0; } DbgPrint("-------------------->DisableUltimap2:Finish<------------------"); } ================================================ FILE: src/ultimap2.h ================================================ #ifndef ULTIMAP2_H #define ULTIMAP2_H #include //MSR's #define IA32_PERF_GLOBAL_STATUS 0x38e #define IA32_PERF_GLOBAL_OVF_CTRL 0x390 #define IA32_RTIT_CTL 0x570 #define IA32_RTIT_STATUS 0x571 #define IA32_RTIT_CR3_MATCH 0x572 #define IA32_RTIT_OUTPUT_BASE 0x560 #define IA32_RTIT_OUTPUT_MASK_PTRS 0x561 #define IA32_RTIT_ADDR0_A 0x580 #define IA32_RTIT_ADDR0_B 0x581 #define IA32_RTIT_ADDR1_A 0x582 #define IA32_RTIT_ADDR1_B 0x583 #define IA32_RTIT_ADDR2_A 0x584 #define IA32_RTIT_ADDR2_B 0x585 #define IA32_RTIT_ADDR3_A 0x586 #define IA32_RTIT_ADDR3_B 0x587 typedef struct { UINT64 StartAddress; UINT64 EndAddress; UINT64 IsStopAddress; } URANGE, *PURANGE; #pragma pack(push) #pragma pack(1) typedef union { struct{ ULONG TraceEn : 1; //0 ULONG Reserved_0 : 1; //1 ULONG OS : 1; //2 ULONG USER : 1; //3 ULONG Reserved_1 : 2; //4,5 ULONG FabricEn : 1; //6 ULONG CR3Filter : 1; //7 ULONG ToPA : 1; //8 ULONG MTCEn : 1; //9 ULONG TSCEn : 1; //10 ULONG DisRETC : 1; //11 ULONG Reserved_2 : 1; //12 ULONG BranchEn : 1; //13 ULONG MTCFreq : 4; //14,15,16,17 ULONG Reserved_3 : 1; //18 ULONG CycThresh : 4; //19,20,21,22 ULONG Reserved_4 : 1; //32 ULONG PSBFreq : 4; //24,25,26,27 ULONG Reserved_5 : 4; //28,29,30,31 ULONG ADDR0_CFG : 4; ULONG ADDR1_CFG : 4; ULONG ADDR2_CFG : 4; ULONG ADDR3_CFG : 4; ULONG Reserved_6 : 16; } Bits; UINT64 Value; } RTIT_CTL, *PRTIT_CTL; typedef union { struct{ UINT64 FilterEn : 1; UINT64 ContextEn : 1; UINT64 TriggerEn : 1; UINT64 Reserved_1 : 1; UINT64 Error : 1; UINT64 Stopped : 1; UINT64 Reserved_2 : 58; } Bits; UINT64 Value; } RTIT_STATUS, *PRTIT_STATUS; typedef union { struct{ UINT64 END : 1; UINT64 Reserved_0 : 1; UINT64 INT : 1; UINT64 Reserved_1 : 1; UINT64 STOP : 1; UINT64 Reserved_2 : 1; UINT64 Size : 4; UINT64 Reserved_3 : 2; UINT64 PhysicalFrameNr : 52; } Bits; UINT64 Value; } ToPA_ENTRY, *PToPA_ENTRY; typedef struct{ UINT64 PhysicalAddress; int index; } ToPA_LOOKUP, *PToPA_LOOKUP; typedef struct { UINT64 Address; UINT64 Size; UINT64 CpuID; } ULTIMAP2DATAEVENT, *PULTIMAP2DATAEVENT; #pragma pack(pop) void SetupUltimap2(UINT32 PID, UINT32 BufferSize, WCHAR *Path, int rangeCount, PURANGE Ranges, int NoPMI, int UserMode, int KernelMode); void DisableUltimap2(void); NTSTATUS ultimap2_waitForData(ULONG timeout, PULTIMAP2DATAEVENT data); NTSTATUS ultimap2_continue(int cpunr); NTSTATUS ultimap2_flushBuffers(); NTSTATUS ultimap2_pause(); NTSTATUS ultimap2_resume(); void ultimap2_LockFile(int cpunr); void ultimap2_ReleaseFile(int cpunr); UINT64 ultimap2_GetTraceFileSize(); void ultimap2_ResetTraceFileSize(); void UnregisterUltimapPMI(); typedef NTSTATUS(*PSSUSPENDPROCESS)(PEPROCESS p); extern PSSUSPENDPROCESS PsSuspendProcess; extern PSSUSPENDPROCESS PsResumeProcess; #endif ================================================ FILE: src/vmxhelper.c ================================================ #pragma warning( disable: 4100 4103 4213) #include #include #include #include "vmxhelper.h" #include "DBKFunc.h" #ifdef AMD64 extern UINT_PTR dovmcall_intel(void *vmcallinfo, unsigned int level1pass); extern UINT_PTR dovmcall_amd(void *vmcallinfo, unsigned int level1pass); //dovmcall is defined in vmxhelpera.asm #else _declspec( naked ) UINT_PTR dovmcall_intel(void *vmcallinfo) { __asm { push edx mov eax,[esp+8] //+8 because of push mov edx, dword ptr vmx_password1 mov ecx, dword ptr vmx_password3 __emit 0x0f __emit 0x01 __emit 0xc1 //vmcall, eax will be edited, or a UD exception will be raised pop edx ret 8 } } _declspec( naked ) UINT_PTR dovmcall_amd(void *vmcallinfo) { __asm { push edx mov eax,[esp+8] mov edx, dword ptr vmx_password1 mov ecx, dword ptr vmx_password3 __emit 0x0f __emit 0x01 __emit 0xd9 //vmmcall, eax will be edited, or a UD exception will be raised pop edx ret 8 } } #endif typedef UINT_PTR (DOVMCALL) (void *vmcallinfo); DOVMCALL *dovmcall; int vmx_hasredirectedint1() { struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_INT1REDIRECTED; return (int)dovmcall(&vmcallinfo); } unsigned int vmx_getversion() /* This will either raise a unhandled opcode exception, or return the used dbvm version */ { struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; DbgPrint("vmx_getversion()\n"); vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_GETVERSION; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_getRealCR0() { struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_GETCR0; return (unsigned int)dovmcall(&vmcallinfo);; } UINT_PTR vmx_getRealCR3() { struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_GETCR3; return dovmcall(&vmcallinfo);; } unsigned int vmx_getRealCR4() { struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_GETCR4; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_redirect_interrupt1(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int1cs, UINT_PTR int1eip) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; unsigned int redirecttype; unsigned int newintvector; UINT64 int1eip; unsigned int int1cs; } vmcallinfo; #pragma pack() DbgPrint("vmx_redirect_interrupt1: redirecttype=%d int1cs=%x int1eip=%llx sizeof(vmcallinfo)=%x\n", redirecttype, int1cs, int1eip, sizeof(vmcallinfo)); vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_REDIRECTINT1; vmcallinfo.redirecttype=redirecttype; vmcallinfo.newintvector=newintvector; vmcallinfo.int1eip=int1eip; vmcallinfo.int1cs=int1cs; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_redirect_interrupt3(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int3cs, UINT_PTR int3eip) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; unsigned int redirecttype; unsigned int newintvector; unsigned long long int3eip; unsigned int int3cs; } vmcallinfo; #pragma pack() DbgPrint("vmx_redirect_interrupt3: int3cs=%x int3eip=%x sizeof(vmcallinfo)=%x\n", int3cs, int3eip, sizeof(vmcallinfo)); vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_REDIRECTINT3; vmcallinfo.redirecttype=redirecttype; vmcallinfo.newintvector=newintvector; vmcallinfo.int3eip=int3eip; vmcallinfo.int3cs=int3cs; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_redirect_interrupt14(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int14cs, UINT_PTR int14eip) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; unsigned int redirecttype; unsigned int newintvector; unsigned long long int14eip; unsigned int int14cs; } vmcallinfo; #pragma pack() DbgPrint("vmx_redirect_interrupt14: int14cs=%x int14eip=%x sizeof(vmcallinfo)=%x\n", int14cs, int14eip, sizeof(vmcallinfo)); vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_REDIRECTINT14; vmcallinfo.redirecttype=redirecttype; vmcallinfo.newintvector=newintvector; vmcallinfo.int14eip=int14eip; vmcallinfo.int14cs=int14cs; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_register_cr3_callback(unsigned int cs, unsigned int eip, unsigned int ss, unsigned int esp) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; unsigned int callbacktype; //32-bit for this driver, so always 0 unsigned long long callback_eip; unsigned int callback_cs; unsigned long long callback_esp; unsigned int callback_ss; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_REGISTER_CR3_EDIT_CALLBACK; vmcallinfo.callbacktype=0; vmcallinfo.callback_eip=eip; vmcallinfo.callback_cs=cs; vmcallinfo.callback_esp=esp; vmcallinfo.callback_ss=ss; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_exit_cr3_callback(unsigned int newcr3) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; unsigned long long newcr3; } vmcallinfo; #pragma pack() //DbgPrint("vmx_exit_cr3_callback(%x)\n",newcr3); vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_RETURN_FROM_CR3_EDIT_CALLBACK; vmcallinfo.newcr3=newcr3; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_watch_pagewrites(UINT64 PhysicalAddress, int Size, int Options, int MaxEntryCount) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_FINDWHATWRITESPAGE UINT64 PhysicalAddress; int Size; int Options; //binary. // Bit 0: 0=Log RIP once. 1=Log RIP multiple times (when different registers) // Bit 1: 0=Only log given Physical Address. 1=Log everything in the page(s) that is/are affected // Bit 2: 0=Do not save FPU/XMM data, 1=Also save FPU/XMM data // Bit 3: 0=Do not save a stack snapshot, 1=Save stack snapshot // Bit 4: 0=No PMI when full, 1=PMI when full int MaxEntryCount; //how much memory should DBVM allocate for the buffer int UsePMI; //trigger a PMI interrupt when full (so you don't lose info) int ID; //ID describing this watcher for this CPU (keep track of this on a per cpu basis if you do more than 1) } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_WATCH_WRITES; vmcallinfo.PhysicalAddress = PhysicalAddress; if (((PhysicalAddress + Size) & 0xfffffffffffff000ULL) > PhysicalAddress) //passes a pageboundary, strip of the excess Size = 0x1000 - (PhysicalAddress & 0xfff); vmcallinfo.Size = Size; vmcallinfo.Options = Options; vmcallinfo.MaxEntryCount = MaxEntryCount; vmcallinfo.ID = 0xffffffff; dovmcall(&vmcallinfo);; return vmcallinfo.ID; } unsigned int vmx_watch_pageaccess(UINT64 PhysicalAddress, int Size, int Options, int MaxEntryCount) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_FINDWHATWRITESPAGE UINT64 PhysicalAddress; int Size; int Options; //binary. // Bit 0: 0=Log RIP once. 1=Log RIP multiple times (when different registers) // Bit 1: 0=Only log given Physical Address. 1=Log everything in the page(s) that is/are affected // Bit 2: 0=Do not save FPU/XMM data, 1=Also save FPU/XMM data // Bit 3: 0=Do not save a stack snapshot, 1=Save stack snapshot // Bit 4: 0=No PMI when full, 1=PMI when full int MaxEntryCount; //how much memory should DBVM allocate for the buffer int ID; //ID describing this watcher for this CPU (keep track of this on a per cpu basis if you do more than 1) } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_WATCH_READS; vmcallinfo.PhysicalAddress = PhysicalAddress; if (((PhysicalAddress + Size) & 0xfffffffffffff000ULL) > PhysicalAddress) //passes a pageboundary, strip of the excess Size = 0x1000 - (PhysicalAddress & 0xfff); vmcallinfo.Size = Size; vmcallinfo.Options = Options; vmcallinfo.MaxEntryCount = MaxEntryCount; vmcallinfo.ID = 0xffffffff; dovmcall(&vmcallinfo);; return vmcallinfo.ID; } unsigned int vmx_watch_retreivelog(int ID, PPageEventListDescriptor result, int *resultsize) /* Used to retrieve both read and write watches */ { unsigned int r; #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_FINDWHATWRITESPAGE DWORD ID; UINT64 results; int resultsize; int copied; //the number of bytes copied so far (This is a repeating instruction) } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_WATCH_RETRIEVELOG; vmcallinfo.ID = ID; vmcallinfo.results = (UINT64)result; vmcallinfo.resultsize = *resultsize; r=(unsigned int)dovmcall(&vmcallinfo);; *resultsize = vmcallinfo.resultsize; return r; //returns 0 on success, 1 on too small buffer. buffersize contains the size in both cases } unsigned int vmx_watch_delete(int ID) { //disables the watch operation #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_FINDWHATWRITESPAGE DWORD ID; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_WATCH_DELETE; vmcallinfo.ID = ID; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } unsigned int vmx_cloak_activate(QWORD physicalPage) /* Copies a page to a shadow page and marks the original page as execute only (or no access at all if the cpu does not support it) On read/write the shadow page's contents are read/written, but execute will execute the original page To access the contents of the original(executing) page use vmx_cloak_readOriginal and vmx_cloak_writeOriginal possible issue: the read and execute operation can be in the same page at the same time, so when the page is swapped by the contents of the unmodified page to facilitate the read of unmodified memory the unmodified code will execute as well (integrity check checking itself) possible solutions: do not cloak pages with integrity checks and then edit the integrity check willy nilly use single byte edits (e.g int3 bps to facilitate changes) make edits so integrity check routines are jumped over Note: Affects ALL cpu's so only needs to be called once */ { //disables the watch operation #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_CLOAK_ACTIVATE QWORD physicalAddress; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CLOAK_ACTIVATE; vmcallinfo.physicalAddress = physicalPage; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } //todo: vmx_cload_passthrougwrites() : lets you specify which write operation locations can be passed through to the execute page unsigned int vmx_cloak_deactivate(QWORD physicalPage) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_CLOAK_ACTIVATE QWORD physicalAddress; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CLOAK_DEACTIVATE; vmcallinfo.physicalAddress = physicalPage; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } unsigned int vmx_cloak_readOriginal(QWORD physicalPage, void *destination) /* reads 4096 bytes from the cloaked page and put it into original (original must be able to hold 4096 bytes, and preferably on a page boundary) */ { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; QWORD physicalAddress; QWORD destination; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CLOAK_READORIGINAL; vmcallinfo.physicalAddress = physicalPage; vmcallinfo.destination = (QWORD)destination; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } unsigned int vmx_cloak_writeOriginal(QWORD physicalPage, void *source) /* reads 4096 bytes from the cloaked page and put it into original (original must be able to hold 4096 bytes, and preferably on a page boundary) */ { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_CLOAK_ACTIVATE QWORD physicalAddress; QWORD source; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CLOAK_WRITEORIGINAL; vmcallinfo.physicalAddress = physicalPage; vmcallinfo.source = (QWORD)source; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } unsigned int vmx_changeregonbp(QWORD physicalAddress, CHANGEREGONBPINFO *changereginfo) /* places an int3 bp at the given address, and on execution changes the state to the given state if a cloaked page is given, the BP will be set in the executing page if no cloaked page is given, cloak it (needed for the single step if no IP change is done) Note: effects ALL cpu's */ { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; //VMCALL_CLOAK_CHANGEREGONBP QWORD physicalAddress; CHANGEREGONBPINFO changereginfo; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CLOAK_CHANGEREGONBP; vmcallinfo.physicalAddress = physicalAddress; vmcallinfo.changereginfo = *changereginfo; return (unsigned int)dovmcall(&vmcallinfo);; //0 on success, anything else fail } unsigned int vmx_ultimap_getDebugInfo(PULTIMAPDEBUGINFO debuginfo) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; ULTIMAPDEBUGINFO debuginfo; } vmcallinfo; #pragma pack() unsigned int i; vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_ULTIMAP_DEBUGINFO; i=(unsigned int)dovmcall(&vmcallinfo);; *debuginfo=vmcallinfo.debuginfo; return i; } unsigned int vmx_ultimap(UINT_PTR cr3towatch, UINT64 debugctl_value, void *storeaddress) { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; UINT64 cr3; UINT64 debugctl; UINT64 storeaddress; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_ULTIMAP; vmcallinfo.cr3=(UINT64)cr3towatch; vmcallinfo.debugctl=(UINT64)debugctl_value; vmcallinfo.storeaddress=(UINT64)(UINT_PTR)storeaddress; DbgPrint("vmx_ultimap(%I64x, %I64x, %I64x)\n", (UINT64)vmcallinfo.cr3, (UINT64)vmcallinfo.debugctl, vmcallinfo.storeaddress); return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_ultimap_disable() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_ULTIMAP_DISABLE; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_ultimap_pause() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_ULTIMAP_PAUSE; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_ultimap_resume() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_ULTIMAP_RESUME; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_disable_dataPageFaults() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_DISABLE_DATAPAGEFAULTS; return (unsigned int)dovmcall(&vmcallinfo);; } unsigned int vmx_enable_dataPageFaults() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_ENABLE_DATAPAGEFAULTS; return (unsigned int)dovmcall(&vmcallinfo);; } UINT_PTR vmx_getLastSkippedPageFault() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize=sizeof(vmcallinfo); vmcallinfo.level2pass=vmx_password2; vmcallinfo.command=VMCALL_GETLASTSKIPPEDPAGEFAULT; return (UINT_PTR)dovmcall(&vmcallinfo);; } unsigned int vmx_add_memory(UINT64 *list, int count) { int r=0; int j=0; #pragma pack(1) typedef struct _vmcall_add_memory { unsigned int structsize; unsigned int level2pass; unsigned int command; UINT64 PhysicalPages[0]; } AddMemoryInfoCall, *PAddMemoryInfoCall; #pragma pack() PAddMemoryInfoCall vmcallinfo=ExAllocatePool(NonPagedPool, sizeof(AddMemoryInfoCall) + count * sizeof(UINT64)); DbgPrint("vmx_add_memory(%p,%d)\n", list, count); DbgPrint("vmx_add_memory(vmx_password1=%x,vmx_password2=%x)\n", vmx_password1, vmx_password2); DbgPrint("structsize at offset %d\n", (UINT64)(&vmcallinfo->structsize) - (UINT64)vmcallinfo); DbgPrint("level2pass at offset %d\n", (UINT64)(&vmcallinfo->level2pass) - (UINT64)vmcallinfo); DbgPrint("command at offset %d\n", (UINT64)(&vmcallinfo->command) - (UINT64)vmcallinfo); DbgPrint("PhysicalPages[0] at offset %d\n", (UINT64)(&vmcallinfo->PhysicalPages[0]) - (UINT64)vmcallinfo); DbgPrint("PhysicalPages[1] at offset %d\n", (UINT64)(&vmcallinfo->PhysicalPages[1]) - (UINT64)vmcallinfo); __try { int i; vmcallinfo->structsize = sizeof(AddMemoryInfoCall) + count * sizeof(UINT64); DbgPrint("vmcallinfo->structsize=%d\n", vmcallinfo->structsize); vmcallinfo->level2pass = vmx_password2; vmcallinfo->command = VMCALL_ADD_MEMORY; j = 1; for (i = 0; i < count; i++) { vmcallinfo->PhysicalPages[i] = list[i]; } j = 2; r = (unsigned int)dovmcall(vmcallinfo); j = 3; //never } __except (1) { DbgPrint("vmx_add_memory(%p,%d) gave an exception at part %d with exception code %x\n", list, count, j, GetExceptionCode()); r = 0x100; } ExFreePool(vmcallinfo); return r; } int vmx_causedCurrentDebugBreak() { #pragma pack(1) struct { unsigned int structsize; unsigned int level2pass; unsigned int command; } vmcallinfo; #pragma pack() vmcallinfo.structsize = sizeof(vmcallinfo); vmcallinfo.level2pass = vmx_password2; vmcallinfo.command = VMCALL_CAUSEDDEBUGBREAK; return (int)dovmcall(&vmcallinfo);; } void vmx_init_dovmcall(int isIntel) { if (isIntel) (void *)dovmcall=(void *)dovmcall_intel; else (void *)dovmcall=(void *)dovmcall_amd; } //DBVMInterruptService ================================================ FILE: src/vmxhelper.h ================================================ #ifndef VMXHELPER_H #define VMXHELPER_H #pragma warning( disable: 4200) #define VMCALL_GETVERSION 0 #define VMCALL_CHANGEPASSWORD 1 #define VMCALL_READ_PHYSICAL_MEMORY 3 #define VMCALL_WRITE_PHYSICAL_MEMORY 4 #define VMCALL_REDIRECTINT1 9 #define VMCALL_INT1REDIRECTED 10 #define VMCALL_CHANGESELECTORS 12 #define VMCALL_BLOCK_INTERRUPTS 13 #define VMCALL_RESTORE_INTERRUPTS 14 #define VMCALL_REGISTER_CR3_EDIT_CALLBACK 16 #define VMCALL_RETURN_FROM_CR3_EDIT_CALLBACK 17 #define VMCALL_GETCR0 18 #define VMCALL_GETCR3 19 #define VMCALL_GETCR4 20 #define VMCALL_RAISEPRIVILEGE 21 #define VMCALL_REDIRECTINT14 22 #define VMCALL_INT14REDIRECTED 23 #define VMCALL_REDIRECTINT3 24 #define VMCALL_INT3REDIRECTED 25 //dbvm v6+ #define VMCALL_READMSR 26 #define VMCALL_WRITEMSR 27 #define VMCALL_ULTIMAP 28 #define VMCALL_ULTIMAP_DISABLE 29 //dbvm v7+ #define VMCALL_SWITCH_TO_KERNELMODE 30 #define VMCALL_DISABLE_DATAPAGEFAULTS 31 #define VMCALL_ENABLE_DATAPAGEFAULTS 32 #define VMCALL_GETLASTSKIPPEDPAGEFAULT 33 #define VMCALL_ULTIMAP_PAUSE 34 #define VMCALL_ULTIMAP_RESUME 35 #define VMCALL_ULTIMAP_DEBUGINFO 36 //dbvm v10+ #define VMCALL_WATCH_WRITES 41 #define VMCALL_WATCH_READS 41 #define VMCALL_WATCH_RETRIEVELOG 43 #define VMCALL_WATCH_DELETE 44 #define VMCALL_CLOAK_ACTIVATE 45 #define VMCALL_CLOAK_DEACTIVATE 46 #define VMCALL_CLOAK_READORIGINAL 47 #define VMCALL_CLOAK_WRITEORIGINAL 48 #define VMCALL_CLOAK_CHANGEREGONBP 49 #define VMCALL_ADD_MEMORY 57 #define VMCALL_CAUSEDDEBUGBREAK 63 typedef UINT64 QWORD; typedef struct _CHANGEREGONBPINFO { struct { unsigned changeRAX : 1; unsigned changeRBX : 1; unsigned changeRCX : 1; unsigned changeRDX : 1; unsigned changeRSI : 1; unsigned changeRDI : 1; unsigned changeRBP : 1; unsigned changeRSP : 1; unsigned changeRIP : 1; unsigned changeR8 : 1; unsigned changeR9 : 1; unsigned changeR10 : 1; unsigned changeR11 : 1; unsigned changeR12 : 1; unsigned changeR13 : 1; unsigned changeR14 : 1; unsigned changeR15 : 1; //flags reg: unsigned changeCF : 1; unsigned changePF : 1; unsigned changeAF : 1; unsigned changeZF : 1; unsigned changeSF : 1; unsigned changeOF : 1; unsigned newCF : 1; unsigned newPF : 1; unsigned newAF : 1; unsigned newZF : 1; unsigned newSF : 1; unsigned newOF : 1; unsigned reserved : 3; } Flags; QWORD newRAX; QWORD newRBX; QWORD newRCX; QWORD newRDX; QWORD newRSI; QWORD newRDI; QWORD newRBP; QWORD newRSP; QWORD newRIP; QWORD newR8; QWORD newR9; QWORD newR10; QWORD newR11; QWORD newR12; QWORD newR13; QWORD newR14; QWORD newR15; } CHANGEREGONBPINFO, *PCHANGEREGONBPINFO; typedef struct _pageevent_basic { QWORD VirtualAddress; QWORD PhysicalAddress; QWORD CR3; //in case of kernel or other process QWORD FSBASE; QWORD GSBASE; QWORD FLAGS; QWORD RAX; QWORD RBX; QWORD RCX; QWORD RDX; QWORD RSI; QWORD RDI; QWORD R8; QWORD R9; QWORD R10; QWORD R11; QWORD R12; QWORD R13; QWORD R14; QWORD R15; QWORD RBP; QWORD RSP; QWORD RIP; WORD CS; WORD DS; WORD ES; WORD SS; WORD FS; WORD GS; DWORD Count; } PageEventBasic, *PPageEventBasic; typedef struct _fxsave64 { WORD FCW; WORD FSW; BYTE FTW; BYTE Reserved; WORD FOP; UINT64 FPU_IP; UINT64 FPU_DP; DWORD MXCSR; DWORD MXCSR_MASK; QWORD FP_MM0; QWORD FP_MM0_H; QWORD FP_MM1; QWORD FP_MM1_H; QWORD FP_MM2; QWORD FP_MM2_H; QWORD FP_MM3; QWORD FP_MM3_H; QWORD FP_MM4; QWORD FP_MM4_H; QWORD FP_MM5; QWORD FP_MM5_H; QWORD FP_MM6; QWORD FP_MM6_H; QWORD FP_MM7; QWORD FP_MM7_H; QWORD XMM0; QWORD XMM0_H; QWORD XMM1; QWORD XMM1_H; QWORD XMM2; QWORD XMM2_H; QWORD XMM3; QWORD XMM3_H; QWORD XMM4; QWORD XMM4_H; QWORD XMM5; QWORD XMM5_H; QWORD XMM6; QWORD XMM6_H; QWORD XMM7; QWORD XMM7_H; QWORD XMM8; QWORD XMM8_H; QWORD XMM9; QWORD XMM9_H; QWORD XMM10; QWORD XMM10_H; QWORD XMM11; QWORD XMM11_H; QWORD XMM12; QWORD XMM12_H; QWORD XMM13; QWORD XMM13_H; QWORD XMM14; QWORD XMM14_H; QWORD XMM15; QWORD XMM15_H; QWORD res1; QWORD res1_H; QWORD res2; QWORD res2_H; QWORD res3; QWORD res3_H; QWORD res4; QWORD res4_H; QWORD res5; QWORD res5_H; QWORD res6; QWORD res6_H; } FXSAVE64, *PFXSAVE64; typedef struct _pageevent_extended { PageEventBasic basic; FXSAVE64 fpudata; } PageEventExtended, *PPageEventExtended; typedef struct _pageevent_basic_withstack { PageEventBasic basic; unsigned char stack[4096]; } PageEventBasicWithStack, *PPageEventBasicWithStack; typedef struct _pageevent_extended_withstack { PageEventBasic basic; FXSAVE64 fpudata; unsigned char stack[4096]; } PageEventExtendedWithStack, *PPageEventExtendedWithStack; typedef struct _pageeventlistdescriptor { DWORD ID; DWORD maxSize; DWORD numberOfEntries; DWORD missedEntries; DWORD entryType; //0=PageEventBasic, 1=PageEventExtended, 2=PageEventBasicWithStack, 3=PageEventExtendedWithStack union { PageEventBasic basic[0]; PageEventExtended extended[0]; PageEventBasicWithStack basics[0]; PageEventExtendedWithStack extendeds[0]; } pe; } PageEventListDescriptor, *PPageEventListDescriptor; typedef enum {virt_differentInterrupt=0, virt_emulateInterrupt=1} VMXInterruptRedirectType; typedef struct { UINT64 Active; //set to 1 when active UINT64 CR3; //Holds the CR3 value to watch taskswitch to and from UINT64 DEBUGCTL; //Holds the DebugCTL value to set when inside the target process UINT64 DS_AREA; //Holds the DS_AREA to set when UINT64 OriginalDebugCTL; //When inside the target process this holds the debugctl that was set before entering. Return this on readMSR (and set with writeMSR when inside the process) UINT64 OriginalDS_AREA; //When inside the target process this holds the DS_AREA that was set before entering. Return this with readMSR ('''') UINT64 CR3_switchcount; UINT64 CR3_switchcount2; UINT64 LastOldCR3; UINT64 LastNewCR3; } ULTIMAPDEBUGINFO, *PULTIMAPDEBUGINFO; unsigned int vmcall(void *vmcallinfo, unsigned int level1pass); unsigned int vmx_getversion(); unsigned int vmx_getRealCR0(); UINT_PTR vmx_getRealCR3(); unsigned int vmx_getRealCR4(); unsigned int vmx_redirect_interrupt1(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int1cs, UINT_PTR int1eip); unsigned int vmx_redirect_interrupt3(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int3cs, UINT_PTR int3eip); unsigned int vmx_redirect_interrupt14(VMXInterruptRedirectType redirecttype, unsigned int newintvector, unsigned int int14cs, UINT_PTR int14eip); unsigned int vmx_register_cr3_callback(unsigned int cs, unsigned int eip, unsigned int ss, unsigned int esp); unsigned int vmx_exit_cr3_callback(unsigned int newcr3); unsigned int vmx_ultimap(UINT_PTR cr3towatch, UINT64 debugctl_value, void *storeaddress); unsigned int vmx_ultimap_disable(); unsigned int vmx_ultimap_pause(); unsigned int vmx_ultimap_resume(); unsigned int vmx_ultimap_getDebugInfo(PULTIMAPDEBUGINFO debuginfo); unsigned int vmxusable; UINT64 vmx_password1; unsigned int vmx_password2; UINT64 vmx_password3; unsigned int vmx_version; UINT_PTR vmx_getLastSkippedPageFault(); unsigned int vmx_enable_dataPageFaults(); unsigned int vmx_disable_dataPageFaults(); unsigned int vmx_add_memory(UINT64 *list, int count); int vmx_causedCurrentDebugBreak(); void vmx_init_dovmcall(int isIntel); #endif ================================================ FILE: src/vmxoffload.c ================================================ /* sets up all the needed data structures copies dbvm into physical memory jumps into dbvm's os entry point */ #pragma warning( disable: 4100 4103 4152 4189 4456) #ifndef AMD64 #pragma warning( disable: 4740) #endif #include #include #include "dbkfunc.h" #include "vmxoffload.h" #include "vmxhelper.h" #ifdef TOBESIGNED #include "sigcheck.h" #endif unsigned char *vmm; #pragma pack(2) struct { WORD limit; UINT_PTR base; } NewGDTDescriptor; #pragma pack() #pragma pack(1) typedef struct _INITVARS { UINT64 loadedOS; //physical address of the loadedOS section UINT64 vmmstart; //physical address of virtual address 00400000 (obsoletish...) UINT64 pagedirlvl4; //Virtual address of the pml4 table (the virtual memory after this until the next 4MB alignment is free to use) UINT64 nextstack; //The virtual address of the stack for the next CPU (vmloader only sets it up when 0) UINT64 extramemory; //Physical address of some extra initial memory (physically contiguous) UINT64 extramemorysize; //the number of pages that extramemory spans UINT64 contiguousmemory; //Physical address of some extra initial memory (physically contiguous) UINT64 contiguousmemorysize; //the number of pages that extramemory spans } INITVARS, *PINITVARS; typedef struct { //ok, everything uint64, I hate these incompatibilities with alignment between gcc and ms c UINT64 cpucount; UINT64 originalEFER; UINT64 originalLME; UINT64 idtbase; UINT64 idtlimit; UINT64 gdtbase; UINT64 gdtlimit; UINT64 cr0; UINT64 cr2; UINT64 cr3; UINT64 cr4; UINT64 dr7; UINT64 rip; UINT64 rax; UINT64 rbx; UINT64 rcx; UINT64 rdx; UINT64 rsi; UINT64 rdi; UINT64 rbp; UINT64 rsp; UINT64 r8; UINT64 r9; UINT64 r10; UINT64 r11; UINT64 r12; UINT64 r13; UINT64 r14; UINT64 r15; UINT64 rflags; UINT64 cs; UINT64 ss; UINT64 ds; UINT64 es; UINT64 fs; UINT64 gs; UINT64 tr; UINT64 ldt; UINT64 cs_AccessRights; UINT64 ss_AccessRights; UINT64 ds_AccessRights; UINT64 es_AccessRights; UINT64 fs_AccessRights; UINT64 gs_AccessRights; UINT64 cs_Limit; UINT64 ss_Limit; UINT64 ds_Limit; UINT64 es_Limit; UINT64 fs_Limit; UINT64 gs_Limit; UINT64 fsbase; UINT64 gsbase; } OriginalState, *POriginalState; #pragma pack() unsigned char *enterVMM2; PMDL enterVMM2MDL; POriginalState originalstate; //one of the reasons why multiple cpu's don't start at exactly the same time PMDL originalstateMDL; UINT_PTR enterVMM2PA; PVOID TemporaryPagingSetup; UINT_PTR TemporaryPagingSetupPA; PMDL TemporaryPagingSetupMDL; UINT_PTR DBVMPML4PA; UINT_PTR originalstatePA; UINT_PTR NewGDTDescriptorVA; UINT_PTR vmmPA; int initializedvmm=0; KSPIN_LOCK LoadedOSSpinLock; //spinlock to prevent LoadedOS from being overwritten (should not be needed, but just being safe) #ifdef AMD64 extern void enterVMM( void ); //declared in vmxoffloada.asm extern void enterVMMPrologue(void); extern void enterVMMEpilogue(void); extern void JTAGBP(void); #else _declspec( naked ) void enterVMM( void ) { __asm { begin: xchg bx,bx //trigger bochs breakpoint //setup the GDT lgdt [ebx] //ebx is the 'virtual address' so just do that before disabling paging ok... //switch to identify mapped pagetable mov cr3,edx jmp short weee weee: //now jump to the physical address (identity mapped to the same virtual address) mov eax,secondentry sub eax,begin add eax,esi jmp eax secondentry: //disable paging mov eax,cr0 and eax,0x7FFFFFFF mov cr0,eax //paging off jmp short weee2 weee2: //load paging for vmm (but don't apply yet, in nonpaged mode) mov cr3,ecx //enable PAE and PSE mov eax,0x30 __emit 0x0f //-| __emit 0x22 //-|-mov cr4,eax (still WTF's me that visual studio doesn't know about cr4) __emit 0xe0 //-| mov ecx,0xc0000080 //enable efer_lme rdmsr or eax,0x100 wrmsr //mov eax,cr0 //or eax,0x80000020 //re-enable pg (and ne to be sure) //edit, who cares, fuck the original state, it's my own state now mov eax,0x80000021 mov cr0,eax mov eax,edi //tell dbvm it's an OS entry and a that location the start info is mov ebx,ebp //tell vmmPA __emit 0xea //-| __emit 0x00 //-| __emit 0x00 //-| __emit 0x40 //-|JMP FAR 0x50:0x00400000 __emit 0x00 //-| __emit 0x50 //-| __emit 0x00 //-| __emit 0xce __emit 0xce __emit 0xce __emit 0xce __emit 0xce __emit 0xce __emit 0xce } } #endif PMDL DBVMMDL; PINITVARS initvars; void cleanupDBVM() { if (!initializedvmm) return; if (enterVMM2MDL) { MmUnlockPages(enterVMM2MDL); IoFreeMdl(enterVMM2MDL); enterVMM2MDL = 0; } if (enterVMM2) { RtlZeroMemory(enterVMM2, 4096); MmFreeContiguousMemory(enterVMM2); enterVMM2 = 0; } if (TemporaryPagingSetupMDL) { MmUnlockPages(TemporaryPagingSetupMDL); IoFreeMdl(TemporaryPagingSetupMDL); TemporaryPagingSetupMDL = 0; } if (TemporaryPagingSetup) { RtlZeroMemory(TemporaryPagingSetup, 4096 * 4); ExFreePool(TemporaryPagingSetup); TemporaryPagingSetup = 0; } if (originalstateMDL) { MmUnlockPages(originalstateMDL); IoFreeMdl(originalstateMDL); originalstateMDL = 0; } if (originalstate) { RtlZeroMemory(originalstate, 4096); ExFreePool(originalstate); originalstate = 0; } initializedvmm = 0; } void initializeDBVM(PCWSTR dbvmimgpath) /* Runs at passive mode */ { if (initializedvmm) return; //already initialized DbgPrint("First time run. Initializing vmm section"); PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes; LowAddress.QuadPart = 0; HighAddress.QuadPart = -1; SkipBytes.QuadPart = 0; DBVMMDL = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 4 * 1024 * 1024, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED); if (!DBVMMDL) { DbgPrint("Failure allocating the required 4MB\n"); return; } vmm = MmMapLockedPagesSpecifyCache(DBVMMDL, KernelMode, MmCached, NULL, FALSE, 0); //default password when dbvm is just loaded (needed for adding extra ram) vmx_password1 = 0x76543210; vmx_password2 = 0xfedcba98; vmx_password3 = 0x90909090; if (vmm) { int i; PHYSICAL_ADDRESS maxPA; HANDLE dbvmimghandle; UNICODE_STRING filename; IO_STATUS_BLOCK statusblock; OBJECT_ATTRIBUTES oa; NTSTATUS OpenedFile; vmmPA = (UINT_PTR)MmGetPhysicalAddress(vmm).QuadPart; DbgPrint("Allocated memory at virtual address %p (physical address %I64x)\n", vmm, MmGetPhysicalAddress(vmm)); vmmPA = MmGetMdlPfnArray(DBVMMDL)[0] << 12; DbgPrint("(physical address %I64x)\n", vmmPA); RtlZeroMemory(vmm, 4 * 1024 * 1024); //initialize RtlInitUnicodeString(&filename, dbvmimgpath); //Load the .img file InitializeObjectAttributes(&oa, &filename, 0, NULL, NULL); OpenedFile = ZwCreateFile(&dbvmimghandle, SYNCHRONIZE | STANDARD_RIGHTS_READ, &oa, &statusblock, NULL, FILE_SYNCHRONOUS_IO_NONALERT | FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, 0, NULL, 0); #ifdef TOBESIGNED if (OpenedFile==STATUS_SUCCESS) OpenedFile=CheckSignatureOfFile(&filename, FALSE); #endif if (OpenedFile == STATUS_SUCCESS) { WORD startsector; LARGE_INTEGER byteoffset; FILE_STANDARD_INFORMATION fsi; NTSTATUS ReadFile; //Getting filesize ZwQueryInformationFile(dbvmimghandle, &statusblock, &fsi, sizeof(fsi), FileStandardInformation); //fsi.EndOfFile contains the filesize if (fsi.EndOfFile.QuadPart>4 * 1024 * 1024) { DbgPrint("File bigger than 4MB. Big retard detected\n"); return; } byteoffset.QuadPart = 0x8; //offset containing sectornumber of the vmm location ReadFile = ZwReadFile(dbvmimghandle, NULL, NULL, NULL, &statusblock, &startsector, 2, &byteoffset, NULL); if (ReadFile == STATUS_PENDING) { if (ZwWaitForSingleObject(dbvmimghandle, FALSE, NULL) != STATUS_SUCCESS) { DbgPrint("Read failure\n"); return; } } if (statusblock.Status == STATUS_SUCCESS) { DWORD vmmsize = fsi.EndOfFile.LowPart;// -(startsector * 512); //now read the vmdisk into the allocated memory DbgPrint("The startsector=%d (that's offset %d)\n", startsector, startsector * 512); byteoffset.QuadPart = startsector * 512; ReadFile = ZwReadFile(dbvmimghandle, NULL, NULL, NULL, &statusblock, vmm, vmmsize, &byteoffset, NULL); if (ReadFile == STATUS_PENDING) ZwWaitForSingleObject(dbvmimghandle, FALSE, NULL); vmmsize = (vmmsize + 4096) & 0xfffffffffffff000ULL; //adjust the size internally to a page boundary (sure, there's some mem loss, but it's predicted, dbvm assumes first 10 pages are scratch pages) DbgPrint("vmmsize=%x\n", vmmsize); if (statusblock.Status == STATUS_SUCCESS) { //basic paging setup for the vmm, will get expanded by the vmm itself UINT64 *GDTBase; PPDPTE_PAE PageMapLevel4; PPDPTE_PAE PageDirPtr; PPDE_PAE PageDir; PPTE_PAE PageTable1, PageTable2; UINT_PTR FreeVA = (((UINT_PTR)vmm + vmmsize) & 0xfffffffffffff000ULL) + 4096; //next free virtual address UINT64 mainstack; initvars = (PINITVARS)&vmm[0x10]; mainstack = FreeVA; FreeVA += 16 * 4096; GDTBase = (UINT64*)FreeVA; FreeVA += 4096; PageDirPtr = (PPDPTE_PAE)FreeVA; FreeVA += 4096; PageDir = (PPDE_PAE)FreeVA; FreeVA += 4096; PageTable1 = (PPTE_PAE)FreeVA; FreeVA += 4096; PageTable2 = (PPTE_PAE)FreeVA; FreeVA += 4096; PageMapLevel4 = (PPDPTE_PAE)FreeVA; FreeVA += 4096; //has to be the last alloc DBVMPML4PA = (UINT_PTR)MmGetPhysicalAddress(PageMapLevel4).QuadPart; //blame MS for making this hard to read DbgPrint("Setting up initial paging table for vmm\n"); *(PUINT64)(&PageMapLevel4[0]) = MmGetPhysicalAddress(PageDirPtr).QuadPart; PageMapLevel4[0].P = 1; PageMapLevel4[0].RW = 1; *(PUINT64)(&PageDirPtr[0]) = MmGetPhysicalAddress(PageDir).QuadPart; PageDirPtr[0].P = 1; PageDirPtr[0].RW = 1; //DBVM 11 does no longer need the map at 0 to 00400000 *(PUINT64)(&PageDir[0]) = 0; //00000000-00200000 PageDir[0].P = 1; PageDir[0].RW = 0; //map as readonly (only for the jump to 0x00400000) PageDir[0].PS = 1; *(PUINT64)(&PageDir[1]) = 0x00200000; //00200000-00400000 PageDir[1].P = 1; PageDir[1].RW = 0; PageDir[1].PS = 1; { *(PUINT64)(&PageDir[2]) = MmGetPhysicalAddress(PageTable1).QuadPart; PageDir[2].P = 1; PageDir[2].RW = 1; PageDir[2].PS = 0; //points to a pagetable *(PUINT64)(&PageDir[3]) = MmGetPhysicalAddress(PageTable2).QuadPart; PageDir[3].P = 1; PageDir[3].RW = 1; PageDir[3].PS = 0; } //fill in the pagetables for (i = 0; i<1024; i++) //pagetable1 and 2 are allocated after eachother, so 1024 can be used here using pagetable1 { *(PUINT64)(&PageTable1[i]) = MmGetPhysicalAddress((PVOID)(((UINT_PTR)vmm) + (4096 * i))).QuadPart; PageTable1[i].P = 1; PageTable1[i].RW = 1; } i = (int)((UINT64)((mainstack - (UINT64)vmm)) >> 12); PageTable1[i].P = 0; //mark the first page of the stack as unreadable //setup GDT GDTBase[0] = 0; //0 : GDTBase[1] = 0x00cf92000000ffffULL; //8 : 32-bit data GDTBase[2] = 0x00cf96000000ffffULL; //16: test, stack, failed, unused GDTBase[3] = 0x00cf9b000000ffffULL; //24: 32-bit code GDTBase[4] = 0x00009a000000ffffULL; //32: 16-bit code GDTBase[5] = 0x000092000000ffffULL; //40: 16-bit data GDTBase[6] = 0x00009a030000ffffULL; //48: 16-bit code, starting at 0x30000 GDTBase[7] = 0; //56: 32-bit task GDTBase[8] = 0; //64: 64-bit task GDTBase[9] = 0; //72: ^ ^ ^ GDTBase[10] = 0x00af9b000000ffffULL; //80: 64-bit code GDTBase[11] = 0; //88: ^ ^ ^ GDTBase[12] = 0; //96: 64-bit tss descriptor (2) GDTBase[13] = 0; //104: ^ ^ ^ NewGDTDescriptor.limit = 0x6f; //111 NewGDTDescriptor.base = 0x00400000 + (UINT64)GDTBase - (UINT64)vmm; DbgPrint("&NewGDTDescriptor=%p, &NewGDTDescriptor.limit=%p, &NewGDTDescriptor.base=%p\n", &NewGDTDescriptor, &NewGDTDescriptor.limit, &NewGDTDescriptor.base); DbgPrint("NewGDTDescriptor.limit=%x\n", NewGDTDescriptor.limit); DbgPrint("NewGDTDescriptor.base=%p\n", NewGDTDescriptor.base); NewGDTDescriptorVA = (UINT_PTR)&NewGDTDescriptor; maxPA.QuadPart = 0x003fffffULL; //allocate 4k at the lower 4MB DbgPrint("Before enterVMM2 alloc: maxPA=%I64x\n", maxPA.QuadPart); enterVMM2 = MmAllocateContiguousMemory(4096, maxPA); if (enterVMM2) { unsigned char *original = (unsigned char *)enterVMM; RtlZeroMemory(enterVMM2, 4096); enterVMM2MDL = IoAllocateMdl(enterVMM2, 4096, FALSE, FALSE, NULL); MmProbeAndLockPages(enterVMM2MDL, KernelMode, IoReadAccess); DbgPrint("enterVMM is located at %p (%I64x)\n", enterVMM, MmGetPhysicalAddress(enterVMM).QuadPart); DbgPrint("enterVMM2 is located at %p (%I64x)\n", enterVMM2, MmGetPhysicalAddress(enterVMM2).QuadPart); DbgPrint("Copying function till end\n"); //copy memory i = 0; while ((i<4096) && ((original[i] != 0xce) || (original[i + 1] != 0xce) || (original[i + 2] != 0xce) || (original[i + 3] != 0xce) || (original[i + 4] != 0xce))) i++; DbgPrint("size is %d", i); RtlCopyMemory(enterVMM2, original, i); DbgPrint("Copy done\n"); } else { DbgPrint("Failure allocating enterVMM2\n"); return; } //now create a paging setup where enterVMM2 is identity mapped AND mapped at the current virtual address, needed to be able to go down to nonpaged mode //easiest way, make every page point to enterVMM2 //allocate 4 pages DbgPrint("Allocating memory for the temp pagedir\n"); TemporaryPagingSetup = ExAllocatePool(PagedPool, 4 * 4096); if (TemporaryPagingSetup == NULL) { DbgPrint("TemporaryPagingSetup==NULL!!!\n"); return; } TemporaryPagingSetupMDL = IoAllocateMdl(TemporaryPagingSetup, 4 * 4096, FALSE, FALSE, NULL); MmProbeAndLockPages(TemporaryPagingSetupMDL, KernelMode, IoReadAccess); RtlZeroMemory(TemporaryPagingSetup, 4096 * 4); DbgPrint("TemporaryPagingSetup is located at %p (%I64x)\n", TemporaryPagingSetup, MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart); TemporaryPagingSetupPA = MmGetMdlPfnArray(TemporaryPagingSetupMDL)[0] << 12; // (UINT_PTR)MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart; enterVMM2PA = MmGetMdlPfnArray(enterVMM2MDL)[0] << 12; DbgPrint("TemporaryPagingSetupPA = (%I64x) (Should be %I64x)\n", (UINT64)TemporaryPagingSetupPA, (UINT64)MmGetPhysicalAddress(TemporaryPagingSetup).QuadPart); #ifdef AMD64 DbgPrint("Setting up temporary paging setup for x64\n"); { PUINT64 PML4Table = (PUINT64)TemporaryPagingSetup; PUINT64 PageDirPtr = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 4096); PUINT64 PageDir = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 2 * 4096); PUINT64 PageTable = (PUINT64)((UINT_PTR)TemporaryPagingSetup + 3 * 4096); DbgPrint("PAE paging\n"); for (i = 0; i<512; i++) { PML4Table[i] = MmGetPhysicalAddress(PageDirPtr).QuadPart; ((PPDPTE_PAE)(&PML4Table[i]))->P = 1; PageDirPtr[i] = MmGetPhysicalAddress(PageDir).QuadPart; ((PPDPTE_PAE)(&PageDirPtr[i]))->P = 1; PageDir[i] = MmGetPhysicalAddress(PageTable).QuadPart; ((PPDE_PAE)(&PageDir[i]))->P = 1; ((PPDE_PAE)(&PageDir[i]))->PS = 0; //4KB PageTable[i] = enterVMM2PA; ((PPTE_PAE)(&PageTable[i]))->P = 1; } } #else DbgPrint("Setting up temporary paging setup\n"); if (PTESize==8) //PAE paging { PUINT64 PageDirPtr=(PUINT64)TemporaryPagingSetup; PUINT64 PageDir=(PUINT64)((UINT_PTR)TemporaryPagingSetup+4096); PUINT64 PageTable=(PUINT64)((UINT_PTR)TemporaryPagingSetup+2*4096); DbgPrint("PAE paging\n"); for (i=0; i<512; i++) { PageDirPtr[i]=MmGetPhysicalAddress(PageDir).QuadPart; ((PPDPTE_PAE)(&PageDirPtr[i]))->P=1; //((PPDPTE_PAE)(&PageDirPtr[i]))->RW=1; PageDir[i]=MmGetPhysicalAddress(PageTable).QuadPart; ((PPDE_PAE)(&PageDir[i]))->P=1; //((PPDE_PAE)(&PageDir[i]))->RW=1; ((PPDE_PAE)(&PageDir[i]))->PS=0; //4KB PageTable[i]=MmGetPhysicalAddress(enterVMM2).QuadPart; ((PPTE_PAE)(&PageTable[i]))->P=1; //((PPTE_PAE)(&PageTable[i]))->RW=1; } } else { //normal(old) 4 byte page entries PDWORD PageDir=(PDWORD)TemporaryPagingSetup; PDWORD PageTable=(PDWORD)((DWORD)TemporaryPagingSetup+4096); DbgPrint("Normal paging\n"); for (i=0; i<1024; i++) { PageDir[i]=MmGetPhysicalAddress(PageTable).LowPart; ((PPDE)(&PageDir[i]))->P=1; ((PPDE)(&PageDir[i]))->RW=1; ((PPDE)(&PageDir[i]))->PS=0; //4KB PageTable[i]=MmGetPhysicalAddress(enterVMM2).LowPart; ((PPTE)(&PageTable[i]))->P=1; ((PPTE)(&PageTable[i]))->RW=1; } } #endif DbgPrint("Temp paging has been setup\n"); //enterVMM2PA = (UINT_PTR)MmGetPhysicalAddress(enterVMM2).QuadPart; originalstate = ExAllocatePool(PagedPool, 4096); originalstateMDL = IoAllocateMdl(originalstate, 4096, FALSE, FALSE, NULL); MmProbeAndLockPages(originalstateMDL, KernelMode, IoReadAccess); RtlZeroMemory(originalstate, 4096); originalstatePA = MmGetMdlPfnArray(originalstateMDL)[0] << 12; //(UINT_PTR)MmGetPhysicalAddress(originalstate).QuadPart; DbgPrint("enterVMM2PA=%llx\n", enterVMM2PA); DbgPrint("originalstatePA=%llx\n", originalstatePA); DbgPrint("originalstatePA=%llx\n", (UINT_PTR)MmGetPhysicalAddress(originalstate).QuadPart); //setup init vars initvars->loadedOS = originalstatePA; initvars->vmmstart = vmmPA; initvars->pagedirlvl4 = 0x00400000 + ((UINT64)PageMapLevel4 - (UINT64)vmm); initvars->nextstack = 0x00400000 + ((UINT64)mainstack - (UINT64)vmm) + (16 * 4096) - 0x40; initvars->contiguousmemory = 0; PMDL contiguousMDL = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 8 * 4096, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED); if (contiguousMDL) { initvars->contiguousmemory = MmGetMdlPfnArray(contiguousMDL)[0] << 12; DbgPrint("contiguous PA =%llx\n", initvars->contiguousmemory); initvars->contiguousmemorysize = 8; ExFreePool(contiguousMDL); } else DbgPrint("Failed allocating 32KB of contiguous memory"); initializedvmm = TRUE; KeInitializeSpinLock(&LoadedOSSpinLock); } } ZwClose(dbvmimghandle); DbgPrint("Opened and processed: %S\n", filename.Buffer); } else { DbgPrint("Failure opening the file. Status=%x (filename=%S)\n", OpenedFile, filename.Buffer); } //fill in some specific memory regions MmUnmapLockedPages(vmm, DBVMMDL); } else { DbgPrint("Failure allocating the required 4MB\n"); } ExFreePool(DBVMMDL); } void vmxoffload(void) { //save entry state for easy exit in ReturnFromvmxoffload EFLAGS eflags; PHYSICAL_ADDRESS minPA, maxPA,boundary; GDT gdt; IDT idt; /* __try { DbgBreakPoint(); } __except (1) { DbgPrint("No debugger\n"); }*/ //allocate 8MB of contigues physical memory minPA.QuadPart=0; maxPA.QuadPart=0xffffffffff000000ULL; boundary.QuadPart=0x00800000ULL; //8 mb boundaries DbgPrint("vmxoffload\n"); DbgPrint("initializedvmm=%d\n", initializedvmm); if (initializedvmm) { DbgPrint("cpunr=%d\n",cpunr()); DbgPrint("Storing original state\n"); originalstate->cpucount=getCpuCount(); DbgPrint("originalstate->cpucount=%d",originalstate->cpucount); originalstate->originalEFER=readMSR(0xc0000080); //amd prefers this over an LME originalstate->originalLME=(int)(((DWORD)(readMSR(0xc0000080)) >> 8) & 1); DbgPrint("originalstate->originalLME=%d",originalstate->originalLME); originalstate->cr0=(UINT_PTR)getCR0(); DbgPrint("originalstate->cr0=%I64x",originalstate->cr0); /* { int xxx; unsigned char *x; x=&originalstate->cr0; for (xxx=0; xxx<8; xxx++) { DbgPrint("%x ",x[xxx]); } } */ originalstate->cr2=(UINT_PTR)getCR2(); DbgPrint("originalstate->cr2=%I64x",originalstate->cr2); /* { int xxx; unsigned char *x; x=&originalstate->cr2; for (xxx=0; xxx<8; xxx++) { DbgPrint("%x ",x[xxx]); } }*/ originalstate->cr3=(UINT_PTR)getCR3(); //DbgPrint("originalstate->cr3=%I64x",originalstate->cr3); originalstate->cr4=(UINT_PTR)getCR4(); //DbgPrint("originalstate->cr4=%I64x",originalstate->cr4); originalstate->ss=getSS(); originalstate->ss_AccessRights = getAccessRights(originalstate->ss); originalstate->ss_Limit = getSegmentLimit(originalstate->ss); //DbgPrint("originalstate->ss=%I64x",originalstate->ss); originalstate->cs=getCS(); originalstate->cs_AccessRights = getAccessRights(originalstate->cs); originalstate->cs_Limit = getSegmentLimit(originalstate->cs); //DbgPrint("originalstate->cs=%I64x",originalstate->cs); originalstate->ds=getDS(); originalstate->ds_AccessRights = getAccessRights(originalstate->ds); originalstate->ds_Limit = getSegmentLimit(originalstate->ds); //DbgPrint("originalstate->ds=%I64x",originalstate->ds); originalstate->es=getES(); originalstate->es_AccessRights = getAccessRights(originalstate->es); originalstate->es_Limit = getSegmentLimit(originalstate->es); //DbgPrint("originalstate->es=%I64x",originalstate->es); originalstate->fs=getFS(); originalstate->fs_AccessRights = getAccessRights(originalstate->fs); originalstate->fs_Limit = getSegmentLimit(originalstate->fs); //DbgPrint("originalstate->fs=%I64x",originalstate->fs); originalstate->gs=getGS(); originalstate->gs_AccessRights = getAccessRights(originalstate->gs); originalstate->gs_Limit = getSegmentLimit(originalstate->gs); //DbgPrint("originalstate->gs=%I64x",originalstate->gs); originalstate->ldt=GetLDT(); //DbgPrint("originalstate->ldt=%I64x",originalstate->ldt); originalstate->tr=GetTR(); //DbgPrint("originalstate->tr=%I64x",originalstate->tr); originalstate->fsbase=readMSR(0xc0000100); originalstate->gsbase=readMSR(0xc0000101); //DbgPrint("originalstate->fsbase=%I64x originalstate->gsbase=%I64x\n", originalstate->fsbase, originalstate->gsbase); originalstate->dr7=getDR7(); gdt.vector=0; gdt.wLimit=0; GetGDT(&gdt); originalstate->gdtbase=(ULONG_PTR)gdt.vector; originalstate->gdtlimit=gdt.wLimit; //DbgPrint("originalstate->gdtbase=%I64x",originalstate->gdtbase); //DbgPrint("originalstate->gdtlimit=%I64x",originalstate->gdtlimit); GetIDT(&idt); originalstate->idtbase=(ULONG_PTR)idt.vector; originalstate->idtlimit=idt.wLimit; //DbgPrint("originalstate->idtbase=%I64x",originalstate->idtbase); //DbgPrint("originalstate->idtlimit=%I64x",originalstate->idtlimit); eflags=getEflags(); eflags.IF = 0; originalstate->rflags=*(PUINT_PTR)&eflags; originalstate->rsp=getRSP(); //DbgPrint("originalstate->rsp=%I64x",originalstate->rsp); originalstate->rbp=getRBP(); //DbgPrint("originalstate->rbp=%I64x",originalstate->rbp); originalstate->rax=getRAX(); //DbgPrint("originalstate->rax=%I64x",originalstate->rax); originalstate->rbx=getRBX(); //DbgPrint("originalstate->rbx=%I64x",originalstate->rbx); originalstate->rcx=getRCX(); //DbgPrint("originalstate->rcx=%I64x",originalstate->rcx); originalstate->rdx=getRDX(); //DbgPrint("originalstate->rdx=%I64x",originalstate->rdx); originalstate->rsi=getRSI(); //DbgPrint("originalstate->rsi=%I64x",originalstate->rsi); originalstate->rdi=getRDI(); //DbgPrint("originalstate->rdi=%I64x",originalstate->rdi); #ifdef AMD64 originalstate->r8=getR8(); //DbgPrint("originalstate->r8=%I64x",originalstate->r8); originalstate->r9=getR9(); //DbgPrint("originalstate->r9=%I64x",originalstate->r9); originalstate->r10=getR10(); //DbgPrint("originalstate->r10=%I64x",originalstate->r10); originalstate->r11=getR11(); //DbgPrint("originalstate->r11=%I64x",originalstate->r11); originalstate->r12=getR12(); //DbgPrint("originalstate->r12=%I64x",originalstate->r12); originalstate->r13=getR13(); //DbgPrint("originalstate->r13=%I64x",originalstate->r13); originalstate->r14=getR14(); //DbgPrint("originalstate->r14=%I64x",originalstate->r14); originalstate->r15=getR15(); //DbgPrint("originalstate->r15=%I64x",originalstate->r15); #endif #ifdef AMD64 originalstate->rsp-=8; //adjust rsp for the "call entervmmprologue" originalstate->rip=(UINT_PTR)enterVMMEpilogue; //enterVMMEpilogue is an address inside the entervmmprologue function //DbgPrint("originalstate->rip=%llx",originalstate->rip); //DbgPrint("Calling entervmm2. (Originalstate=%p (%llx))\n",originalstate,originalstatePA); //call to entervmmprologue, pushes the return value on the stack enterVMMPrologue(); enableInterrupts(); //DbgPrint("Returned from enterVMMPrologue\n"); //DbgPrint("cpunr=%d\n",cpunr()); //KeLowerIrql(oldirql); //DbgPrint("cpunr=%d\n",cpunr()); #else { ULONG vmmentryeip; __asm { lea eax,[enterVMMEpilogue] mov vmmentryeip,eax } originalstate->rip=(UINT64)vmmentryeip; } __asm{ cli //goodbye interrupts xchg bx,bx mov ebx,vmmPA __emit 0x8b __emit 0xeb //mov ebp,ebx lea ebx,NewGDTDescriptor mov ecx,DBVMPML4PA; mov edx,TemporaryPagingSetupPA //for the mov cr3,ecx mov esi,enterVMM2PA mov edi,originalstatePA call [enterVMM2] //Will never get here. NEVER FUUUUU: xchg bx,bx jmp FUUUUU enterVMMEpilogue: //cli //test nop nop xchg bx,bx //bochs bp nop nop sti nop nop nop nop nop nop } //KeLowerIrql(oldirql); #endif //DbgPrint("Returning\n"); return; } } void vmxoffload_override(CCHAR cpunr, PKDEFERRED_ROUTINE Dpc, PVOID DeferredContext, PVOID *SystemArgument1, PVOID *SystemArgument2) { //runs at passive (in any unrelated cpu) //allocate 64KB of extra memory for this(and every other) cpu's DBVM PHYSICAL_ADDRESS LowAddress, HighAddress, SkipBytes; PMDL mdl; DbgPrint("vmxoffload_override\n"); LowAddress.QuadPart = 0; HighAddress.QuadPart = 0xffffffffffffffffI64; SkipBytes.QuadPart = 0; mdl = MmAllocatePagesForMdlEx(LowAddress, HighAddress, SkipBytes, 64 * 1024, MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS | MM_ALLOCATE_FULLY_REQUIRED); //do not free this, EVER if (mdl) { //convert the pfnlist to a list DBVM understands PDBVMOffloadMemInfo mi = ExAllocatePool(NonPagedPool, sizeof(DBVMOffloadMemInfo)); int i; PFN_NUMBER *pfnlist; DbgPrint("vmxoffload_override: mi=%p\n", mi); mi->List = ExAllocatePool(NonPagedPool, sizeof(UINT64) * 16); DbgPrint("vmxoffload_override: mi->list=%p\n", mi->List); pfnlist = MmGetMdlPfnArray(mdl); for (i = 0; i < 16; i++) mi->List[i] = pfnlist[i] << 12; mi->Count = 16; ExFreePool(mdl); *SystemArgument1 = mi; } } __drv_functionClass(KDEFERRED_ROUTINE) __drv_maxIRQL(DISPATCH_LEVEL) __drv_minIRQL(DISPATCH_LEVEL) __drv_requiresIRQL(DISPATCH_LEVEL) __drv_sameIRQL VOID vmxoffload_dpc( __in struct _KDPC *Dpc, __in_opt PVOID DeferredContext, __in_opt PVOID SystemArgument1, __in_opt PVOID SystemArgument2 ) { int c = cpunr(); DbgPrint("vmxoffload_dpc: CPU %d\n", c); KeAcquireSpinLockAtDpcLevel(&LoadedOSSpinLock); vmxoffload(); //still here so very likely DBVM is loaded if (SystemArgument1) { int x; PDBVMOffloadMemInfo mi = (PDBVMOffloadMemInfo)SystemArgument1; DbgPrint("mi->List=%p mi->Count=%d\n", mi->List, mi->Count); x=vmx_add_memory(mi->List, mi->Count); DbgPrint("vmx_add_memory returned %x\n", x); if (mi->List) ExFreePool(mi->List); ExFreePool(mi); } else DbgPrint("Error: SystemArgument1=NULL\n"); KeReleaseSpinLockFromDpcLevel(&LoadedOSSpinLock); } ================================================ FILE: src/vmxoffload.h ================================================ #ifndef VMXOFFLOAD_H #define VMXOFFLOAD_H void cleanupDBVM(); void initializeDBVM(PCWSTR dbvmimgpath); void vmxoffload(void); void vmxoffload_override(CCHAR cpunr, PKDEFERRED_ROUTINE Dpc, PVOID DeferredContext, PVOID *SystemArgument1, PVOID *SystemArgument2); VOID vmxoffload_dpc( __in struct _KDPC *Dpc, __in_opt PVOID DeferredContext, __in_opt PVOID SystemArgument1, __in_opt PVOID SystemArgument2 ); typedef struct _DBVMOffloadMemInfo { UINT64 *List; int Count; } DBVMOffloadMemInfo, *PDBVMOffloadMemInfo; #pragma pack (1) typedef struct _PTE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned D : 1; // dirty unsigned PAT : 1; // PAT unsigned G : 1; // global page unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN : 20; // page-frame number } *PPTE; typedef struct _PDE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned D : 1; // dirty unsigned PS : 1; // reserved (0) unsigned G : 1; // reserved (0) unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN : 20; // page-frame number } *PPDE; typedef struct _PDE2MB { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned reserved1 : 1; // reserved (0) unsigned PS : 1; // reserved (0) unsigned reserved3 : 1; // reserved (0) unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN : 20; // page-frame number (>> 13 instead of >>12); } *PPDE2MB; typedef struct _PTE_PAE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned D : 1; // dirty unsigned PAT : 1; // unsigned G : 1; // global page unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN_LOW : 20; unsigned PFN_HIGH : 32; } PTE_PAE, *PPTE_PAE; typedef struct _PDE_PAE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned D : 1; // dirty unsigned PS : 1; // pagesize unsigned G : 1; // reserved (0) unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN_LOW : 20; unsigned PFN_HIGH : 32; } PDE_PAE, *PPDE_PAE; typedef struct _PDE2MB_PAE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // read/write unsigned US : 1; // user/supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned A : 1; // accessed unsigned reserved1 : 1; // reserved (0) unsigned PS : 1; // reserved (0) unsigned reserved3 : 1; // reserved (0) unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PAT : 1; // unsigned PFN_LOW : 19; unsigned PFN_HIGH : 32; } *PPDE2MB_PAE; typedef struct _PDPTE_PAE { unsigned P : 1; // present (1 = present) unsigned RW : 1; // Read Write unsigned US : 1; // User supervisor unsigned PWT : 1; // page-level write-through unsigned PCD : 1; // page-level cache disabled unsigned reserved0 : 1; // reserved unsigned reserved1 : 1; // reserved unsigned reserved2 : 1; // reserved unsigned reserved3 : 1; // reserved unsigned A1 : 1; // available 1 aka copy-on-write unsigned A2 : 1; // available 2/ is 1 when paged to disk unsigned A3 : 1; // available 3 unsigned PFN_LOW : 20; unsigned PFN_HIGH : 32; } *PPDPTE_PAE; #endif ================================================ FILE: templates/CMakeLists.txt.tpl ================================================ cmake_minimum_required(VERSION 3.18.0) project({{DRIVER_NAME}} VERSION 1.0.0) enable_language(C ASM_MASM) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../wdk") find_package(WDK REQUIRED) string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /WX") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W0 /WX") add_definitions( -DRELEASE="DEFINED" ) wdk_add_driver({{DRIVER_NAME}} KMDF 1.15 "${CMAKE_CURRENT_SOURCE_DIR}/DBKDrvr.c" "${CMAKE_CURRENT_SOURCE_DIR}/DBKFunc.c" "${CMAKE_CURRENT_SOURCE_DIR}/debugger.c" "${CMAKE_CURRENT_SOURCE_DIR}/deepkernel.c" "${CMAKE_CURRENT_SOURCE_DIR}/interruptHook.c" "${CMAKE_CURRENT_SOURCE_DIR}/IOPLDispatcher.c" "${CMAKE_CURRENT_SOURCE_DIR}/memscan.c" "${CMAKE_CURRENT_SOURCE_DIR}/noexceptions.c" "${CMAKE_CURRENT_SOURCE_DIR}/processlist.c" "${CMAKE_CURRENT_SOURCE_DIR}/threads.c" "${CMAKE_CURRENT_SOURCE_DIR}/ultimap.c" "${CMAKE_CURRENT_SOURCE_DIR}/ultimap2.c" "${CMAKE_CURRENT_SOURCE_DIR}/ultimap2/apic.c" "${CMAKE_CURRENT_SOURCE_DIR}/vmxhelper.c" "${CMAKE_CURRENT_SOURCE_DIR}/vmxoffload.c" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/dbkfunca.asm" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/debuggera.asm" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/noexceptionsa.asm" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/ultimapa.asm" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/vmxhelpera.asm" "${CMAKE_CURRENT_SOURCE_DIR}/amd64/vmxoffloada.asm" ) target_include_directories({{DRIVER_NAME}} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set_target_properties({{DRIVER_NAME}} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}" RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}" ) ================================================ FILE: templates/chamd.inf.tpl ================================================ ; ; {{DRIVER_NAME}}.inf ; [Version] Signature="$WINDOWS NT$" Class=Sample ; TODO: edit Class ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid Provider=%ManufacturerName% CatalogFile={{DRIVER_NAME}}.cat DriverVer= PnpLockDown=1 [DestinationDirs] DefaultDestDir = 12 {{DRIVER_NAME}}_Device_CoInstaller_CopyFiles = 11 ; ================= Class section ===================== [ClassInstall32] Addreg=SampleClassReg [SampleClassReg] HKR,,,0,%ClassName% HKR,,Icon,,-5 [SourceDisksNames] 1 = %DiskName%,,,"" [SourceDisksFiles] {{DRIVER_NAME}}.sys = 1,, ;WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames ;***************************************** ; Install Section ;***************************************** [Manufacturer] %ManufacturerName%=Standard,NT$ARCH$ [Standard.NT$ARCH$] %{{DRIVER_NAME}}.DeviceDesc%={{DRIVER_NAME}}_Device, Root\\{{DRIVER_NAME}} ; TODO: edit hw-id [{{DRIVER_NAME}}_Device.NT] CopyFiles=Drivers_Dir [Drivers_Dir] {{DRIVER_NAME}}.sys ;-------------- Service installation [{{DRIVER_NAME}}_Device.NT.Services] AddService = {{DRIVER_NAME}},%SPSVCINST_ASSOCSERVICE%, {{DRIVER_NAME}}_Service_Inst ; -------------- {{DRIVER_NAME}} driver install sections [{{DRIVER_NAME}}_Service_Inst] DisplayName = %{{DRIVER_NAME}}.SVCDESC% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\\{{DRIVER_NAME}}.sys ; ;--- {{DRIVER_NAME}}_Device Coinstaller installation ------ ; [{{DRIVER_NAME}}_Device.NT.CoInstallers] AddReg={{DRIVER_NAME}}_Device_CoInstaller_AddReg CopyFiles={{DRIVER_NAME}}_Device_CoInstaller_CopyFiles [{{DRIVER_NAME}}_Device_CoInstaller_AddReg] ;HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" [{{DRIVER_NAME}}_Device_CoInstaller_CopyFiles] ;WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll [{{DRIVER_NAME}}_Device.NT.Wdf] KmdfService = {{DRIVER_NAME}}, {{DRIVER_NAME}}_wdfsect [{{DRIVER_NAME}}_wdfsect] KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 ManufacturerName="{{DRIVER_NAME}} Inc." ClassName="Samples" ; TODO: edit ClassName DiskName = "{{DRIVER_NAME}} Installation Disk" {{DRIVER_NAME}}.DeviceDesc = "{{DRIVER_NAME}} Device" {{DRIVER_NAME}}.SVCDESC = "{{DRIVER_NAME}} Service" ================================================ FILE: templates/driver64.dat.tpl ================================================ fgfgfgfh fghcghfhg fvfghfbg {{DRIVER_NAME}}.sys 76543210 fedcba98 ================================================ FILE: templates/makecat.cdf.tpl ================================================ [CatalogHeader] Name={{DRIVER_NAME}}.cat PublicVersion=0x0000001 EncodingType=0x00010001 CATATTR1=0x10010001:OSAttr:2:6.0 [CatalogFiles] File1={{DRIVER_NAME}}.sys ================================================ FILE: wdk/FindWDK.cmake ================================================ # Redistribution and use is allowed under the OSI-approved 3-clause BSD license. # Copyright (c) 2018 Sergey Podobry (sergey.podobry at gmail.com). All rights reserved. #.rst: # FindWDK # ---------- # # This module searches for the installed Windows Development Kit (WDK) and # exposes commands for creating kernel drivers and kernel libraries. # # Output variables: # - `WDK_FOUND` -- if false, do not try to use WDK # - `WDK_ROOT` -- where WDK is installed # - `WDK_VERSION` -- the version of the selected WDK # - `WDK_WINVER` -- the WINVER used for kernel drivers and libraries # (default value is `0x0601` and can be changed per target or globally) # - `WDK_NTDDI_VERSION` -- the NTDDI_VERSION used for kernel drivers and libraries, # if not set, the value will be automatically calculated by WINVER # (default value is left blank and can be changed per target or globally) # # Example usage: # # find_package(WDK REQUIRED) # # wdk_add_library(KmdfCppLib STATIC KMDF 1.15 # KmdfCppLib.h # KmdfCppLib.cpp # ) # target_include_directories(KmdfCppLib INTERFACE .) # # wdk_add_driver(KmdfCppDriver KMDF 1.15 # Main.cpp # ) # target_link_libraries(KmdfCppDriver KmdfCppLib) # if(DEFINED ENV{WDKContentRoot}) file(GLOB WDK_NTDDK_FILES "$ENV{WDKContentRoot}/Include/*/km/ntddk.h" # WDK 10 "$ENV{WDKContentRoot}/Include/km/ntddk.h" # WDK 8.0, 8.1 ) else() file(GLOB WDK_NTDDK_FILES "C:/Program Files*/Windows Kits/*/Include/*/km/ntddk.h" # WDK 10 "C:/Program Files*/Windows Kits/*/Include/km/ntddk.h" # WDK 8.0, 8.1 ) endif() if(WDK_NTDDK_FILES) if (NOT CMAKE_VERSION VERSION_LESS 3.18.0) list(SORT WDK_NTDDK_FILES COMPARE NATURAL) # sort to use the latest available WDK endif() list(GET WDK_NTDDK_FILES -1 WDK_LATEST_NTDDK_FILE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WDK REQUIRED_VARS WDK_LATEST_NTDDK_FILE) if(NOT WDK_LATEST_NTDDK_FILE) return() endif() get_filename_component(WDK_ROOT ${WDK_LATEST_NTDDK_FILE} DIRECTORY) get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) get_filename_component(WDK_VERSION ${WDK_ROOT} NAME) get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) if (NOT WDK_ROOT MATCHES ".*/[0-9][0-9.]*$") # WDK 10 has a deeper nesting level get_filename_component(WDK_ROOT ${WDK_ROOT} DIRECTORY) # go up once more set(WDK_LIB_VERSION "${WDK_VERSION}") set(WDK_INC_VERSION "${WDK_VERSION}") else() # WDK 8.0, 8.1 set(WDK_INC_VERSION "") foreach(VERSION winv6.3 win8 win7) if (EXISTS "${WDK_ROOT}/Lib/${VERSION}/") set(WDK_LIB_VERSION "${VERSION}") break() endif() endforeach() set(WDK_VERSION "${WDK_LIB_VERSION}") endif() message(STATUS "WDK_ROOT: " ${WDK_ROOT}) message(STATUS "WDK_VERSION: " ${WDK_VERSION}) set(WDK_WINVER "0x0601" CACHE STRING "Default WINVER for WDK targets") set(WDK_NTDDI_VERSION "" CACHE STRING "Specified NTDDI_VERSION for WDK targets if needed") set(WDK_ADDITIONAL_FLAGS_FILE "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/wdkflags.h") file(WRITE ${WDK_ADDITIONAL_FLAGS_FILE} "#pragma runtime_checks(\"suc\", off)") set(WDK_COMPILE_FLAGS "/Zp8" # set struct alignment "/GF" # enable string pooling "/GR-" # disable RTTI "/Gz" # __stdcall by default "/kernel" # create kernel mode binary "/FIwarning.h" # disable warnings in WDK headers "/FI${WDK_ADDITIONAL_FLAGS_FILE}" # include file to disable RTC "/Oi" # enable intrinsic functions so that you can use functions like _disable or _enable ) set(WDK_COMPILE_DEFINITIONS "WINNT=1") set(WDK_COMPILE_DEFINITIONS_DEBUG "MSC_NOOPT;DEPRECATE_DDK_FUNCTIONS=1;DBG=1") if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND WDK_COMPILE_DEFINITIONS "_X86_=1;i386=1;STD_CALL") set(WDK_PLATFORM "x86") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8 AND CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64") list(APPEND WDK_COMPILE_DEFINITIONS "_ARM64_;ARM64;_USE_DECLSPECS_FOR_SAL=1;STD_CALL") set(WDK_PLATFORM "arm64") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND WDK_COMPILE_DEFINITIONS "_AMD64_;AMD64") set(WDK_PLATFORM "x64") else() message(FATAL_ERROR "Unsupported architecture") endif() string(CONCAT WDK_LINK_FLAGS "/MANIFEST:NO " # "/DRIVER " # "/OPT:REF " # "/INCREMENTAL:NO " # "/OPT:ICF " # "/SUBSYSTEM:NATIVE " # "/MERGE:_TEXT=.text;_PAGE=PAGE " # "/NODEFAULTLIB " # do not link default CRT "/SECTION:INIT,d " # "/VERSION:10.0 " # ) # Generate imported targets for WDK lib files file(GLOB WDK_LIBRARIES "${WDK_ROOT}/Lib/${WDK_LIB_VERSION}/km/${WDK_PLATFORM}/*.lib") foreach(LIBRARY IN LISTS WDK_LIBRARIES) get_filename_component(LIBRARY_NAME ${LIBRARY} NAME_WE) string(TOUPPER ${LIBRARY_NAME} LIBRARY_NAME) add_library(WDK::${LIBRARY_NAME} INTERFACE IMPORTED) set_property(TARGET WDK::${LIBRARY_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBRARY}) endforeach(LIBRARY) unset(WDK_LIBRARIES) function(wdk_add_driver _target) cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN}) add_executable(${_target} ${WDK_UNPARSED_ARGUMENTS}) set_target_properties(${_target} PROPERTIES SUFFIX ".sys") set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG}>;_WIN32_WINNT=${WDK_WINVER}" ) set_target_properties(${_target} PROPERTIES LINK_FLAGS "${WDK_LINK_FLAGS}") if(WDK_NTDDI_VERSION) target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION}) endif() target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared" "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km" "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt" ) target_link_libraries(${_target} WDK::NTOSKRNL WDK::HAL WDK::WMILIB) if(WDK::BUFFEROVERFLOWK) target_link_libraries(${_target} WDK::BUFFEROVERFLOWK) # to support Windows 7 and Vista else() target_link_libraries(${_target} WDK::BUFFEROVERFLOWFASTFAILK) endif() if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64") target_link_libraries(${_target} "arm64rt.lib") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 4) target_link_libraries(${_target} WDK::MEMCMP) endif() if(DEFINED WDK_KMDF) target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") target_link_libraries(${_target} "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfDriverEntry.lib" "${WDK_ROOT}/Lib/wdf/kmdf/${WDK_PLATFORM}/${WDK_KMDF}/WdfLdr.lib" ) if(CMAKE_SIZEOF_VOID_P EQUAL 4) set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry@8") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:FxDriverEntry") endif() else() if(CMAKE_SIZEOF_VOID_P EQUAL 4) set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry@8") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS "/ENTRY:GsDriverEntry") endif() endif() endfunction() function(wdk_add_library _target) cmake_parse_arguments(WDK "" "KMDF;WINVER;NTDDI_VERSION" "" ${ARGN}) add_library(${_target} ${WDK_UNPARSED_ARGUMENTS}) set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${WDK_COMPILE_FLAGS}") set_target_properties(${_target} PROPERTIES COMPILE_DEFINITIONS "${WDK_COMPILE_DEFINITIONS};$<$:${WDK_COMPILE_DEFINITIONS_DEBUG};>_WIN32_WINNT=${WDK_WINVER}" ) if(WDK_NTDDI_VERSION) target_compile_definitions(${_target} PRIVATE NTDDI_VERSION=${WDK_NTDDI_VERSION}) endif() target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/${WDK_INC_VERSION}/shared" "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km" "${WDK_ROOT}/Include/${WDK_INC_VERSION}/km/crt" ) if(DEFINED WDK_KMDF) target_include_directories(${_target} SYSTEM PRIVATE "${WDK_ROOT}/Include/wdf/kmdf/${WDK_KMDF}") endif() endfunction()