├── README.md ├── ConditionCodes.h ├── WarbirdTest.inl ├── WarbirdRandom.inl ├── AntiDebug.cpp ├── WarbirdVSM.h ├── WarbirdAD_x86.asm ├── WarbirdTermination_x86.asm ├── WarbirdTermination_ARM.asm ├── WarbirdVM_AMD64.asm ├── WarbirdAD_AMD64.asm ├── WarbirdLock.inl ├── WarbirdTermination_E2.asm ├── WarbirdEncyptionSysCalls.inl ├── WarbirdVM_x86.asm ├── WarbirdVerification.inl ├── WarbirdHash.inl ├── WarbirdInitialize.inl ├── WarbirdDynamicCodeGen.inl ├── WarbirdCUtil.inl ├── WarbirdVSMTest.inl ├── WarbirdRuntime.h ├── WarbirdTermination.inl ├── WarbirdReloc.inl └── WarbirdMemory.inl /README.md: -------------------------------------------------------------------------------- 1 | # Warbird runtime 2 | Warbird runtime source code for Windows 10 Redstone 3. 3 | -------------------------------------------------------------------------------- /ConditionCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // The condition code in warbird.h always need to be in sync with the condition 3 | // codes in runtime\ConditionCodes.h 4 | // 5 | enum ConditionCode 6 | { 7 | ConditionCodeNone, 8 | ConditionCodeEq, 9 | ConditionCodeGe, 10 | ConditionCodeGt, 11 | ConditionCodeLbc, 12 | ConditionCodeLbs, 13 | ConditionCodeLe, 14 | ConditionCodeLt, 15 | ConditionCodeNe, 16 | ConditionCodeNo, 17 | ConditionCodeNp, 18 | ConditionCodeNs, 19 | ConditionCodeO, 20 | ConditionCodeP, 21 | ConditionCodeS, 22 | ConditionCodeUge, 23 | ConditionCodeUgt, 24 | ConditionCodeUle, 25 | ConditionCodeULt, 26 | ConditionCodeBt, // Check if the specified bit is set 27 | ConditionCodeUnknown = 0xff, // special case for unexpected values 28 | }; 29 | -------------------------------------------------------------------------------- /WarbirdTest.inl: -------------------------------------------------------------------------------- 1 | // Warbird 2 used this symbol to reference all runtime elements. 2 | // Some libs may still reference this symbol. 3 | // This instantiation of a symbol with this name has no current purpose 4 | // except to satisfy that requirement for linking. 5 | extern "C" unsigned int WarbirdRuntimeRef = 0; 6 | 7 | #ifdef WARBIRD_TEST 8 | 9 | class CTest 10 | { 11 | public: 12 | CTest(); 13 | 14 | void ReportVerifyFailure(); 15 | 16 | void IncrementVerifyCount(); 17 | 18 | ULONG GetVerifyCount(); 19 | 20 | void ResetVerifyCount(); 21 | 22 | private: 23 | ULONG m_nVerifyCount; 24 | }; 25 | 26 | CTest::CTest() 27 | { 28 | m_nVerifyCount = 0; 29 | } 30 | 31 | void CTest::ReportVerifyFailure() 32 | { 33 | __debugbreak(); 34 | return; 35 | } 36 | 37 | void CTest::IncrementVerifyCount() 38 | { 39 | m_nVerifyCount++; 40 | } 41 | 42 | ULONG CTest::GetVerifyCount() 43 | { 44 | return m_nVerifyCount; 45 | } 46 | 47 | void CTest::ResetVerifyCount() 48 | { 49 | m_nVerifyCount = 0; 50 | } 51 | 52 | extern CTest* g_pTestClass; 53 | 54 | #endif -------------------------------------------------------------------------------- /WarbirdRandom.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Pseudorandom number generator 4 | * 5 | **/ 6 | 7 | namespace WarbirdRuntime 8 | { 9 | 10 | class CRand 11 | { 12 | public: 13 | VOID 14 | Init( 15 | LONG nSeed 16 | ) 17 | { 18 | m_HoldRandom = nSeed; 19 | } 20 | 21 | SIZE_T 22 | Random( 23 | ) 24 | { 25 | return ((m_HoldRandom = m_HoldRandom * 214013L + 2531011L) >> 16) & 0xffff; 26 | } 27 | 28 | SIZE_T 29 | Random( 30 | SIZE_T nMin, 31 | SIZE_T nMax 32 | ) 33 | { 34 | if (nMin > nMax) 35 | { 36 | return nMin; 37 | } 38 | 39 | SIZE_T nRange = nMax - nMin + 1; 40 | 41 | SIZE_T nRandom = 0; 42 | 43 | for (SIZE_T i = nRange; i != 0; i >>= 16) 44 | { 45 | nRandom = (nRandom << 16) | Random(); 46 | } 47 | 48 | return nMin + (nRandom % nRange); 49 | } 50 | 51 | private: 52 | SIZE_T m_HoldRandom; 53 | 54 | }; // class CRand 55 | 56 | CRand g_Rand; 57 | 58 | }; // namespace WarbirdRuntime -------------------------------------------------------------------------------- /AntiDebug.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // 3 | // Copyright (C) Microsoft Corporation. All Rights Reserved. 4 | // 5 | // File: AntiDebug.cpp 6 | // 7 | // Description: 8 | // 9 | // Warbird Anti-Debugging Helper Functions 10 | // 11 | //----------------------------------------------------------------------------- 12 | 13 | #if (defined(_X86_) || defined(_AMD64_)) && !defined(WARBIRD_KERNEL_MODE) && !defined(WARBIRD_AD_DISABLED) 14 | 15 | DWORD WINAPI WarbirdAD_ThreadCompareDRs(void* p) 16 | { 17 | DWORD dwRet = 0xf3b02c90; 18 | HANDLE hMainThread = (HANDLE)p; 19 | 20 | if(-1 != SuspendThread(hMainThread)) 21 | { 22 | CONTEXT context; 23 | context.ContextFlags = CONTEXT_DEBUG_REGISTERS; 24 | 25 | if(GetThreadContext(hMainThread, &context)) 26 | { 27 | if(ad_CompareDRs(&context)) 28 | dwRet = 1; 29 | } 30 | ResumeThread(hMainThread); 31 | } 32 | 33 | CloseHandle(hMainThread); 34 | return dwRet; 35 | } 36 | 37 | #endif // #if (defined(_X86_) || defined(_AMD64_)) && !defined(WARBIRD_KERNEL_MODE) -------------------------------------------------------------------------------- /WarbirdVSM.h: -------------------------------------------------------------------------------- 1 | #if $(WARBIRD_ENABLE_VSM) && (defined(WARBIRD_KERNEL_MODE) || defined(WARBIRD_VSM_TEST)) 2 | 3 | namespace WarbirdRuntime 4 | { 5 | 6 | #if ($(WARBIRD_VSM_VERSION) == 2) 7 | struct VSM_PAGE_ENTRY 8 | { 9 | UCHAR HashBuffer[$(WARBIRD_VSM_HASH_LENGTH)]; 10 | }; 11 | 12 | __declspec( align($(WARBIRD_VSM_HASH_LENGTH)) ) struct VSM_HASH_TABLE 13 | { 14 | ULONG Length; 15 | ULONG Version; 16 | ALG_ID HashAlgorithm; 17 | ULONG NumberOfPageHashes; 18 | ULONG Flag; 19 | BYTE Padding[$(WARBIRD_VSM_HASH_LENGTH) - sizeof(ULONG) - sizeof(ULONG) - sizeof(ALG_ID) - sizeof(ULONG) - sizeof(ULONG)]; 20 | VSM_PAGE_ENTRY Hashes [ANYSIZE_ARRAY]; 21 | }; 22 | 23 | struct VSM_INDEX_ENTRY 24 | { 25 | ULONG Index; // Index in Hashes for the first page of the Warbird segment 26 | ULONG Pages; // Number of pages in the Warbird segment 27 | ULONG Rva; // First page in the Warbird segment 28 | ULONG SegId; // Segment Id 29 | ULONG FileOffset; // Added in version 2 - invalid memory location in early version 1 images 30 | }; 31 | 32 | // Hash must be at larger then header fields in VSM_HASH_TABLE 33 | C_ASSERT($(WARBIRD_VSM_HASH_LENGTH) >= sizeof(ULONG)*5); 34 | #else // ($(WARBIRD_VSM_VERSION) == 2) 35 | #error UNDEFINED WARBIRD VSM VERSION 36 | #endif // ($(WARBIRD_VSM_VERSION) == 2) 37 | } // WarbirdRuntime 38 | 39 | // 40 | // VSM global variables 41 | // 42 | 43 | namespace WarbirdRuntime 44 | { 45 | 46 | #ifndef STATUS_SUCCESS 47 | #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) 48 | #endif 49 | 50 | #define GetVsmHashTable() (VSM_HASH_TABLE*)((ULONG_PTR)(g_VsmHashTable) + CUtil::GetImageBase()) 51 | 52 | volatile ULONG g_VsmHashTable = 0x12456908; 53 | 54 | #pragma section(".rdata$VsmData", read) 55 | 56 | __declspec(allocate(".rdata$VsmData")) 57 | __declspec(selectany) 58 | volatile VSM_INDEX_ENTRY g_VsmSegmentIndex[$(NumSegments)] = {0}; 59 | 60 | // 61 | // Coming soon for HVCI 62 | #if !defined(MM_CHANGE_ENABLE_EXECUTE) 63 | #define MM_CHANGE_ENABLE_EXECUTE 1 64 | #endif 65 | #if !defined(MM_CHANGE_DISABLE_EXECUTE) 66 | #define MM_CHANGE_DISABLE_EXECUTE 2 67 | #endif 68 | 69 | } // WarbirdRuntime 70 | 71 | #endif // $(WARBIRD_ENABLE_VSM) && (defined(WARBIRD_KERNEL_MODE) || defined(WARBIRD_VSM_TEST)) -------------------------------------------------------------------------------- /WarbirdAD_x86.asm: -------------------------------------------------------------------------------- 1 | #ifdef _X86_ 2 | 3 | title "Warbird Anti-Debugging assembly functions for x86" 4 | 5 | .386 6 | 7 | ;----------------------------------------------------------------------------- 8 | ; 9 | ; Copyright (C) Microsoft Corporation. All Rights Reserved. 10 | ; 11 | ; File: WarbirdAD_x86.asm 12 | ; 13 | ; Description: 14 | ; 15 | ; Anti-Debug macro implementations for x86 16 | ; 17 | ;----------------------------------------------------------------------------- 18 | 19 | #ifndef WARBIRD_KERNEL_MODE 20 | 21 | ; 22 | ; Guard Debugger Functions 23 | ; 24 | _TEXT SEGMENT 25 | 26 | ; Add 1 to the first arg and then invoke the GD to undo it. Second arg must be zero 27 | @WarbirdAD_GD_Add1_Sub1@8 PROC public 28 | inc ecx ; add 1 to the first arg 29 | mov eax, ecx ; move the first arg into eax, ready to divide 30 | mov ecx, edx ; the second arg is zero, so move it into ecx to generate the AV 31 | mov edx, 1 ; edx==1 informs the GD to subtract 1 32 | div ecx ; generate the AV 33 | ret 34 | @WarbirdAD_GD_Add1_Sub1@8 ENDP 35 | 36 | 37 | ; Invokes the GD to add 1 to the first arg and then return it. Second arg must be zero 38 | @WarbirdAD_GD_Add1@8 PROC public 39 | mov eax, ecx ; move the first arg into eax, ready to divide 40 | mov ecx, edx ; the second arg is zero, so move it into ecx to generate the AV 41 | div ecx ; generate the AV 42 | ret 43 | @WarbirdAD_GD_Add1@8 ENDP 44 | 45 | 46 | ; Invokes the GD to subtract 1 to the first arg and then return it. Second arg must be zero 47 | @WarbirdAD_GD_Sub1@8 PROC public 48 | mov eax, ecx ; move the first arg into eax, ready to divide 49 | mov ecx, edx ; the second arg is zero, so move it into ecx to generate the AV 50 | mov edx, 1 ; edx==1 informs the GD to subtract 1 51 | div ecx ; generate the AV 52 | ret 53 | @WarbirdAD_GD_Sub1@8 ENDP 54 | 55 | 56 | ; Masks the first arg and then invokes the GD to undo the mask. Second arg must be zero 57 | @WarbirdAD_GD_XorA_XorA@8 PROC public 58 | mov eax, ecx ; move the first arg into eax, ready to xor and then divide 59 | xor eax, 03c9e4fd1h ; xor the first arg 60 | mov ecx, edx ; the second arg is zero, so move it into ecx to generate the AV 61 | mov edx, 2 ; edx==2 informs the GD to xor A 62 | div ecx ; generate the AV 63 | ret 64 | @WarbirdAD_GD_XorA_XorA@8 ENDP 65 | 66 | 67 | _TEXT ENDS 68 | 69 | #endif 70 | 71 | #endif -------------------------------------------------------------------------------- /WarbirdTermination_x86.asm: -------------------------------------------------------------------------------- 1 | #if defined(_M_IX86) 2 | 3 | include ks386.inc 4 | 5 | _TEXT$00 segment para 'CODE' 6 | 7 | ; 8 | ; private: static void __fastcall WarbirdRuntime::CTermination::TrashStack(int (__stdcall*)(void)) 9 | ; 10 | ; Clears the stack and registers, and jumps to termination function 11 | ; 12 | 13 | ?TrashStack@CTermination@WarbirdRuntime@@CIXP6GHXZ@Z proc public 14 | 15 | sub eax, eax ; set a scratch register to zero 16 | 17 | mov edx, fs:[TeStackLimit] ; set a destination register to point to top of the stack 18 | mov esp, fs:[TeStackBase] ; move stack pointer to the bottom of the stack 19 | push ecx ; push the termination function address (which will look like a 20 | ; fake return address after we jump to the termination function) 21 | ClearStack: 22 | cmp edx, esp ; is the destination past the bottom of stack? 23 | jae ClearRegs ; yes - exit loop 24 | mov dword ptr [edx], eax ; no - set destination to zero 25 | add edx, 4 ; increment destination pointer 26 | jmp ClearStack ; loop 27 | 28 | ClearRegs: 29 | mov ebx, eax ; set all integer registers to zero 30 | mov ecx, eax 31 | mov edx, eax 32 | mov esi, eax 33 | mov edi, eax 34 | mov ebp, eax 35 | 36 | jmp dword ptr [esp] ; jump to termination function 37 | 38 | ?TrashStack@CTermination@WarbirdRuntime@@CIXP6GHXZ@Z endp 39 | 40 | _TEXT$00 ends 41 | 42 | #endif 43 | #if defined(_M_AMD64) 44 | 45 | include ksamd64.inc 46 | 47 | _TEXT segment para 'CODE' 48 | 49 | ; 50 | ; 51 | ; private: static void __cdecl WarbirdRuntime::CTermination::TrashStack(__int64 (__cdecl*)(void)) 52 | ; 53 | ; Clears the stack and registers, and jumps to termination function 54 | ; 55 | 56 | ?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z proc public frame 57 | 58 | .endprolog 59 | 60 | sub eax, eax ; set a scratch register to zero 61 | 62 | mov rdx, gs:[TeStackLimit] ; set a destination register to point to top of the stack 63 | mov rsp, gs:[TeStackBase] ; move stack pointer to the bottom of the stack 64 | push rcx ; push the termination function address (which will look like a 65 | ; fake return address after we jump to the termination function) 66 | ClearStack: 67 | cmp rdx, rsp ; is the destination past the bottom of stack? 68 | jae ClearRegs ; yes - exit loop 69 | mov qword ptr [rdx], rax ; no - set destination to zero 70 | add rdx, 8 ; increment destination pointer 71 | jmp ClearStack ; loop 72 | 73 | ClearRegs: 74 | mov rbx, rax ; set all integer registers to zero 75 | mov rcx, rax 76 | mov rdx, rax 77 | mov rsi, rax 78 | mov rdi, rax 79 | mov rbp, rax 80 | mov r8, rax 81 | mov r9, rax 82 | mov r10, rax 83 | mov r11, rax 84 | mov r12, rax 85 | mov r13, rax 86 | mov r14, rax 87 | mov r15, rax 88 | 89 | jmp qword ptr [rsp] ; jump to termination function 90 | 91 | ?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z endp 92 | 93 | _TEXT ends 94 | 95 | #endif -------------------------------------------------------------------------------- /WarbirdTermination_ARM.asm: -------------------------------------------------------------------------------- 1 | #if defined(_M_ARM) 2 | 3 | #include "ksarm.h" 4 | 5 | TEXTAREA 6 | 7 | ; 8 | ; private: static void __cdecl WarbirdRuntime::CTermination::TrashStack(int (__cdecl*)(void)) 9 | ; 10 | ; Clears the stack and registers, and jumps to termination function 11 | ; 12 | 13 | NESTED_ENTRY ?TrashStack@CTermination@WarbirdRuntime@@CAXP6AHXZ@Z 14 | 15 | sub r1, r1, r1 ; set a scratch register to zero 16 | 17 | TEB_READ r2 18 | ldr r3, [r2, #TeStackLimit] ; set a destination register to point to top of the stack 19 | ldr sp, [r2, #TeStackBase] ; move stack pointer to the bottom of the stack 20 | mov r4, sp ; save sp to a temp tegister (otherwise assembler errors out on "cmp r3, sp") 21 | mov lr, r0 ; set lr to the termination function address (which will look like a 22 | ; fake return address after we jump to the termination function) 23 | ClearStack 24 | cmp r3, r4 ; is the destination past the bottom of stack? 25 | bge ClearRegs ; yes - exit loop 26 | str r1, [r3], #4 ; no - set destination to zero and increment destination pointer 27 | b ClearStack ; loop 28 | ClearRegs 29 | mov r0, r1 ; set all integer registers to zero 30 | mov r2, r1 31 | mov r3, r1 32 | mov r4, r1 33 | mov r5, r1 34 | mov r6, r1 35 | mov r7, r1 36 | mov r8, r1 37 | mov r9, r1 38 | mov r10, r1 39 | mov r11, r1 40 | mov r12, r1 41 | 42 | bx lr ; jump to termination function 43 | 44 | NESTED_END ?TrashStack@CTermination@WarbirdRuntime@@CAXP6AHXZ@Z 45 | 46 | #endif 47 | #if defined(_M_ARM64) 48 | 49 | #include "ksarm64.h" 50 | 51 | TEXTAREA 52 | 53 | ; 54 | ; private: static void __cdecl WarbirdRuntime::CTermination::TrashStack(int (__cdecl*)(void)) 55 | ; 56 | ; Clears the stack and registers, and jumps to termination function 57 | ; 58 | 59 | NESTED_ENTRY ?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z 60 | 61 | ldr x3, [x18, #TeStackLimit] ; set a destination register to point to top of the stack 62 | ldr x4, [x18, #TeStackBase] ; move stack pointer to the bottom of the stack 63 | mov sp, x4 ; save sp to a temp tegister (otherwise assembler errors out on "cmp r3, sp") 64 | mov lr, x0 ; set lr to the termination function address (which will look like a 65 | ; fake return address after we jump to the termination function) 66 | ClearStack 67 | cmp x3, x4 ; is the destination past the bottom of stack? 68 | bge ClearRegs ; yes - exit loop 69 | str xzr, [x3], #8 ; no - set destination to zero and increment destination pointer 70 | b ClearStack ; loop 71 | ClearRegs 72 | mov x0, #0 ; set all integer registers to zero 73 | mov x1, #0 74 | mov x2, #0 75 | mov x3, #0 76 | mov x4, #0 77 | mov x5, #0 78 | mov x6, #0 79 | mov x7, #0 80 | mov x8, #0 81 | mov x9, #0 82 | mov x10, #0 83 | mov x11, #0 84 | mov x12, #0 85 | mov x13, #0 86 | mov x14, #0 87 | mov x15, #0 88 | mov x16, #0 89 | mov x17, #0 90 | mov x19, #0 91 | mov x20, #0 92 | mov x21, #0 93 | mov x22, #0 94 | mov x23, #0 95 | mov x24, #0 96 | mov x25, #0 97 | mov x26, #0 98 | mov x27, #0 99 | mov x28, #0 100 | mov x29, #0 101 | 102 | ret ; jump to termination function 103 | 104 | NESTED_END ?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z 105 | 106 | #endif -------------------------------------------------------------------------------- /WarbirdVM_AMD64.asm: -------------------------------------------------------------------------------- 1 | #ifdef _M_AMD64 2 | 3 | #if $(WARBIRD_ENABLE_VM_EXECUTION) 4 | 5 | title "VMExecAmd64Runtimes" 6 | 7 | SR struct 8 | P1 dq ? 9 | P2 dq ? 10 | P3 dq ? 11 | P4 dq ? 12 | 13 | SavedAx dq ? 14 | SavedCx dq ? 15 | SavedDx dq ? 16 | SavedBx dq ? 17 | SavedSp dq ? 18 | SavedBp dq ? 19 | SavedSi dq ? 20 | SavedDi dq ? 21 | 22 | SavedR8 dq ? 23 | SavedR9 dq ? 24 | SavedR10 dq ? 25 | SavedR11 dq ? 26 | SavedR12 dq ? 27 | SavedR13 dq ? 28 | SavedR14 dq ? 29 | SavedR15 dq ? 30 | SR ends 31 | 32 | _TEXT segment para 'CODE' 33 | 34 | VMReEntry proc public frame 35 | 36 | ; "void __cdecl Warbird::VMExecMainLoop(void *,void *)" (?VMExecMainLoop@Warbird@@YAXPEAX0@Z) 37 | extrn ?VMExecMainLoop@Warbird@@YAXPEAX0@Z: Proc 38 | 39 | ; space 40 | sub rsp, 1000 41 | .ENDPROLOG 42 | 43 | ; pusha 44 | mov qword ptr SR.SavedAx[rsp], rax 45 | mov qword ptr SR.SavedCx[rsp], rcx 46 | mov qword ptr SR.SavedDx[rsp], rdx 47 | mov qword ptr SR.SavedBx[rsp], rbx 48 | 49 | ; must be after ax save 50 | lea rax, [rsp + 1000] 51 | mov qword ptr SR.SavedSP[rsp], rax 52 | 53 | mov qword ptr SR.SavedBp[rsp], rbp 54 | mov qword ptr SR.SavedSi[rsp], rsi 55 | mov qword ptr SR.SavedDi[rsp], rdi 56 | mov qword ptr SR.SavedR8[rsp], r8 57 | mov qword ptr SR.SavedR9[rsp], r9 58 | mov qword ptr SR.SavedR10[rsp], r10 59 | mov qword ptr SR.SavedR11[rsp], r11 60 | ; removed restore for r12 liveness usage 61 | ;mov qword ptr SR.SavedR12[rsp], r12 62 | mov qword ptr SR.SavedR13[rsp], r13 63 | mov qword ptr SR.SavedR14[rsp], r14 64 | mov qword ptr SR.SavedR15[rsp], r15 65 | 66 | ; r12 was live over the call and points to the offset 67 | mov rcx, r12 68 | ; pass the pointer to the registers as the argument to the function 69 | lea rdx, [rsp] 70 | 71 | ; call the runtime support function 72 | call ?VMExecMainLoop@Warbird@@YAXPEAX0@Z 73 | 74 | mov rax, qword ptr SR.SavedAx[rsp] 75 | mov rcx, qword ptr SR.SavedCx[rsp] 76 | mov rdx, qword ptr SR.SavedDx[rsp] 77 | mov rbx, qword ptr SR.SavedBx[rsp] 78 | mov rbp, qword ptr SR.SavedBp[rsp] 79 | mov rsi, qword ptr SR.SavedSi[rsp] 80 | mov rdi, qword ptr SR.SavedDi[rsp] 81 | mov r8, qword ptr SR.SavedR8[rsp] 82 | mov r9, qword ptr SR.SavedR9[rsp] 83 | mov r10, qword ptr SR.SavedR10[rsp] 84 | mov r11, qword ptr SR.SavedR11[rsp] 85 | mov r12, qword ptr SR.SavedR12[rsp] 86 | mov r13, qword ptr SR.SavedR13[rsp] 87 | mov r14, qword ptr SR.SavedR14[rsp] 88 | mov r15, qword ptr SR.SavedR15[rsp] 89 | ; and finally the sp 90 | mov rsp, qword ptr SR.SavedSP[rsp] 91 | 92 | ret 93 | VMReEntry endp 94 | 95 | VMExit proc public frame 96 | ; given rcx to regs 97 | ; given reentry param is live in SavedR12 98 | 99 | ; rsp now points at "proper" stack loc. 100 | .ENDPROLOG 101 | 102 | mov rdx, qword ptr SR.SavedDx[rcx] 103 | mov rbx, qword ptr SR.SavedBx[rcx] 104 | mov rsi, qword ptr SR.SavedSi[rcx] 105 | mov rdi, qword ptr SR.SavedDi[rcx] 106 | mov r8, qword ptr SR.SavedR8[rcx] 107 | mov r9, qword ptr SR.SavedR9[rcx] 108 | mov r10, qword ptr SR.SavedR10[rcx] 109 | mov r11, qword ptr SR.SavedR11[rcx] 110 | mov r12, qword ptr SR.SavedR12[rcx] 111 | mov r13, qword ptr SR.SavedR13[rcx] 112 | mov r14, qword ptr SR.SavedR14[rcx] 113 | mov r15, qword ptr SR.SavedR15[rcx] 114 | mov rax, qword ptr SR.SavedAx[rcx] 115 | mov rbp, qword ptr SR.SavedBp[rcx] 116 | ; drop the stack to the top of the regs 117 | mov rsp, rcx 118 | ; load rcx 119 | mov rcx, qword ptr SR.SavedCx[rcx] 120 | ; finally drop the stack to the top of the "real" stack 121 | mov rsp, qword ptr SR.SavedSP[rsp] 122 | ret 123 | VMExit endp 124 | 125 | _TEXT ends 126 | 127 | #endif 128 | 129 | #endif -------------------------------------------------------------------------------- /WarbirdAD_AMD64.asm: -------------------------------------------------------------------------------- 1 | #ifdef _AMD64_ 2 | 3 | title "Warbird Anti-Debugging assembly functions for AMD64" 4 | 5 | ;----------------------------------------------------------------------------- 6 | ; 7 | ; Copyright (C) Microsoft Corporation. All Rights Reserved. 8 | ; 9 | ; File: WarbirdAD_AMD64.asm 10 | ; 11 | ; Description: 12 | ; 13 | ; Anti-Debug macro implementations for AMD64 14 | ; 15 | ;----------------------------------------------------------------------------- 16 | 17 | #ifndef WARBIRD_KERNEL_MODE 18 | 19 | ; 20 | ; Anti-Debugging Functions 21 | ; 22 | _TEXT SEGMENT 23 | 24 | ; Generate an AV to setup the Debug Registers 25 | ?Warbird_AD_AV4DebugRegisters@@YAXXZ PROC public 26 | mov dword ptr [00007978h],4c71950bh 27 | ret 28 | ?Warbird_AD_AV4DebugRegisters@@YAXXZ ENDP 29 | 30 | 31 | ; Generate a breakpoint 32 | ?Warbird_AD_FireBreakpoint@@YAXXZ PROC public 33 | int 3 34 | ?Warbird_AD_FireBreakpoint@@YAXXZ ENDP 35 | 36 | 37 | ; Divide by zero 38 | ?Warbird_AD_DivideByZero1@@YAXXZ PROC public 39 | xor rax, rax 40 | div rax 41 | ret 42 | ?Warbird_AD_DivideByZero1@@YAXXZ ENDP 43 | 44 | 45 | ?Warbird_AD_DivideByZero2@@YAXXZ PROC public 46 | xor rax, rax 47 | div rax 48 | ret 49 | ?Warbird_AD_DivideByZero2@@YAXXZ ENDP 50 | 51 | 52 | 53 | ; 54 | ; Read the PEB structure, just like IsDebuggerPresent() 55 | ; 56 | 57 | ; BOOL IsDebuggerPresent() 58 | ?Warbird_AD_UMD_TIBNT_1@@YAHXZ PROC public 59 | mov rax, gs:[30h] 60 | mov rax, [rax + 60h] 61 | movzx eax, byte ptr[rax + 2] 62 | ret 63 | ?Warbird_AD_UMD_TIBNT_1@@YAHXZ ENDP 64 | 65 | 66 | ; void IsDebuggerPresent(DWORD *IsPresent) 67 | ?Warbird_AD_UMD_TIBNT_2@@YAXPEAK@Z PROC public 68 | mov rdx, gs:[30h] 69 | mov rdx, [rdx + 60h] 70 | movzx rax, byte ptr[rdx + 2] 71 | mov [rcx], eax 72 | ret 73 | ?Warbird_AD_UMD_TIBNT_2@@YAXPEAK@Z ENDP 74 | 75 | _TEXT ENDS 76 | 77 | 78 | ; 79 | ; Guard Debugger Functions 80 | ; 81 | _TEXT SEGMENT 82 | 83 | ; Add 1 to the first arg and then invoke the GD to undo it. Second arg must be zero 84 | WarbirdAD_GD_Add1_Sub1 PROC public 85 | inc rcx ; add 1 to the first arg 86 | mov rax, rcx ; move the first arg into rax, ready to divide 87 | mov rcx, rdx ; the second arg is zero, so move it into rcx to generate the AV 88 | mov rdx, 1 ; rdx==1 informs the GD to subtract 1 89 | div rcx ; generate the AV 90 | ret 91 | WarbirdAD_GD_Add1_Sub1 ENDP 92 | 93 | 94 | ; Invokes the GD to add 1 to the first arg and then return it. Second arg must be zero 95 | WarbirdAD_GD_Add1 PROC public 96 | mov rax, rcx ; move the first arg into rax, ready to divide 97 | mov rcx, rdx ; the second arg is zero, so move it into rcx to generate the AV 98 | div rcx ; generate the AV 99 | ret 100 | WarbirdAD_GD_Add1 ENDP 101 | 102 | 103 | ; Invokes the GD to subtract 1 to the first arg and then return it. Second arg must be zero 104 | WarbirdAD_GD_Sub1 PROC public 105 | mov rax, rcx ; move the first arg into rax, ready to divide 106 | mov rcx, rdx ; the second arg is zero, so move it into rcx to generate the AV 107 | mov rdx, 1 ; rdx==1 informs the GD to subtract 1 108 | div rcx ; generate the AV 109 | ret 110 | WarbirdAD_GD_Sub1 ENDP 111 | 112 | 113 | ; Masks the first arg and then invokes the GD to undo the mask. Second arg must be zero 114 | WarbirdAD_GD_XorA_XorA PROC public 115 | mov rax, rcx ; move the first arg into rax, ready to xor and then divide 116 | xor rax, 03c9e4fd1h ; xor the first arg 117 | mov rcx, rdx ; the second arg is zero, so move it into rcx to generate the AV 118 | mov rdx, 2 ; rdx==2 informs the GD to xor A 119 | div rcx ; generate the AV 120 | ret 121 | WarbirdAD_GD_XorA_XorA ENDP 122 | 123 | 124 | _TEXT ENDS 125 | 126 | ; 127 | ; Guard Shadow Stack support 128 | ; 129 | _TEXT SEGMENT 130 | 131 | ; 132 | ; Workaround function that allows updating of control stack addresses. 133 | ; Temporary solution -- real solution needs to avoid modifying return 134 | ; addresses. 135 | ; 136 | ; There is no __writefsqword intrinsic on AMD64. 137 | ; 138 | ; rcx - Control stack offset (data stack RSP) 139 | ; 140 | ; rdx - Value to write 141 | ; 142 | WarbirdWriteToControlStack PROC public 143 | mov fs:[rcx], rdx 144 | ret 145 | WarbirdWriteToControlStack ENDP 146 | 147 | ; 148 | ; Workaround function to read the control stack contents. 149 | ; Temporary solution -- real solution needs to avoid modifying return 150 | ; addresses 151 | ; 152 | ; There is no __readfsqword intrinsic on AMD64. 153 | ; 154 | ; rcx - Control stack offset (data stack RSP) 155 | ; 156 | WarbirdReadFromControlStack PROC public 157 | mov rax, fs:[rcx] 158 | ret 159 | WarbirdReadFromControlStack ENDP 160 | 161 | _TEXT ENDS 162 | 163 | #endif 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /WarbirdLock.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Lock 4 | * 5 | **/ 6 | 7 | namespace WarbirdRuntime 8 | { 9 | 10 | // Implements a lightweight multiple-reader-single-writer lock. 11 | class CRWLock 12 | { 13 | private: 14 | enum : LONG_PTR 15 | { 16 | FREE = 0, 17 | FIRST_SHARED = 1, 18 | EXCLUSIVE = -1, 19 | }; 20 | 21 | public: 22 | VOID 23 | Init( 24 | ) 25 | { 26 | m_Lock = FREE; 27 | } 28 | 29 | VOID 30 | AcquireShared( 31 | ) 32 | { 33 | // Try transitioning the state from FREE to 1ST_SHARED lock. 34 | 35 | LONG_PTR ExpectedOldValue = FREE; 36 | LONG_PTR DesiredNewValue = FIRST_SHARED; 37 | 38 | for (;;) 39 | { 40 | LONG_PTR OldValue = _InterlockedCompareExchangeSizeT(&m_Lock, DesiredNewValue, ExpectedOldValue); 41 | 42 | if (OldValue == ExpectedOldValue) 43 | { 44 | // If we successfully transitioned the state, exit. 45 | 46 | break; 47 | } 48 | else if (OldValue == EXCLUSIVE) 49 | { 50 | // If the lock is held exclusively, continue spinning. 51 | } 52 | else 53 | { 54 | // If some other thread(s) grabbed the shared lock, try incrementing the refcount. 55 | 56 | ExpectedOldValue = OldValue; 57 | DesiredNewValue = OldValue + 1; 58 | 59 | // Assert that the new lock state is not FREE or EXCLUSIVE, which are invalid states 60 | // for holding a shared lock. 61 | 62 | WARBIRD_ASSERT(DesiredNewValue != FREE && DesiredNewValue != EXCLUSIVE); 63 | } 64 | 65 | // Continue spinning. 66 | } 67 | } 68 | 69 | VOID 70 | ReleaseShared( 71 | ) 72 | { 73 | // Assert that the lock state is not FREE or EXCLUSIVE, which are invalid states 74 | // for holding a shared lock. 75 | 76 | WARBIRD_ASSERT(m_Lock != FREE && m_Lock != EXCLUSIVE); 77 | 78 | // Decrement the refcount. 79 | 80 | _InterlockedDecrementSizeT(&m_Lock); 81 | } 82 | 83 | VOID 84 | AcquireExclusive( 85 | ) 86 | { 87 | // Try transitioning the state from FREE to EXCLUSIVE lock. 88 | 89 | LONG_PTR ExpectedOldValue = FREE; 90 | LONG_PTR DesiredNewValue = EXCLUSIVE; 91 | 92 | for (;;) 93 | { 94 | LONG_PTR OldValue = _InterlockedCompareExchangeSizeT(&m_Lock, DesiredNewValue, ExpectedOldValue); 95 | 96 | if (OldValue == ExpectedOldValue) 97 | { 98 | // If we successfully transitioned the state, exit. 99 | 100 | break; 101 | } 102 | 103 | // Continue spinning. 104 | } 105 | } 106 | 107 | VOID 108 | ReleaseExclusive( 109 | ) 110 | { 111 | // Mark the lock as FREE. 112 | 113 | WARBIRD_ASSERT(m_Lock == EXCLUSIVE); 114 | _InterlockedExchangeSizeT(&m_Lock, FREE); 115 | } 116 | 117 | private: 118 | LONG_PTR m_Lock; 119 | 120 | }; //class CRWLock 121 | 122 | #if defined(WARBIRD_KERNEL_MODE) 123 | 124 | // Wrapper around FAST_MUTEX 125 | class CLock 126 | { 127 | public: 128 | HRESULT 129 | Init( 130 | ) 131 | { 132 | ExInitializeFastMutex(&m_FastMutex); 133 | 134 | return S_OK; 135 | } 136 | 137 | VOID 138 | Cleanup( 139 | ) 140 | { 141 | } 142 | 143 | VOID 144 | Acquire( 145 | ) 146 | { 147 | ExAcquireFastMutex(&m_FastMutex); 148 | } 149 | 150 | VOID 151 | Release( 152 | ) 153 | { 154 | ExReleaseFastMutex(&m_FastMutex); 155 | } 156 | 157 | private: 158 | FAST_MUTEX m_FastMutex; 159 | 160 | }; // class CLock 161 | 162 | #else // defined(WARBIRD_KERNEL_MODE) 163 | 164 | // Wrapper around CRITICAL_SECTION 165 | class CLock 166 | { 167 | public: 168 | HRESULT 169 | Init( 170 | ) 171 | { 172 | HRESULT hr = S_OK; 173 | 174 | __try 175 | { 176 | InitializeCriticalSection(&m_CriticalSection); 177 | } 178 | __except(EXCEPTION_EXECUTE_HANDLER) 179 | { 180 | hr = HRESULT_FROM_WIN32(GetLastError()); 181 | } 182 | 183 | return hr; 184 | } 185 | 186 | VOID 187 | Cleanup( 188 | ) 189 | { 190 | DeleteCriticalSection(&m_CriticalSection); 191 | } 192 | 193 | VOID 194 | Acquire( 195 | ) 196 | { 197 | EnterCriticalSection(&m_CriticalSection); 198 | } 199 | 200 | VOID 201 | Release( 202 | ) 203 | { 204 | LeaveCriticalSection(&m_CriticalSection); 205 | } 206 | 207 | private: 208 | CRITICAL_SECTION m_CriticalSection; 209 | 210 | }; // class CLock 211 | 212 | #endif // defined(WARBIRD_KERNEL_MODE) 213 | 214 | }; // namespace WarbirdRuntime -------------------------------------------------------------------------------- /WarbirdTermination_E2.asm: -------------------------------------------------------------------------------- 1 | #if defined(_M_E2) 2 | 3 | #include "kse2.h" 4 | 5 | TEXTAREA 6 | 7 | ; 8 | ; private: static void __cdecl WarbirdRuntime::CTermination::TrashStack(int (__cdecl*)(void)) 9 | ; 10 | ; Clears the stack and registers, and jumps to termination function 11 | ; 12 | 13 | ; WORKAROUND: Can't use LEAF_ENTRY here because of the quoted name 14 | TEXTAREA 15 | 16 | ALIGN 4 17 | 18 | .globl "?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z" 19 | "?TrashStack@CTermination@WarbirdRuntime@@CAXP6A_JXZ@Z": 20 | 21 | read t0, pr ; t0 = TEB 22 | ld t1, TeStackLimit(t0) ; t1 = TEB->StackLimit 23 | ld t2, TeStackBase(t0) ; t2 = TEB->StackBase 24 | read t3, r3 ; get parameter which is return address 25 | write r11, t1 ; r11 = destination pointer 26 | write sp, t2 ; sp = TEB->StackBase 27 | write lr, t3 ; lr = return address 28 | tge t4, t1, t2 ; t3 = (dest >= TEB->StackBase) 29 | bro.t ClearRegs ; skip if invalid 30 | bro.f ClearStack ; else continue 31 | 32 | ClearStack: 33 | movu t0, 0 ; t0 = 0 34 | read t1, r11 ; t1 = dest pointer 35 | addi t2, t1, 16 ; t2 = dest pointer + 16 36 | read t3, sp ; t3 = sp 37 | write r11, t2 ; r11 = updated dest pointer 38 | sd t0, 0(t1) ; write 0 to dest 39 | sd t0, 8(t1) ; write 0 to dest+8 40 | tge t4, t2, t3 ; t4 = (dest >= sp) 41 | bro.t ClearRegs ; skip if invalid 42 | bro.f ClearStack ; else loop 43 | 44 | ClearRegs: 45 | movu t0, 0 ; get a zero 46 | write r3, t0 ; clear all registers 47 | write r4, t0 ; 48 | write r5, t0 ; 49 | write r6, t0 ; 50 | write r7, t0 ; 51 | write r8, t0 ; 52 | write r9, t0 ; 53 | write r10, t0 ; 54 | write r11, t0 ; 55 | write r12, t0 ; 56 | write r13, t0 ; 57 | write r14, t0 ; 58 | write r15, t0 ; 59 | write r16, t0 ; 60 | write r17, t0 ; 61 | write r18, t0 ; 62 | write r19, t0 ; 63 | write r20, t0 ; 64 | write r21, t0 ; 65 | write r22, t0 ; 66 | write r23, t0 ; 67 | write r24, t0 ; 68 | write r25, t0 ; 69 | write r26, t0 ; 70 | write r27, t0 ; 71 | write r28, t0 ; 72 | write r29, t0 ; 73 | write r30, t0 ; 74 | write r31, t0 ; 75 | write r32, t0 ; 76 | write r33, t0 ; 77 | write r34, t0 ; 78 | write r35, t0 ; 79 | write r36, t0 ; 80 | write r37, t0 ; 81 | write r38, t0 ; 82 | write r39, t0 ; 83 | write r40, t0 ; 84 | write r41, t0 ; 85 | write r42, t0 ; 86 | write r43, t0 ; 87 | write r44, t0 ; 88 | write r45, t0 ; 89 | write r46, t0 ; 90 | write r47, t0 ; 91 | write r48, t0 ; 92 | write r49, t0 ; 93 | write r50, t0 ; 94 | write r51, t0 ; 95 | write r52, t0 ; 96 | write r53, t0 ; 97 | write r54, t0 ; 98 | write r55, t0 ; 99 | write r56, t0 ; 100 | write r57, t0 ; 101 | write r58, t0 ; 102 | write r59, t0 ; 103 | write r60, t0 ; 104 | write r61, t0 ; 105 | write r62, t0 ; 106 | write r63, t0 ; 107 | 108 | read t1, lr ; get return address 109 | ret t1 ; return 110 | 111 | #endif 112 | 113 | #if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_E2) 114 | END 115 | #else 116 | END 117 | #endif // defined(_M_ARM) || defined(_M_ARM64) || defined(_M_E2) -------------------------------------------------------------------------------- /WarbirdEncyptionSysCalls.inl: -------------------------------------------------------------------------------- 1 | #if $(ENCRYPTION_SYSTEM_CALLS) 2 | 3 | namespace WarbirdRuntime { 4 | 5 | #pragma warbird(begin_foreach $(ID) $(EncryptedSegmentIDs)) 6 | 7 | // 8 | // List of all symbols in this segment. Assign the global a value so it is not 9 | // put in the uninitialized section (BSS). 10 | // 11 | __declspec(selectany) ENCRYPTION_SEGMENT g_EncryptedSegmentSystemCall_$(ID) = {$(ID)}; 12 | 13 | EXTERN_C HRESULT __fastcall WarbirdEncryptSegment$(ID)Inline() 14 | { 15 | ULONG_PTR arguments[] = { 16 | 2, // Operation Type In Place Encrypt, 17 | (ULONG_PTR)CUtil::GetImageBase(), 18 | (ULONG_PTR)CUtil::GetPreferedImageBase(), 19 | (ULONG_PTR)g_PrivateRelocationsTable + CUtil::GetImageBase(), 20 | (ULONG_PTR)g_PrivateRelocationsTableCount, 21 | (ULONG_PTR)&g_EncryptedSegmentSystemCall_$(ID) 22 | }; 23 | 24 | return HRESULT_FROM_NTSTATUS(NtQuerySystemInformation( 25 | /*(SYSTEM_INFORMATION_CLASS)*/SystemCodeFlowTransition, 26 | (PVOID)&arguments, 27 | sizeof(arguments), 28 | NULL 29 | )); 30 | } 31 | 32 | EXTERN_C HRESULT __fastcall WarbirdEncryptSegment$(ID)NoInline() 33 | { 34 | return WarbirdEncryptSegment$(ID)Inline(); 35 | } 36 | 37 | EXTERN_C HRESULT __fastcall WarbirdDecryptSegment$(ID)Inline() 38 | { 39 | ULONG_PTR arguments[] = { 40 | 1, // Operation Type In Place Decrypt, 41 | (ULONG_PTR)CUtil::GetImageBase(), 42 | (ULONG_PTR)CUtil::GetPreferedImageBase(), 43 | (ULONG_PTR)g_PrivateRelocationsTable + CUtil::GetImageBase(), 44 | (ULONG_PTR)g_PrivateRelocationsTableCount, 45 | (ULONG_PTR)&g_EncryptedSegmentSystemCall_$(ID) 46 | }; 47 | 48 | return HRESULT_FROM_NTSTATUS(NtQuerySystemInformation( 49 | /*(SYSTEM_INFORMATION_CLASS)*/SystemCodeFlowTransition, 50 | (PVOID)&arguments, 51 | sizeof(arguments), 52 | NULL 53 | )); 54 | } 55 | 56 | EXTERN_C HRESULT __fastcall WarbirdDecryptSegment$(ID)NoInline() 57 | { 58 | return WarbirdDecryptSegment$(ID)Inline(); 59 | } 60 | 61 | #pragma warbird(end_foreach) 62 | 63 | }; // namespace WarbirdRuntime 64 | 65 | #else // ENCRYPTION_SYSTEM_CALLS 66 | 67 | #pragma warbird(begin_foreach $(ID) $(EncryptedSegmentIDs)) 68 | 69 | namespace WarbirdRuntime { 70 | // 71 | // The warbird globals need to be in a read write section so warbird can 72 | // populate and expand these globals during compile time. By default the 73 | // globals end up in the BSS section and in the windows tree are never 74 | // initailized. 75 | // 76 | #define WARBIRD_ENCRYPTION_SECTION_CONST_$(ID) STRINGIZE(.rdata$wbrdencr##$(ID)) 77 | #define WARBIRD_ENCRYPTION_SECTION_READ_WRITE_$(ID) STRINGIZE(.data$wbrdencr##$(ID)) 78 | 79 | #pragma section(WARBIRD_ENCRYPTION_SECTION_CONST_$(ID), read) 80 | 81 | __declspec(allocate(WARBIRD_ENCRYPTION_SECTION_CONST_$(ID))) 82 | __declspec(selectany) ENCRYPTED_SEGMENT_DATA_CONST_$(EncryptedSegment$(ID)RuntimeIndex) g_EncryptedSegmentConstData_$(ID); 83 | 84 | #pragma section(WARBIRD_ENCRYPTION_SECTION_READ_WRITE_$(ID), read, write) 85 | 86 | __declspec(allocate(WARBIRD_ENCRYPTION_SECTION_READ_WRITE_$(ID))) 87 | __declspec(selectany) ENCRYPTED_SEGMENT_DATA_READ_WRITE_$(EncryptedSegment$(ID)RuntimeIndex) g_EncryptedSegmentReadWriteData_$(ID); 88 | 89 | } 90 | 91 | EXTERN_C __forceinline HRESULT __fastcall WarbirdEncryptSegment$(ID)Inline() 92 | { 93 | return WarbirdRuntime::CEncryption<$(EncryptedSegment$(ID)RuntimeIndex)-1, 94 | $(EncryptionRuntime$(EncryptedSegment$(ID)RuntimeIndex)Cipher), 95 | $(EncryptionRuntime$(EncryptedSegment$(ID)RuntimeIndex)HashFunction), 96 | WarbirdRuntime::ENCRYPTED_SEGMENT_DATA_CONST_$(EncryptedSegment$(ID)RuntimeIndex), 97 | WarbirdRuntime::ENCRYPTED_SEGMENT_DATA_READ_WRITE_$(EncryptedSegment$(ID)RuntimeIndex)>::Encrypt( 98 | &WarbirdRuntime::g_EncryptedSegmentConstData_$(ID), 99 | &WarbirdRuntime::g_EncryptedSegmentReadWriteData_$(ID)); 100 | } 101 | 102 | EXTERN_C __declspec(noinline) HRESULT __fastcall WarbirdEncryptSegment$(ID)NoInline() 103 | { 104 | return WarbirdEncryptSegment$(ID)Inline(); 105 | } 106 | 107 | EXTERN_C __forceinline HRESULT __fastcall WarbirdDecryptSegment$(ID)Inline() 108 | { 109 | return WarbirdRuntime::CEncryption<$(EncryptedSegment$(ID)RuntimeIndex)-1, 110 | $(EncryptionRuntime$(EncryptedSegment$(ID)RuntimeIndex)Cipher), 111 | $(EncryptionRuntime$(EncryptedSegment$(ID)RuntimeIndex)HashFunction), 112 | WarbirdRuntime::ENCRYPTED_SEGMENT_DATA_CONST_$(EncryptedSegment$(ID)RuntimeIndex), 113 | WarbirdRuntime::ENCRYPTED_SEGMENT_DATA_READ_WRITE_$(EncryptedSegment$(ID)RuntimeIndex)>::Decrypt( 114 | &WarbirdRuntime::g_EncryptedSegmentConstData_$(ID), 115 | &WarbirdRuntime::g_EncryptedSegmentReadWriteData_$(ID)); 116 | } 117 | 118 | EXTERN_C __declspec(noinline) HRESULT __fastcall WarbirdDecryptSegment$(ID)NoInline() 119 | { 120 | return WarbirdDecryptSegment$(ID)Inline(); 121 | } 122 | 123 | #pragma warbird(end_foreach) 124 | 125 | #endif // ENCRYPTION_SYSTEM_CALLS -------------------------------------------------------------------------------- /WarbirdVM_x86.asm: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | namespace WarbirdRuntime { 3 | #endif // _cpluplus 4 | 5 | // 6 | // Size of the common fields of the runtime argument structures. 7 | // 8 | #define RVA_BIT_COUNT 28 9 | #define FUNCTION_SIZE_BIT_COUNT 28 10 | #define KEY_BIT_COUNT 64 11 | #define CHECKSUM_BIT_COUNT 8 12 | #define HASH_BIT_COUNT 64 13 | #define CONDITION_CODE_BIT_COUNT 4 14 | #define STACK_SIZE_BIT_COUNT 28 15 | 16 | #define NUMBER_FEISTEL64_ROUNDS 10 17 | 18 | // 19 | // Warbird operation types. Indicates for which operation the argument structure 20 | // is for. 21 | // 22 | typedef enum { 23 | WbOperationNone, 24 | WbOperationDecryptEncryptionSegment, 25 | WbOperationReEncryptEncryptionSegment, 26 | WbOperationHeapExecuteCall, 27 | WbOperationHeapExecuteReturn, 28 | WbOperationHeapExecuteUnconditionalBranch, 29 | WbOperationHeapExecuteConditionalBranch, 30 | WbOperationProcessEnd, 31 | WbOperationProcessStartup, 32 | } WbOperationType; 33 | 34 | typedef struct _FEISTEL64_ROUND_DATA 35 | { 36 | unsigned long FunctionID; 37 | unsigned long Rand0; 38 | unsigned long Rand1; 39 | unsigned long Rand2; 40 | } FEISTEL64_ROUND_DATA, PFEISTEL64_ROUND_DATA; 41 | 42 | typedef struct _ENCRYPTION_BLOCK { 43 | unsigned long bUnitialized:1; 44 | unsigned long bData:1; 45 | unsigned long ulChecksum:CHECKSUM_BIT_COUNT; 46 | unsigned long ulRva:RVA_BIT_COUNT; 47 | unsigned long ulSize:FUNCTION_SIZE_BIT_COUNT; 48 | } ENCRYPTION_BLOCK, *PENCRYPTION_BLOCK; 49 | 50 | typedef struct _ENCRYPTION_SEGMENT { 51 | unsigned long ulVersion; 52 | unsigned long ulSegmentID; 53 | unsigned __int64 ullKey; 54 | FEISTEL64_ROUND_DATA bRoundData[NUMBER_FEISTEL64_ROUNDS]; 55 | unsigned long cBlocks; 56 | ENCRYPTION_BLOCK Blocks[1]; 57 | } ENCRYPTION_SEGMENT, *PENCRYPTION_SEGMENT; 58 | 59 | // 60 | // System call heap execution runtime structures and runtime 61 | // 62 | typedef struct _HEAP_EXECUTE_CALL_ARGUMENT { 63 | unsigned long ulVersion; 64 | unsigned long ulCheckStackSize; 65 | unsigned long ulChecksum:CHECKSUM_BIT_COUNT; 66 | unsigned long ulWrapperChecksum:CHECKSUM_BIT_COUNT; 67 | unsigned long ulRva:RVA_BIT_COUNT; 68 | unsigned long ulSize:FUNCTION_SIZE_BIT_COUNT; 69 | unsigned long ulWrapperRva:RVA_BIT_COUNT; 70 | unsigned long ulWrapperSize:FUNCTION_SIZE_BIT_COUNT; 71 | unsigned __int64 ullKey; 72 | FEISTEL64_ROUND_DATA RoundData[NUMBER_FEISTEL64_ROUNDS]; 73 | } HEAP_EXECUTE_CALL_ARGUMENT, *PHEAP_EXECUTE_CALL_ARGUMENT; 74 | 75 | // 76 | // Warbird kernel configuration. The user mode process passes this configuration 77 | // to the kernel when it is first started. 78 | // 79 | typedef struct _PROCESS_STARTUP_ARGUMENT { 80 | unsigned long ulVersion; 81 | unsigned long cMaxHeapExecutedCacheEntries; 82 | void* pPreAllocatedReadExecuteMemory; 83 | unsigned long cbPreAllocatedReadExecuteMemory; 84 | } PROCESS_STARTUP_ARGUMENT, *PPROCESS_STARTUP_ARGUMENT; 85 | 86 | typedef struct _PROCESS_STARTUP_ARGUMENT_LIST { 87 | unsigned __int64 eType; 88 | PPROCESS_STARTUP_ARGUMENT pArguments; 89 | } PROCESS_STARTUP_ARGUMENT_LIST, *PPROCESS_STARTUP_ARGUMENT_LIST; 90 | 91 | #ifdef __cplusplus 92 | }; // namespace WarbirdRuntime 93 | #endif // _cpluplus 94 | #ifdef _M_IX86 95 | 96 | #if $(WARBIRD_ENABLE_VM_EXECUTION) 97 | 98 | .686 99 | 100 | SavedRegs struct 101 | SavedAx dd ? 102 | SavedCx dd ? 103 | SavedDx dd ? 104 | SavedBx dd ? 105 | SavedSp dd ? 106 | SavedBp dd ? 107 | SavedSi dd ? 108 | SavedDi dd ? 109 | SavedRegs ends 110 | 111 | 112 | _TEXT$00 segment para 'CODE' 113 | 114 | ; "void __fastcall Warbird::VMExecMainLoop(void *,void *)" (?VMExecMainLoop@Warbird@@YIXPAX0@Z) 115 | extrn ?VMExecMainLoop@Warbird@@YIXPAX0@Z:near 116 | 117 | @VMReEntry@0 proc public 118 | sub esp, 1000 119 | 120 | mov dword ptr SavedRegs.SavedAx[esp], eax 121 | ; mov dword ptr SavedRegs.SavedBx[esp], ebx 122 | mov dword ptr SavedRegs.SavedCx[esp], ecx 123 | mov dword ptr SavedRegs.SavedDx[esp], edx 124 | mov dword ptr SavedRegs.SavedSi[esp], esi 125 | mov dword ptr SavedRegs.SavedDi[esp], edi 126 | mov dword ptr SavedRegs.SavedBp[esp], ebp 127 | lea eax, [esp + 1000] 128 | mov dword ptr SavedRegs.SavedSp[esp], eax 129 | 130 | mov ecx, ebx 131 | mov edx, esp 132 | call ?VMExecMainLoop@Warbird@@YIXPAX0@Z 133 | 134 | mov eax, dword ptr SavedRegs.SavedAx[esp] 135 | mov ecx, dword ptr SavedRegs.SavedCx[esp] 136 | mov edx, dword ptr SavedRegs.SavedDx[esp] 137 | mov ebx, dword ptr SavedRegs.SavedBx[esp] 138 | mov esi, dword ptr SavedRegs.SavedSi[esp] 139 | mov edi, dword ptr SavedRegs.SavedDi[esp] 140 | mov ebp, dword ptr SavedRegs.SavedBp[esp] 141 | mov esp, dword ptr SavedRegs.SavedSp[esp] 142 | ret 143 | @VMReEntry@0 endp 144 | 145 | @VMExit@4 proc public 146 | ; reg pointer in ecx 147 | mov eax, dword ptr SavedRegs.SavedAx[ecx] 148 | mov ebx, dword ptr SavedRegs.SavedBx[ecx] 149 | mov edx, dword ptr SavedRegs.SavedDx[ecx] 150 | mov esi, dword ptr SavedRegs.SavedSi[ecx] 151 | mov edi, dword ptr SavedRegs.SavedDi[ecx] 152 | mov ebp, dword ptr SavedRegs.SavedBp[ecx] 153 | mov esp, ecx 154 | mov ecx, dword ptr SavedRegs.SavedCx[ecx] 155 | mov esp, dword ptr SavedRegs.SavedSp[esp] 156 | ret 157 | @VMExit@4 endp 158 | 159 | 160 | _TEXT$00 ends 161 | 162 | #endif 163 | 164 | #endif -------------------------------------------------------------------------------- /WarbirdVerification.inl: -------------------------------------------------------------------------------- 1 | #if $(WARBIRD_ENABLE_VERIFICATION) 2 | /** 3 | * 4 | * Verification Runtime Functionality 5 | * 6 | **/ 7 | namespace WarbirdRuntime 8 | { 9 | 10 | #pragma warbird(begin_for $(VI) 1 $(NumVerificationRuntimes) 1) 11 | struct VERIFIED_BLOCK_DATA_$(VI) 12 | { 13 | #pragma warbird(begin_shuffle) 14 | // Address of the verified block. 15 | ULONG64 nRva: RVA_BIT_COUNT; 16 | #pragma warbird(next_shuffle) 17 | // Size of the block. 18 | ULONG64 nSize: FUNCTION_SIZE_BIT_COUNT; 19 | #pragma warbird(next_shuffle) 20 | ULONG64 dummy1: 3; 21 | #pragma warbird(next_shuffle) 22 | ULONG64 dummy2: 3; 23 | #pragma warbird(next_shuffle) 24 | ULONG64 dummy3: 2; 25 | #pragma warbird(end_shuffle) 26 | }; 27 | 28 | struct VERIFIED_SEGMENT_DATA_CONST_$(VI) 29 | { 30 | #pragma warbird(begin_shuffle) 31 | // Has this segment been processed with Warbird? 32 | ULONG fIsProcessed: 1; 33 | #pragma warbird(next_shuffle) 34 | // Number of blocks in the segment. 35 | ULONG nNumBlocks: 30; 36 | #pragma warbird(next_shuffle) 37 | // Adding dummy bits for padding 38 | ULONG dummy: 1; 39 | #pragma warbird(end_shuffle) 40 | // List of blocks. 41 | VERIFIED_BLOCK_DATA_$(VI) Blocks[1]; 42 | }; 43 | 44 | struct VERIFIED_SEGMENT_DATA_READ_WRITE_$(VI) 45 | { 46 | unsigned __int64 Hash; 47 | }; 48 | 49 | #define APPLY_RELOC_FOR_HASH_FLAG 0x8000000000000000; 50 | 51 | #pragma warbird(end_for) 52 | 53 | template 54 | class CVerifier 55 | { 56 | public: 57 | __forceinline 58 | static BOOL 59 | Verify( 60 | __in ULONG* pnRva, 61 | __in ULONG* pnRvaReadWrite 62 | ) 63 | { 64 | VerifySegment* pVerifySegment = (VerifySegment*)((ULONG_PTR)(*pnRva) + CUtil::GetImageBase()); 65 | VerifySegmentReadWrite* pVerifySegmentReadWrite = (VerifySegmentReadWrite*)((ULONG_PTR)(*pnRvaReadWrite) + CUtil::GetImageBase()); 66 | return VerifyActual(pVerifySegment, pVerifySegmentReadWrite); 67 | } 68 | 69 | __forceinline 70 | static BOOL 71 | VerifyActual( 72 | __in VerifySegment* pverifySegment, 73 | __in VerifySegmentReadWrite* pVerifySegmentReadWrite 74 | ) 75 | { 76 | WARBIRD_ASSERT(pverifySegment != NULL && pVerifySegmentReadWrite != NULL); 77 | 78 | unsigned __int64 StoredHash = pVerifySegmentReadWrite->Hash; 79 | unsigned __int64 mask = APPLY_RELOC_FOR_HASH_FLAG; 80 | BOOL bApplyReloc = FALSE; 81 | 82 | 83 | if ((StoredHash & mask) == mask) 84 | { 85 | bApplyReloc = TRUE; 86 | } 87 | 88 | DebugPrint( 89 | "%s Number of blocks %d and bApplyReloc %d\n", 90 | __FUNCTION__, 91 | pverifySegment->nNumBlocks, 92 | bApplyReloc 93 | ); 94 | 95 | HashFunction hashFunction; 96 | WarbirdCrypto::CHash Hash = 0; 97 | hashFunction.Reset(&Hash); 98 | 99 | for (ULONG i=0; inNumBlocks; i++) 100 | { 101 | DebugPrint( 102 | "%s Buffer Rva %X Size %X\n", 103 | __FUNCTION__, 104 | pverifySegment->Blocks[i].nRva, 105 | pverifySegment->Blocks[i].nSize 106 | ); 107 | 108 | #ifdef WARBIRD_TEST 109 | g_pTestClass->IncrementVerifyCount(); 110 | #endif 111 | 112 | GetHash(pverifySegment->Blocks[i].nRva, pverifySegment->Blocks[i].nSize, &hashFunction, bApplyReloc, &Hash); 113 | } 114 | 115 | if ((Hash | mask) != (StoredHash | mask)) 116 | { 117 | DebugPrint( 118 | "Calculated hash %I64X %s passed in hash %I64X\n", 119 | Hash | mask, 120 | (Hash | mask) == (StoredHash | mask) ? "matched" : "did not match", 121 | (StoredHash | mask) 122 | ); 123 | 124 | #ifdef WARBIRD_TEST 125 | g_pTestClass->ReportVerifyFailure(); 126 | #else 127 | // 128 | // Hashes did not match 129 | // 130 | CTermination::TrashStack(); 131 | #endif 132 | } 133 | else 134 | { 135 | if (bApplyReloc) 136 | { 137 | hashFunction.Reset(&Hash); 138 | for (ULONG i=0; inNumBlocks; i++) 139 | { 140 | GetHash(pverifySegment->Blocks[i].nRva, pverifySegment->Blocks[i].nSize, &hashFunction, FALSE, &Hash); 141 | } 142 | 143 | Hash &= ~mask; 144 | 145 | DebugPrint( 146 | "Updating the hash from %I64X to %I64X\n", 147 | pVerifySegmentReadWrite->Hash, 148 | Hash 149 | ); 150 | 151 | _InterlockedCompareExchange64((volatile __int64*)&pVerifySegmentReadWrite->Hash, Hash, StoredHash); 152 | } 153 | } 154 | 155 | return TRUE; 156 | } 157 | 158 | __forceinline 159 | static HRESULT 160 | GetHash( 161 | __in ULONG nRva, 162 | __in ULONG nSize, 163 | __in WarbirdCrypto::CHashFunction* pHashFunction, 164 | __in BOOL bApplyReloc, 165 | __out WarbirdCrypto::CHash* pHash 166 | ) 167 | { 168 | HRESULT hr = S_OK; 169 | 170 | PBYTE pBuffer = (PBYTE)(CUtil::GetImageBase() + (ULONG_PTR)nRva); 171 | CPrivateRelocationsTable relocations; 172 | PRIVATE_RELOCATION_ITEM nReloc = {0}; 173 | 174 | bool hasNextReloc = false; 175 | 176 | if (bApplyReloc) 177 | { 178 | relocations.Init(nRva, nSize); 179 | hasNextReloc = relocations.GetNextReloc(&nReloc); 180 | } 181 | 182 | for(ULONG nByteIndex = 0; nByteIndex < nSize;) 183 | { 184 | if (bApplyReloc && 185 | hasNextReloc && 186 | nByteIndex + nRva == nReloc.RVA ) 187 | { 188 | BYTE relocBuffer[16]; 189 | SIZE_T nRelocBytes = relocations.ApplyRelocation( 190 | &pBuffer[nByteIndex], 191 | nReloc.RelocationType, 192 | CUtil::GetPreferedImageBase() - CUtil::GetImageBase(), 193 | relocBuffer 194 | ); 195 | ULONG nRelocIndex = 0; 196 | for(nRelocIndex = 0; nRelocIndex < nRelocBytes; ++nRelocIndex) 197 | { 198 | pHashFunction->Update(pHash, relocBuffer[nRelocIndex]); 199 | } 200 | nByteIndex += nRelocIndex; 201 | 202 | hasNextReloc = relocations.GetNextReloc(&nReloc); 203 | } 204 | else 205 | { 206 | pHashFunction->Update(pHash, pBuffer[nByteIndex]); 207 | ++nByteIndex; 208 | } 209 | } 210 | 211 | return hr; 212 | } 213 | }; // class CVerification 214 | 215 | #pragma warbird(begin_for $(VI) 1 $(NumVerificationRuntimes) 1) 216 | template class CVerifier<$(VI)-1, $(Verification$(VI)HashFunction), VERIFIED_SEGMENT_DATA_CONST_$(VI), VERIFIED_SEGMENT_DATA_READ_WRITE_$(VI)>; 217 | #pragma warbird(end_for) 218 | 219 | }; // namespace WarbirdRuntime 220 | 221 | #pragma warbird(begin_foreach $(ID) $(VerifiedSegmentIDs)) 222 | 223 | namespace WarbirdRuntime { 224 | #define WARBIRD_VERIFICATION_SECTION_CONST_$(ID) STRINGIZE(.rdata$wbrdverif##$(ID)) 225 | #define WARBIRD_VERIFICATION_SECTION_READ_WRITE_$(ID) STRINGIZE(.data$wbrdverif##$(ID)) 226 | 227 | #pragma section(WARBIRD_VERIFICATION_SECTION_CONST_$(ID), read) 228 | 229 | __declspec(allocate(WARBIRD_VERIFICATION_SECTION_CONST_$(ID))) 230 | __declspec(selectany) VERIFIED_SEGMENT_DATA_CONST_$(VerifiedSegment$(ID)RuntimeIndex) g_VerifiedSegmentData_$(ID); 231 | 232 | #pragma section(WARBIRD_VERIFICATION_SECTION_READ_WRITE_$(ID), read, write) 233 | 234 | __declspec(allocate(WARBIRD_VERIFICATION_SECTION_READ_WRITE_$(ID))) 235 | __declspec(selectany) VERIFIED_SEGMENT_DATA_READ_WRITE_$(VerifiedSegment$(ID)RuntimeIndex) g_VerifiedSegmentDataReadWrite_$(ID); 236 | }; 237 | 238 | EXTERN_C __forceinline VOID __fastcall WarbirdVerifySegment$(ID)Inline( 239 | ) 240 | { 241 | // 242 | // If the image isn't Warbird processed, don't touch the hash, as the 243 | // customers expect our macros to succeed in an unobfuscated image. 244 | // TODO: Find a better way to do this. Although the fIsProcessed is 245 | // buried in a different location in each individualized pData, 246 | // it still gives the hacker an easy way to deactivate verification. 247 | // 248 | if (WarbirdRuntime::g_VerifiedSegmentData_$(ID).fIsProcessed) 249 | { 250 | WarbirdRuntime::CVerifier<$(VerifiedSegment$(ID)RuntimeIndex)-1, 251 | $(Verification$(VerifiedSegment$(ID)RuntimeIndex)HashFunction), 252 | WarbirdRuntime::VERIFIED_SEGMENT_DATA_CONST_$(VerifiedSegment$(ID)RuntimeIndex), 253 | WarbirdRuntime::VERIFIED_SEGMENT_DATA_READ_WRITE_$(VerifiedSegment$(ID)RuntimeIndex)>::VerifyActual(&WarbirdRuntime::g_VerifiedSegmentData_$(ID), &WarbirdRuntime::g_VerifiedSegmentDataReadWrite_$(ID)); 254 | } 255 | } 256 | 257 | EXTERN_C __declspec(noinline) VOID __fastcall WarbirdVerifySegment$(ID)NoInline( 258 | ) 259 | { 260 | WarbirdVerifySegment$(ID)Inline(); 261 | } 262 | 263 | #pragma warbird(end_foreach) 264 | 265 | #endif -------------------------------------------------------------------------------- /WarbirdHash.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Hash Functions 4 | * 5 | **/ 6 | 7 | namespace WarbirdCrypto 8 | { 9 | // Must be kept in sync with Configuration values 10 | #define WARBIRD_VSM_HASH_NO_MODIFIER 0x0 11 | #define WARBIRD_VSM_HASH_LOWER_HALF 0x1 12 | #define WARBIRD_VSM_HASH_UPPER_HALF 0x2 13 | 14 | typedef unsigned __int64 CHash; 15 | 16 | /*++ 17 | 18 | Description: 19 | 20 | Base class for Warbird hash functions 21 | 22 | --*/ 23 | class CHashFunction 24 | { 25 | public: 26 | virtual VOID 27 | Reset( 28 | __out CHash* pHash 29 | ) = 0; 30 | 31 | virtual VOID 32 | Update( 33 | __inout CHash* pHash, 34 | BYTE Data 35 | ) = 0; 36 | 37 | VOID 38 | Update( 39 | __inout CHash* pHash, 40 | __in_bcount(nBytes) PBYTE pData, 41 | SIZE_T nBytes 42 | ) 43 | { 44 | for (SIZE_T i = 0; i < nBytes; ++i) 45 | { 46 | Update(pHash, pData[i]); 47 | } 48 | } 49 | }; 50 | 51 | /*++ 52 | 53 | Description: 54 | 55 | The XOR hash function simply XORs bytes. Only to be used for debugging. 56 | 57 | --*/ 58 | #if !defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) && !defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 59 | template 60 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) && !defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 61 | class CHashFunctionXor : public CHashFunction 62 | { 63 | public: 64 | 65 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) 66 | 67 | static std::shared_ptr 68 | CreateRandom( 69 | ) 70 | { 71 | std::shared_ptr pHashFunction(new CHashFunctionXor); 72 | 73 | pHashFunction->iv = (BYTE) Random(0, 0xFF); 74 | 75 | return pHashFunction; 76 | } 77 | 78 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) 79 | 80 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 81 | 82 | static std::shared_ptr 83 | CreateFromType( 84 | __in PCSTR pszTypeName 85 | ) 86 | { 87 | std::shared_ptr pHashFunction; 88 | 89 | ULONG iv = 0; 90 | 91 | if (sscanf_s(pszTypeName, "WarbirdCrypto::CHashFunctionXor<%d>", &iv) == 1) 92 | { 93 | pHashFunction.reset(new CHashFunctionXor); 94 | pHashFunction->iv = (BYTE) iv; 95 | } 96 | 97 | return pHashFunction; 98 | } 99 | 100 | std::string 101 | GetName( 102 | ) const 103 | { 104 | std::ostringstream Name; 105 | 106 | Name << "WarbirdCrypto::CHashFunctionXor<" << (ULONG) iv << '>'; 107 | 108 | return Name.str(); 109 | } 110 | 111 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 112 | 113 | __forceinline VOID 114 | Reset( 115 | __out CHash* pHash 116 | ) 117 | { 118 | *pHash = iv; 119 | } 120 | 121 | __forceinline VOID 122 | Update( 123 | __inout CHash* pHash, 124 | BYTE Data 125 | ) 126 | { 127 | *pHash ^= Data; 128 | } 129 | 130 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 131 | private: 132 | BYTE iv; 133 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 134 | }; 135 | 136 | 137 | /*++ 138 | 139 | Description: 140 | 141 | Defines the hash function developed for the SCP tool. 142 | 143 | --*/ 144 | #if !defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) && !defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 145 | template< 146 | SIZE_T MacBodyID0, 147 | SIZE_T MacBodyID1, 148 | SIZE_T MacBodyID2, 149 | ULONG64 Key64 150 | > 151 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) && !defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 152 | class CHashFunctionSCP : public CHashFunction 153 | { 154 | public: 155 | 156 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) 157 | 158 | static std::shared_ptr 159 | CreateRandom( 160 | ) 161 | { 162 | std::shared_ptr pHashFunction(new CHashFunctionSCP); 163 | 164 | for (SIZE_T i = 0; i < NUM_ROUNDS; ++i) 165 | { 166 | pHashFunction->MacBodyIDs[i] = Random(0, MAX_BODY_TYPE); 167 | } 168 | 169 | FillRandom(&pHashFunction->Key64, sizeof(pHashFunction->Key64)); 170 | 171 | return pHashFunction; 172 | } 173 | 174 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) 175 | 176 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 177 | 178 | static std::shared_ptr 179 | CreateFromType( 180 | __in PCSTR pszTypeName 181 | ) 182 | { 183 | std::shared_ptr pHashFunction; 184 | 185 | if (strncmp(pszTypeName, "WarbirdCrypto::CHashFunctionSCP<", strlen("WarbirdCrypto::CHashFunctionSCP<")) == 0) 186 | { 187 | pHashFunction.reset(new CHashFunctionSCP); 188 | 189 | std::istringstream Name(pszTypeName + strlen("WarbirdCrypto::CHashFunctionSCP<")); 190 | 191 | char Comma; 192 | 193 | for (SIZE_T i = 0; i < NUM_ROUNDS; ++i) 194 | { 195 | Name >> pHashFunction->MacBodyIDs[i] >> Comma; 196 | } 197 | 198 | Name >> pHashFunction->Key64; 199 | } 200 | 201 | return pHashFunction; 202 | } 203 | 204 | std::string 205 | GetName( 206 | ) const 207 | { 208 | std::ostringstream Name; 209 | 210 | Name << "WarbirdCrypto::CHashFunctionSCP<"; 211 | 212 | for (SIZE_T i = 0; i < NUM_ROUNDS; ++i) 213 | { 214 | Name << MacBodyIDs[i] << ','; 215 | } 216 | 217 | Name << Key64 << '>'; 218 | 219 | return Name.str(); 220 | } 221 | 222 | VOID 223 | CallMacBody( 224 | SIZE_T nRound, 225 | __inout ULARGE_INTEGER& r, 226 | ULONG aParam, 227 | ULONG bParam 228 | ) 229 | { 230 | switch (MacBodyIDs[nRound]) 231 | { 232 | case 0: return MacBody<0>(r, aParam, bParam); 233 | case 1: return MacBody<1>(r, aParam, bParam); 234 | case 2: return MacBody<2>(r, aParam, bParam); 235 | case 3: return MacBody<3>(r, aParam, bParam); 236 | default: return; 237 | } 238 | } 239 | 240 | #else //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 241 | 242 | #define CallMacBody(nRound, r, aParam, bParam) \ 243 | \ 244 | MacBody( \ 245 | r, \ 246 | aParam, \ 247 | bParam \ 248 | ); \ 249 | 250 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 251 | 252 | __forceinline VOID 253 | Reset( 254 | __out CHash* pHash 255 | ) 256 | { 257 | *pHash = 0; 258 | } 259 | 260 | __forceinline VOID 261 | Update( 262 | __inout CHash* pHash, 263 | BYTE Data 264 | ) 265 | { 266 | ULARGE_INTEGER r; 267 | 268 | ULARGE_INTEGER Key; 269 | Key.QuadPart = Key64; 270 | 271 | ULARGE_INTEGER& Hash = *(ULARGE_INTEGER*)pHash; 272 | 273 | // MAC header 274 | r.LowPart = Data; // get next input byte 275 | r.LowPart += Key.LowPart; // add key to input 276 | Hash.LowPart += r.LowPart; // add input to sum 277 | r.LowPart += Hash.HighPart; // chain previous result 278 | 279 | // MAC bodies 280 | CallMacBody(0, r, Key.LowPart, LOWORD(Key.HighPart)); 281 | CallMacBody(1, r, Key.LowPart, LOWORD(Key.HighPart)); 282 | CallMacBody(2, r, Key.LowPart, LOWORD(Key.HighPart)); 283 | 284 | // MAC footer 285 | Hash.HighPart = r.LowPart; // chain 286 | Hash.LowPart += r.LowPart; // sum 287 | } 288 | 289 | private: 290 | enum : SIZE_T 291 | { 292 | NUM_ROUNDS = 3, 293 | MAX_BODY_TYPE = 3, 294 | }; 295 | 296 | template 297 | __forceinline VOID 298 | MacBody( 299 | __inout ULARGE_INTEGER& r, 300 | ULONG aParam, 301 | ULONG bParam 302 | ); 303 | 304 | template <> __forceinline static VOID MacBody<0>(__inout ULARGE_INTEGER& r, ULONG aParam, ULONG bParam) 305 | { 306 | r.LowPart = RotateRight32(r.LowPart * aParam, bParam & 31); 307 | } 308 | 309 | template <> __forceinline static VOID MacBody<1>(__inout ULARGE_INTEGER& r, ULONG aParam, ULONG) 310 | { 311 | r.QuadPart = UInt32x32To64(r.LowPart, aParam); 312 | r.LowPart += r.HighPart; 313 | } 314 | 315 | template <> __forceinline static VOID MacBody<2>(__inout ULARGE_INTEGER& r, ULONG aParam, ULONG) 316 | { 317 | r.QuadPart = UInt32x32To64(r.LowPart, aParam); 318 | 319 | r.HighPart = r.LowPart + 2*r.HighPart - 0x7FFFFFFF; 320 | r.HighPart -= ((signed) r.LowPart >> 31) & 0x7FFFFFFF; 321 | r.HighPart += ((signed) r.HighPart >> 31) & 0x7FFFFFFF; 322 | 323 | r.LowPart = r.HighPart; 324 | } 325 | 326 | template <> __forceinline static VOID MacBody<3>(__inout ULARGE_INTEGER& r, ULONG aParam, ULONG bParam) 327 | { 328 | r.LowPart = RotateRight32(r.LowPart, aParam & 15) ^ 329 | RotateLeft32(r.LowPart * bParam, aParam & 15); 330 | } 331 | 332 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 333 | private: 334 | SIZE_T MacBodyIDs[NUM_ROUNDS]; 335 | ULONG64 Key64; 336 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_RANDOM) || defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 337 | }; 338 | 339 | #if defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 340 | 341 | inline std::shared_ptr 342 | HashFunctionCreateFromType( 343 | PCSTR pszTypeName 344 | ) 345 | { 346 | std::shared_ptr pHashFunction; 347 | 348 | if ((pHashFunction = CHashFunctionSCP::CreateFromType(pszTypeName)) != NULL || 349 | (pHashFunction = CHashFunctionXor::CreateFromType(pszTypeName)) != NULL) 350 | { 351 | } 352 | 353 | return pHashFunction; 354 | } 355 | 356 | #endif //defined(WARBIRD_CRYPTO_ENABLE_CREATE_FROM_TYPE) 357 | 358 | }; // namespace WarbirdCrypto 359 | -------------------------------------------------------------------------------- /WarbirdInitialize.inl: -------------------------------------------------------------------------------- 1 | namespace WarbirdRuntime { 2 | HRESULT 3 | WarbirdProcessInitialize( 4 | PPROCESS_STARTUP_ARGUMENT pStartupArguments 5 | ); 6 | }; // namespace WarbirdRuntime 7 | 8 | namespace WarbirdRuntime 9 | { 10 | 11 | /*++ 12 | 13 | Description: 14 | 15 | Performs global initialization for the runtime support lib. 16 | 17 | Adding the __declspec(safebuffers) would make sure that the compiler 18 | will not emit a GS cookie for the function. Sticking a cookie check in 19 | the entry functions that call RuntimeInit and RuntimeCleanup will surely 20 | fail since the cookie value will change in the middle 21 | (upon entering these functions, the cookie value saved on the stack 22 | will be the default value, but then the original entry point will 23 | initialize the cookie to a random value, so the check at the end 24 | will fail). 25 | 26 | It's okay if the compiler sticks cookie checks into RuntimeInit or 27 | RuntimeCleanup, the cookie value won't change in the middle of them. 28 | The only downside is the cookie check is less secure because the cookie 29 | value is not random. 30 | 31 | Arguments: 32 | 33 | None. 34 | 35 | Returns: 36 | 37 | S_OK if successful, an error code otherwise. 38 | 39 | --*/ 40 | #if defined(WARBIRD_KERNEL_MODE) 41 | #pragma code_seg("INIT") 42 | #endif 43 | 44 | // 45 | // Pre allocated buffers for the heap executed calls that use system calls. 46 | // Normally the kernel would allocate these buffers but the memory manager does 47 | // not expose an API to allocate executable memory if dynamic code generation is 48 | // disabled. 49 | // TODO: Remove when memory manager exposes this API. 50 | // 51 | #if $(HEAP_EXECUTION_SYSTEM_CALL_PRE_ALLOCATED_BUFFER_SIZE) 52 | #pragma section("wbrdrx", read, write) 53 | #pragma comment(linker, "/section:wbrdrx,ER") 54 | BYTE __declspec(allocate("wbrdrx")) g_readExecuteMemory[$(HEAP_EXECUTION_SYSTEM_CALL_PRE_ALLOCATED_BUFFER_SIZE)]; 55 | #endif // HEAP_EXECUTION_SYSTEM_CALL_PRE_ALLOCATED_BUFFER_SIZE 56 | 57 | HRESULT 58 | RuntimeInit( 59 | void 60 | ) 61 | { 62 | HRESULT hr = S_OK; 63 | 64 | g_Rand.Init(static_cast(CUtil::ReadCpuTimeStamp() & LONG_MAX)); 65 | 66 | #if $(WARBIRD_ENABLE_HEAP_EXECUTION) && !defined(WARBIRD_KERNEL_MODE) 67 | 68 | if (SUCCEEDED(hr)) 69 | { 70 | hr = g_MemoryAllocator.Init(); 71 | } 72 | 73 | #endif 74 | 75 | #if $(WARBIRD_ENABLE_ENCRYPTION) 76 | 77 | if (SUCCEEDED(hr)) 78 | { 79 | hr = EncryptionRuntimeInit(); 80 | } 81 | 82 | #endif 83 | 84 | #if $(WARBIRD_ENABLE_HEAP_EXECUTION) && !defined(WARBIRD_KERNEL_MODE) 85 | 86 | if (SUCCEEDED(hr)) 87 | { 88 | hr = HeapExecutionRuntimeInit(); 89 | } 90 | 91 | #endif 92 | 93 | #if $(HEAP_EXECUTION_SYSTEM_CALLS) 94 | if (SUCCEEDED(hr)) 95 | { 96 | PROCESS_STARTUP_ARGUMENT startup; 97 | 98 | startup.cMaxHeapExecutedCacheEntries = $(HEAP_EXECUTED_CACHE_ENTRIES); 99 | startup.pPreAllocatedReadExecuteMemory = NULL; 100 | startup.cbPreAllocatedReadExecuteMemory = 0; 101 | 102 | #if $(HEAP_EXECUTION_SYSTEM_CALL_PRE_ALLOCATED_BUFFER_SIZE) 103 | startup.pPreAllocatedReadExecuteMemory = g_readExecuteMemory; 104 | startup.cbPreAllocatedReadExecuteMemory = sizeof(g_readExecuteMemory); 105 | #endif // HEAP_EXECUTION_SYSTEM_CALL_PRE_ALLOCATED_BUFFER_SIZE 106 | 107 | hr = WarbirdRuntime::WarbirdProcessInitialize( 108 | &startup 109 | ); 110 | } 111 | #endif 112 | 113 | return hr; 114 | } 115 | 116 | #if defined(WARBIRD_KERNEL_MODE) 117 | #pragma code_seg() 118 | #endif 119 | 120 | /*++ 121 | 122 | Description: 123 | 124 | Performs global cleanup for the runtime support lib. 125 | 126 | Arguments: 127 | 128 | None. 129 | 130 | Returns: 131 | 132 | None. 133 | 134 | --*/ 135 | 136 | VOID 137 | RuntimeCleanup( 138 | void 139 | ) 140 | { 141 | #if $(WARBIRD_ENABLE_HEAP_EXECUTION) && !defined(WARBIRD_KERNEL_MODE) 142 | 143 | HeapExecutionRuntimeCleanup(); 144 | 145 | #endif 146 | 147 | #if $(WARBIRD_ENABLE_ENCRYPTION) 148 | 149 | EncryptionRuntimeCleanup(); 150 | 151 | #endif 152 | 153 | #if $(WARBIRD_ENABLE_HEAP_EXECUTION) && !defined(WARBIRD_KERNEL_MODE) 154 | 155 | g_MemoryAllocator.Cleanup(); 156 | 157 | #endif 158 | } 159 | 160 | // "C" entry points for these functions 161 | 162 | extern "C" HRESULT __stdcall WarbirdRuntimeInit( 163 | ) 164 | { 165 | return RuntimeInit(); 166 | } 167 | 168 | extern "C" void __stdcall WarbirdRuntimeCleanup( 169 | ) 170 | { 171 | RuntimeCleanup(); 172 | } 173 | 174 | #if defined(WARBIRD_KERNEL_MODE) 175 | 176 | /*++ 177 | 178 | Description: 179 | 180 | Stores the original unload handler. 181 | 182 | --*/ 183 | PDRIVER_UNLOAD g_pOriginalEntryDriverUnload = NULL; 184 | 185 | /*++ 186 | 187 | Description: 188 | 189 | NewDriverEntry() will change the DriverObject struct to register 190 | this function as the unload handler. When this function is called 191 | during driver unload, it will call the original unload handler 192 | (if any) and do the cleanup for the runtime support lib. 193 | 194 | Arguments: 195 | 196 | DriverObject 197 | Caller-supplied pointer to a DRIVER_OBJECT structure. 198 | 199 | Returns: 200 | 201 | None. 202 | 203 | --*/ 204 | VOID NTAPI 205 | NewDriverUnload( 206 | __inout DRIVER_OBJECT* DriverObject 207 | ) 208 | { 209 | if (g_pOriginalEntryDriverUnload != NULL) 210 | { 211 | g_pOriginalEntryDriverUnload(DriverObject); 212 | } 213 | 214 | RuntimeCleanup(); 215 | } 216 | 217 | /*++ 218 | 219 | Description: 220 | 221 | Stores the original entry point of the driver. It should be initialized to a 222 | value other than 0 because the symbol would end up in .bss section 223 | if it is either uninitialized or initialized to 0. 224 | 225 | --*/ 226 | volatile ULONG g_nRvaOriginalEntryDriver = 0x1; 227 | 228 | /*++ 229 | 230 | Description: 231 | 232 | If the tool is processing a kernel mode SYS, it will inject this 233 | function, set it as the entry point of the module, and redirect 234 | the OriginalDriverEntry() call in it to the original entry point. 235 | This allows us to initialize runtime support lib before any 236 | original code is run and do the cleanup after the original code 237 | exits. 238 | 239 | Note that the declaration of the entry point must exactly match 240 | the one the OS is expecting (for WinXP, the entry point is 241 | called by IopLoadDriver in base\ntos\io\iomgr\internal.c) 242 | 243 | Arguments: 244 | 245 | DriverObject 246 | Caller-supplied pointer to a DRIVER_OBJECT structure. 247 | 248 | RegistryPath 249 | Pointer to a counted Unicode string specifying the path to the 250 | driver's registry key. 251 | 252 | Returns: 253 | 254 | STATUS_SUCCESS if successful, error status value otherwise. 255 | 256 | --*/ 257 | #pragma code_seg("INIT") 258 | 259 | __declspec(safebuffers) 260 | NTSTATUS NTAPI 261 | NewDriverEntry( 262 | __inout DRIVER_OBJECT* DriverObject, 263 | __in UNICODE_STRING* RegistryPath 264 | ) 265 | { 266 | HRESULT hr = RuntimeInit(); 267 | 268 | if (FAILED(hr)) 269 | { 270 | RuntimeCleanup(); 271 | 272 | return STATUS_FAILED_DRIVER_ENTRY; 273 | } 274 | 275 | PDRIVER_INITIALIZE pOriginalEntryDriver = (PDRIVER_INITIALIZE)(g_nRvaOriginalEntryDriver + CUtil::GetImageBase()); 276 | 277 | NTSTATUS Status = pOriginalEntryDriver( 278 | DriverObject, 279 | RegistryPath 280 | ); 281 | 282 | if (!NT_SUCCESS(Status)) 283 | { 284 | RuntimeCleanup(); 285 | 286 | return Status; 287 | } 288 | 289 | g_pOriginalEntryDriverUnload = DriverObject->DriverUnload; 290 | 291 | DriverObject->DriverUnload = NewDriverUnload; 292 | 293 | return Status; 294 | } 295 | 296 | #pragma code_seg() 297 | 298 | // 299 | // TODO:: Need to handle export drivers 300 | // Currently there is one export driver using warbird (audio.sys) and 301 | // this component is currently calling the warbird entry explicitly 302 | // in their initilization code. 303 | // Reference: //depot/winmain/drivers/wdm/audio/drm/krm/drmk/device.cpp#4 304 | 305 | #else 306 | 307 | /*++ 308 | 309 | Description: 310 | 311 | Stores the original entry point of the exe. Initialize it to a value 312 | other than 0. If not it is put in the .bss section 313 | 314 | --*/ 315 | 316 | typedef ULONG (__cdecl *MAIN_FUNCTION)( 317 | ); 318 | 319 | volatile ULONG g_nRvaOriginalEntryMain = 0x1; 320 | 321 | /*++ 322 | 323 | Description: 324 | 325 | If the tool is processing a user mode EXE, it will inject this 326 | function, set it as the entry point of the module, and redirect 327 | the OriginalMain() call in it to the original entry point. 328 | This allows us to initialize runtime support lib before any 329 | original code is run and do the cleanup after the original code 330 | exits. 331 | 332 | Note that the declaration of the entry point must exactly match 333 | the one the OS is expecting (for WinXP, the entry point is 334 | called by BaseThreadStart in base\win32\client\support.c) 335 | 336 | Arguments: 337 | 338 | None. 339 | 340 | Returns: 341 | 342 | Exit code. 343 | 344 | --*/ 345 | __declspec(safebuffers) 346 | ULONG WINAPI 347 | NewEntryMain( 348 | void 349 | ) 350 | { 351 | HRESULT hr = S_OK; 352 | ULONG nResult = 0; 353 | 354 | hr = RuntimeInit(); 355 | 356 | if (FAILED(hr)) 357 | { 358 | goto CleanUp; 359 | } 360 | 361 | MAIN_FUNCTION pOriginalEntryMain = (MAIN_FUNCTION)(g_nRvaOriginalEntryMain + CUtil::GetImageBase()); 362 | 363 | nResult = pOriginalEntryMain(); 364 | 365 | CleanUp: 366 | RuntimeCleanup(); 367 | 368 | return FAILED(hr) ? hr : nResult; 369 | } 370 | 371 | /*++ 372 | 373 | Description: 374 | 375 | Stores the original entry point of the dll. Initialize it to a value 376 | other than 0. If not it is put in the .bss section 377 | 378 | --*/ 379 | typedef BOOL (WINAPI *DLL_MAIN_FUNCTION)( 380 | __in HINSTANCE hInstance, 381 | ULONG nReason, 382 | __in VOID* pReserved 383 | ); 384 | 385 | volatile ULONG g_nRvaOriginalEntryDllMain = 0x1; 386 | 387 | /*++ 388 | 389 | Description: 390 | 391 | If the tool is processing a user mode DLL, it will inject this 392 | function, set it as the entry point of the module, and redirect 393 | the OriginalDllMain() call in it to the original entry point. 394 | This allows us to initialize runtime support lib before any 395 | original code is run and do the cleanup after the original code 396 | exits. 397 | 398 | Note that the declaration of the entry point must exactly match 399 | the one the OS is expecting (for WinXP, the entry point is 400 | called by LdrpRunInitializeRoutines in base\ntdll\ldrsnap.c) 401 | 402 | Arguments: 403 | 404 | hInstance 405 | Handle to the DLL module. 406 | 407 | nReason 408 | Indicates why the DLL entry-point function is being called. 409 | 410 | pReserved 411 | Not used. 412 | 413 | Returns: 414 | 415 | TRUE if successful, FALSE otherwise. 416 | 417 | --*/ 418 | __declspec(safebuffers) 419 | BOOL WINAPI 420 | NewEntryDllMain( 421 | __in HINSTANCE hInstance, 422 | ULONG nReason, 423 | __in VOID* pReserved 424 | ) 425 | { 426 | BOOL fResult = TRUE; 427 | HRESULT hr = S_OK; 428 | 429 | DLL_MAIN_FUNCTION pOriginalEntryDllMain = (DLL_MAIN_FUNCTION)(g_nRvaOriginalEntryDllMain + CUtil::GetImageBase()); 430 | 431 | if (nReason == DLL_PROCESS_ATTACH) 432 | { 433 | hr = RuntimeInit(); 434 | 435 | if (FAILED(hr)) 436 | { 437 | RuntimeCleanup(); 438 | fResult = FALSE; 439 | goto CleanUp; 440 | } 441 | } 442 | 443 | fResult = pOriginalEntryDllMain( 444 | hInstance, 445 | nReason, 446 | pReserved 447 | ); 448 | 449 | if (nReason == DLL_PROCESS_DETACH) 450 | { 451 | RuntimeCleanup(); 452 | } 453 | 454 | CleanUp: 455 | return fResult; 456 | } 457 | 458 | #endif // defined (WARBIRD_KERNEL_MODE) 459 | 460 | }; // namespace WarbirdRuntime -------------------------------------------------------------------------------- /WarbirdDynamicCodeGen.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Allows a thread to enable dynamic code generation for ACG. 4 | * 5 | **/ 6 | 7 | #undef THREAD_DYNAMIC_CODE_ALLOW 8 | #define THREAD_DYNAMIC_CODE_ALLOW 1 // Opt-out of dynamic code generation. 9 | 10 | namespace WarbirdRuntime 11 | { 12 | 13 | #if defined(WARBIRD_KERNEL_MODE) 14 | class AutoEnableDynamicCodeGen 15 | { 16 | public: 17 | AutoEnableDynamicCodeGen(bool enable = true) 18 | { 19 | UNREFERENCED_PARAMETER(enable); 20 | } 21 | }; 22 | #else 23 | class AutoEnableDynamicCodeGen 24 | { 25 | public: 26 | // 27 | // These structures are and callback prototypes are declared in windows header 28 | // files but the updated definitions are not yet in the compiler tree so the 29 | // structures are redefined inside this class. Once the new definitions make 30 | // it to the compiler branch these definitions can be removed. 31 | // 32 | 33 | #pragma warning(push) 34 | #pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union 35 | #if !defined(_HRESULT_DEFINED) 36 | typedef LONG HRESULT 37 | #define S_OK 0 38 | #endif 39 | 40 | #include "guiddef.h" 41 | 42 | #if !defined(__IUnknown_INTERFACE_DEFINED__) 43 | #define __IUnknown_INTERFACE_DEFINED__ 44 | 45 | typedef __declspec(novtable) struct IUnknown 46 | { 47 | public: 48 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, _COM_Outptr_ void ** ppvObject) = 0; 49 | virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0; 50 | virtual ULONG STDMETHODCALLTYPE Release( void) = 0; 51 | 52 | template 53 | HRESULT 54 | #ifdef _M_CEE_PURE 55 | __clrcall 56 | #else 57 | STDMETHODCALLTYPE 58 | #endif 59 | QueryInterface(_COM_Outptr_ Q** pp) 60 | { 61 | return QueryInterface(__uuidof(Q), (void **)pp); 62 | } 63 | 64 | }; 65 | #endif 66 | 67 | #ifndef __IEditionUpgradeHelper_INTERFACE_DEFINED__ 68 | #define __IEditionUpgradeHelper_INTERFACE_DEFINED__ 69 | typedef struct __declspec(uuid("D3E9E342-5DEB-43B6-849E-6913B85D503A")) __declspec(novtable) IEditionUpgradeHelper 70 | : public IUnknown 71 | { 72 | public: 73 | virtual HRESULT STDMETHODCALLTYPE CanUpgrade(_Out_ BOOL *isAllowed) = 0; 74 | virtual HRESULT STDMETHODCALLTYPE UpdateOperatingSystem(_In_ LPCWSTR contentId) = 0; 75 | virtual HRESULT STDMETHODCALLTYPE ShowProductKeyUI( void) = 0; 76 | virtual HRESULT STDMETHODCALLTYPE GetOsProductContentId(_Out_ LPWSTR *contentId) = 0; 77 | virtual HRESULT STDMETHODCALLTYPE GetGenuineLocalStatus(_Out_ BOOL *isGenuine) = 0; 78 | virtual HRESULT STDMETHODCALLTYPE DisableAcgEnforcement(_In_ BOOL isEnforcementSet) = 0; 79 | }; 80 | 81 | GUID CLSID_EditionUpgradeHelper = {0x01776DF3,0xB9AF,0x4E50,0x9B,0x1C,0x56,0xE9,0x31,0x16,0xD7,0x04}; 82 | #endif 83 | 84 | #if !defined(__objidlbase_h__) && !defined(__objidl_h__) 85 | typedef struct tagMULTI_QI 86 | { 87 | const GUID *pIID; 88 | IUnknown *pItf; 89 | HRESULT hr; 90 | } MULTI_QI; 91 | #endif 92 | 93 | typedef enum _THREAD_INFORMATION_CLASS { 94 | ThreadMemoryPriority, 95 | ThreadAbsoluteCpuPriority, 96 | ThreadDynamicCodePolicy, 97 | ThreadInformationClassMax 98 | } THREAD_INFORMATION_CLASS; 99 | 100 | typedef struct _PROCESS_MITIGATION_DYNAMIC_CODE_POLICY { 101 | union { 102 | ULONG Flags; 103 | struct { 104 | ULONG ProhibitDynamicCode : 1; 105 | ULONG AllowThreadOptOut : 1; 106 | ULONG ReservedFlags : 30; 107 | } DUMMYSTRUCTNAME; 108 | } DUMMYUNIONNAME; 109 | } PROCESS_MITIGATION_DYNAMIC_CODE_POLICY, *PPROCESS_MITIGATION_DYNAMIC_CODE_POLICY; 110 | 111 | typedef 112 | BOOL 113 | (WINAPI *PGET_PROCESS_MITIGATION_POLICY_PROC)( 114 | _In_ HANDLE hProcess, 115 | _In_ PROCESS_MITIGATION_POLICY MitigationPolicy, 116 | _Out_ PVOID lpBuffer, 117 | _In_ SIZE_T dwLength 118 | ); 119 | 120 | typedef 121 | BOOL 122 | (WINAPI *PSET_THREAD_INFORMATION_PROC)( 123 | _In_ HANDLE hThread, 124 | _In_ THREAD_INFORMATION_CLASS ThreadInformationClass, 125 | _In_reads_bytes_(ThreadInformationSize) PVOID ThreadInformation, 126 | _In_ DWORD ThreadInformationSize 127 | ); 128 | 129 | typedef 130 | BOOL 131 | (WINAPI *PGET_THREAD_INFORMATION_PROC)( 132 | _In_ HANDLE hThread, 133 | _In_ THREAD_INFORMATION_CLASS ThreadInformationClass, 134 | _Out_writes_bytes_(ThreadInformationSize) PVOID ThreadInformation, 135 | _In_ DWORD ThreadInformationSize 136 | ); 137 | 138 | typedef 139 | HRESULT 140 | (__stdcall *PCO_CREATE_INSTANCE_FROM_APP_PROC)( 141 | _In_ REFCLSID rclsid, 142 | _In_opt_ IUnknown *punkOuter, 143 | _In_ DWORD dwClsCtx, 144 | _In_opt_ void *reserved, 145 | _In_ DWORD dwCount, 146 | _Inout_ MULTI_QI *pResults 147 | ); 148 | #pragma warning(pop) 149 | 150 | public: 151 | AutoEnableDynamicCodeGen(bool enable = true) : enabled(false) 152 | { 153 | if (enable == false) 154 | { 155 | return; 156 | } 157 | 158 | // 159 | // Snap the dynamic code generation policy for this process so that we 160 | // don't need to resolve APIs and query it each time. We expect the policy 161 | // to have been established upfront. 162 | // 163 | // processPolicyObtained is volaitile so the compiler does not change 164 | // the order of the instructions causing a thread to use uninitialized 165 | // static class members. 166 | // 167 | 168 | if (processPolicyObtained == false) 169 | { 170 | HMODULE module = GetModuleHandleW(L"api-ms-win-core-processthreads-l1-1-3.dll"); 171 | 172 | if (module != nullptr) 173 | { 174 | GetProcessMitigationPolicyProc = (PGET_PROCESS_MITIGATION_POLICY_PROC) GetProcAddress(module, "GetProcessMitigationPolicy"); 175 | SetThreadInformationProc = (PSET_THREAD_INFORMATION_PROC) GetProcAddress(module, "SetThreadInformation"); 176 | GetThreadInformationProc = (PGET_THREAD_INFORMATION_PROC) GetProcAddress(module, "GetThreadInformation"); 177 | } 178 | 179 | if ((GetProcessMitigationPolicyProc == nullptr) || 180 | (!GetProcessMitigationPolicyProc(GetCurrentProcess(), ProcessDynamicCodePolicy, &processPolicy, sizeof(processPolicy)))) 181 | { 182 | processPolicy.ProhibitDynamicCode = 0; 183 | } 184 | 185 | processPolicyObtained = true; 186 | } 187 | 188 | // 189 | // The process is not prohibiting dynamic code or does not allow threads 190 | // to opt out. In either case, return to the caller. 191 | // 192 | // N.B. It is OK that this policy is mutable at runtime. If a process 193 | // really does not allow thread opt-out, then the call below will fail 194 | // benignly. 195 | // 196 | 197 | if (processPolicy.ProhibitDynamicCode == 0) 198 | { 199 | return; 200 | } 201 | 202 | if (processPolicy.AllowThreadOptOut == 0) 203 | { 204 | 205 | // 206 | // If the process is an appcontainer and the CCIIFA pointer was 207 | // initialized, call in to the broker to turn off the flag. 208 | // 209 | if ((disableACGCalled == false)) 210 | { 211 | LoadCoCreateInstanceProc(); 212 | if (CoCreateInstanceFromAppProc != nullptr) 213 | { 214 | DisableAcgFromAppContainer(TRUE); 215 | disableACGCalled = true; 216 | } 217 | } 218 | } 219 | 220 | if (SetThreadInformationProc == nullptr || GetThreadInformationProc == nullptr) 221 | { 222 | return; 223 | } 224 | 225 | // 226 | // If dynamic code is already allowed for this thread, then don't attempt to allow it again. 227 | // 228 | 229 | DWORD threadPolicy; 230 | 231 | if ((GetThreadInformationProc(GetCurrentThread(), ThreadDynamicCodePolicy, &threadPolicy, sizeof(DWORD))) && 232 | (threadPolicy == THREAD_DYNAMIC_CODE_ALLOW)) 233 | { 234 | return; 235 | } 236 | 237 | threadPolicy = (enable) ? THREAD_DYNAMIC_CODE_ALLOW : 0; 238 | 239 | (VOID) SetThreadInformationProc(GetCurrentThread(), ThreadDynamicCodePolicy, &threadPolicy, sizeof(DWORD)); 240 | 241 | enabled = true; 242 | } 243 | 244 | ~AutoEnableDynamicCodeGen() 245 | { 246 | if (enabled) 247 | { 248 | DWORD threadPolicy = 0; 249 | 250 | (VOID) SetThreadInformationProc(GetCurrentThread(), ThreadDynamicCodePolicy, &threadPolicy, sizeof(DWORD)); 251 | 252 | enabled = false; 253 | } 254 | } 255 | 256 | private: 257 | 258 | // 259 | // This API attempts to change the dynamic code policy by calling into the 260 | // Licensing app broker. If this fails, Warbird runtime will attempt to 261 | // change the policy directly 262 | // 263 | void LoadCoCreateInstanceProc() 264 | { 265 | HMODULE module = GetModuleHandleW(L"api-ms-win-core-com-l1-1-1.dll"); 266 | if (module == nullptr) 267 | { 268 | // 269 | // The API is present in combase, which may not be loaded in every 270 | // process. In that case, use LoadLibrary 271 | // 272 | module = LoadLibraryW(L"api-ms-win-core-com-l1-1-1.dll"); 273 | if (module == nullptr) 274 | { 275 | return; 276 | } 277 | } 278 | 279 | CoCreateInstanceFromAppProc = (PCO_CREATE_INSTANCE_FROM_APP_PROC) GetProcAddress(module, "CoCreateInstanceFromApp"); 280 | } 281 | 282 | // 283 | // This API Calls into the Licensing Broker to turn off ACG enforcement 284 | // 285 | bool DisableAcgFromAppContainer(BOOL disableFlag) 286 | { 287 | MULTI_QI mq = {0}; 288 | GUID rEditionHelper = __uuidof(IEditionUpgradeHelper); 289 | WCHAR binaryName[MAX_PATH] = {0}; 290 | 291 | mq.hr = S_OK; 292 | mq.pItf = NULL; 293 | mq.pIID = &rEditionHelper; 294 | 295 | LONG resultCode = CoCreateInstanceFromAppProc(CLSID_EditionUpgradeHelper, nullptr, 0x1, nullptr, 1, &mq); 296 | if (resultCode != S_OK) 297 | { 298 | return false; 299 | } 300 | GetModuleFileNameW((HMODULE) CUtil::GetImageBase(), binaryName, MAX_PATH); 301 | IEditionUpgradeHelper* upgradeHelper = ((static_cast (mq.pItf))); 302 | resultCode = upgradeHelper->DisableAcgEnforcement(disableFlag); 303 | 304 | if (resultCode != S_OK) 305 | { 306 | return false; 307 | } 308 | 309 | return true; 310 | } 311 | 312 | bool enabled; 313 | 314 | static PGET_PROCESS_MITIGATION_POLICY_PROC GetProcessMitigationPolicyProc; 315 | static PSET_THREAD_INFORMATION_PROC SetThreadInformationProc; 316 | static PGET_THREAD_INFORMATION_PROC GetThreadInformationProc; 317 | static PCO_CREATE_INSTANCE_FROM_APP_PROC CoCreateInstanceFromAppProc; 318 | static PROCESS_MITIGATION_DYNAMIC_CODE_POLICY processPolicy; 319 | 320 | // Used to determine if the statics have been initialized. This is volatile 321 | // so the compile does not change the order of the instructions in the 322 | // constructor. 323 | 324 | volatile static bool processPolicyObtained; 325 | volatile static bool disableACGCalled; 326 | 327 | }; 328 | 329 | AutoEnableDynamicCodeGen::PGET_PROCESS_MITIGATION_POLICY_PROC AutoEnableDynamicCodeGen::GetProcessMitigationPolicyProc = NULL; 330 | AutoEnableDynamicCodeGen::PSET_THREAD_INFORMATION_PROC AutoEnableDynamicCodeGen::SetThreadInformationProc = NULL; 331 | AutoEnableDynamicCodeGen::PGET_THREAD_INFORMATION_PROC AutoEnableDynamicCodeGen::GetThreadInformationProc = NULL; 332 | AutoEnableDynamicCodeGen::PCO_CREATE_INSTANCE_FROM_APP_PROC AutoEnableDynamicCodeGen::CoCreateInstanceFromAppProc = NULL; 333 | AutoEnableDynamicCodeGen::PROCESS_MITIGATION_DYNAMIC_CODE_POLICY AutoEnableDynamicCodeGen::processPolicy; 334 | volatile bool AutoEnableDynamicCodeGen::processPolicyObtained = false; 335 | volatile bool AutoEnableDynamicCodeGen::disableACGCalled = false; 336 | 337 | #endif // #if defined(WARBIRD_KERNEL_MODE) 338 | 339 | } // namespace WarbirdRuntime 340 | -------------------------------------------------------------------------------- /WarbirdCUtil.inl: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | namespace WarbirdRuntime { 3 | #endif // _cpluplus 4 | 5 | // 6 | // Size of the common fields of the runtime argument structures. 7 | // 8 | #define RVA_BIT_COUNT 28 9 | #define FUNCTION_SIZE_BIT_COUNT 28 10 | #define KEY_BIT_COUNT 64 11 | #define CHECKSUM_BIT_COUNT 8 12 | #define HASH_BIT_COUNT 64 13 | #define CONDITION_CODE_BIT_COUNT 4 14 | #define STACK_SIZE_BIT_COUNT 28 15 | 16 | #define NUMBER_FEISTEL64_ROUNDS 10 17 | 18 | // 19 | // Warbird operation types. Indicates for which operation the argument structure 20 | // is for. 21 | // 22 | typedef enum { 23 | WbOperationNone, 24 | WbOperationDecryptEncryptionSegment, 25 | WbOperationReEncryptEncryptionSegment, 26 | WbOperationHeapExecuteCall, 27 | WbOperationHeapExecuteReturn, 28 | WbOperationHeapExecuteUnconditionalBranch, 29 | WbOperationHeapExecuteConditionalBranch, 30 | WbOperationProcessEnd, 31 | WbOperationProcessStartup, 32 | } WbOperationType; 33 | 34 | typedef struct _FEISTEL64_ROUND_DATA 35 | { 36 | unsigned long FunctionID; 37 | unsigned long Rand0; 38 | unsigned long Rand1; 39 | unsigned long Rand2; 40 | } FEISTEL64_ROUND_DATA, PFEISTEL64_ROUND_DATA; 41 | 42 | typedef struct _ENCRYPTION_BLOCK { 43 | unsigned long bUnitialized:1; 44 | unsigned long bData:1; 45 | unsigned long ulChecksum:CHECKSUM_BIT_COUNT; 46 | unsigned long ulRva:RVA_BIT_COUNT; 47 | unsigned long ulSize:FUNCTION_SIZE_BIT_COUNT; 48 | } ENCRYPTION_BLOCK, *PENCRYPTION_BLOCK; 49 | 50 | typedef struct _ENCRYPTION_SEGMENT { 51 | unsigned long ulVersion; 52 | unsigned long ulSegmentID; 53 | unsigned __int64 ullKey; 54 | FEISTEL64_ROUND_DATA bRoundData[NUMBER_FEISTEL64_ROUNDS]; 55 | unsigned long cBlocks; 56 | ENCRYPTION_BLOCK Blocks[1]; 57 | } ENCRYPTION_SEGMENT, *PENCRYPTION_SEGMENT; 58 | 59 | // 60 | // System call heap execution runtime structures and runtime 61 | // 62 | typedef struct _HEAP_EXECUTE_CALL_ARGUMENT { 63 | unsigned long ulVersion; 64 | unsigned long ulCheckStackSize; 65 | unsigned long ulChecksum:CHECKSUM_BIT_COUNT; 66 | unsigned long ulWrapperChecksum:CHECKSUM_BIT_COUNT; 67 | unsigned long ulRva:RVA_BIT_COUNT; 68 | unsigned long ulSize:FUNCTION_SIZE_BIT_COUNT; 69 | unsigned long ulWrapperRva:RVA_BIT_COUNT; 70 | unsigned long ulWrapperSize:FUNCTION_SIZE_BIT_COUNT; 71 | unsigned __int64 ullKey; 72 | FEISTEL64_ROUND_DATA RoundData[NUMBER_FEISTEL64_ROUNDS]; 73 | } HEAP_EXECUTE_CALL_ARGUMENT, *PHEAP_EXECUTE_CALL_ARGUMENT; 74 | 75 | // 76 | // Warbird kernel configuration. The user mode process passes this configuration 77 | // to the kernel when it is first started. 78 | // 79 | typedef struct _PROCESS_STARTUP_ARGUMENT { 80 | unsigned long ulVersion; 81 | unsigned long cMaxHeapExecutedCacheEntries; 82 | void* pPreAllocatedReadExecuteMemory; 83 | unsigned long cbPreAllocatedReadExecuteMemory; 84 | } PROCESS_STARTUP_ARGUMENT, *PPROCESS_STARTUP_ARGUMENT; 85 | 86 | typedef struct _PROCESS_STARTUP_ARGUMENT_LIST { 87 | unsigned __int64 eType; 88 | PPROCESS_STARTUP_ARGUMENT pArguments; 89 | } PROCESS_STARTUP_ARGUMENT_LIST, *PPROCESS_STARTUP_ARGUMENT_LIST; 90 | 91 | #ifdef __cplusplus 92 | }; // namespace WarbirdRuntime 93 | #endif // _cpluplus 94 | #if defined(WARBIRD_KERNEL_MODE) 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | extern "C" void* _AddressOfReturnAddress(); 103 | #else // defined(WARBIRD_KERNEL_MODE) 104 | #include 105 | #include 106 | #include 107 | #include 108 | #endif // defined(WARBIRD_KERNEL_MODE) 109 | #include 110 | #ifdef _M_E2 111 | #include 112 | #undef PAGE_SIZE 113 | #define PAGE_SIZE NTARCH_PAGE_SIZE 114 | #elif defined(_X86_) || defined(_AMD64_) || defined(_ARM_) || defined(_ARM64_) 115 | #undef PAGE_SIZE 116 | #define PAGE_SIZE 0x1000 117 | #endif // _M_E2 118 | 119 | // 120 | // Warbird runtime's use intrinsics to reduce hookable attack points 121 | // 122 | #pragma intrinsic(memcpy, memcmp, memset) 123 | 124 | #define STRINGIZE(x) #x 125 | 126 | #if !defined(ALG_TYPE_ANY) 127 | #define ALG_TYPE_ANY (0) 128 | #endif 129 | #if !defined(ALG_CLASS_ANY) 130 | #define ALG_CLASS_ANY (0) 131 | #endif 132 | #if !defined(ALG_CLASS_HASH) 133 | #define ALG_CLASS_HASH (4 << 13) 134 | #endif 135 | #if !defined(ALG_SID_SHA_256) 136 | #define ALG_SID_SHA_256 12 137 | #endif 138 | #if !defined(ALG_SID_SHA_512) 139 | #define ALG_SID_SHA_512 14 140 | #endif 141 | #if !defined(CALG_SHA_512) 142 | #define CALG_SHA_512 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512) 143 | #endif 144 | 145 | 146 | #if defined(_WIN64) 147 | #define _InterlockedCompareExchangeSizeT _InterlockedCompareExchange64 148 | #define _InterlockedDecrementSizeT _InterlockedDecrement64 149 | #define _InterlockedExchangeSizeT _InterlockedExchange64 150 | #else 151 | #define _InterlockedCompareExchangeSizeT _InterlockedCompareExchange 152 | #define _InterlockedDecrementSizeT _InterlockedDecrement 153 | #define _InterlockedExchangeSizeT _InterlockedExchange 154 | #endif 155 | 156 | #if defined(WARBIRD_DEBUG) 157 | #define DebugPrint CUtil::DebugPrintFunc 158 | #define WARBIRD_ASSERT CUtil::AssertFunc 159 | #else 160 | #define DebugPrint __noop 161 | #define WARBIRD_ASSERT __noop 162 | #endif 163 | 164 | #ifndef HRESULT_FROM_NTSTATUS 165 | #define HRESULT_FROM_NTSTATUS(x) ((HRESULT) ((x) | FACILITY_NT_BIT)) 166 | #endif // HRESULT_FROM_NTSTATUS 167 | 168 | #ifndef WARBIRD_KERNEL_MODE 169 | 170 | #define SystemCodeFlowTransition 185 171 | 172 | EXTERN_C ULONG WINAPI 173 | NtQuerySystemInformation( 174 | ULONG SystemInformationClass, 175 | PVOID SystemInformation, 176 | ULONG SystemInformationLength, 177 | PULONG ReturnLength 178 | ); 179 | 180 | #endif // !WARBIRD_KERNEL_MODE 181 | 182 | #if defined(WARBIRD_KERNEL_MODE) 183 | /*++ 184 | 185 | Description: 186 | 187 | Define ZwFlushInstructionCache (from zwapi.h) 188 | 189 | --*/ 190 | EXTERN_C 191 | NTSYSAPI 192 | NTSTATUS 193 | NTAPI 194 | ZwFlushInstructionCache ( 195 | __in HANDLE ProcessHandle, 196 | __in_opt PVOID BaseAddress, 197 | __in SIZE_T Length 198 | ); 199 | 200 | #ifndef ALG_ID 201 | typedef unsigned int ALG_ID; 202 | #endif 203 | 204 | #endif 205 | 206 | 207 | // Random seed used to create this file is : $(RandomSeed) 208 | 209 | int __cdecl TestWarbirdRuntime(int x) 210 | { 211 | return x * x; 212 | } 213 | 214 | namespace WarbirdRuntime 215 | { 216 | 217 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 218 | volatile UINT64 g_preferedImageBase = 0x123456789ABCDEF; 219 | 220 | extern volatile UINT64 g_preferedImageBase; 221 | 222 | class CUtil 223 | { 224 | public: 225 | #ifdef WARBIRD_DEBUG 226 | static VOID 227 | AssertFunc(BOOL x) 228 | { 229 | if (!x) 230 | { 231 | __debugbreak(); 232 | } 233 | } 234 | 235 | static VOID 236 | DebugPrintFunc( 237 | __in PCSTR pszFormatString, 238 | ... 239 | ) 240 | { 241 | char szMessage[1024]; 242 | va_list arguments; 243 | va_start(arguments, pszFormatString); 244 | 245 | vsprintf_s(szMessage, ARRAYSIZE(szMessage), pszFormatString, arguments); 246 | 247 | #if defined(WARBIRD_KERNEL_MODE) 248 | DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, (PSTR)szMessage); 249 | #else 250 | vprintf(pszFormatString, arguments); 251 | 252 | OutputDebugStringA(szMessage); 253 | 254 | #endif // WARBIRD_KERNEL_MODE 255 | 256 | va_end(arguments); 257 | } 258 | #endif // WARBIRD_DEBUG 259 | 260 | template 261 | static T* 262 | AddOffset( 263 | __in T* pSource, 264 | INT_PTR nOffset 265 | ) 266 | { 267 | return reinterpret_cast(reinterpret_cast(pSource) + nOffset); 268 | } 269 | 270 | template 271 | static CONST T* 272 | AddOffset( 273 | __in CONST T* pSource, 274 | INT_PTR nOffset 275 | ) 276 | { 277 | return reinterpret_cast(reinterpret_cast(pSource) + nOffset); 278 | } 279 | 280 | template 281 | static T* 282 | SubOffset( 283 | __in T* pSource, 284 | INT_PTR nOffset 285 | ) 286 | { 287 | return reinterpret_cast(reinterpret_cast(pSource) - nOffset); 288 | } 289 | 290 | template 291 | static CONST T* 292 | SubOffset( 293 | __in CONST T* pSource, 294 | INT_PTR nOffset 295 | ) 296 | { 297 | return reinterpret_cast(reinterpret_cast(pSource) - nOffset); 298 | } 299 | 300 | static INT_PTR 301 | GetOffset( 302 | __in CONST VOID* pTarget, 303 | __in CONST VOID* pSource 304 | ) 305 | { 306 | return reinterpret_cast(pTarget) - reinterpret_cast(pSource); 307 | } 308 | 309 | template 310 | static T 311 | RoundDown( 312 | T x, 313 | SIZE_T y 314 | ) 315 | { 316 | WARBIRD_ASSERT((y & (y - 1)) == 0); 317 | return reinterpret_cast(reinterpret_cast(x) & ~(y-1)); 318 | } 319 | 320 | template 321 | static T 322 | RoundUp( 323 | T x, 324 | SIZE_T y 325 | ) 326 | { 327 | return RoundDown(reinterpret_cast(reinterpret_cast(x) + (y - 1)), y); 328 | } 329 | 330 | static UINT_PTR 331 | GetImageBase( 332 | VOID 333 | ) 334 | { 335 | WARBIRD_ASSERT(__ImageBase.e_magic == IMAGE_DOS_SIGNATURE); 336 | return (UINT_PTR)&__ImageBase; 337 | } 338 | 339 | static UINT_PTR 340 | GetPreferedImageBase( 341 | VOID 342 | ) 343 | { 344 | return (UINT_PTR)g_preferedImageBase; 345 | } 346 | 347 | static IMAGE_NT_HEADERS* 348 | GetImageNtHeaders( 349 | __in UINT_PTR pImageBase 350 | ) 351 | { 352 | IMAGE_DOS_HEADER* pImageDosHeader = 353 | reinterpret_cast((void*)pImageBase); 354 | 355 | WARBIRD_ASSERT(pImageDosHeader->e_magic == IMAGE_DOS_SIGNATURE); 356 | WARBIRD_ASSERT(pImageDosHeader->e_lfanew >= sizeof(IMAGE_DOS_HEADER)); 357 | 358 | IMAGE_NT_HEADERS* pImageNtHeaders = 359 | reinterpret_cast(AddOffset((void*)pImageBase, pImageDosHeader->e_lfanew)); 360 | 361 | WARBIRD_ASSERT(pImageNtHeaders->Signature == IMAGE_NT_SIGNATURE); 362 | 363 | return pImageNtHeaders; 364 | } 365 | 366 | DECLSPEC_NOINLINE static VOID* 367 | GetCurrentSp( 368 | ) 369 | { 370 | return AddOffset(_AddressOfReturnAddress(), sizeof(VOID*)); 371 | } 372 | 373 | static VOID 374 | FillRandom( 375 | __out_bcount(nSize) PVOID pMemory, 376 | SIZE_T nSize 377 | ) 378 | { 379 | // Fill the target buffer with random stuff on the last page of the stack 380 | // (which will with high probability be in memory, so we won't take a page-in hit). 381 | 382 | PVOID pSrc = RoundDown(GetCurrentSp(), PAGE_SIZE); 383 | 384 | PVOID pDst = pMemory; 385 | PVOID pEnd = AddOffset(pMemory, nSize); 386 | 387 | while (pDst < pEnd) 388 | { 389 | CUtil::Memcpy( 390 | pDst, 391 | pSrc, 392 | min(PAGE_SIZE, GetOffset(pEnd, pDst)) 393 | ); 394 | 395 | pDst = AddOffset(pDst, PAGE_SIZE); 396 | } 397 | } 398 | 399 | static VOID 400 | FlushCpuCache( 401 | __in_bcount_opt(nSize) CONST VOID* pBaseAddress, 402 | SIZE_T nSize 403 | ) 404 | { 405 | #if defined(_X86_) || defined(_AMD64_) 406 | 407 | // X86 and AMD64 have transparent caches, so we don't need to call 408 | // FlushInstructionCache (which is a noop anyway). 409 | 410 | UNREFERENCED_PARAMETER(pBaseAddress); 411 | UNREFERENCED_PARAMETER(nSize); 412 | 413 | #elif defined(WARBIRD_KERNEL_MODE) 414 | 415 | ZwFlushInstructionCache( 416 | NtCurrentProcess(), 417 | const_cast(pBaseAddress), 418 | nSize 419 | ); 420 | 421 | #else 422 | DWORD LastError = GetLastError(); 423 | 424 | FlushInstructionCache( 425 | GetCurrentProcess(), 426 | pBaseAddress, 427 | nSize 428 | ); 429 | 430 | SetLastError(LastError); 431 | 432 | #endif 433 | } 434 | 435 | static ULONG64 436 | ReadCpuTimeStamp( 437 | ) 438 | { 439 | #if defined(_M_ARM) 440 | return __rdpmccntr64(); 441 | #elif defined(_M_ARM64) 442 | // Timer statistics obtained via intrinsics that access the coprocessor 443 | // The ARM64_SYSREG macro creates an argument for ReadStatusReg. The 444 | // exact definition is in winnt.h 445 | return _ReadStatusReg(ARM64_SYSREG(3,3, 9,13,0)); 446 | #else 447 | return ReadTimeStampCounter(); 448 | #endif 449 | } 450 | 451 | // 452 | // Basic implementation of memcpy. A call to memcpy is a hookable attack 453 | // point. If the hacker sets a breakpoint on memcpy they can detect when 454 | // the function/data is decrypted into the clear. The pragma intrinsic is 455 | // only a hint to the compiler. The compiler might still decide to convert 456 | // the intrinsic into a call to improve performance. 457 | // 458 | static __forceinline void* 459 | Memcpy( 460 | __out_bcount_full(cbCount) void* pDst, 461 | __in_bcount(cbCount) const void* pSrc, 462 | __in size_t cbCount 463 | ) 464 | { 465 | // 466 | // Make the source parameter volatile to prevent the compiler from 467 | // converting this loop into a call to memcpy. 468 | // 469 | volatile const char *pIn = (const char *)pSrc; 470 | volatile char *pOut = (char *)pDst; 471 | while (cbCount--) 472 | { 473 | *pOut++ = *pIn++; 474 | } 475 | return pDst; 476 | } 477 | 478 | // 479 | // Basic implementation of memset. A call to memset is a hookable attack 480 | // point. The parameter is casted to a volatile to try and prevent the 481 | // compiler from converting the loop into a call to memset. 482 | // 483 | static __forceinline void* 484 | Memset( 485 | __out_bcount(cbCount) VOID *pSrc, 486 | __in CHAR nValue, 487 | __in SIZE_T cbCount 488 | ) 489 | { 490 | // 491 | // Make the source parameter volatile to prevent the compiler from 492 | // converting this loop into a call to memset. 493 | // 494 | volatile char *vptr = (volatile char *)pSrc; 495 | while (cbCount--) 496 | { 497 | *vptr = (char)nValue; 498 | vptr++; 499 | } 500 | return pSrc; 501 | } 502 | 503 | 504 | }; //class CUtil 505 | 506 | }; // namespace WarbirdRuntime -------------------------------------------------------------------------------- /WarbirdVSMTest.inl: -------------------------------------------------------------------------------- 1 | // 2 | // Emulation support for running in two modes when 3 | // 1) User-mode 4 | // WARBIRD_VSM_TEST - is defined 5 | // WARBIRD_KERNEL_MODE - is defined locally 6 | // WARBIRD_KERNEL_MODE_PRESET - is not defined 7 | // 2) Kernel-mode : uses kernel Mm functions except for MmChangeImageProtection 8 | // WARBIRD_VSM_TEST - is defined 9 | // WARBIRD_KERNEL_MODE - is defined 10 | // WARBIRD_KERNEL_MODE_PRESET - is defined 11 | // 12 | #if defined(WARBIRD_VSM_TEST) 13 | 14 | namespace WarbirdRuntime 15 | { 16 | 17 | // Dummy Mode - fakes kernel 18 | #if defined(WARBIRD_KERNEL_MODE) 19 | // Test mode + kernel mode implies we do not have the VSM available but are running 20 | // in kernel 21 | #define WARBIRD_KERNEL_MODE_PRESET 22 | #else // WARBIRD_KERNEL_MODE 23 | // For VSM_TEST - emulate some of the kernel functions in user mode 24 | #define WARBIRD_KERNEL_MODE 1 25 | #endif // WARBIRD_KERNEL_MODE 26 | 27 | #if defined(WARBIRD_KERNEL_MODE_PRESET) 28 | #define ZeroMemory RtlZeroMemory 29 | #else // defined(WARBIRD_KERNEL_MODE_PRESET) 30 | 31 | // Psuedo Kernel mode (actually running in user-mode - for testing only!) 32 | 33 | #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) 34 | 35 | typedef enum _LOCK_OPERATION { 36 | IoReadAccess, 37 | IoWriteAccess, 38 | IoModifyAccess 39 | } LOCK_OPERATION; 40 | 41 | typedef enum _MM_PAGE_PRIORITY { 42 | LowPagePriority, 43 | NormalPagePriority = 16, 44 | HighPagePriority = 32, 45 | } MM_PAGE_PRIORITY; 46 | 47 | #ifndef MdlMappingNoExecute 48 | #define MdlMappingNoExecute 0x40000000 // Create the mapping as noexecute 49 | #endif // MdlMappingNoExecute 50 | 51 | #define MDL_MAPPED_TO_SYSTEM_VA 0x0001 52 | #define MDL_PAGES_LOCKED 0x0002 53 | #define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 54 | #define MDL_ALLOCATED_FIXED_SIZE 0x0008 55 | #define MDL_PARTIAL 0x0010 56 | #define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 57 | #define MDL_IO_PAGE_READ 0x0040 58 | #define MDL_WRITE_OPERATION 0x0080 59 | #define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 60 | #define MDL_LOCK_HELD 0x0200 61 | 62 | typedef struct _MDL 63 | { 64 | PVOID StartVa; 65 | ULONG ByteCount; 66 | ULONG MdlFlags; 67 | } MDL, *PMDL, *PMDLX; 68 | 69 | typedef struct _Irp 70 | { 71 | PVOID dummy; 72 | } IRP, *PIRP; 73 | 74 | typedef enum _MODE { 75 | KernelMode, 76 | UserMode, 77 | MaximumMode 78 | } MODE; 79 | 80 | #define KPROCESSOR_MODE int 81 | 82 | typedef BOOL BOOLEAN; 83 | typedef ULONG NTSTATUS; 84 | 85 | PVOID 86 | MmLockPagableDataSection ( 87 | _In_ PVOID AddressWithinSection 88 | ) 89 | { 90 | return AddressWithinSection; 91 | } 92 | 93 | VOID 94 | MmUnlockPagableImageSection( 95 | IN PVOID ImageSectionHandle 96 | ) 97 | { 98 | UNREFERENCED_PARAMETER(ImageSectionHandle); 99 | } 100 | 101 | PMDL IoAllocateMdl( 102 | _In_opt_ PVOID VirtualAddress, 103 | _In_ ULONG Length, 104 | _In_ BOOLEAN SecondaryBuffer, 105 | _In_ BOOLEAN ChargeQuota, 106 | _Inout_opt_ PIRP Irp 107 | ) 108 | { 109 | UNREFERENCED_PARAMETER(Length); 110 | UNREFERENCED_PARAMETER(SecondaryBuffer); 111 | UNREFERENCED_PARAMETER(ChargeQuota); 112 | UNREFERENCED_PARAMETER(Irp); 113 | PMDL m = new MDL; 114 | m->StartVa = VirtualAddress; 115 | m->ByteCount = Length; 116 | return m; 117 | } 118 | 119 | VOID MmProbeAndLockPages( 120 | _Inout_ PMDLX MemoryDescriptorList, 121 | _In_ KPROCESSOR_MODE AccessMode, 122 | _In_ LOCK_OPERATION Operation 123 | ) 124 | { 125 | UNREFERENCED_PARAMETER(AccessMode); 126 | 127 | DWORD protect; 128 | DWORD OldProtect; 129 | 130 | switch(Operation) 131 | { 132 | case IoReadAccess: 133 | protect = PAGE_READONLY; 134 | break; 135 | case IoWriteAccess: 136 | case IoModifyAccess: 137 | default: 138 | protect = PAGE_READWRITE; 139 | } 140 | 141 | VirtualProtect( 142 | MemoryDescriptorList->StartVa, 143 | MemoryDescriptorList->ByteCount, 144 | protect, 145 | &OldProtect ); 146 | } 147 | 148 | PVOID MmGetSystemAddressForMdlSafe( 149 | _In_ PMDL Mdl, 150 | _In_ MM_PAGE_PRIORITY Priority 151 | ) 152 | { 153 | UNREFERENCED_PARAMETER(Priority); 154 | return Mdl->StartVa; 155 | } 156 | 157 | VOID MmUnlockPages( 158 | _Inout_ PMDL MemoryDescriptorList 159 | ) 160 | { 161 | UNREFERENCED_PARAMETER(MemoryDescriptorList); 162 | } 163 | 164 | VOID 165 | MmUnmapLockedPages ( 166 | _In_ PVOID BaseAddress, 167 | _Inout_ PMDL MemoryDescriptorList 168 | ) 169 | { 170 | UNREFERENCED_PARAMETER(BaseAddress); 171 | UNREFERENCED_PARAMETER(MemoryDescriptorList); 172 | 173 | return; 174 | } 175 | 176 | VOID IoFreeMdl( 177 | _In_ PMDL Mdl 178 | ) 179 | { 180 | delete Mdl; 181 | } 182 | #endif // defined(WARBIRD_KERNEL_MODE_PRESET) 183 | 184 | typedef NTSTATUS (WINAPI * PFNBCryptOpenAlgorithmProvider)( 185 | __out BCRYPT_ALG_HANDLE *phAlgorithm, 186 | __in LPCWSTR pszAlgId, 187 | __in_opt LPCWSTR pszImplementation, 188 | __in ULONG dwFlags 189 | ); 190 | 191 | typedef NTSTATUS (WINAPI * PFNBCryptCreateHash)( 192 | __inout BCRYPT_ALG_HANDLE hAlgorithm, 193 | __out BCRYPT_HASH_HANDLE *phHash, 194 | __out_bcount_full(cbHashObject) PUCHAR pbHashObject, 195 | __in ULONG cbHashObject, 196 | __in_bcount_opt(cbSecret) PUCHAR pbSecret, 197 | __in ULONG cbSecret, 198 | __in ULONG dwFlags 199 | ); 200 | 201 | typedef NTSTATUS (WINAPI * PFNBCryptHashData)( 202 | __inout BCRYPT_HASH_HANDLE hHash, 203 | __in_bcount(cbInput) PUCHAR pbInput, 204 | __in ULONG cbInput, 205 | __in ULONG dwFlags 206 | ); 207 | 208 | typedef NTSTATUS (WINAPI * PFNBCryptFinishHash)( 209 | __inout BCRYPT_HASH_HANDLE hHash, 210 | __out_bcount_full(cbOutput) PUCHAR pbOutput, 211 | __in ULONG cbOutput, 212 | __in ULONG dwFlags 213 | ); 214 | 215 | typedef NTSTATUS (WINAPI * PFNBCryptCloseAlgorithmProvider)( 216 | __inout BCRYPT_ALG_HANDLE hAlgorithm, 217 | __in ULONG dwFlags 218 | ); 219 | 220 | typedef NTSTATUS (WINAPI * PFNBCryptDestroyHash)( 221 | __inout BCRYPT_HASH_HANDLE hHash 222 | ); 223 | 224 | HMODULE g_hBcryptPrimitives = NULL; 225 | PFNBCryptCreateHash g_pfnBCryptCreateHash = NULL; 226 | PFNBCryptHashData g_pfnBCryptHashData = NULL; 227 | PFNBCryptFinishHash g_pfnBCryptFinishHash = NULL; 228 | PFNBCryptCloseAlgorithmProvider g_pfnBCryptCloseAlgorithmProvider = NULL; 229 | PFNBCryptDestroyHash g_pfnBCryptDestroyHash = NULL; 230 | BCRYPT_ALG_HANDLE g_hSignatureHashProvider = NULL; 231 | 232 | BOOL SetupBcrypt( ) 233 | { 234 | 235 | PFNBCryptOpenAlgorithmProvider pfnBCryptOpenAlgorithmProvider = NULL; 236 | #if defined(WARBIRD_KERNEL_MODE_PRESET) 237 | pfnBCryptOpenAlgorithmProvider = BCryptOpenAlgorithmProvider; 238 | g_pfnBCryptCreateHash = BCryptCreateHash; 239 | g_pfnBCryptHashData = BCryptHashData; 240 | g_pfnBCryptFinishHash = BCryptFinishHash; 241 | g_pfnBCryptCloseAlgorithmProvider = BCryptCloseAlgorithmProvider; 242 | g_pfnBCryptDestroyHash = BCryptDestroyHash; 243 | #else // defined(WARBIRD_KERNEL_MODE_PRESET) 244 | if(g_hBcryptPrimitives == NULL) 245 | { 246 | g_hBcryptPrimitives = LoadLibraryEx( 247 | "bcrypt.dll", 248 | NULL, 249 | LOAD_LIBRARY_SEARCH_SYSTEM32 ); 250 | 251 | if (g_hBcryptPrimitives) 252 | { 253 | pfnBCryptOpenAlgorithmProvider = (PFNBCryptOpenAlgorithmProvider)GetProcAddress(g_hBcryptPrimitives, "BCryptOpenAlgorithmProvider"); 254 | g_pfnBCryptCreateHash = (PFNBCryptCreateHash)GetProcAddress(g_hBcryptPrimitives, "BCryptCreateHash"); 255 | g_pfnBCryptHashData = (PFNBCryptHashData)GetProcAddress(g_hBcryptPrimitives, "BCryptHashData"); 256 | g_pfnBCryptFinishHash = (PFNBCryptFinishHash)GetProcAddress(g_hBcryptPrimitives, "BCryptFinishHash"); 257 | g_pfnBCryptCloseAlgorithmProvider = (PFNBCryptCloseAlgorithmProvider)GetProcAddress(g_hBcryptPrimitives, "BCryptCloseAlgorithmProvider"); 258 | g_pfnBCryptDestroyHash = (PFNBCryptDestroyHash)GetProcAddress(g_hBcryptPrimitives, "BCryptDestroyHash"); 259 | 260 | } 261 | else 262 | { 263 | return FALSE; 264 | } 265 | } 266 | #endif // defined(WARBIRD_KERNEL_MODE_PRESET) 267 | LPCWSTR algId; 268 | HRESULT hr; 269 | VSM_HASH_TABLE* table = GetVsmHashTable(); 270 | 271 | if ( (table->Version == 2) && (table->HashAlgorithm == CALG_SHA_512) ) 272 | { 273 | algId = BCRYPT_SHA512_ALGORITHM; 274 | } 275 | else 276 | { 277 | algId = BCRYPT_SHA256_ALGORITHM; 278 | } 279 | 280 | hr = HRESULT_FROM_NT(pfnBCryptOpenAlgorithmProvider( 281 | &g_hSignatureHashProvider, 282 | algId, 283 | NULL, 284 | 0 )); 285 | if(FAILED(hr)) 286 | { 287 | return FALSE; 288 | } 289 | 290 | 291 | return TRUE; 292 | } 293 | 294 | void ShutDownBcrypt() 295 | { 296 | if (g_hSignatureHashProvider) 297 | { 298 | g_pfnBCryptCloseAlgorithmProvider(g_hSignatureHashProvider, 0); 299 | } 300 | #if defined(WARBIRD_KERNEL_MODE_PRESET) 301 | #else // defined(WARBIRD_KERNEL_MODE_PRESET) 302 | if (g_hBcryptPrimitives) 303 | { 304 | FreeLibrary(g_hBcryptPrimitives); 305 | g_hBcryptPrimitives = NULL; 306 | } 307 | #endif // defined(WARBIRD_KERNEL_MODE_PRESET) 308 | } 309 | 310 | HRESULT VerifyCodePages( _In_reads_bytes_(PageCount * PAGE_SIZE) PVOID BaseAddress, 311 | _In_ SIZE_T TotalSize, 312 | PVOID SignatureBlock, 313 | ULONG SignatureSize) 314 | { 315 | 316 | HRESULT hr = S_OK; 317 | 318 | BCRYPT_HASH_HANDLE hHash = NULL; 319 | PBYTE pBuffer = (BYTE *)BaseAddress; 320 | ULONG nSize = PAGE_SIZE; 321 | ULONG PageCount = (ULONG) (TotalSize / PAGE_SIZE); 322 | ULONG HashLength = 0; 323 | BYTE bHash[512/8]; 324 | PBYTE bHashStart = NULL; 325 | 326 | SIZE_T HashLimit = SignatureSize / $(WARBIRD_VSM_HASH_LENGTH); 327 | VSM_HASH_TABLE* table = GetVsmHashTable(); 328 | 329 | WARBIRD_ASSERT(PageCount == SignatureSize / $(WARBIRD_VSM_HASH_LENGTH)); 330 | 331 | if(HashLimit != PageCount) 332 | { 333 | return E_ACCESSDENIED; 334 | } 335 | 336 | if ( (table->Version == 2) && (table->HashAlgorithm == CALG_SHA_512) ) 337 | { 338 | HashLength = 512 / 8; 339 | if(table->Flag == WARBIRD_VSM_HASH_UPPER_HALF) 340 | { 341 | bHashStart = &(bHash[HashLength/2]); 342 | } 343 | else 344 | { 345 | bHashStart = bHash; 346 | } 347 | } 348 | else 349 | { 350 | HashLength = 256 / 8; 351 | bHashStart = bHash; 352 | } 353 | 354 | for (ULONG pageIndex = 0; pageIndex < PageCount; pageIndex++) 355 | { 356 | PBYTE pData = pBuffer + pageIndex*nSize; 357 | ZeroMemory(bHash, HashLength); 358 | 359 | hr = HRESULT_FROM_NT(g_pfnBCryptCreateHash( 360 | g_hSignatureHashProvider, 361 | &hHash, 362 | NULL, 363 | 0, 364 | NULL, 365 | 0, 366 | 0 )); 367 | if(FAILED(hr)) 368 | { 369 | goto Cleanup; 370 | } 371 | 372 | 373 | #if defined(_M_IX86) 374 | // 375 | // We need to handle relocations when we are calculating the page hashes. 376 | // We cannot use the private relocation table in this case because the 377 | // compiler is outlining some code when using the flag d2dbstressoutline 378 | // which is not encrypted and hence relocations are not part of the private 379 | // relocation table but only part of the OS relocation table. 380 | // 381 | ULONG nRva = (ULONG)((ULONG_PTR)pData - WarbirdRuntime::CUtil::GetImageBase()); 382 | WarbirdRuntime::CRelocations relocations; 383 | relocations.Init(nRva, nSize); 384 | 385 | WarbirdRuntime::COSRelocationBlock UNALIGNED* pOSRelocationBlock; 386 | SIZE_T nOSRelocationItem; 387 | 388 | bool hasNextReloc = relocations.GetNext(&nOSRelocationItem, &pOSRelocationBlock); 389 | 390 | for(SIZE_T nByteIndex = 0; nByteIndex < nSize;) 391 | { 392 | if (hasNextReloc && 393 | (nByteIndex + nRva == pOSRelocationBlock->RVA() + (*pOSRelocationBlock)[nOSRelocationItem].Offset)) 394 | { 395 | BYTE relocBuffer[16] = {0}; 396 | SIZE_T nRelocBytes = relocations.ApplyRelocation( 397 | &pData[nByteIndex], 398 | (*pOSRelocationBlock)[nOSRelocationItem].Type, 399 | WarbirdRuntime::CUtil::GetPreferedImageBase() - WarbirdRuntime::CUtil::GetImageBase(), 400 | relocBuffer 401 | ); 402 | 403 | hr = HRESULT_FROM_NT(g_pfnBCryptHashData(hHash, relocBuffer, nRelocBytes, 0)); 404 | nByteIndex += nRelocBytes; 405 | hasNextReloc = relocations.GetNext(&nOSRelocationItem, &pOSRelocationBlock); 406 | } 407 | else 408 | { 409 | hr = HRESULT_FROM_NT(g_pfnBCryptHashData(hHash, &pData[nByteIndex], 1, 0)); 410 | ++nByteIndex; 411 | } 412 | if(FAILED(hr)) 413 | { 414 | goto Cleanup; 415 | } 416 | } 417 | #else // defined(_M_IX86) 418 | hr = HRESULT_FROM_NT(g_pfnBCryptHashData(hHash, pData, nSize, 0)); 419 | if(FAILED(hr)) 420 | { 421 | goto Cleanup; 422 | } 423 | #endif // defined(_M_IX86) 424 | 425 | hr = HRESULT_FROM_NT(g_pfnBCryptFinishHash(hHash, bHash, HashLength, 0)); 426 | if(FAILED(hr)) 427 | { 428 | goto Cleanup; 429 | } 430 | 431 | PVOID signature = (PVOID) (((PBYTE)SignatureBlock) + (pageIndex * $(WARBIRD_VSM_HASH_LENGTH))); 432 | ULONG difference = memcmp( bHashStart, signature, $(WARBIRD_VSM_HASH_LENGTH) ); 433 | if(difference != 0) 434 | { 435 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 436 | goto Cleanup; 437 | } 438 | 439 | g_pfnBCryptDestroyHash(hHash); 440 | hHash = NULL; 441 | } // For 442 | 443 | #if !defined(WARBIRD_KERNEL_MODE_PRESET) 444 | // If emulating KERNEL mode in UserMode there is only one set of 445 | // virtual pages, therefore the protection must be set back to read-execute 446 | // 447 | // This is not necessary in kernel mode because there are separate virtual pages 448 | // pointing to the same physical pages. 449 | if(SUCCEEDED(hr)) 450 | { 451 | // Enable automatic code generation so the page properties can be 452 | // changed to read-execute. Disabled when class goes out of scope. 453 | 454 | AutoEnableDynamicCodeGen codeGen(true); 455 | 456 | DWORD OldProtect; 457 | VirtualProtect(BaseAddress, 458 | TotalSize, 459 | PAGE_EXECUTE_READ, 460 | &OldProtect); 461 | } 462 | #endif // !defined(WARBIRD_KERNEL_MODE_PRESET) 463 | 464 | 465 | Cleanup: 466 | if (hHash) 467 | { 468 | g_pfnBCryptDestroyHash(hHash); 469 | hHash = NULL; 470 | } 471 | 472 | return hr; 473 | } 474 | 475 | 476 | // Coming soon for HVCI 477 | NTSTATUS 478 | MmChangeImageProtection( 479 | PMDL CodeMdl, 480 | PVOID SignatureAddress, 481 | SIZE_T SignatureSize, 482 | ULONG ExecuteFlags) 483 | { 484 | #if !defined(WARBIRD_KERNEL_MODE_PRESET) 485 | if(ExecuteFlags == MM_CHANGE_ENABLE_EXECUTE) 486 | { 487 | if(FAILED(VerifyCodePages(CodeMdl->StartVa, 488 | CodeMdl->ByteCount, 489 | SignatureAddress, 490 | SignatureSize))) 491 | { 492 | return (NTSTATUS) -1; 493 | } 494 | } 495 | #else // !defined(WARBIRD_KERNEL_MODE_PRESET) 496 | UNREFERENCED_PARAMETER(CodeMdl); 497 | UNREFERENCED_PARAMETER(SignatureAddress); 498 | UNREFERENCED_PARAMETER(SignatureSize); 499 | UNREFERENCED_PARAMETER(ExecuteFlags); 500 | #endif // !defined(WARBIRD_KERNEL_MODE_PRESET) 501 | return STATUS_SUCCESS; 502 | } 503 | 504 | } // WarbirdRuntime 505 | 506 | 507 | #endif // defined(WARBIRD_VSM_TEST) -------------------------------------------------------------------------------- /WarbirdRuntime.h: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) 2012 Microsoft Corporation 4 | 5 | Module Name: 6 | 7 | WarbirdRuntime.h 8 | 9 | Abstract: 10 | 11 | Definitions of Warbird runtime APIs 12 | 13 | Author: 14 | 15 | olafm 11-Sept-2012 16 | markzag 5-Nov-2012 17 | 18 | --*/ 19 | 20 | #pragma once 21 | 22 | #if defined(__cplusplus) 23 | extern "C" { 24 | #endif 25 | 26 | #if !defined __success 27 | #define __success(x) 28 | #endif 29 | 30 | #if !defined __checkReturn 31 | #define __checkReturn 32 | #endif 33 | 34 | #if !defined(_HRESULT_DEFINED) && !defined(HRESULT) 35 | typedef __success(return >= 0) long HRESULT; 36 | #endif 37 | 38 | #if !defined(_NTSTATUS_DEFINED) && !defined(NTSTATUS) 39 | typedef __success(return >= 0) long NTSTATUS; 40 | #endif 41 | 42 | #if !defined(ULONG_PTR) 43 | #if defined(_WIN64) 44 | typedef unsigned __int64 ULONG_PTR; 45 | #else 46 | typedef _W64 unsigned long ULONG_PTR; 47 | #endif 48 | #endif 49 | 50 | #if !defined(SIZE_T) 51 | typedef ULONG_PTR SIZE_T; 52 | #endif 53 | 54 | #if !defined(UINT) 55 | typedef unsigned UINT; 56 | #endif 57 | 58 | #if !defined(BYTE) 59 | typedef unsigned char BYTE; 60 | #endif 61 | 62 | #if !defined(BOOL) 63 | typedef int BOOL; 64 | #endif 65 | 66 | #if defined(LEGACY_WARBIRD) || defined(SLC_PROTECTED_RUNTIME) 67 | 68 | #define WARBIRD_NUM_RVA_BITS 28 69 | 70 | #endif 71 | 72 | /*++ 73 | 74 | Description: 75 | 76 | Initializes the runtime library. This function needs to be called 77 | for export drivers only, and it must be called near the entry point 78 | of the module (preferably in DllInitialize), before any other warbird 79 | runtime functions are used. 80 | 81 | Arguments: 82 | 83 | None. 84 | 85 | Returns: 86 | 87 | S_OK if successful, an error code otherwise. 88 | 89 | --*/ 90 | HRESULT __stdcall WarbirdRuntimeInit(void); 91 | 92 | /*++ 93 | 94 | Description: 95 | 96 | Cleans up the resources used by the runtime library. This function 97 | needs to be called for export drivers only, and it must be called 98 | just before unloading the module (preferably in DllUnload), because 99 | no other warbird runtime function can be used after calling this 100 | function. 101 | 102 | Arguments: 103 | 104 | None. 105 | 106 | Returns: 107 | 108 | None. 109 | 110 | --*/ 111 | void __stdcall WarbirdRuntimeCleanup(void); 112 | 113 | #if defined(LEGACY_WARBIRD) 114 | 115 | /*++ 116 | 117 | Description: 118 | 119 | Creates a dummy reference to the runtime support functions. 120 | This is needed because otherwise the linker may optimize them 121 | out, or Vulcan may consider them as unreachable dead code. 122 | 123 | Arguments: 124 | 125 | None. 126 | 127 | Returns: 128 | 129 | None. 130 | 131 | --*/ 132 | extern const void* const WarbirdRuntimeRef[]; 133 | 134 | #define WARBIRD_DECRYPT_AT_STARTUP_SEGMENT_ID 0 135 | #define WARBIRD_COMMA "_COMMA_" 136 | #define WARBIRD_DBLCLN "_DBLCLN_" 137 | #define WARBIRD_DUMMY_NAMESPACE "WARBIRDMARKER_" 138 | #define WARBIRD_GEN_REROUTE_STR "WARBIRDMARKER_PXE_REROUTE_GEN_" 139 | 140 | #define WARBIRD_RUNTIME_REF \ 141 | { \ 142 | const void* volatile p = WarbirdRuntimeRef; p; \ 143 | } \ 144 | 145 | 146 | #define WARBIRD_VERIFY_SEGMENT_INLINE(ID) WarbirdVerifySegment##ID##Inline() 147 | #define WARBIRD_VERIFY_SEGMENT_NOINLINE(ID) WarbirdVerifySegment##ID##NoInline() 148 | #define WARBIRD_VERIFY_SEGMENT_VAR_INLINE(ID, Value) WarbirdVerifySegment##ID##VarInlineTemplate() 149 | 150 | #define __WARBIRD_DECRYPT_SEGMENT_INLINE(ID) WarbirdDecryptSegment##ID##Inline() 151 | #define WARBIRD_DECRYPT_SEGMENT_INLINE(ID) __WARBIRD_DECRYPT_SEGMENT_INLINE(ID) 152 | #define __WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) WarbirdDecryptSegment##ID##NoInline() 153 | #define WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) __WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) 154 | #define WARBIRD_DECRYPT_SEGMENT(ID) WARBIRD_DECRYPT_SEGMENT_INLINE(ID) 155 | 156 | #define __WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) WarbirdEncryptSegment##ID##Inline() 157 | #define WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) __WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) 158 | #define __WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) WarbirdEncryptSegment##ID##NoInline() 159 | #define WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) __WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) 160 | #define WARBIRD_ENCRYPT_SEGMENT(ID) WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) 161 | 162 | #define WARBIRD_SEGMENT_PROTOTYPE(ID) \ 163 | __checkReturn HRESULT __fastcall WarbirdDecryptSegment##ID##Inline(); \ 164 | __checkReturn HRESULT __fastcall WarbirdDecryptSegment##ID##NoInline(); \ 165 | HRESULT __fastcall WarbirdEncryptSegment##ID##Inline(); \ 166 | HRESULT __fastcall WarbirdEncryptSegment##ID##NoInline(); \ 167 | \ 168 | size_t __fastcall WarbirdVerifySegment##ID##VarInline( \ 169 | __in __int64* pStoredChecksum \ 170 | ); \ 171 | \ 172 | void __fastcall WarbirdVerifySegment##ID##Inline(); \ 173 | void __fastcall WarbirdVerifySegment##ID##NoInline(); \ 174 | 175 | #else 176 | 177 | #define WARBIRD_RUNTIME_REF 178 | 179 | #define __WARBIRD_VERIFY_SEGMENT_INLINE(ID) WarbirdVerifySegment##ID##Inline() 180 | #define WARBIRD_VERIFY_SEGMENT_INLINE(ID) __WARBIRD_VERIFY_SEGMENT_INLINE(ID) 181 | #define __WARBIRD_VERIFY_SEGMENT_NOINLINE(ID) WarbirdVerifySegment##ID##NoInline() 182 | #define WARBIRD_VERIFY_SEGMENT_NOINLINE(ID) __WARBIRD_VERIFY_SEGMENT_NOINLINE(ID) 183 | 184 | #define WARBIRD_VERIFY_SEGMENT WARBIRD_VERIFY_SEGMENT_INLINE 185 | 186 | 187 | // 188 | // Macros to replace the calls to the warbird encryption functions 189 | // 190 | // Each macro has two layers e.g. WARBIRD_DECRYPT_SEGMENT_INLINE an 191 | // __WARBIRD_DECRYPT_SEGMENT_INLINE. This is to allow the ID to be a #define: 192 | // #define MY_CRYPTO_SEGMENT 10 193 | // WARBIRD_DECRYPT_SEGMENT_INLINE(MY_CRYPTO_SEGMENT) 194 | // 195 | // This will generate a call to function: 196 | // WarbirdDecryptSegment10Inline. 197 | // 198 | // Without the second layer the macro would generate a call to a non-existing 199 | // function because the define is not substituted before building the function 200 | // name: 201 | // WarbirdDecryptSegmentMY_CRYPTO_SEGMENTInline 202 | // 203 | #define __WARBIRD_DECRYPT_SEGMENT_INLINE(ID) WarbirdDecryptSegment##ID##Inline() 204 | #define WARBIRD_DECRYPT_SEGMENT_INLINE(ID) __WARBIRD_DECRYPT_SEGMENT_INLINE(ID) 205 | #define __WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) WarbirdDecryptSegment##ID##NoInline() 206 | #define WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) __WARBIRD_DECRYPT_SEGMENT_NOINLINE(ID) 207 | #define WARBIRD_DECRYPT_SEGMENT(ID) WARBIRD_DECRYPT_SEGMENT_INLINE(ID) 208 | 209 | #define __WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) WarbirdEncryptSegment##ID##Inline() 210 | #define WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) __WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) 211 | #define __WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) WarbirdEncryptSegment##ID##NoInline() 212 | #define WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) __WARBIRD_ENCRYPT_SEGMENT_NOINLINE(ID) 213 | #define WARBIRD_ENCRYPT_SEGMENT(ID) WARBIRD_ENCRYPT_SEGMENT_INLINE(ID) 214 | 215 | // 216 | // Encryption function prototypes 217 | // 218 | #define WARBIRD_SEGMENT_PROTOTYPE(ID) \ 219 | HRESULT __fastcall WarbirdDecryptSegment##ID##Inline(); \ 220 | HRESULT __fastcall WarbirdDecryptSegment##ID##NoInline(); \ 221 | HRESULT __fastcall WarbirdEncryptSegment##ID##Inline(); \ 222 | HRESULT __fastcall WarbirdEncryptSegment##ID##NoInline(); \ 223 | void __fastcall WarbirdVerifySegment##ID##Inline(); \ 224 | void __fastcall WarbirdVerifySegment##ID##NoInline(); \ 225 | 226 | #endif 227 | 228 | /* 229 | #pragma warbird(begin_foreach $(SID) $(EncryptedSegmentIDs)) 230 | WARBIRD_SEGMENT_PROTOTYPE( $(SID) ); 231 | #pragma warbird(end_foreach) 232 | */ 233 | 234 | // 235 | // Creates a 255 encryption/verification function prototypes using the 236 | // encryption/verification prototypes. 237 | // macros. 238 | // 239 | WARBIRD_SEGMENT_PROTOTYPE(0); 240 | WARBIRD_SEGMENT_PROTOTYPE(1); 241 | WARBIRD_SEGMENT_PROTOTYPE(2); 242 | WARBIRD_SEGMENT_PROTOTYPE(3); 243 | WARBIRD_SEGMENT_PROTOTYPE(4); 244 | WARBIRD_SEGMENT_PROTOTYPE(5); 245 | WARBIRD_SEGMENT_PROTOTYPE(6); 246 | WARBIRD_SEGMENT_PROTOTYPE(7); 247 | WARBIRD_SEGMENT_PROTOTYPE(8); 248 | WARBIRD_SEGMENT_PROTOTYPE(9); 249 | WARBIRD_SEGMENT_PROTOTYPE(10); 250 | WARBIRD_SEGMENT_PROTOTYPE(11); 251 | WARBIRD_SEGMENT_PROTOTYPE(12); 252 | WARBIRD_SEGMENT_PROTOTYPE(13); 253 | WARBIRD_SEGMENT_PROTOTYPE(14); 254 | WARBIRD_SEGMENT_PROTOTYPE(15); 255 | WARBIRD_SEGMENT_PROTOTYPE(16); 256 | WARBIRD_SEGMENT_PROTOTYPE(17); 257 | WARBIRD_SEGMENT_PROTOTYPE(18); 258 | WARBIRD_SEGMENT_PROTOTYPE(19); 259 | WARBIRD_SEGMENT_PROTOTYPE(20); 260 | WARBIRD_SEGMENT_PROTOTYPE(21); 261 | WARBIRD_SEGMENT_PROTOTYPE(22); 262 | WARBIRD_SEGMENT_PROTOTYPE(23); 263 | WARBIRD_SEGMENT_PROTOTYPE(24); 264 | WARBIRD_SEGMENT_PROTOTYPE(25); 265 | WARBIRD_SEGMENT_PROTOTYPE(26); 266 | WARBIRD_SEGMENT_PROTOTYPE(27); 267 | WARBIRD_SEGMENT_PROTOTYPE(28); 268 | WARBIRD_SEGMENT_PROTOTYPE(29); 269 | WARBIRD_SEGMENT_PROTOTYPE(30); 270 | WARBIRD_SEGMENT_PROTOTYPE(31); 271 | WARBIRD_SEGMENT_PROTOTYPE(32); 272 | WARBIRD_SEGMENT_PROTOTYPE(33); 273 | WARBIRD_SEGMENT_PROTOTYPE(34); 274 | WARBIRD_SEGMENT_PROTOTYPE(35); 275 | WARBIRD_SEGMENT_PROTOTYPE(36); 276 | WARBIRD_SEGMENT_PROTOTYPE(37); 277 | WARBIRD_SEGMENT_PROTOTYPE(38); 278 | WARBIRD_SEGMENT_PROTOTYPE(39); 279 | WARBIRD_SEGMENT_PROTOTYPE(40); 280 | WARBIRD_SEGMENT_PROTOTYPE(41); 281 | WARBIRD_SEGMENT_PROTOTYPE(42); 282 | WARBIRD_SEGMENT_PROTOTYPE(43); 283 | WARBIRD_SEGMENT_PROTOTYPE(44); 284 | WARBIRD_SEGMENT_PROTOTYPE(45); 285 | WARBIRD_SEGMENT_PROTOTYPE(46); 286 | WARBIRD_SEGMENT_PROTOTYPE(47); 287 | WARBIRD_SEGMENT_PROTOTYPE(48); 288 | WARBIRD_SEGMENT_PROTOTYPE(49); 289 | WARBIRD_SEGMENT_PROTOTYPE(50); 290 | WARBIRD_SEGMENT_PROTOTYPE(51); 291 | WARBIRD_SEGMENT_PROTOTYPE(52); 292 | WARBIRD_SEGMENT_PROTOTYPE(53); 293 | WARBIRD_SEGMENT_PROTOTYPE(54); 294 | WARBIRD_SEGMENT_PROTOTYPE(55); 295 | WARBIRD_SEGMENT_PROTOTYPE(56); 296 | WARBIRD_SEGMENT_PROTOTYPE(57); 297 | WARBIRD_SEGMENT_PROTOTYPE(58); 298 | WARBIRD_SEGMENT_PROTOTYPE(59); 299 | WARBIRD_SEGMENT_PROTOTYPE(60); 300 | WARBIRD_SEGMENT_PROTOTYPE(61); 301 | WARBIRD_SEGMENT_PROTOTYPE(62); 302 | WARBIRD_SEGMENT_PROTOTYPE(63); 303 | WARBIRD_SEGMENT_PROTOTYPE(64); 304 | WARBIRD_SEGMENT_PROTOTYPE(65); 305 | WARBIRD_SEGMENT_PROTOTYPE(66); 306 | WARBIRD_SEGMENT_PROTOTYPE(67); 307 | WARBIRD_SEGMENT_PROTOTYPE(68); 308 | WARBIRD_SEGMENT_PROTOTYPE(69); 309 | WARBIRD_SEGMENT_PROTOTYPE(70); 310 | WARBIRD_SEGMENT_PROTOTYPE(71); 311 | WARBIRD_SEGMENT_PROTOTYPE(72); 312 | WARBIRD_SEGMENT_PROTOTYPE(73); 313 | WARBIRD_SEGMENT_PROTOTYPE(74); 314 | WARBIRD_SEGMENT_PROTOTYPE(75); 315 | WARBIRD_SEGMENT_PROTOTYPE(76); 316 | WARBIRD_SEGMENT_PROTOTYPE(77); 317 | WARBIRD_SEGMENT_PROTOTYPE(78); 318 | WARBIRD_SEGMENT_PROTOTYPE(79); 319 | WARBIRD_SEGMENT_PROTOTYPE(80); 320 | WARBIRD_SEGMENT_PROTOTYPE(81); 321 | WARBIRD_SEGMENT_PROTOTYPE(82); 322 | WARBIRD_SEGMENT_PROTOTYPE(83); 323 | WARBIRD_SEGMENT_PROTOTYPE(84); 324 | WARBIRD_SEGMENT_PROTOTYPE(85); 325 | WARBIRD_SEGMENT_PROTOTYPE(86); 326 | WARBIRD_SEGMENT_PROTOTYPE(87); 327 | WARBIRD_SEGMENT_PROTOTYPE(88); 328 | WARBIRD_SEGMENT_PROTOTYPE(89); 329 | WARBIRD_SEGMENT_PROTOTYPE(90); 330 | WARBIRD_SEGMENT_PROTOTYPE(91); 331 | WARBIRD_SEGMENT_PROTOTYPE(92); 332 | WARBIRD_SEGMENT_PROTOTYPE(93); 333 | WARBIRD_SEGMENT_PROTOTYPE(94); 334 | WARBIRD_SEGMENT_PROTOTYPE(95); 335 | WARBIRD_SEGMENT_PROTOTYPE(96); 336 | WARBIRD_SEGMENT_PROTOTYPE(97); 337 | WARBIRD_SEGMENT_PROTOTYPE(98); 338 | WARBIRD_SEGMENT_PROTOTYPE(99); 339 | WARBIRD_SEGMENT_PROTOTYPE(100); 340 | WARBIRD_SEGMENT_PROTOTYPE(101); 341 | WARBIRD_SEGMENT_PROTOTYPE(102); 342 | WARBIRD_SEGMENT_PROTOTYPE(103); 343 | WARBIRD_SEGMENT_PROTOTYPE(104); 344 | WARBIRD_SEGMENT_PROTOTYPE(105); 345 | WARBIRD_SEGMENT_PROTOTYPE(106); 346 | WARBIRD_SEGMENT_PROTOTYPE(107); 347 | WARBIRD_SEGMENT_PROTOTYPE(108); 348 | WARBIRD_SEGMENT_PROTOTYPE(109); 349 | WARBIRD_SEGMENT_PROTOTYPE(111); 350 | WARBIRD_SEGMENT_PROTOTYPE(112); 351 | WARBIRD_SEGMENT_PROTOTYPE(113); 352 | WARBIRD_SEGMENT_PROTOTYPE(114); 353 | WARBIRD_SEGMENT_PROTOTYPE(115); 354 | WARBIRD_SEGMENT_PROTOTYPE(116); 355 | WARBIRD_SEGMENT_PROTOTYPE(117); 356 | WARBIRD_SEGMENT_PROTOTYPE(118); 357 | WARBIRD_SEGMENT_PROTOTYPE(119); 358 | WARBIRD_SEGMENT_PROTOTYPE(120); 359 | WARBIRD_SEGMENT_PROTOTYPE(121); 360 | WARBIRD_SEGMENT_PROTOTYPE(122); 361 | WARBIRD_SEGMENT_PROTOTYPE(123); 362 | WARBIRD_SEGMENT_PROTOTYPE(124); 363 | WARBIRD_SEGMENT_PROTOTYPE(125); 364 | WARBIRD_SEGMENT_PROTOTYPE(126); 365 | WARBIRD_SEGMENT_PROTOTYPE(127); 366 | WARBIRD_SEGMENT_PROTOTYPE(128); 367 | WARBIRD_SEGMENT_PROTOTYPE(129); 368 | WARBIRD_SEGMENT_PROTOTYPE(130); 369 | WARBIRD_SEGMENT_PROTOTYPE(131); 370 | WARBIRD_SEGMENT_PROTOTYPE(132); 371 | WARBIRD_SEGMENT_PROTOTYPE(133); 372 | WARBIRD_SEGMENT_PROTOTYPE(134); 373 | WARBIRD_SEGMENT_PROTOTYPE(135); 374 | WARBIRD_SEGMENT_PROTOTYPE(136); 375 | WARBIRD_SEGMENT_PROTOTYPE(137); 376 | WARBIRD_SEGMENT_PROTOTYPE(138); 377 | WARBIRD_SEGMENT_PROTOTYPE(139); 378 | WARBIRD_SEGMENT_PROTOTYPE(140); 379 | WARBIRD_SEGMENT_PROTOTYPE(141); 380 | WARBIRD_SEGMENT_PROTOTYPE(142); 381 | WARBIRD_SEGMENT_PROTOTYPE(143); 382 | WARBIRD_SEGMENT_PROTOTYPE(144); 383 | WARBIRD_SEGMENT_PROTOTYPE(145); 384 | WARBIRD_SEGMENT_PROTOTYPE(146); 385 | WARBIRD_SEGMENT_PROTOTYPE(147); 386 | WARBIRD_SEGMENT_PROTOTYPE(148); 387 | WARBIRD_SEGMENT_PROTOTYPE(149); 388 | WARBIRD_SEGMENT_PROTOTYPE(150); 389 | WARBIRD_SEGMENT_PROTOTYPE(151); 390 | WARBIRD_SEGMENT_PROTOTYPE(152); 391 | WARBIRD_SEGMENT_PROTOTYPE(153); 392 | WARBIRD_SEGMENT_PROTOTYPE(154); 393 | WARBIRD_SEGMENT_PROTOTYPE(155); 394 | WARBIRD_SEGMENT_PROTOTYPE(156); 395 | WARBIRD_SEGMENT_PROTOTYPE(157); 396 | WARBIRD_SEGMENT_PROTOTYPE(158); 397 | WARBIRD_SEGMENT_PROTOTYPE(159); 398 | WARBIRD_SEGMENT_PROTOTYPE(160); 399 | WARBIRD_SEGMENT_PROTOTYPE(161); 400 | WARBIRD_SEGMENT_PROTOTYPE(162); 401 | WARBIRD_SEGMENT_PROTOTYPE(163); 402 | WARBIRD_SEGMENT_PROTOTYPE(164); 403 | WARBIRD_SEGMENT_PROTOTYPE(165); 404 | WARBIRD_SEGMENT_PROTOTYPE(166); 405 | WARBIRD_SEGMENT_PROTOTYPE(167); 406 | WARBIRD_SEGMENT_PROTOTYPE(168); 407 | WARBIRD_SEGMENT_PROTOTYPE(169); 408 | WARBIRD_SEGMENT_PROTOTYPE(170); 409 | WARBIRD_SEGMENT_PROTOTYPE(171); 410 | WARBIRD_SEGMENT_PROTOTYPE(172); 411 | WARBIRD_SEGMENT_PROTOTYPE(173); 412 | WARBIRD_SEGMENT_PROTOTYPE(174); 413 | WARBIRD_SEGMENT_PROTOTYPE(175); 414 | WARBIRD_SEGMENT_PROTOTYPE(176); 415 | WARBIRD_SEGMENT_PROTOTYPE(177); 416 | WARBIRD_SEGMENT_PROTOTYPE(178); 417 | WARBIRD_SEGMENT_PROTOTYPE(179); 418 | WARBIRD_SEGMENT_PROTOTYPE(180); 419 | WARBIRD_SEGMENT_PROTOTYPE(181); 420 | WARBIRD_SEGMENT_PROTOTYPE(182); 421 | WARBIRD_SEGMENT_PROTOTYPE(183); 422 | WARBIRD_SEGMENT_PROTOTYPE(184); 423 | WARBIRD_SEGMENT_PROTOTYPE(185); 424 | WARBIRD_SEGMENT_PROTOTYPE(186); 425 | WARBIRD_SEGMENT_PROTOTYPE(187); 426 | WARBIRD_SEGMENT_PROTOTYPE(188); 427 | WARBIRD_SEGMENT_PROTOTYPE(189); 428 | WARBIRD_SEGMENT_PROTOTYPE(190); 429 | WARBIRD_SEGMENT_PROTOTYPE(191); 430 | WARBIRD_SEGMENT_PROTOTYPE(192); 431 | WARBIRD_SEGMENT_PROTOTYPE(193); 432 | WARBIRD_SEGMENT_PROTOTYPE(194); 433 | WARBIRD_SEGMENT_PROTOTYPE(195); 434 | WARBIRD_SEGMENT_PROTOTYPE(196); 435 | WARBIRD_SEGMENT_PROTOTYPE(197); 436 | WARBIRD_SEGMENT_PROTOTYPE(198); 437 | WARBIRD_SEGMENT_PROTOTYPE(199); 438 | WARBIRD_SEGMENT_PROTOTYPE(200); 439 | WARBIRD_SEGMENT_PROTOTYPE(201); 440 | WARBIRD_SEGMENT_PROTOTYPE(202); 441 | WARBIRD_SEGMENT_PROTOTYPE(203); 442 | WARBIRD_SEGMENT_PROTOTYPE(204); 443 | WARBIRD_SEGMENT_PROTOTYPE(205); 444 | WARBIRD_SEGMENT_PROTOTYPE(206); 445 | WARBIRD_SEGMENT_PROTOTYPE(207); 446 | WARBIRD_SEGMENT_PROTOTYPE(208); 447 | WARBIRD_SEGMENT_PROTOTYPE(209); 448 | WARBIRD_SEGMENT_PROTOTYPE(211); 449 | WARBIRD_SEGMENT_PROTOTYPE(212); 450 | WARBIRD_SEGMENT_PROTOTYPE(213); 451 | WARBIRD_SEGMENT_PROTOTYPE(214); 452 | WARBIRD_SEGMENT_PROTOTYPE(215); 453 | WARBIRD_SEGMENT_PROTOTYPE(216); 454 | WARBIRD_SEGMENT_PROTOTYPE(217); 455 | WARBIRD_SEGMENT_PROTOTYPE(218); 456 | WARBIRD_SEGMENT_PROTOTYPE(219); 457 | WARBIRD_SEGMENT_PROTOTYPE(220); 458 | WARBIRD_SEGMENT_PROTOTYPE(221); 459 | WARBIRD_SEGMENT_PROTOTYPE(222); 460 | WARBIRD_SEGMENT_PROTOTYPE(223); 461 | WARBIRD_SEGMENT_PROTOTYPE(224); 462 | WARBIRD_SEGMENT_PROTOTYPE(225); 463 | WARBIRD_SEGMENT_PROTOTYPE(226); 464 | WARBIRD_SEGMENT_PROTOTYPE(227); 465 | WARBIRD_SEGMENT_PROTOTYPE(228); 466 | WARBIRD_SEGMENT_PROTOTYPE(229); 467 | WARBIRD_SEGMENT_PROTOTYPE(230); 468 | WARBIRD_SEGMENT_PROTOTYPE(231); 469 | WARBIRD_SEGMENT_PROTOTYPE(232); 470 | WARBIRD_SEGMENT_PROTOTYPE(233); 471 | WARBIRD_SEGMENT_PROTOTYPE(234); 472 | WARBIRD_SEGMENT_PROTOTYPE(235); 473 | WARBIRD_SEGMENT_PROTOTYPE(236); 474 | WARBIRD_SEGMENT_PROTOTYPE(237); 475 | WARBIRD_SEGMENT_PROTOTYPE(238); 476 | WARBIRD_SEGMENT_PROTOTYPE(239); 477 | WARBIRD_SEGMENT_PROTOTYPE(240); 478 | WARBIRD_SEGMENT_PROTOTYPE(241); 479 | WARBIRD_SEGMENT_PROTOTYPE(242); 480 | WARBIRD_SEGMENT_PROTOTYPE(243); 481 | WARBIRD_SEGMENT_PROTOTYPE(244); 482 | WARBIRD_SEGMENT_PROTOTYPE(245); 483 | WARBIRD_SEGMENT_PROTOTYPE(246); 484 | WARBIRD_SEGMENT_PROTOTYPE(247); 485 | WARBIRD_SEGMENT_PROTOTYPE(248); 486 | WARBIRD_SEGMENT_PROTOTYPE(249); 487 | WARBIRD_SEGMENT_PROTOTYPE(250); 488 | WARBIRD_SEGMENT_PROTOTYPE(251); 489 | WARBIRD_SEGMENT_PROTOTYPE(252); 490 | WARBIRD_SEGMENT_PROTOTYPE(253); 491 | WARBIRD_SEGMENT_PROTOTYPE(254); 492 | WARBIRD_SEGMENT_PROTOTYPE(255); 493 | WARBIRD_SEGMENT_PROTOTYPE(256); 494 | WARBIRD_SEGMENT_PROTOTYPE(257); 495 | WARBIRD_SEGMENT_PROTOTYPE(258); 496 | WARBIRD_SEGMENT_PROTOTYPE(259); 497 | WARBIRD_SEGMENT_PROTOTYPE(260); 498 | WARBIRD_SEGMENT_PROTOTYPE(261); 499 | WARBIRD_SEGMENT_PROTOTYPE(262); 500 | WARBIRD_SEGMENT_PROTOTYPE(263); 501 | WARBIRD_SEGMENT_PROTOTYPE(264); 502 | WARBIRD_SEGMENT_PROTOTYPE(265); 503 | WARBIRD_SEGMENT_PROTOTYPE(266); 504 | WARBIRD_SEGMENT_PROTOTYPE(267); 505 | WARBIRD_SEGMENT_PROTOTYPE(268); 506 | WARBIRD_SEGMENT_PROTOTYPE(269); 507 | WARBIRD_SEGMENT_PROTOTYPE(270); 508 | WARBIRD_SEGMENT_PROTOTYPE(271); 509 | WARBIRD_SEGMENT_PROTOTYPE(272); 510 | WARBIRD_SEGMENT_PROTOTYPE(273); 511 | WARBIRD_SEGMENT_PROTOTYPE(274); 512 | WARBIRD_SEGMENT_PROTOTYPE(275); 513 | WARBIRD_SEGMENT_PROTOTYPE(276); 514 | WARBIRD_SEGMENT_PROTOTYPE(277); 515 | WARBIRD_SEGMENT_PROTOTYPE(178); 516 | WARBIRD_SEGMENT_PROTOTYPE(279); 517 | WARBIRD_SEGMENT_PROTOTYPE(280); 518 | WARBIRD_SEGMENT_PROTOTYPE(281); 519 | WARBIRD_SEGMENT_PROTOTYPE(282); 520 | WARBIRD_SEGMENT_PROTOTYPE(283); 521 | WARBIRD_SEGMENT_PROTOTYPE(284); 522 | WARBIRD_SEGMENT_PROTOTYPE(285); 523 | WARBIRD_SEGMENT_PROTOTYPE(286); 524 | WARBIRD_SEGMENT_PROTOTYPE(287); 525 | WARBIRD_SEGMENT_PROTOTYPE(288); 526 | WARBIRD_SEGMENT_PROTOTYPE(289); 527 | WARBIRD_SEGMENT_PROTOTYPE(290); 528 | WARBIRD_SEGMENT_PROTOTYPE(291); 529 | WARBIRD_SEGMENT_PROTOTYPE(292); 530 | WARBIRD_SEGMENT_PROTOTYPE(293); 531 | WARBIRD_SEGMENT_PROTOTYPE(294); 532 | WARBIRD_SEGMENT_PROTOTYPE(295); 533 | WARBIRD_SEGMENT_PROTOTYPE(296); 534 | WARBIRD_SEGMENT_PROTOTYPE(297); 535 | WARBIRD_SEGMENT_PROTOTYPE(298); 536 | WARBIRD_SEGMENT_PROTOTYPE(299); 537 | WARBIRD_SEGMENT_PROTOTYPE(300); 538 | 539 | #ifdef WARBIRD_TEST 540 | 541 | class CTest 542 | { 543 | public: 544 | CTest(); 545 | 546 | void ReportVerifyFailure(); 547 | 548 | void IncrementVerifyCount(); 549 | 550 | ULONG GetVerifyCount(); 551 | 552 | void ResetVerifyCount(); 553 | 554 | private: 555 | ULONG m_nVerifyCount; 556 | }; 557 | 558 | #endif 559 | 560 | #if defined(__cplusplus) 561 | } 562 | #endif -------------------------------------------------------------------------------- /WarbirdTermination.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Termination 4 | * 5 | **/ 6 | 7 | namespace WarbirdRuntime 8 | { 9 | 10 | class CTermination 11 | { 12 | public: 13 | // If we detect tampering, we want to terminate execution without letting 14 | // the hacker easily understand what he did wrong. To make this happen, 15 | // we clear the stack and the registers in an ASM helper function, and 16 | // jump to a C function that exits the process. 17 | static VOID 18 | __declspec(noinline) TrashStack( 19 | ) 20 | { 21 | TrashStack(TerminationFunction); 22 | } 23 | 24 | #if !defined(WARBIRD_KERNEL_MODE) 25 | 26 | #if defined(_M_AMD64) 27 | 28 | // On architectures with table based exception handling (AMD64 and ARM), 29 | // we don't support exceptions unwinding across a heap executed function 30 | // because we don't register proper unwind information for these functions. 31 | // Any such exception during runtime means a bug in the obfuscated code. 32 | // 33 | // So, if we detect an exception across a heap executed function; 34 | // 35 | // 1) We want to catch the exception and terminate the process, rather than letting 36 | // the unwind logic make random decisions based on the random data on the stack 37 | // (which would be a security risk). 38 | // 39 | // 2) We would like to pass the EXCEPTION_RECORD and CONTEXT structs to Watson, 40 | // so that we get actionable minidumps to fix the bug. 41 | // 42 | // To make this happen, we need to register an exception handler function with the OS 43 | // to call with the EXCEPTION_RECORD and CONTEXT parameters. To register this handler, 44 | // we need to call RtlAddFunctionTable API with a RUNTIME_FUNCTION struct that covers 45 | // the heap execution buffer and points to an UNWIND_INFO struct that points to the 46 | // exception handler. 47 | 48 | class CFunctionTable 49 | { 50 | public: 51 | BOOL Init( 52 | __in PVOID pBegin, 53 | ULONG nSize 54 | ) 55 | { 56 | // First, fill in a RUNTIME_FUNCTION that covers the buffer and points to the UNWIND_INFO. 57 | 58 | FunctionTable[0].BeginAddress = 0; 59 | FunctionTable[0].EndAddress = nSize; 60 | FunctionTable[0].UnwindData = static_cast(CUtil::GetOffset(&UnwindInfo, pBegin)); 61 | 62 | // Then, fill in an UNWIND_INFO that declares an exception handler and has no unwind codes. 63 | 64 | UnwindInfo.Version = 1; 65 | UnwindInfo.Flags = UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER; 66 | UnwindInfo.SizeOfProlog = 0; 67 | UnwindInfo.CountOfCodes = 0; 68 | UnwindInfo.FrameRegister = 0; 69 | UnwindInfo.FrameOffset = 0; 70 | UnwindInfo.RvaExceptionHandler = static_cast(CUtil::GetOffset(&JmpIndirect, pBegin)); 71 | 72 | // Since the RVA fields are 32bit offsets relative to the start of the buffer, 73 | // and since the buffer may be located anywhere in the 64bit memory, the fields 74 | // may not be wide enough to point to the exception handler function in the image. 75 | // So, we need to create a stub function that jumps to the function in the image. 76 | // Assemble "jmp qword ptr [ExceptionHandler]". 77 | 78 | JmpIndirect.Opcode[0] = 0xFF; 79 | JmpIndirect.Opcode[1] = 0x25; 80 | JmpIndirect.Offset = static_cast(CUtil::GetOffset(&pExceptionHandler, &JmpIndirect + 1)); 81 | pExceptionHandler = ExceptionHandler; 82 | 83 | // Flush the CPU cache for generated code (this is a NOP for AMD64, but it's good 84 | // for documenting that we generate code here). 85 | 86 | CUtil::FlushCpuCache(&JmpIndirect, sizeof(JmpIndirect)); 87 | 88 | return RtlAddFunctionTable( 89 | FunctionTable, 90 | 1, 91 | reinterpret_cast(pBegin) 92 | ); 93 | } 94 | 95 | VOID Cleanup( 96 | ) 97 | { 98 | RtlDeleteFunctionTable(FunctionTable); 99 | } 100 | 101 | private: 102 | // Pointer to the exception handler routine in the image. 103 | PVOID pExceptionHandler; 104 | 105 | // Declare a function table with a single RUNTIME_FUNCTION entry. 106 | RUNTIME_FUNCTION FunctionTable[1]; 107 | 108 | // UNWIND_INFO struct has a fixed header, followed by an array of 0 or more 109 | // UNWIND_CODE structs, and (optionally) an RVA of the exception handler. 110 | // Since we won't register any UNWIND_CODE's in our UNWIND_INFO, the following 111 | // accurately represents the memory layout: 112 | struct 113 | { 114 | UCHAR Version : 3; 115 | UCHAR Flags : 5; 116 | UCHAR SizeOfProlog; 117 | UCHAR CountOfCodes; 118 | UCHAR FrameRegister : 4; 119 | UCHAR FrameOffset : 4; 120 | ULONG RvaExceptionHandler; 121 | } 122 | UnwindInfo; 123 | 124 | // jmp qword ptr [ExceptionHandler] 125 | #pragma pack(push, 1) 126 | struct 127 | { 128 | BYTE Opcode[2]; 129 | ULONG Offset; 130 | } 131 | JmpIndirect; 132 | #pragma pack(pop) 133 | }; 134 | 135 | #elif defined(_M_E2) 136 | 137 | class CFunctionTable 138 | { 139 | public: 140 | BOOL Init( 141 | __in PVOID pBegin, 142 | ULONG nSize 143 | ) 144 | { 145 | // First, fill in a RUNTIME_FUNCTION that covers the buffer and points to the UNWIND_INFO. 146 | // Low two bits of UnwindData must be set to zero to indicate that we are using a separate 147 | // UNWIND_INFO struct (as opposed to packing the unwind data in the RUNTIME_FUNCTION). 148 | // This is implicitly achieved; since both the UNWIND_INFO and the buffer are ULONG aligned, 149 | // the low two bits of the RVA is always zero. 150 | 151 | FunctionTable[0].BeginAddress = 0; 152 | FunctionTable[0].UnwindData = ULONG(CUtil::GetOffset(&UnwindInfo, pBegin)); 153 | 154 | // Then, fill in an UNWIND_INFO that declares an exception handler and has no unwind codes. 155 | 156 | UnwindInfo.X = 1; // X=1 indicates presence of exception data 157 | UnwindInfo.CS = 0; // CS=0 indicates original pdata section 158 | UnwindInfo.FunctionLength = nSize / 4; // must be divided by 4 according to the convention 159 | UnwindInfo.FrameSize = 0; // no allocated stack 160 | UnwindInfo.LR = 0; // LR not saved 161 | UnwindInfo.FP = 0; // FP not saved 162 | UnwindInfo.Home = 0; // registers not homed 163 | UnwindInfo.NVReg = 0; // no non-volatiles saved 164 | UnwindInfo.RvaExceptionHandler = ULONG(CUtil::GetOffset(&ExceptionHandler, pBegin)); 165 | 166 | return RtlAddFunctionTable( 167 | FunctionTable, 168 | 1, 169 | reinterpret_cast(pBegin) 170 | ); 171 | } 172 | 173 | VOID Cleanup( 174 | ) 175 | { 176 | RtlDeleteFunctionTable(FunctionTable); 177 | } 178 | 179 | private: 180 | // Declare a function table with a single RUNTIME_FUNCTION entry. 181 | RUNTIME_FUNCTION FunctionTable[1]; 182 | 183 | // UNWIND_INFO struct that registers 1 unwind code word and an exception handler. 184 | struct 185 | { 186 | ULONG X: 1; 187 | ULONG CS: 2; 188 | ULONG FunctionLength: 11; 189 | ULONG FrameSize: 10; 190 | ULONG LR: 1; 191 | ULONG FP: 1; 192 | ULONG Home: 1; 193 | ULONG NVReg: 5; 194 | ULONG RvaExceptionHandler; 195 | } 196 | UnwindInfo; 197 | }; 198 | 199 | #elif defined(_M_ARM64) 200 | 201 | class CFunctionTable 202 | { 203 | public: 204 | BOOL Init( 205 | __in PVOID pBegin, 206 | ULONG nSize 207 | ) 208 | { 209 | // First, fill in a RUNTIME_FUNCTION that covers the buffer and points to the UNWIND_INFO. 210 | // Low two bits of UnwindData must be set to zero to indicate that we are using a separate 211 | // UNWIND_INFO struct (as opposed to packing the unwind data in the RUNTIME_FUNCTION). 212 | // This is implicitly achieved; since both the UNWIND_INFO and the buffer are ULONG aligned, 213 | // the low two bits of the RVA is always zero. 214 | 215 | FunctionTable[0].BeginAddress = 0; 216 | FunctionTable[0].UnwindData = ULONG(CUtil::GetOffset(&UnwindInfo, pBegin)); 217 | 218 | // Then, fill in an UNWIND_INFO that declares an exception handler and has no unwind codes. 219 | 220 | UnwindInfo.FunctionLength = nSize / 4; // must be divided by 4 according to the convention 221 | UnwindInfo.Version = 0; // version is currently defined as 0 222 | UnwindInfo.X = 1; // X=1 indicates presence of exception data 223 | UnwindInfo.E = 0; // E=0 indicates we need a scope word 224 | UnwindInfo.EpilogCount = 0; // No exception scopes 225 | UnwindInfo.CodeWords = 1; // A single 32bit word to contain all unwind codes 226 | UnwindInfo.UnwindCode[0] = 0xE4; // We need just one unwind code: 0xe4 (end) 227 | UnwindInfo.UnwindCode[1] = 0; 228 | UnwindInfo.UnwindCode[2] = 0; 229 | UnwindInfo.UnwindCode[3] = 0; 230 | UnwindInfo.RvaExceptionHandler = ULONG(CUtil::GetOffset(&ExceptionHandler, pBegin)); 231 | 232 | return RtlAddFunctionTable( 233 | FunctionTable, 234 | 1, 235 | reinterpret_cast(pBegin) 236 | ); 237 | } 238 | 239 | VOID Cleanup( 240 | ) 241 | { 242 | RtlDeleteFunctionTable(FunctionTable); 243 | } 244 | 245 | private: 246 | // Declare a function table with a single RUNTIME_FUNCTION entry. 247 | RUNTIME_FUNCTION FunctionTable[1]; 248 | 249 | // UNWIND_INFO struct that registers 1 unwind code word and an exception handler. 250 | struct 251 | { 252 | ULONG FunctionLength: 18; 253 | ULONG Version: 2; 254 | ULONG X: 1; 255 | ULONG E: 1; 256 | ULONG EpilogCount: 5; 257 | ULONG CodeWords: 5; 258 | BYTE UnwindCode[4]; 259 | ULONG RvaExceptionHandler; 260 | } 261 | UnwindInfo; 262 | }; 263 | 264 | #elif defined(_M_ARM) 265 | 266 | class CFunctionTable 267 | { 268 | public: 269 | BOOL Init( 270 | __in PVOID pBegin, 271 | ULONG nSize 272 | ) 273 | { 274 | // First, fill in a RUNTIME_FUNCTION that covers the buffer and points to the UNWIND_INFO. 275 | // The low bit of BeginAddress must be set to 1 to indicate thumb mode. 276 | // Low two bits of UnwindData must be set to zero to indicate that we are using a separate 277 | // UNWIND_INFO struct (as opposed to packing the unwind data in the RUNTIME_FUNCTION). 278 | // This is implicitly achieved; since both the UNWIND_INFO and the buffer are ULONG aligned, 279 | // the low two bits of the RVA is always zero. 280 | 281 | FunctionTable[0].BeginAddress = 1; 282 | FunctionTable[0].UnwindData = CUtil::GetOffset(&UnwindInfo, pBegin); 283 | 284 | // Then, fill in an UNWIND_INFO that declares an exception handler and has no unwind codes. 285 | 286 | UnwindInfo.FunctionLength = nSize / 2; // must be divided by 2 according to the convention 287 | UnwindInfo.Version = 0; // version is currently defined as 0 288 | UnwindInfo.X = 1; // X=1 indicates presence of exception data 289 | UnwindInfo.E = 0; // E=0 indicates we need a scope word 290 | UnwindInfo.F = 1; // F=1 means this is a function fragment (i.e. no prolog/epilog) 291 | UnwindInfo.EpilogCount = 0; // No exception scopes 292 | UnwindInfo.CodeWords = 1; // A single 32bit word to contain all unwind codes 293 | UnwindInfo.UnwindCode[0] = 0xFF; // We need just one unwind code: 0xff (end) 294 | UnwindInfo.UnwindCode[1] = 0; 295 | UnwindInfo.UnwindCode[2] = 0; 296 | UnwindInfo.UnwindCode[3] = 0; 297 | UnwindInfo.RvaExceptionHandler = CUtil::GetOffset(&ExceptionHandler, pBegin) | 1; 298 | 299 | return RtlAddFunctionTable( 300 | FunctionTable, 301 | 1, 302 | reinterpret_cast(pBegin) 303 | ); 304 | } 305 | 306 | VOID Cleanup( 307 | ) 308 | { 309 | RtlDeleteFunctionTable(FunctionTable); 310 | } 311 | 312 | private: 313 | // Declare a function table with a single RUNTIME_FUNCTION entry. 314 | RUNTIME_FUNCTION FunctionTable[1]; 315 | 316 | // UNWIND_INFO struct that registers 1 unwind code word and an exception handler. 317 | struct 318 | { 319 | ULONG FunctionLength: 18; 320 | ULONG Version: 2; 321 | ULONG X: 1; 322 | ULONG E: 1; 323 | ULONG F: 1; 324 | ULONG EpilogCount: 5; 325 | ULONG CodeWords: 4; 326 | BYTE UnwindCode[4]; 327 | ULONG RvaExceptionHandler; 328 | } 329 | UnwindInfo; 330 | }; 331 | 332 | #elif defined(_M_IX86) 333 | 334 | // On X86, we let the exceptions unwind across heap executed functions. 335 | // We leak the heap execution buffer, but at least there's no undefined behavior 336 | // during unwind (as opposed to architectures that do table based exception handling). 337 | 338 | class CFunctionTable 339 | { 340 | public: 341 | BOOL Init( 342 | __in PVOID pBegin, 343 | ULONG nSize 344 | ) 345 | { 346 | UNREFERENCED_PARAMETER(pBegin); 347 | UNREFERENCED_PARAMETER(nSize); 348 | 349 | return TRUE; 350 | } 351 | 352 | VOID Cleanup( 353 | ) 354 | { 355 | } 356 | }; 357 | 358 | #endif 359 | 360 | #endif // !defined(WARBIRD_KERNEL_MODE) 361 | 362 | private: 363 | static DECLSPEC_NORETURN VOID 364 | Abort( 365 | __in EXCEPTION_RECORD* pExceptionRecord, 366 | __in CONTEXT* pContextRecord 367 | ) 368 | { 369 | #if defined(WARBIRD_KERNEL_MODE) 370 | 371 | UNREFERENCED_PARAMETER(pContextRecord); 372 | 373 | KeBugCheckEx( 374 | KERNEL_MODE_EXCEPTION_NOT_HANDLED, 375 | pExceptionRecord == NULL ? STATUS_FATAL_APP_EXIT : pExceptionRecord->ExceptionCode, 376 | reinterpret_cast(pExceptionRecord == NULL ? _ReturnAddress() : pExceptionRecord->ExceptionAddress), 377 | 0, 378 | 0 379 | ); 380 | 381 | #elif (WINVER >= _WIN32_WINNT_WIN7) 382 | 383 | // On Win7 and above, use RaiseFailFastException to bring up Watson and terminate. 384 | RaiseFailFastException( 385 | pExceptionRecord, 386 | pContextRecord, 387 | 0 388 | ); 389 | 390 | #else 391 | 392 | EXCEPTION_RECORD ExceptionRecord; 393 | 394 | if (pExceptionRecord == NULL) 395 | { 396 | // If no EXCEPTION_RECORD is passed in, construct one. 397 | pExceptionRecord = &ExceptionRecord; 398 | ZeroMemory(pExceptionRecord, sizeof(EXCEPTION_RECORD)); 399 | pExceptionRecord->ExceptionCode = STATUS_FATAL_APP_EXIT; 400 | pExceptionRecord->ExceptionFlags = EXCEPTION_NONCONTINUABLE; 401 | pExceptionRecord->ExceptionAddress = _ReturnAddress(); 402 | } 403 | else 404 | { 405 | // Otherwise, reassure that the exception is not continuable. 406 | pExceptionRecord->ExceptionFlags |= EXCEPTION_NONCONTINUABLE; 407 | } 408 | 409 | CONTEXT ContextRecord; 410 | 411 | if (pContextRecord == NULL) 412 | { 413 | // If no CONTEXT is passed in, construct one. 414 | pContextRecord = &ContextRecord; 415 | ZeroMemory(pContextRecord, sizeof(CONTEXT)); 416 | RtlCaptureContext(pContextRecord); 417 | } 418 | 419 | // Fill the exception pointers for UnhandledExceptionFilter. 420 | EXCEPTION_POINTERS ExceptionPointers; 421 | ExceptionPointers.ExceptionRecord = pExceptionRecord; 422 | ExceptionPointers.ContextRecord = pContextRecord; 423 | 424 | // Make sure any filter already in place is deleted. 425 | SetUnhandledExceptionFilter(NULL); 426 | 427 | // Invoke Watson or JIT debugger if configured. 428 | UnhandledExceptionFilter(&ExceptionPointers); 429 | 430 | // UnhandledExceptionFilter will return if it detects that a debugger is 431 | // connected. In this case, terminate the process. 432 | TerminateProcess(GetCurrentProcess(), pExceptionRecord->ExceptionCode); 433 | 434 | #endif 435 | } 436 | 437 | // ExceptionHandler is called when we detect an exception unwinding across 438 | // a heap executed function. This means a bug in the obfuscated code, so 439 | // we want as much information as possible in the Watson report, so we call 440 | // Abort with proper EXCEPTION_RECORD and CONTEXT structs in this case. 441 | static EXCEPTION_DISPOSITION __cdecl 442 | ExceptionHandler( 443 | __in EXCEPTION_RECORD* pExceptionRecord, 444 | __in VOID* pEstablisherFrame, 445 | __inout CONTEXT* pContextRecord, 446 | __inout VOID* pDispatcherContext 447 | ) 448 | { 449 | UNREFERENCED_PARAMETER(pEstablisherFrame); 450 | UNREFERENCED_PARAMETER(pDispatcherContext); 451 | 452 | Abort(pExceptionRecord, pContextRecord); 453 | } 454 | 455 | // TerminationFunction is called when we detect tamper. In this case, 456 | // we want to leave the attacker as little information as possible when 457 | // we exit the process, so we call Abort with no info. 458 | static INT_PTR WINAPI 459 | TerminationFunction( 460 | ) 461 | { 462 | Abort(NULL, NULL); 463 | } 464 | 465 | // Clears the stack and registers, and jumps to termination function. 466 | // Since this is hard to do with C code, the function is implemented in 467 | // architecture dependent ASM files. 468 | static VOID __fastcall 469 | TrashStack( 470 | FARPROC pTerminationFunction 471 | ); 472 | 473 | }; //class CTermination 474 | 475 | }; // namespace WarbirdRuntime -------------------------------------------------------------------------------- /WarbirdReloc.inl: -------------------------------------------------------------------------------- 1 | namespace WarbirdRuntime 2 | { 3 | 4 | // Image base defined in delayimp.h 5 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 6 | 7 | /*++ 8 | 9 | Description: 10 | 11 | Represents a relocation item in the private format used in this 12 | program. 13 | --*/ 14 | #include 15 | struct PRIVATE_RELOCATION_ITEM 16 | { 17 | ULONG RVA: RVA_BIT_COUNT; 18 | ULONG RelocationType: 4; 19 | }; 20 | #include 21 | 22 | volatile ULONG g_PrivateRelocationsTable = 0x12456908; 23 | volatile ULONG g_PrivateRelocationsTableCount = 0x12456908; 24 | 25 | /*++ 26 | 27 | Description: 28 | 29 | Represents relocations stored in the binary in the private format used 30 | in this program. 31 | 32 | --*/ 33 | #include 34 | class CPrivateRelocationsTable 35 | { 36 | public: 37 | CPrivateRelocationsTable() 38 | { 39 | m_pItems = NULL; 40 | m_nNumItems = 0; 41 | m_nFirstIndex = 0; 42 | } 43 | 44 | VOID 45 | Init( 46 | __in ULONG nRVA, 47 | __in ULONG nSize 48 | ) 49 | { 50 | m_pItems = (PRIVATE_RELOCATION_ITEM*)((ULONG_PTR)(g_PrivateRelocationsTable) + CUtil::GetImageBase()); 51 | m_nNumItems = g_PrivateRelocationsTableCount; 52 | m_nFirstIndex = static_cast (FindFirstReloc(nRVA)); 53 | m_nEndRva = nRVA + nSize; 54 | m_nCurrentIndex = m_nFirstIndex; 55 | } 56 | 57 | bool 58 | GetNextReloc( 59 | __out PRIVATE_RELOCATION_ITEM *nReloc 60 | ) 61 | { 62 | if (m_nCurrentIndex < m_nNumItems && 63 | m_pItems[m_nCurrentIndex].RVA < m_nEndRva) 64 | { 65 | *nReloc = m_pItems[m_nCurrentIndex]; 66 | 67 | m_nCurrentIndex++; 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | SIZE_T 74 | FindFirstReloc( 75 | __in ULONG BeginRVA 76 | ) 77 | { 78 | int nLow = 0; 79 | int nHigh = m_nNumItems - 1; 80 | 81 | while (nHigh >= nLow) 82 | { 83 | int nMiddle = (nLow + nHigh) / 2; 84 | 85 | if (BeginRVA < m_pItems[nMiddle].RVA) 86 | { 87 | nHigh = nMiddle - 1; 88 | } 89 | else if (BeginRVA > m_pItems[nMiddle].RVA) 90 | { 91 | nLow = nMiddle + 1; 92 | } 93 | else 94 | { 95 | return nMiddle; 96 | } 97 | } 98 | 99 | return nLow; 100 | } 101 | 102 | SIZE_T 103 | ApplyRelocation( 104 | __in CONST VOID* pInBuffer, 105 | USHORT nRelocationType, 106 | UINT_PTR nDelta, 107 | __out VOID* pOutBuffer 108 | ) const 109 | { 110 | SIZE_T nRelocationTypeSize = 0; 111 | 112 | switch (nRelocationType) 113 | { 114 | case IMAGE_REL_BASED_HIGHLOW: 115 | { 116 | unsigned __int32 UNALIGNED* pAddress = (unsigned __int32 UNALIGNED*)pOutBuffer; 117 | WARBIRD_ASSERT(nDelta == static_cast<__int32>(nDelta)); 118 | *pAddress = *(__int32 UNALIGNED*)pInBuffer + static_cast<__int32>(nDelta); 119 | nRelocationTypeSize = sizeof(__int32); 120 | break; 121 | } 122 | 123 | #ifdef _WIN64 124 | 125 | case IMAGE_REL_BASED_DIR64: 126 | { 127 | unsigned __int64 UNALIGNED* pAddress = (unsigned __int64 UNALIGNED*)pOutBuffer; 128 | *pAddress = *(__int64 UNALIGNED*)pInBuffer + nDelta; 129 | nRelocationTypeSize = sizeof(__int64); 130 | break; 131 | } 132 | 133 | #endif //_WIN64 134 | 135 | #ifdef _ARM_ 136 | case IMAGE_REL_BASED_THUMB_MOV32: 137 | { 138 | // Still need to figure out how to do this for ARM with an inout 139 | // buffer 140 | ULONG nAddress; 141 | *(PUINT64)pOutBuffer = *(PUINT64)pInBuffer; 142 | nAddress = ThumbExtractImmediate16((PUSHORT)pOutBuffer + 0) | 143 | (ThumbExtractImmediate16((PUSHORT)pOutBuffer + 2) << 16); 144 | nAddress += (ULONG)nDelta; 145 | 146 | ThumbInsertImmediate16((PUSHORT)pOutBuffer + 0, (USHORT)nAddress); 147 | ThumbInsertImmediate16((PUSHORT)pOutBuffer + 2, nAddress >> 16); 148 | nRelocationTypeSize = sizeof(__int64); 149 | break; 150 | } 151 | #endif //_ARM_ 152 | 153 | case IMAGE_REL_BASED_ABSOLUTE: 154 | // 155 | // Padding relocation, do nothing 156 | // 157 | break; 158 | 159 | default: 160 | // Unknown Relocation type 161 | DebugPrint("Unknown relocation type %d\n", nRelocationType); 162 | WARBIRD_ASSERT(false); 163 | nRelocationTypeSize = SIZE_T(-1); 164 | break; 165 | } 166 | 167 | return nRelocationTypeSize; 168 | } 169 | 170 | USHORT 171 | ThumbExtractImmediate16( 172 | __in_ecount(2) PUSHORT OpcodePtr 173 | ) const 174 | { 175 | return ((OpcodePtr[0] << 12) & 0xf000) | // bits[15:12] in OP0[3:0] 176 | ((OpcodePtr[0] << 1) & 0x0800) | // bits[11] in OP0[10] 177 | ((OpcodePtr[1] >> 4) & 0x0700) | // bits[10:8] in OP1[14:12] 178 | ((OpcodePtr[1] >> 0) & 0x00ff); // bits[7:0] in OP1[7:0] 179 | } 180 | 181 | VOID 182 | ThumbInsertImmediate16( 183 | __inout_ecount(2) PUSHORT OpcodePtr, 184 | __in USHORT Immediate 185 | ) const 186 | { 187 | USHORT Opcode0; 188 | USHORT Opcode1; 189 | 190 | Opcode0 = OpcodePtr[0]; 191 | Opcode1 = OpcodePtr[1]; 192 | Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1)); 193 | Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0)); 194 | Opcode0 |= (Immediate & 0xf000) >> 12; // bits[15:12] in OP0[3:0] 195 | Opcode0 |= (Immediate & 0x0800) >> 1; // bits[11] in OP0[10] 196 | Opcode1 |= (Immediate & 0x0700) << 4; // bits[10:8] in OP1[14:12] 197 | Opcode1 |= (Immediate & 0x00ff) << 0; // bits[7:0] in OP1[7:0] 198 | OpcodePtr[0] = Opcode0; 199 | OpcodePtr[1] = Opcode1; 200 | } 201 | 202 | private: 203 | 204 | // Number of relocations in the private relocation table 205 | ULONG m_nNumItems; 206 | 207 | // Pointer to the list of all relocations in the table 208 | PRIVATE_RELOCATION_ITEM* m_pItems; 209 | 210 | // Index of the first relocation for a given RVA 211 | ULONG m_nFirstIndex; 212 | 213 | // Ending RVA 214 | ULONG m_nEndRva; 215 | 216 | // Current index in to relocation table 217 | ULONG m_nCurrentIndex; 218 | }; // class CPrivateRelocationsTable 219 | 220 | #include 221 | 222 | #if defined(WARBIRD_VSM_TEST) 223 | 224 | // 225 | // This code is used by the user mode VSM test to handle 226 | // relcoations. We cannot use the private relocation table 227 | // in this case because the compiler is outlining some code 228 | // when using the flag d2dbstressoutline which is not encrypted 229 | // and hence relocations are not part of the private relocation 230 | // table but only part of the OS relocation table. 231 | // 232 | 233 | #include 234 | struct OS_RELOCATION_ITEM 235 | { 236 | USHORT Offset:12; 237 | USHORT Type:4; 238 | }; 239 | #include 240 | 241 | /*++ 242 | 243 | Description: 244 | 245 | Represents a relocation block in the format used by the OS loader. 246 | The block stores all relocations in a 4K page. 247 | 248 | --*/ 249 | #include 250 | class COSRelocationBlock 251 | { 252 | public: 253 | // Returns the virtual address of the base of the relocations block. 254 | ULONG RVA( 255 | ) const 256 | { 257 | return m_RVA; 258 | } 259 | 260 | // Returns the number of relocation items in the relocations block. 261 | SIZE_T NumItems( 262 | ) const 263 | { 264 | return (m_SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(OS_RELOCATION_ITEM); 265 | } 266 | 267 | // Retrieves the specified relocation item. 268 | CONST OS_RELOCATION_ITEM& operator []( 269 | SIZE_T n 270 | ) const 271 | { 272 | WARBIRD_ASSERT(n < NumItems()); 273 | 274 | return m_Items[n]; 275 | } 276 | 277 | // Returns a pointer to the next relocation block in the .reloc section. 278 | COSRelocationBlock UNALIGNED* Next( 279 | ) 280 | { 281 | return CUtil::AddOffset(this, m_SizeOfBlock); 282 | } 283 | 284 | private: 285 | ULONG m_RVA; 286 | ULONG m_SizeOfBlock; 287 | OS_RELOCATION_ITEM m_Items[1]; 288 | }; 289 | #include 290 | 291 | class CRelocations 292 | { 293 | public: 294 | CRelocations( 295 | ) : 296 | m_nEndBase(0), 297 | m_nEndOffset(0), 298 | m_pOSRelocationBlockEnd(NULL), 299 | m_pOSRelocationBlock(NULL), 300 | m_nOSRelocationItem(0) 301 | { 302 | } 303 | 304 | /*++ 305 | 306 | Description: 307 | 308 | Initializes the enumeration object. 309 | 310 | Arguments: 311 | 312 | BeginRVA 313 | Beginning address of the region we will enumerate relocations for. 314 | 315 | nSize 316 | Size of the region we will enumerate relocations for. 317 | 318 | Return Value: 319 | 320 | None. 321 | 322 | --*/ 323 | VOID 324 | Init( 325 | ULONG BeginRVA, 326 | SIZE_T nSize 327 | ) 328 | { 329 | IMAGE_NT_HEADERS* pImageNtHeaders = (IMAGE_NT_HEADERS*)CUtil::GetImageNtHeaders(CUtil::GetImageBase()); 330 | IMAGE_DATA_DIRECTORY* pRelocationDirectory = 331 | &pImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 332 | 333 | VOID* pRelocationTable = CUtil::AddOffset((void*)CUtil::GetImageBase(), pRelocationDirectory->VirtualAddress); 334 | 335 | // Calculate the limits of the region we are interested in. 336 | 337 | SIZE_T nBeginBase = BeginRVA & ~(OS_RELOCATION_PAGE_SIZE - 1); 338 | SIZE_T nBeginOffset = BeginRVA - nBeginBase; 339 | 340 | SIZE_T EndRVA = BeginRVA + nSize; 341 | m_nEndBase = EndRVA & ~(OS_RELOCATION_PAGE_SIZE - 1); 342 | m_nEndOffset = EndRVA - m_nEndBase; 343 | 344 | DebugPrint("rrinit start %x\n", nBeginBase); 345 | DebugPrint("rrinit end %x\n", m_nEndBase); 346 | 347 | m_pOSRelocationBlock = reinterpret_cast(pRelocationTable); 348 | m_pOSRelocationBlockEnd = CUtil::AddOffset(m_pOSRelocationBlock, pRelocationDirectory->Size); 349 | 350 | // Skip the relocation blocks until we go past the last block or the block 351 | // that contains the beginning of the address range. 352 | 353 | while (m_pOSRelocationBlock != m_pOSRelocationBlockEnd && 354 | m_pOSRelocationBlock->RVA() < nBeginBase) 355 | { 356 | m_pOSRelocationBlock = m_pOSRelocationBlock->Next(); 357 | } 358 | 359 | m_nOSRelocationItem = 0; 360 | 361 | // If we are on the block where the address range begins, skip relocations 362 | // until we go past the first relocation in the address range. 363 | 364 | if (m_pOSRelocationBlock != m_pOSRelocationBlockEnd && 365 | m_pOSRelocationBlock->RVA() == nBeginBase) 366 | { 367 | while (m_nOSRelocationItem != m_pOSRelocationBlock->NumItems() && 368 | (*m_pOSRelocationBlock)[m_nOSRelocationItem].Offset < nBeginOffset) 369 | { 370 | ++m_nOSRelocationItem; 371 | } 372 | } 373 | } 374 | 375 | /*++ 376 | 377 | Description: 378 | 379 | Finds the next relocation in the .reloc section corresponding to 380 | the specified address range. 381 | GetNext() calls GetNextAll() to get the next relocation. If it's 382 | type is IMAGE_REL_BASED_ABSOLUTE then it's just padding, so it 383 | skips it and tries to find the next one. 384 | 385 | Arguments: 386 | 387 | pnOSRelocationItem 388 | Pointer to the location that will receive the index of the next 389 | relocation item. 390 | 391 | ppOSRelocationBlock 392 | Pointer to the location that will receive a pointer to the 393 | relocation block the item is located in. 394 | 395 | Return Value: 396 | 397 | true if we found another relocation in the address range, false 398 | otherwise. 399 | 400 | --*/ 401 | bool 402 | GetNext( 403 | __out SIZE_T* pnOSRelocationItem, 404 | __out COSRelocationBlock UNALIGNED** ppOSRelocationBlock 405 | ) 406 | { 407 | bool fRet = false; 408 | COSRelocationBlock UNALIGNED* pOSRelocationBlock = NULL; 409 | SIZE_T nOSRelocationItem = 0; 410 | 411 | do 412 | { 413 | fRet = GetNextAll( 414 | &nOSRelocationItem, 415 | &pOSRelocationBlock 416 | ); 417 | if (fRet) 418 | { 419 | //found a valid relocation item 420 | if ((*pOSRelocationBlock)[nOSRelocationItem].Type != IMAGE_REL_BASED_ABSOLUTE) 421 | { 422 | *pnOSRelocationItem = nOSRelocationItem; 423 | *ppOSRelocationBlock = pOSRelocationBlock; 424 | break; 425 | } 426 | //else - this is just padding, skip it - try the next one 427 | } 428 | } 429 | while(fRet); 430 | 431 | return fRet; 432 | } 433 | 434 | SIZE_T 435 | ApplyRelocation( 436 | __in CONST VOID* pInBuffer, 437 | USHORT nRelocationType, 438 | UINT_PTR nDelta, 439 | __out VOID* pOutBuffer 440 | ) const 441 | { 442 | SIZE_T nRelocationTypeSize = 0; 443 | 444 | switch (nRelocationType) 445 | { 446 | case IMAGE_REL_BASED_HIGHLOW: 447 | { 448 | unsigned __int32 UNALIGNED* pAddress = (unsigned __int32 UNALIGNED*)pOutBuffer; 449 | WARBIRD_ASSERT(nDelta == static_cast<__int32>(nDelta)); 450 | *pAddress = *(__int32 UNALIGNED*)pInBuffer + static_cast<__int32>(nDelta); 451 | nRelocationTypeSize = sizeof(__int32); 452 | break; 453 | } 454 | 455 | #ifdef _WIN64 456 | 457 | case IMAGE_REL_BASED_DIR64: 458 | { 459 | unsigned __int64 UNALIGNED* pAddress = (unsigned __int64 UNALIGNED*)pOutBuffer; 460 | *pAddress = *(__int64 UNALIGNED*)pInBuffer + nDelta; 461 | nRelocationTypeSize = sizeof(__int64); 462 | break; 463 | } 464 | 465 | #endif //_WIN64 466 | 467 | #ifdef _ARM_ 468 | case IMAGE_REL_BASED_THUMB_MOV32: 469 | { 470 | // Still need to figure out how to do this for ARM with an inout 471 | // buffer 472 | ULONG nAddress; 473 | *(PUINT64)pOutBuffer = *(PUINT64)pInBuffer; 474 | nAddress = ThumbExtractImmediate16((PUSHORT)pOutBuffer + 0) | 475 | (ThumbExtractImmediate16((PUSHORT)pOutBuffer + 2) << 16); 476 | nAddress += (ULONG)nDelta; 477 | 478 | ThumbInsertImmediate16((PUSHORT)pOutBuffer + 0, (USHORT)nAddress); 479 | ThumbInsertImmediate16((PUSHORT)pOutBuffer + 2, nAddress >> 16); 480 | nRelocationTypeSize = sizeof(__int64); 481 | break; 482 | } 483 | #endif //_ARM_ 484 | 485 | case IMAGE_REL_BASED_ABSOLUTE: 486 | // 487 | // Padding relocation, do nothing 488 | // 489 | break; 490 | 491 | default: 492 | // Unknown Relocation type 493 | DebugPrint("Unknown relocation type %d\n", nRelocationType); 494 | WARBIRD_ASSERT(false); 495 | nRelocationTypeSize = SIZE_T(-1); 496 | break; 497 | } 498 | 499 | return nRelocationTypeSize; 500 | } 501 | 502 | /*++ 503 | 504 | Description: 505 | 506 | Finds the next relocation in the .reloc section corresponding to 507 | the specified address range. 508 | 509 | Arguments: 510 | 511 | pnOSRelocationItem 512 | Pointer to the location that will receive the index of the next 513 | relocation item. 514 | 515 | ppOSRelocationBlock 516 | Pointer to the location that will receive a pointer to the 517 | relocation block the item is located in. 518 | 519 | Return Value: 520 | 521 | true if we found another relocation in the address range, false 522 | otherwise. 523 | 524 | --*/ 525 | bool 526 | GetNextAll( 527 | __out SIZE_T* pnOSRelocationItem, 528 | __out COSRelocationBlock UNALIGNED** ppOSRelocationBlock 529 | ) 530 | { 531 | *pnOSRelocationItem = 0; 532 | *ppOSRelocationBlock = NULL; 533 | 534 | // If we are past the last block or didn't find any blocks to begin 535 | // with, then exit. 536 | 537 | if (m_pOSRelocationBlock == m_pOSRelocationBlockEnd || 538 | m_pOSRelocationBlock->RVA() > m_nEndBase) 539 | { 540 | return false; 541 | } 542 | 543 | // While there are no relocations left on this block, proceed to the next. 544 | 545 | while (m_pOSRelocationBlock != m_pOSRelocationBlockEnd && 546 | m_nOSRelocationItem == m_pOSRelocationBlock->NumItems()) 547 | { 548 | m_pOSRelocationBlock = m_pOSRelocationBlock->Next(); 549 | 550 | // If we went past the last block or past the block that contains 551 | // the end of the address range, then exit. 552 | 553 | if (m_pOSRelocationBlock == m_pOSRelocationBlockEnd || 554 | m_pOSRelocationBlock->RVA() > m_nEndBase) 555 | { 556 | return false; 557 | } 558 | 559 | m_nOSRelocationItem = 0; 560 | } 561 | 562 | // If we are on the block that contains the end of the address range 563 | // and went past the end, then exit. 564 | 565 | if (m_pOSRelocationBlock->RVA() == m_nEndBase && 566 | (*m_pOSRelocationBlock)[m_nOSRelocationItem].Offset >= m_nEndOffset) 567 | { 568 | return false; 569 | } 570 | 571 | // Return this relocation and proceed to the next. 572 | 573 | *pnOSRelocationItem = m_nOSRelocationItem; 574 | *ppOSRelocationBlock = m_pOSRelocationBlock; 575 | 576 | ++m_nOSRelocationItem; 577 | 578 | return true; 579 | } 580 | 581 | USHORT 582 | ThumbExtractImmediate16( 583 | __in_ecount(2) PUSHORT OpcodePtr 584 | ) const 585 | { 586 | return ((OpcodePtr[0] << 12) & 0xf000) | // bits[15:12] in OP0[3:0] 587 | ((OpcodePtr[0] << 1) & 0x0800) | // bits[11] in OP0[10] 588 | ((OpcodePtr[1] >> 4) & 0x0700) | // bits[10:8] in OP1[14:12] 589 | ((OpcodePtr[1] >> 0) & 0x00ff); // bits[7:0] in OP1[7:0] 590 | } 591 | 592 | VOID 593 | ThumbInsertImmediate16( 594 | __inout_ecount(2) PUSHORT OpcodePtr, 595 | __in USHORT Immediate 596 | ) const 597 | { 598 | USHORT Opcode0; 599 | USHORT Opcode1; 600 | 601 | Opcode0 = OpcodePtr[0]; 602 | Opcode1 = OpcodePtr[1]; 603 | Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1)); 604 | Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0)); 605 | Opcode0 |= (Immediate & 0xf000) >> 12; // bits[15:12] in OP0[3:0] 606 | Opcode0 |= (Immediate & 0x0800) >> 1; // bits[11] in OP0[10] 607 | Opcode1 |= (Immediate & 0x0700) << 4; // bits[10:8] in OP1[14:12] 608 | Opcode1 |= (Immediate & 0x00ff) << 0; // bits[7:0] in OP1[7:0] 609 | OpcodePtr[0] = Opcode0; 610 | OpcodePtr[1] = Opcode1; 611 | } 612 | 613 | private: 614 | // Relocation block page size as defined by the OS loader. Note that the 615 | // size is currently the same for 32bit and 64bit images. 616 | static CONST SIZE_T OS_RELOCATION_PAGE_SIZE = 4096; 617 | 618 | // Base of the page that contains the end of the address range to 619 | // enumerate relocations in. 620 | SIZE_T m_nEndBase; 621 | 622 | // Offset within the page that contains the end of the address range to 623 | // enumerate relocations in. 624 | SIZE_T m_nEndOffset; 625 | 626 | // End of the .reloc section. 627 | COSRelocationBlock UNALIGNED* m_pOSRelocationBlockEnd; 628 | 629 | // Currently enumerated relocations block. 630 | COSRelocationBlock UNALIGNED* m_pOSRelocationBlock; 631 | 632 | // Currently enumerated relocation item. 633 | SIZE_T m_nOSRelocationItem; 634 | }; // class CRelocations 635 | #endif 636 | 637 | }; // namespace WarbirdRuntime 638 | -------------------------------------------------------------------------------- /WarbirdMemory.inl: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Memory Allocator 4 | * 5 | **/ 6 | 7 | namespace WarbirdRuntime 8 | { 9 | 10 | #if $(WARBIRD_ENABLE_HEAP_EXECUTION) && !defined(WARBIRD_KERNEL_MODE) 11 | 12 | class CMemoryAllocator 13 | { 14 | public: 15 | HRESULT 16 | Init( 17 | ) 18 | { 19 | // Initialize the lock. 20 | m_nEmptied = 0; 21 | m_BlockListLock.Init(); 22 | 23 | // Allocate the first block. 24 | m_pCurrentBlock = CBlock::Allocate(FALSE); 25 | 26 | if (m_pCurrentBlock != NULL) 27 | { 28 | m_nNumBlocks = 1; 29 | m_nMaxBlocks = 1; 30 | return S_OK; 31 | } 32 | else 33 | { 34 | m_nNumBlocks = 0; 35 | m_nMaxBlocks = 0; 36 | return E_OUTOFMEMORY; 37 | } 38 | 39 | } 40 | 41 | VOID 42 | Cleanup( 43 | ) 44 | { 45 | // Free all blocks. 46 | 47 | CBlock* pBlock = m_pCurrentBlock; 48 | 49 | while (pBlock != NULL) 50 | { 51 | CBlock* pBlockToFree = pBlock; 52 | pBlock = pBlock->m_pNext; 53 | pBlockToFree->Free(); 54 | --m_nNumBlocks; 55 | } 56 | 57 | m_pCurrentBlock = NULL; 58 | WARBIRD_ASSERT(m_nNumBlocks == 0); 59 | } 60 | 61 | PVOID 62 | AllocateMemory( 63 | SIZE_T nSize 64 | ) 65 | { 66 | WARBIRD_ASSERT(nSize > 0 && nSize < CBlock::MaxAllocationSize()); 67 | 68 | PVOID pMemory = NULL; 69 | 70 | // Go in an infinite loop until we allocate the memory. 71 | 72 | for (;;) 73 | { 74 | // Take the shared lock and walk the block list. 75 | 76 | m_BlockListLock.AcquireShared(); 77 | 78 | for (CBlock* pBlock = m_pCurrentBlock; pBlock != NULL; pBlock = pBlock->m_pNext) 79 | { 80 | pMemory = pBlock->AllocateMemory(nSize); 81 | 82 | if (pMemory != NULL) 83 | { 84 | break; 85 | } 86 | } 87 | 88 | m_BlockListLock.ReleaseShared(); 89 | 90 | // If allocation succeeded, exit. 91 | 92 | if (pMemory != NULL) 93 | { 94 | break; 95 | } 96 | 97 | // If not, allocate a new block after leaving the lock. 98 | 99 | CBlock* pNewBlock = CBlock::Allocate(TRUE); 100 | 101 | if (pNewBlock != NULL) 102 | { 103 | // If allocation is successful, take the exclusive lock and 104 | // store the new block at the top of the linked list. 105 | 106 | m_BlockListLock.AcquireExclusive(); 107 | 108 | pNewBlock->m_pNext = m_pCurrentBlock; 109 | m_pCurrentBlock = pNewBlock; 110 | ++m_nNumBlocks; 111 | if(m_nNumBlocks > m_nMaxBlocks) 112 | { 113 | m_nMaxBlocks = m_nNumBlocks; 114 | } 115 | m_BlockListLock.ReleaseExclusive(); 116 | } 117 | } 118 | 119 | WARBIRD_ASSERT(pMemory != NULL); 120 | return pMemory; 121 | } 122 | 123 | VOID 124 | FreeMemory( 125 | __in PVOID pMemory 126 | ) 127 | { 128 | CBlock* pBlockToFree = NULL; 129 | 130 | // Take the shared lock and walk the block list to find the memory. 131 | 132 | m_BlockListLock.AcquireShared(); 133 | 134 | for (CBlock* pBlock = m_pCurrentBlock; pBlock != NULL; pBlock = pBlock->m_pNext) 135 | { 136 | if (pBlock->Contains(pMemory)) 137 | { 138 | // Free the slot(s). 139 | 140 | pBlock->FreeMemory(pMemory); 141 | 142 | // If the whole block is empty after this free, and it's not the only block left, 143 | // mark it as deletable. 144 | 145 | if (pBlock->IsEmpty()) 146 | { 147 | pBlockToFree = pBlock; 148 | } 149 | 150 | break; 151 | } 152 | } 153 | 154 | m_BlockListLock.ReleaseShared(); 155 | 156 | // If the block is found to be deletable, take the exclusive lock and test again. 157 | 158 | if (pBlockToFree != NULL) 159 | { 160 | BOOL fOkayToFree = FALSE; 161 | 162 | m_BlockListLock.AcquireExclusive(); 163 | 164 | for (CBlock** ppBlock = &m_pCurrentBlock; *ppBlock != NULL; ppBlock = &(*ppBlock)->m_pNext) 165 | { 166 | if (*ppBlock == pBlockToFree && (*ppBlock)->IsEmpty()) 167 | { 168 | if (m_nNumBlocks > 1) 169 | { 170 | *ppBlock = (*ppBlock)->m_pNext; 171 | --m_nNumBlocks; 172 | fOkayToFree = TRUE; 173 | } 174 | else 175 | { 176 | (*ppBlock)->ResetPermissions(); 177 | m_nEmptied++; 178 | } 179 | break; 180 | } 181 | } 182 | 183 | m_BlockListLock.ReleaseExclusive(); 184 | 185 | // If the block is okay to free, free it after leaving the exclusive lock. 186 | 187 | if (fOkayToFree) 188 | { 189 | pBlockToFree->Free(); 190 | } 191 | } 192 | } 193 | 194 | BOOL 195 | QueryAllocation( 196 | __in PVOID pMemory, 197 | __out PVOID* ppStartAddress, 198 | __out SIZE_T* pnSize 199 | ) 200 | { 201 | BOOL fFound = FALSE; 202 | 203 | // Take the shared lock and walk the block list to find the memory. 204 | 205 | m_BlockListLock.AcquireShared(); 206 | 207 | for (CBlock* pBlock = m_pCurrentBlock; pBlock != NULL; pBlock = pBlock->m_pNext) 208 | { 209 | if (pBlock->Contains(pMemory)) 210 | { 211 | fFound = TRUE; 212 | pBlock->QueryAllocation(pMemory, ppStartAddress, pnSize); 213 | break; 214 | } 215 | } 216 | 217 | m_BlockListLock.ReleaseShared(); 218 | 219 | return fFound; 220 | } 221 | 222 | private: 223 | class CBlock 224 | { 225 | private: 226 | enum SLOT_STATE : BYTE 227 | { 228 | // Indicates that the slot is free. 229 | FREE_SLOT, 230 | 231 | // Indicates that the slot is the last slot in the allocation. 232 | LAST_SLOT_IN_ALLOCATION, 233 | 234 | // Indicates that the slot is allocated, and it's not the last one in the allocation. 235 | MIDDLE_SLOT_IN_ALLOCATION, 236 | }; 237 | 238 | enum : SIZE_T 239 | { 240 | // Block size is 64K to match with VirtualAlloc granularity. 241 | BLOCK_SIZE = 64 * 1024, 242 | 243 | // Slot size is 64 bytes, which covers average heap executed block size. 244 | SLOT_SIZE = 64, 245 | 246 | // Calculate how many SLOT_SIZE byte slots fit into a BLOCK_SIZE block. 247 | // We need some bookkeeping data for the entire block (a lock, "next" 248 | // pointer for the linked list, and unwind info for an exception handler), 249 | // so subtract the size of these from BLOCK_SIZE. Then, we need some 250 | // bookkeeping data per slot (just an entry in the slot states array), 251 | // so add this size to SLOT_SIZE. Divide the two to get the final result. 252 | PER_BLOCK_BOOKKEEPING_SIZE = sizeof(CRWLock) + sizeof(CBlock*) + sizeof(BOOL) + sizeof(CTermination::CFunctionTable), 253 | PER_SLOT_BOOKKEEPING_SIZE = sizeof(SLOT_STATE), 254 | NUM_SLOTS = (BLOCK_SIZE - PER_BLOCK_BOOKKEEPING_SIZE) / (SLOT_SIZE + PER_SLOT_BOOKKEEPING_SIZE), 255 | 256 | // Magic value to indicate "slot not found" condition. 257 | SLOT_NOT_FOUND = static_cast(-1), 258 | }; 259 | 260 | // An allocation slot is defined as an array of SLOT_SIZE bytes. 261 | typedef BYTE SLOT[SLOT_SIZE]; 262 | 263 | public: 264 | // Allocates a new block. 265 | static CBlock* 266 | Allocate(BOOL makeExecutable) 267 | { 268 | // Preserve system LastError value across system API calls. 269 | 270 | DWORD LastError = GetLastError(); 271 | 272 | C_ASSERT(sizeof(CBlock) <= BLOCK_SIZE); 273 | 274 | // Enable automatic code generation so read-write-execute pages can 275 | // be allocated. Disabled when class goes out of scope 276 | 277 | AutoEnableDynamicCodeGen codeGen(makeExecutable ? true : false); 278 | 279 | CBlock* pNewBlock = static_cast(VirtualAlloc( 280 | NULL, 281 | BLOCK_SIZE, 282 | MEM_COMMIT | MEM_RESERVE, 283 | makeExecutable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE 284 | )); 285 | 286 | if (pNewBlock != NULL) 287 | { 288 | // Fill the buffer with random code. 289 | CUtil::FillRandom(pNewBlock, BLOCK_SIZE); 290 | 291 | // Only architectures with table based exception handling, 292 | // register an exception handler in order to catch exceptions 293 | // thrown from code executing in the buffer. 294 | 295 | BOOL fResult = pNewBlock->m_FunctionTable.Init( 296 | pNewBlock->m_Slots, 297 | sizeof(pNewBlock->m_Slots) 298 | ); 299 | 300 | if (fResult == FALSE) 301 | { 302 | // Exception handler registration may fail since 303 | // internally it uses HeapAlloc. In this case, 304 | // free the allocated buffer. 305 | 306 | VirtualFree( 307 | pNewBlock, 308 | 0, 309 | MEM_RELEASE 310 | ); 311 | 312 | pNewBlock = NULL; 313 | } 314 | } 315 | 316 | if (pNewBlock != NULL) 317 | { 318 | // Mark the block as executable (unless it was the first one) 319 | pNewBlock->m_IsRWX = makeExecutable; 320 | 321 | // Initialize the lock. 322 | pNewBlock->m_SlotStatesLock.Init(); 323 | 324 | // Initialize linked list next pointer. 325 | 326 | pNewBlock->m_pNext = NULL; 327 | 328 | // Set the slot states to free. 329 | CUtil::Memset(pNewBlock->m_SlotStates, FREE_SLOT, NUM_SLOTS * sizeof(SLOT_STATE)); 330 | } 331 | 332 | SetLastError(LastError); 333 | 334 | return pNewBlock; 335 | } 336 | 337 | // Frees a block. 338 | VOID 339 | Free( 340 | ) 341 | { 342 | // Preserve system LastError value across system API calls. 343 | 344 | DWORD LastError = GetLastError(); 345 | 346 | m_FunctionTable.Cleanup(); 347 | 348 | VirtualFree( 349 | this, 350 | 0, 351 | MEM_RELEASE 352 | ); 353 | 354 | SetLastError(LastError); 355 | } 356 | 357 | VOID ResetPermissions() 358 | { 359 | // assert(IsEmpty()); 360 | DWORD OldProtect; 361 | VirtualProtect( 362 | this, 363 | BLOCK_SIZE, 364 | PAGE_READWRITE, 365 | &OldProtect); 366 | m_IsRWX = FALSE; 367 | // assert(OldProtect == PAGE_EXECUTE_READWRITE); 368 | } 369 | 370 | static SIZE_T 371 | MaxAllocationSize() 372 | { 373 | return SLOT_SIZE * NUM_SLOTS; 374 | } 375 | 376 | BOOL 377 | Contains( 378 | __in PVOID pMemory 379 | ) const 380 | { 381 | return pMemory >= m_Slots && 382 | pMemory < CUtil::AddOffset(m_Slots, MaxAllocationSize()); 383 | } 384 | 385 | BOOL 386 | IsEmpty( 387 | ) 388 | { 389 | // Take the shared lock and check if there are any allocated slots. 390 | 391 | m_SlotStatesLock.AcquireShared(); 392 | 393 | SIZE_T nFirstAllocatedSlot = FindFirstAllocatedSlot(0, NUM_SLOTS); 394 | 395 | m_SlotStatesLock.ReleaseShared(); 396 | 397 | // If there are no allocated slots, return TRUE. 398 | 399 | return nFirstAllocatedSlot == SLOT_NOT_FOUND; 400 | } 401 | 402 | PVOID 403 | AllocateMemory( 404 | SIZE_T nSize 405 | ) 406 | { 407 | PVOID pMemory = NULL; 408 | 409 | if (nSize > 0) 410 | { 411 | // Determine the minimum number of slots necessary to cover the desired size. 412 | 413 | SIZE_T nNumSlotsToAlloc = (nSize + (SLOT_SIZE - 1)) / SLOT_SIZE; 414 | 415 | // Try to allocate starting from a random slot offset. 416 | 417 | SIZE_T nRandomSlot = g_Rand.Random(0, NUM_SLOTS - nNumSlotsToAlloc); 418 | 419 | pMemory = AllocateSlots( 420 | nRandomSlot, 421 | NUM_SLOTS - nNumSlotsToAlloc, 422 | nNumSlotsToAlloc 423 | ); 424 | 425 | // If no slots are available in the range [random start slot, last slot], 426 | // try the range [first slot, random start slot]. 427 | 428 | if (pMemory == NULL) 429 | { 430 | pMemory = AllocateSlots( 431 | 0, 432 | nRandomSlot, 433 | nNumSlotsToAlloc 434 | ); 435 | } 436 | 437 | // If allocation succeeded, return a random offset within the slot. 438 | 439 | if (pMemory != NULL) 440 | { 441 | pMemory = CUtil::AddOffset( 442 | pMemory, 443 | g_Rand.Random(0, (SLOT_SIZE * nNumSlotsToAlloc) - nSize) 444 | ); 445 | } 446 | } 447 | 448 | return pMemory; 449 | } 450 | 451 | VOID 452 | FreeMemory( 453 | __in PVOID pMemory 454 | ) 455 | { 456 | // Take the exclusive lock, and mark all the slots in the allocation as free. 457 | 458 | m_SlotStatesLock.AcquireExclusive(); 459 | 460 | INT_PTR nFirstSlot, nLastSlot; 461 | FindAllocatedSlotRange(pMemory, &nFirstSlot, &nLastSlot); 462 | 463 | if (nFirstSlot <= nLastSlot) 464 | { 465 | CUtil::Memset( 466 | &m_SlotStates[nFirstSlot], 467 | FREE_SLOT, 468 | (nLastSlot - nFirstSlot + 1) * sizeof(SLOT_STATE) 469 | ); 470 | } 471 | 472 | m_SlotStatesLock.ReleaseExclusive(); 473 | } 474 | 475 | VOID 476 | QueryAllocation( 477 | __in PVOID pMemory, 478 | __out PVOID* ppStartAddress, 479 | __out SIZE_T* pnSize 480 | ) 481 | { 482 | // Take the shared lock and find the first and last slots in the allocation. 483 | 484 | m_SlotStatesLock.AcquireShared(); 485 | 486 | INT_PTR nFirstSlot, nLastSlot; 487 | FindAllocatedSlotRange(pMemory, &nFirstSlot, &nLastSlot); 488 | 489 | m_SlotStatesLock.ReleaseShared(); 490 | 491 | // Return the results. 492 | 493 | *ppStartAddress = m_Slots[nFirstSlot]; 494 | *pnSize = SLOT_SIZE * (nLastSlot - nFirstSlot + 1); 495 | } 496 | 497 | private: 498 | PVOID 499 | AllocateSlots( 500 | SIZE_T nFirst, 501 | SIZE_T nLast, 502 | SIZE_T nNumSlotsToAlloc 503 | ) 504 | { 505 | PVOID pMemory = NULL; 506 | 507 | // Take the shared lock, and search for nNumSlotsToAlloc free slots in the in the slot allocation map. 508 | 509 | m_SlotStatesLock.AcquireShared(); 510 | 511 | SIZE_T nFirstSlotToAllocate = nFirst; 512 | 513 | for (;;) 514 | { 515 | // Find the first free slot. 516 | 517 | nFirstSlotToAllocate = FindFirstFreeSlot( 518 | nFirstSlotToAllocate, 519 | nLast - nFirstSlotToAllocate + 1 520 | ); 521 | 522 | // If not found, then it means the block is full, exit search. 523 | 524 | if (nFirstSlotToAllocate == SLOT_NOT_FOUND) 525 | { 526 | break; 527 | } 528 | 529 | // Find the first allocated slot after the free slot. 530 | 531 | SIZE_T nNextAllocatedSlot = FindFirstAllocatedSlot( 532 | nFirstSlotToAllocate + 1, 533 | nNumSlotsToAlloc - 1 534 | ); 535 | 536 | // If the first allocated slot is more than nNumSlotsToAlloc away, then 537 | // it means we have found a free space for the the allocation. 538 | 539 | if (nNextAllocatedSlot == SLOT_NOT_FOUND) 540 | { 541 | break; 542 | } 543 | 544 | // Continue searching at an index one past the last allocated block. 545 | 546 | nFirstSlotToAllocate = nNextAllocatedSlot + 1; 547 | } 548 | 549 | m_SlotStatesLock.ReleaseShared(); 550 | 551 | // If we found a free space for allocation, take the exclusive lock and check again. 552 | 553 | if (nFirstSlotToAllocate != SLOT_NOT_FOUND) 554 | { 555 | m_SlotStatesLock.AcquireExclusive(); 556 | 557 | if (FindFirstAllocatedSlot(nFirstSlotToAllocate, nNumSlotsToAlloc) == SLOT_NOT_FOUND) 558 | { 559 | // If the slots are still free while we are in the exclusive lock, it's time to 560 | // mark them as allocated now. 561 | 562 | pMemory = m_Slots[nFirstSlotToAllocate]; 563 | 564 | // Mark the middle slots (if any). 565 | if (nNumSlotsToAlloc > 0) 566 | { 567 | CUtil::Memset( 568 | &m_SlotStates[nFirstSlotToAllocate], 569 | MIDDLE_SLOT_IN_ALLOCATION, 570 | (nNumSlotsToAlloc - 1) * sizeof(SLOT_STATE) 571 | ); 572 | } 573 | 574 | // Mark the last slot. 575 | 576 | m_SlotStates[nFirstSlotToAllocate + nNumSlotsToAlloc - 1] = LAST_SLOT_IN_ALLOCATION; 577 | 578 | if(m_IsRWX == FALSE) 579 | { 580 | // Enable automatic code generation so the page properties can be 581 | // changed to read-write-execute. Disabled when class goes out of 582 | // scope 583 | 584 | AutoEnableDynamicCodeGen codeGen(true); 585 | 586 | DWORD OldProtect; 587 | VirtualProtect(this, 588 | BLOCK_SIZE, 589 | PAGE_EXECUTE_READWRITE, 590 | &OldProtect); 591 | m_IsRWX = TRUE; 592 | // assert(OldProtect == PAGE_READWRITE); 593 | 594 | } 595 | 596 | } 597 | 598 | m_SlotStatesLock.ReleaseExclusive(); 599 | } 600 | 601 | return pMemory; 602 | } 603 | 604 | // Scans the nNumSlots number of slots after the nStartingSlot, and returns 605 | // the index of the first free slot if found, or SLOT_NOT_FOUND otherwise. 606 | SIZE_T 607 | FindFirstFreeSlot( 608 | SIZE_T nStartingSlot, 609 | SIZE_T nNumSlots 610 | ) const 611 | { 612 | for (SIZE_T i = nStartingSlot; i < nStartingSlot + nNumSlots; ++i) 613 | { 614 | if (m_SlotStates[i] == FREE_SLOT) 615 | { 616 | return i; 617 | } 618 | } 619 | 620 | return SLOT_NOT_FOUND; 621 | } 622 | 623 | // Scans the nNumSlots number of slots after the nStartingSlot, and returns 624 | // the index of the first allocated slot if found, or SLOT_NOT_FOUND otherwise. 625 | SIZE_T 626 | FindFirstAllocatedSlot( 627 | SIZE_T nStartingSlot, 628 | SIZE_T nNumSlots 629 | ) const 630 | { 631 | for (SIZE_T i = nStartingSlot; i < nStartingSlot + nNumSlots; ++i) 632 | { 633 | if (m_SlotStates[i] != FREE_SLOT) 634 | { 635 | return i; 636 | } 637 | } 638 | 639 | return SLOT_NOT_FOUND; 640 | } 641 | 642 | VOID FindAllocatedSlotRange( 643 | __in PVOID pMemory, 644 | __out INT_PTR* pnFirstSlot, 645 | __out INT_PTR* pnLastSlot 646 | ) 647 | { 648 | // Convert the address to a slot index. 649 | 650 | SIZE_T nMiddleSlot = CUtil::GetOffset(pMemory, m_Slots) / SLOT_SIZE; 651 | 652 | // First, walk back until we go past the first slot, or hit a non-MIDDLE_SLOT_IN_ALLOCATION 653 | // (which must be a free slot or the last slot in the previous allocation). 654 | 655 | for (*pnFirstSlot = nMiddleSlot - 1; 656 | *pnFirstSlot >= 0 && m_SlotStates[*pnFirstSlot] == MIDDLE_SLOT_IN_ALLOCATION; 657 | *pnFirstSlot = *pnFirstSlot - 1) 658 | { 659 | } 660 | 661 | // Now we must have walked past the first block in allocation, so back up one slot. 662 | 663 | *pnFirstSlot = *pnFirstSlot + 1; 664 | 665 | // Next, walk forward until we hit a hit a non-MIDDLE_SLOT_IN_ALLOCATION (which must be 666 | // the last slot in allocation assuming that the slot states array is filled properly). 667 | 668 | for (*pnLastSlot = nMiddleSlot; 669 | m_SlotStates[*pnLastSlot] == MIDDLE_SLOT_IN_ALLOCATION; 670 | *pnLastSlot = *pnLastSlot + 1) 671 | { 672 | } 673 | 674 | WARBIRD_ASSERT(*pnLastSlot < NUM_SLOTS); 675 | } 676 | 677 | 678 | private: 679 | SLOT m_Slots[NUM_SLOTS]; 680 | 681 | private: 682 | // Multiple-Reader-Single-Writer Lock that protects the slot usage table. 683 | CRWLock m_SlotStatesLock; 684 | 685 | // Is the block RWX or RW 686 | BOOL m_IsRWX; 687 | 688 | public: 689 | // The Next pointer to maintain the linked list in the outer class. 690 | // Marked as public so that the outer class can access and modify it. 691 | // Protected by m_BlockListLock in the outer class. 692 | CBlock* m_pNext; 693 | 694 | private: 695 | CTermination::CFunctionTable m_FunctionTable; 696 | 697 | private: 698 | // Maintains the usage state of each slot. Protected by m_SlotStatesLock. 699 | SLOT_STATE m_SlotStates[NUM_SLOTS]; 700 | 701 | }; // class CBlock 702 | 703 | private: 704 | // Head pointer of the listed list of allocated blocks. 705 | CBlock* m_pCurrentBlock; 706 | 707 | // Multiple-Reader-Single-Writer Lock that protects the linked list. 708 | CRWLock m_BlockListLock; 709 | 710 | // Number of allocated blocks in the link list. 711 | SIZE_T m_nNumBlocks; 712 | 713 | SIZE_T m_nEmptied; 714 | SIZE_T m_nMaxBlocks; 715 | 716 | }; // class CMemoryAllocator 717 | 718 | CMemoryAllocator g_MemoryAllocator; 719 | 720 | #endif 721 | 722 | }; // namespace WarbirdRuntime --------------------------------------------------------------------------------