Last active
November 30, 2017 09:26
-
-
Save shmuelfomberg/231b685d39e4d52c10f8a4ef4c88da75 to your computer and use it in GitHub Desktop.
Wow64Ext_ShowSnap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
An exmaple of the crash with Wow64Ext and Loader ShowSnaps active | |
Runs OK as is. | |
When importing the registry file: (activating Loader Show Snaps) | |
Runs OK in the debugger | |
Crashes when runned without debugger | |
The crash is inside GetProcAddress64. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
#include <Windows.h> | |
/** | |
* | |
* WOW64Ext Library | |
* | |
* Copyright (c) 2014 ReWolf | |
* http://blog.rewolf.pl/ | |
* | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU Lesser General Public License as published | |
* by the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
* | |
*/ | |
#include <cstddef> | |
#include <stdlib.h> | |
#define EMIT(a) __asm __emit (a) | |
#define X64_Start_with_CS(_cs) \ | |
{ \ | |
EMIT(0x6A) EMIT(_cs) /* push _cs */ \ | |
EMIT(0xE8) EMIT(0) EMIT(0) EMIT(0) EMIT(0) /* call $+5 */ \ | |
EMIT(0x83) EMIT(4) EMIT(0x24) EMIT(5) /* add dword [esp], 5 */ \ | |
EMIT(0xCB) /* retf */ \ | |
} | |
#define X64_End_with_CS(_cs) \ | |
{ \ | |
EMIT(0xE8) EMIT(0) EMIT(0) EMIT(0) EMIT(0) /* call $+5 */ \ | |
EMIT(0xC7) EMIT(0x44) EMIT(0x24) EMIT(4) EMIT(_cs) EMIT(0) EMIT(0) EMIT(0) /* mov dword [rsp + 4], _cs */ \ | |
EMIT(0x83) EMIT(4) EMIT(0x24) EMIT(0xD) /* add dword [rsp], 0xD */ \ | |
EMIT(0xCB) /* retf */ \ | |
} | |
#define X64_Start() X64_Start_with_CS(0x33) | |
#define X64_End() X64_End_with_CS(0x23) | |
#define _RAX 0 | |
#define _RCX 1 | |
#define _RDX 2 | |
#define _RBX 3 | |
#define _RSP 4 | |
#define _RBP 5 | |
#define _RSI 6 | |
#define _RDI 7 | |
#define _R8 8 | |
#define _R9 9 | |
#define _R10 10 | |
#define _R11 11 | |
#define _R12 12 | |
#define _R13 13 | |
#define _R14 14 | |
#define _R15 15 | |
#define X64_Push(r) EMIT(0x48 | ((r) >> 3)) EMIT(0x50 | ((r) & 7)) | |
#define X64_Pop(r) EMIT(0x48 | ((r) >> 3)) EMIT(0x58 | ((r) & 7)) | |
#define REX_W EMIT(0x48) __asm | |
//to fool M$ inline asm compiler I'm using 2 DWORDs instead of DWORD64 | |
//use of DWORD64 will generate wrong 'pop word ptr[]' and it will break stack | |
union reg64 | |
{ | |
DWORD64 v; | |
DWORD dw[2]; | |
}; | |
class CMemPtr | |
{ | |
private: | |
void** m_ptr; | |
bool watchActive; | |
public: | |
CMemPtr(void** ptr) : m_ptr(ptr), watchActive(true) {} | |
~CMemPtr() | |
{ | |
if (*m_ptr && watchActive) | |
{ | |
free(*m_ptr); | |
*m_ptr = 0; | |
} | |
} | |
void disableWatch() { watchActive = false; } | |
}; | |
#define WATCH(ptr) \ | |
CMemPtr watch_##ptr((void**)&ptr) | |
#define DISABLE_WATCH(ptr) \ | |
watch_##ptr.disableWatch() | |
#ifndef STATUS_SUCCESS | |
# define STATUS_SUCCESS 0 | |
#endif | |
#pragma pack(push) | |
#pragma pack(1) | |
template <class T> | |
struct _LIST_ENTRY_T | |
{ | |
T Flink; | |
T Blink; | |
}; | |
template <class T> | |
struct _UNICODE_STRING_T | |
{ | |
union | |
{ | |
struct | |
{ | |
WORD Length; | |
WORD MaximumLength; | |
}; | |
T dummy; | |
}; | |
T Buffer; | |
}; | |
template <class T> | |
struct _NT_TIB_T | |
{ | |
T ExceptionList; | |
T StackBase; | |
T StackLimit; | |
T SubSystemTib; | |
T FiberData; | |
T ArbitraryUserPointer; | |
T Self; | |
}; | |
template <class T> | |
struct _CLIENT_ID | |
{ | |
T UniqueProcess; | |
T UniqueThread; | |
}; | |
template <class T> | |
struct _TEB_T_ | |
{ | |
_NT_TIB_T<T> NtTib; | |
T EnvironmentPointer; | |
_CLIENT_ID<T> ClientId; | |
T ActiveRpcHandle; | |
T ThreadLocalStoragePointer; | |
T ProcessEnvironmentBlock; | |
DWORD LastErrorValue; | |
DWORD CountOfOwnedCriticalSections; | |
T CsrClientThread; | |
T Win32ThreadInfo; | |
DWORD User32Reserved[26]; | |
//rest of the structure is not defined for now, as it is not needed | |
}; | |
template <class T> | |
struct _LDR_DATA_TABLE_ENTRY_T | |
{ | |
_LIST_ENTRY_T<T> InLoadOrderLinks; | |
_LIST_ENTRY_T<T> InMemoryOrderLinks; | |
_LIST_ENTRY_T<T> InInitializationOrderLinks; | |
T DllBase; | |
T EntryPoint; | |
union | |
{ | |
DWORD SizeOfImage; | |
T dummy01; | |
}; | |
_UNICODE_STRING_T<T> FullDllName; | |
_UNICODE_STRING_T<T> BaseDllName; | |
DWORD Flags; | |
WORD LoadCount; | |
WORD TlsIndex; | |
union | |
{ | |
_LIST_ENTRY_T<T> HashLinks; | |
struct | |
{ | |
T SectionPointer; | |
T CheckSum; | |
}; | |
}; | |
union | |
{ | |
T LoadedImports; | |
DWORD TimeDateStamp; | |
}; | |
T EntryPointActivationContext; | |
T PatchInformation; | |
_LIST_ENTRY_T<T> ForwarderLinks; | |
_LIST_ENTRY_T<T> ServiceTagLinks; | |
_LIST_ENTRY_T<T> StaticLinks; | |
T ContextInformation; | |
T OriginalBase; | |
_LARGE_INTEGER LoadTime; | |
}; | |
template <class T> | |
struct _PEB_LDR_DATA_T | |
{ | |
DWORD Length; | |
DWORD Initialized; | |
T SsHandle; | |
_LIST_ENTRY_T<T> InLoadOrderModuleList; | |
_LIST_ENTRY_T<T> InMemoryOrderModuleList; | |
_LIST_ENTRY_T<T> InInitializationOrderModuleList; | |
T EntryInProgress; | |
DWORD ShutdownInProgress; | |
T ShutdownThreadId; | |
}; | |
template <class T, class NGF, int A> | |
struct _PEB_T | |
{ | |
union | |
{ | |
struct | |
{ | |
BYTE InheritedAddressSpace; | |
BYTE ReadImageFileExecOptions; | |
BYTE BeingDebugged; | |
BYTE BitField; | |
}; | |
T dummy01; | |
}; | |
T Mutant; | |
T ImageBaseAddress; | |
T Ldr; | |
T ProcessParameters; | |
T SubSystemData; | |
T ProcessHeap; | |
T FastPebLock; | |
T AtlThunkSListPtr; | |
T IFEOKey; | |
T CrossProcessFlags; | |
T UserSharedInfoPtr; | |
DWORD SystemReserved; | |
DWORD AtlThunkSListPtr32; | |
T ApiSetMap; | |
T TlsExpansionCounter; | |
T TlsBitmap; | |
DWORD TlsBitmapBits[2]; | |
T ReadOnlySharedMemoryBase; | |
T HotpatchInformation; | |
T ReadOnlyStaticServerData; | |
T AnsiCodePageData; | |
T OemCodePageData; | |
T UnicodeCaseTableData; | |
DWORD NumberOfProcessors; | |
union | |
{ | |
DWORD NtGlobalFlag; | |
NGF dummy02; | |
}; | |
LARGE_INTEGER CriticalSectionTimeout; | |
T HeapSegmentReserve; | |
T HeapSegmentCommit; | |
T HeapDeCommitTotalFreeThreshold; | |
T HeapDeCommitFreeBlockThreshold; | |
DWORD NumberOfHeaps; | |
DWORD MaximumNumberOfHeaps; | |
T ProcessHeaps; | |
T GdiSharedHandleTable; | |
T ProcessStarterHelper; | |
T GdiDCAttributeList; | |
T LoaderLock; | |
DWORD OSMajorVersion; | |
DWORD OSMinorVersion; | |
WORD OSBuildNumber; | |
WORD OSCSDVersion; | |
DWORD OSPlatformId; | |
DWORD ImageSubsystem; | |
DWORD ImageSubsystemMajorVersion; | |
T ImageSubsystemMinorVersion; | |
T ActiveProcessAffinityMask; | |
T GdiHandleBuffer[A]; | |
T PostProcessInitRoutine; | |
T TlsExpansionBitmap; | |
DWORD TlsExpansionBitmapBits[32]; | |
T SessionId; | |
ULARGE_INTEGER AppCompatFlags; | |
ULARGE_INTEGER AppCompatFlagsUser; | |
T pShimData; | |
T AppCompatInfo; | |
_UNICODE_STRING_T<T> CSDVersion; | |
T ActivationContextData; | |
T ProcessAssemblyStorageMap; | |
T SystemDefaultActivationContextData; | |
T SystemAssemblyStorageMap; | |
T MinimumStackCommit; | |
T FlsCallback; | |
_LIST_ENTRY_T<T> FlsListHead; | |
T FlsBitmap; | |
DWORD FlsBitmapBits[4]; | |
T FlsHighIndex; | |
T WerRegistrationData; | |
T WerShipAssertPtr; | |
T pContextData; | |
T pImageHeaderHash; | |
T TracingFlags; | |
}; | |
typedef _LDR_DATA_TABLE_ENTRY_T<DWORD> LDR_DATA_TABLE_ENTRY32; | |
typedef _LDR_DATA_TABLE_ENTRY_T<DWORD64> LDR_DATA_TABLE_ENTRY64; | |
typedef _TEB_T_<DWORD> TEB32; | |
typedef _TEB_T_<DWORD64> TEB64; | |
typedef _PEB_LDR_DATA_T<DWORD> PEB_LDR_DATA32; | |
typedef _PEB_LDR_DATA_T<DWORD64> PEB_LDR_DATA64; | |
typedef _PEB_T<DWORD, DWORD64, 34> PEB32; | |
typedef _PEB_T<DWORD64, DWORD, 30> PEB64; | |
struct _XSAVE_FORMAT64 | |
{ | |
WORD ControlWord; | |
WORD StatusWord; | |
BYTE TagWord; | |
BYTE Reserved1; | |
WORD ErrorOpcode; | |
DWORD ErrorOffset; | |
WORD ErrorSelector; | |
WORD Reserved2; | |
DWORD DataOffset; | |
WORD DataSelector; | |
WORD Reserved3; | |
DWORD MxCsr; | |
DWORD MxCsr_Mask; | |
_M128A FloatRegisters[8]; | |
_M128A XmmRegisters[16]; | |
BYTE Reserved4[96]; | |
}; | |
struct _CONTEXT64 | |
{ | |
DWORD64 P1Home; | |
DWORD64 P2Home; | |
DWORD64 P3Home; | |
DWORD64 P4Home; | |
DWORD64 P5Home; | |
DWORD64 P6Home; | |
DWORD ContextFlags; | |
DWORD MxCsr; | |
WORD SegCs; | |
WORD SegDs; | |
WORD SegEs; | |
WORD SegFs; | |
WORD SegGs; | |
WORD SegSs; | |
DWORD EFlags; | |
DWORD64 Dr0; | |
DWORD64 Dr1; | |
DWORD64 Dr2; | |
DWORD64 Dr3; | |
DWORD64 Dr6; | |
DWORD64 Dr7; | |
DWORD64 Rax; | |
DWORD64 Rcx; | |
DWORD64 Rdx; | |
DWORD64 Rbx; | |
DWORD64 Rsp; | |
DWORD64 Rbp; | |
DWORD64 Rsi; | |
DWORD64 Rdi; | |
DWORD64 R8; | |
DWORD64 R9; | |
DWORD64 R10; | |
DWORD64 R11; | |
DWORD64 R12; | |
DWORD64 R13; | |
DWORD64 R14; | |
DWORD64 R15; | |
DWORD64 Rip; | |
_XSAVE_FORMAT64 FltSave; | |
_M128A Header[2]; | |
_M128A Legacy[8]; | |
_M128A Xmm0; | |
_M128A Xmm1; | |
_M128A Xmm2; | |
_M128A Xmm3; | |
_M128A Xmm4; | |
_M128A Xmm5; | |
_M128A Xmm6; | |
_M128A Xmm7; | |
_M128A Xmm8; | |
_M128A Xmm9; | |
_M128A Xmm10; | |
_M128A Xmm11; | |
_M128A Xmm12; | |
_M128A Xmm13; | |
_M128A Xmm14; | |
_M128A Xmm15; | |
_M128A VectorRegister[26]; | |
DWORD64 VectorControl; | |
DWORD64 DebugControl; | |
DWORD64 LastBranchToRip; | |
DWORD64 LastBranchFromRip; | |
DWORD64 LastExceptionToRip; | |
DWORD64 LastExceptionFromRip; | |
}; | |
// Below defines for .ContextFlags field are taken from WinNT.h | |
#ifndef CONTEXT_AMD64 | |
#define CONTEXT_AMD64 0x100000 | |
#endif | |
#define CONTEXT64_CONTROL (CONTEXT_AMD64 | 0x1L) | |
#define CONTEXT64_INTEGER (CONTEXT_AMD64 | 0x2L) | |
#define CONTEXT64_SEGMENTS (CONTEXT_AMD64 | 0x4L) | |
#define CONTEXT64_FLOATING_POINT (CONTEXT_AMD64 | 0x8L) | |
#define CONTEXT64_DEBUG_REGISTERS (CONTEXT_AMD64 | 0x10L) | |
#define CONTEXT64_FULL (CONTEXT64_CONTROL | CONTEXT64_INTEGER | CONTEXT64_FLOATING_POINT) | |
#define CONTEXT64_ALL (CONTEXT64_CONTROL | CONTEXT64_INTEGER | CONTEXT64_SEGMENTS | CONTEXT64_FLOATING_POINT | CONTEXT64_DEBUG_REGISTERS) | |
#define CONTEXT64_XSTATE (CONTEXT_AMD64 | 0x20L) | |
#pragma warning(push) | |
#pragma warning(disable : 4409) | |
extern "C" DWORD64 __cdecl X64Call(DWORD64 func, int argC, ...) | |
{ | |
va_list args; | |
va_start(args, argC); | |
reg64 _rcx = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; | |
reg64 _rdx = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; | |
reg64 _r8 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; | |
reg64 _r9 = { (argC > 0) ? argC--, va_arg(args, DWORD64) : 0 }; | |
reg64 _rax = { 0 }; | |
reg64 restArgs = { (DWORD64)&va_arg(args, DWORD64) }; | |
// conversion to QWORD for easier use in inline assembly | |
reg64 _argC = { (DWORD64)argC }; | |
DWORD back_esp = 0; | |
WORD back_fs = 0; | |
__asm | |
{ | |
;// reset FS segment, to properly handle RFG | |
mov back_fs, fs | |
mov eax, 0x2B | |
mov fs, ax | |
;// keep original esp in back_esp variable | |
mov back_esp, esp | |
;// align esp to 0x10, without aligned stack some syscalls may return errors ! | |
;// (actually, for syscalls it is sufficient to align to 8, but SSE opcodes | |
;// requires 0x10 alignment), it will be further adjusted according to the | |
;// number of arguments above 4 | |
and esp, 0xFFFFFFF0 | |
X64_Start(); | |
;// below code is compiled as x86 inline asm, but it is executed as x64 code | |
;// that's why it need sometimes REX_W() macro, right column contains detailed | |
;// transcription how it will be interpreted by CPU | |
;// fill first four arguments | |
REX_W mov ecx, _rcx.dw[0];// mov rcx, qword ptr [_rcx] | |
REX_W mov edx, _rdx.dw[0];// mov rdx, qword ptr [_rdx] | |
push _r8.v;// push qword ptr [_r8] | |
X64_Pop(_R8); ;// pop r8 | |
push _r9.v;// push qword ptr [_r9] | |
X64_Pop(_R9); ;// pop r9 | |
;// | |
REX_W mov eax, _argC.dw[0];// mov rax, qword ptr [_argC] | |
;// | |
;// final stack adjustment, according to the ;// | |
;// number of arguments above 4 ;// | |
test al, 1;// test al, 1 | |
jnz _no_adjust;// jnz _no_adjust | |
sub esp, 8;// sub rsp, 8 | |
_no_adjust:;// | |
;// | |
push edi;// push rdi | |
REX_W mov edi, restArgs.dw[0];// mov rdi, qword ptr [restArgs] | |
;// | |
;// put rest of arguments on the stack ;// | |
REX_W test eax, eax;// test rax, rax | |
jz _ls_e;// je _ls_e | |
REX_W lea edi, dword ptr[edi + 8 * eax - 8];// lea rdi, [rdi + rax*8 - 8] | |
;// | |
_ls:;// | |
REX_W test eax, eax;// test rax, rax | |
jz _ls_e;// je _ls_e | |
push dword ptr[edi];// push qword ptr [rdi] | |
REX_W sub edi, 8;// sub rdi, 8 | |
REX_W sub eax, 1;// sub rax, 1 | |
jmp _ls;// jmp _ls | |
_ls_e:;// | |
;// | |
;// create stack space for spilling registers ;// | |
REX_W sub esp, 0x20;// sub rsp, 20h | |
;// | |
call func;// call qword ptr [func] | |
;// | |
;// cleanup stack ;// | |
REX_W mov ecx, _argC.dw[0];// mov rcx, qword ptr [_argC] | |
REX_W lea esp, dword ptr[esp + 8 * ecx + 0x20];// lea rsp, [rsp + rcx*8 + 20h] | |
;// | |
pop edi;// pop rdi | |
;// | |
// set return value ;// | |
REX_W mov _rax.dw[0], eax;// mov qword ptr [_rax], rax | |
X64_End(); | |
mov ax, ds | |
mov ss, ax | |
mov esp, back_esp | |
;// restore FS segment | |
mov ax, back_fs | |
mov fs, ax | |
} | |
return _rax.v; | |
} | |
#pragma warning(pop) | |
void getMem64(void* dstMem, DWORD64 srcMem, size_t sz) | |
{ | |
if ((nullptr == dstMem) || (0 == srcMem) || (0 == sz)) | |
return; | |
reg64 _src = { srcMem }; | |
__asm | |
{ | |
X64_Start(); | |
;// below code is compiled as x86 inline asm, but it is executed as x64 code | |
;// that's why it need sometimes REX_W() macro, right column contains detailed | |
;// transcription how it will be interpreted by CPU | |
push edi;// push rdi | |
push esi;// push rsi | |
;// | |
mov edi, dstMem;// mov edi, dword ptr [dstMem] ; high part of RDI is zeroed | |
REX_W mov esi, _src.dw[0];// mov rsi, qword ptr [_src] | |
mov ecx, sz;// mov ecx, dword ptr [sz] ; high part of RCX is zeroed | |
;// | |
mov eax, ecx;// mov eax, ecx | |
and eax, 3;// and eax, 3 | |
shr ecx, 2;// shr ecx, 2 | |
;// | |
rep movsd;// rep movs dword ptr [rdi], dword ptr [rsi] | |
;// | |
test eax, eax;// test eax, eax | |
je _move_0;// je _move_0 | |
cmp eax, 1;// cmp eax, 1 | |
je _move_1;// je _move_1 | |
;// | |
movsw;// movs word ptr [rdi], word ptr [rsi] | |
cmp eax, 2;// cmp eax, 2 | |
je _move_0;// je _move_0 | |
;// | |
_move_1:;// | |
movsb;// movs byte ptr [rdi], byte ptr [rsi] | |
;// | |
_move_0:;// | |
pop esi;// pop rsi | |
pop edi;// pop rdi | |
X64_End(); | |
} | |
} | |
bool cmpMem64(void* dstMem, DWORD64 srcMem, size_t sz) | |
{ | |
if ((nullptr == dstMem) || (0 == srcMem) || (0 == sz)) | |
return false; | |
bool result = false; | |
reg64 _src = { srcMem }; | |
__asm | |
{ | |
X64_Start(); | |
;// below code is compiled as x86 inline asm, but it is executed as x64 code | |
;// that's why it need sometimes REX_W() macro, right column contains detailed | |
;// transcription how it will be interpreted by CPU | |
push edi;// push rdi | |
push esi;// push rsi | |
;// | |
mov edi, dstMem;// mov edi, dword ptr [dstMem] ; high part of RDI is zeroed | |
REX_W mov esi, _src.dw[0];// mov rsi, qword ptr [_src] | |
mov ecx, sz;// mov ecx, dword ptr [sz] ; high part of RCX is zeroed | |
;// | |
mov eax, ecx;// mov eax, ecx | |
and eax, 3;// and eax, 3 | |
shr ecx, 2;// shr ecx, 2 | |
;// | |
repe cmpsd;// repe cmps dword ptr [rsi], dword ptr [rdi] | |
jnz _ret_false;// jnz _ret_false | |
;// | |
test eax, eax;// test eax, eax | |
je _move_0;// je _move_0 | |
cmp eax, 1;// cmp eax, 1 | |
je _move_1;// je _move_1 | |
;// | |
cmpsw;// cmps word ptr [rsi], word ptr [rdi] | |
jnz _ret_false;// jnz _ret_false | |
cmp eax, 2;// cmp eax, 2 | |
je _move_0;// je _move_0 | |
;// | |
_move_1:;// | |
cmpsb;// cmps byte ptr [rsi], byte ptr [rdi] | |
jnz _ret_false;// jnz _ret_false | |
;// | |
_move_0:;// | |
mov result, 1;// mov byte ptr [result], 1 | |
;// | |
_ret_false:;// | |
pop esi;// pop rsi | |
pop edi;// pop rdi | |
X64_End(); | |
} | |
return result; | |
} | |
DWORD64 getTEB64() | |
{ | |
reg64 reg; | |
reg.v = 0; | |
X64_Start(); | |
// R12 register should always contain pointer to TEB64 in WoW64 processes | |
X64_Push(_R12); | |
// below pop will pop QWORD from stack, as we're in x64 mode now | |
__asm pop reg.dw[0] | |
X64_End(); | |
return reg.v; | |
} | |
extern "C" DWORD64 __cdecl GetModuleHandle64(wchar_t* lpModuleName) | |
{ | |
TEB64 teb64; | |
getMem64(&teb64, getTEB64(), sizeof(TEB64)); | |
PEB64 peb64; | |
getMem64(&peb64, teb64.ProcessEnvironmentBlock, sizeof(PEB64)); | |
PEB_LDR_DATA64 ldr; | |
getMem64(&ldr, peb64.Ldr, sizeof(PEB_LDR_DATA64)); | |
DWORD64 LastEntry = peb64.Ldr + offsetof(PEB_LDR_DATA64, InLoadOrderModuleList); | |
LDR_DATA_TABLE_ENTRY64 head; | |
head.InLoadOrderLinks.Flink = ldr.InLoadOrderModuleList.Flink; | |
do | |
{ | |
getMem64(&head, head.InLoadOrderLinks.Flink, sizeof(LDR_DATA_TABLE_ENTRY64)); | |
wchar_t* tempBuf = (wchar_t*)malloc(head.BaseDllName.MaximumLength); | |
if (nullptr == tempBuf) | |
return 0; | |
WATCH(tempBuf); | |
getMem64(tempBuf, head.BaseDllName.Buffer, head.BaseDllName.MaximumLength); | |
if (0 == _wcsicmp(lpModuleName, tempBuf)) | |
return head.DllBase; | |
} while (head.InLoadOrderLinks.Flink != LastEntry); | |
return 0; | |
} | |
DWORD64 getNTDLL64() | |
{ | |
static DWORD64 ntdll64 = 0; | |
if (0 != ntdll64) | |
return ntdll64; | |
ntdll64 = GetModuleHandle64(L"ntdll.dll"); | |
return ntdll64; | |
} | |
DWORD64 getLdrGetProcedureAddress() | |
{ | |
DWORD64 modBase = getNTDLL64(); | |
if (0 == modBase) | |
return 0; | |
IMAGE_DOS_HEADER idh; | |
getMem64(&idh, modBase, sizeof(idh)); | |
IMAGE_NT_HEADERS64 inh; | |
getMem64(&inh, modBase + idh.e_lfanew, sizeof(IMAGE_NT_HEADERS64)); | |
IMAGE_DATA_DIRECTORY& idd = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; | |
if (0 == idd.VirtualAddress) | |
return 0; | |
IMAGE_EXPORT_DIRECTORY ied; | |
getMem64(&ied, modBase + idd.VirtualAddress, sizeof(ied)); | |
DWORD* rvaTable = (DWORD*)malloc(sizeof(DWORD)*ied.NumberOfFunctions); | |
if (nullptr == rvaTable) | |
return 0; | |
WATCH(rvaTable); | |
getMem64(rvaTable, modBase + ied.AddressOfFunctions, sizeof(DWORD)*ied.NumberOfFunctions); | |
WORD* ordTable = (WORD*)malloc(sizeof(WORD)*ied.NumberOfFunctions); | |
if (nullptr == ordTable) | |
return 0; | |
WATCH(ordTable); | |
getMem64(ordTable, modBase + ied.AddressOfNameOrdinals, sizeof(WORD)*ied.NumberOfFunctions); | |
DWORD* nameTable = (DWORD*)malloc(sizeof(DWORD)*ied.NumberOfNames); | |
if (nullptr == nameTable) | |
return 0; | |
WATCH(nameTable); | |
getMem64(nameTable, modBase + ied.AddressOfNames, sizeof(DWORD)*ied.NumberOfNames); | |
// lazy search, there is no need to use binsearch for just one function | |
for (DWORD i = 0; i < ied.NumberOfFunctions; i++) | |
{ | |
if (!cmpMem64("LdrGetProcedureAddress", modBase + nameTable[i], sizeof("LdrGetProcedureAddress"))) | |
continue; | |
else | |
return modBase + rvaTable[ordTable[i]]; | |
} | |
return 0; | |
} | |
extern "C" VOID __cdecl SetLastErrorFromX64Call(DWORD64 status) | |
{ | |
typedef ULONG(WINAPI *RtlNtStatusToDosError_t)(NTSTATUS Status); | |
typedef ULONG(WINAPI *RtlSetLastWin32Error_t)(NTSTATUS Status); | |
static RtlNtStatusToDosError_t RtlNtStatusToDosError = nullptr; | |
static RtlSetLastWin32Error_t RtlSetLastWin32Error = nullptr; | |
if ((nullptr == RtlNtStatusToDosError) || (nullptr == RtlSetLastWin32Error)) | |
{ | |
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll"); | |
RtlNtStatusToDosError = (RtlNtStatusToDosError_t)GetProcAddress(ntdll, "RtlNtStatusToDosError"); | |
RtlSetLastWin32Error = (RtlSetLastWin32Error_t)GetProcAddress(ntdll, "RtlSetLastWin32Error"); | |
} | |
if ((nullptr != RtlNtStatusToDosError) && (nullptr != RtlSetLastWin32Error)) | |
{ | |
RtlSetLastWin32Error(RtlNtStatusToDosError((DWORD)status)); | |
} | |
} | |
static volatile int waitforme = 0; | |
extern "C" DWORD64 __cdecl GetProcAddress64(DWORD64 hModule, char* funcName) | |
{ | |
static DWORD64 _LdrGetProcedureAddress = 0; | |
if (0 == _LdrGetProcedureAddress) | |
{ | |
_LdrGetProcedureAddress = getLdrGetProcedureAddress(); | |
if (0 == _LdrGetProcedureAddress) | |
return 0; | |
} | |
_UNICODE_STRING_T<DWORD64> fName = { 0 }; | |
fName.Buffer = (DWORD64)funcName; | |
fName.Length = (WORD)strlen(funcName); | |
fName.MaximumLength = fName.Length + 1; | |
DWORD64 funcRet = 0; | |
X64Call(_LdrGetProcedureAddress, 4, (DWORD64)hModule, (DWORD64)&fName, (DWORD64)0, (DWORD64)&funcRet); | |
return funcRet; | |
} | |
extern "C" SIZE_T __cdecl VirtualQueryEx64(HANDLE hProcess, DWORD64 lpAddress, MEMORY_BASIC_INFORMATION64* lpBuffer, SIZE_T dwLength) | |
{ | |
static DWORD64 ntqvm = 0; | |
if (0 == ntqvm) | |
{ | |
ntqvm = GetProcAddress64(getNTDLL64(), "NtQueryVirtualMemory"); | |
if (0 == ntqvm) | |
return 0; | |
} | |
DWORD64 ret = 0; | |
DWORD64 status = X64Call(ntqvm, 6, (DWORD64)hProcess, lpAddress, (DWORD64)0, (DWORD64)lpBuffer, (DWORD64)dwLength, (DWORD64)&ret); | |
if (STATUS_SUCCESS != status) | |
SetLastErrorFromX64Call(status); | |
return (SIZE_T)ret; | |
} | |
extern "C" DWORD64 __cdecl VirtualAllocEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) | |
{ | |
static DWORD64 ntavm = 0; | |
if (0 == ntavm) | |
{ | |
ntavm = GetProcAddress64(getNTDLL64(), "NtAllocateVirtualMemory"); | |
if (0 == ntavm) | |
return 0; | |
} | |
DWORD64 tmpAddr = lpAddress; | |
DWORD64 tmpSize = dwSize; | |
DWORD64 ret = X64Call(ntavm, 6, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)0, (DWORD64)&tmpSize, (DWORD64)flAllocationType, (DWORD64)flProtect); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
return tmpAddr; | |
} | |
extern "C" BOOL __cdecl VirtualFreeEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD dwFreeType) | |
{ | |
static DWORD64 ntfvm = 0; | |
if (0 == ntfvm) | |
{ | |
ntfvm = GetProcAddress64(getNTDLL64(), "NtFreeVirtualMemory"); | |
if (0 == ntfvm) | |
return 0; | |
} | |
DWORD64 tmpAddr = lpAddress; | |
DWORD64 tmpSize = dwSize; | |
DWORD64 ret = X64Call(ntfvm, 4, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)&tmpSize, (DWORD64)dwFreeType); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
return TRUE; | |
} | |
extern "C" BOOL __cdecl VirtualProtectEx64(HANDLE hProcess, DWORD64 lpAddress, SIZE_T dwSize, DWORD flNewProtect, DWORD* lpflOldProtect) | |
{ | |
static DWORD64 ntpvm = 0; | |
if (0 == ntpvm) | |
{ | |
ntpvm = GetProcAddress64(getNTDLL64(), "NtProtectVirtualMemory"); | |
if (0 == ntpvm) | |
return 0; | |
} | |
DWORD64 tmpAddr = lpAddress; | |
DWORD64 tmpSize = dwSize; | |
DWORD64 ret = X64Call(ntpvm, 5, (DWORD64)hProcess, (DWORD64)&tmpAddr, (DWORD64)&tmpSize, (DWORD64)flNewProtect, (DWORD64)lpflOldProtect); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
return TRUE; | |
} | |
extern "C" BOOL __cdecl ReadProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead) | |
{ | |
static DWORD64 nrvm = 0; | |
if (0 == nrvm) | |
{ | |
nrvm = GetProcAddress64(getNTDLL64(), "NtReadVirtualMemory"); | |
if (0 == nrvm) | |
return 0; | |
} | |
DWORD64 numOfBytes = lpNumberOfBytesRead ? *lpNumberOfBytesRead : 0; | |
DWORD64 ret = X64Call(nrvm, 5, (DWORD64)hProcess, lpBaseAddress, (DWORD64)lpBuffer, (DWORD64)nSize, (DWORD64)&numOfBytes); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
{ | |
if (lpNumberOfBytesRead) | |
*lpNumberOfBytesRead = (SIZE_T)numOfBytes; | |
return TRUE; | |
} | |
} | |
extern "C" BOOL __cdecl WriteProcessMemory64(HANDLE hProcess, DWORD64 lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten) | |
{ | |
static DWORD64 nrvm = 0; | |
if (0 == nrvm) | |
{ | |
nrvm = GetProcAddress64(getNTDLL64(), "NtWriteVirtualMemory"); | |
if (0 == nrvm) | |
return 0; | |
} | |
DWORD64 numOfBytes = lpNumberOfBytesWritten ? *lpNumberOfBytesWritten : 0; | |
DWORD64 ret = X64Call(nrvm, 5, (DWORD64)hProcess, lpBaseAddress, (DWORD64)lpBuffer, (DWORD64)nSize, (DWORD64)&numOfBytes); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
{ | |
if (lpNumberOfBytesWritten) | |
*lpNumberOfBytesWritten = (SIZE_T)numOfBytes; | |
return TRUE; | |
} | |
} | |
extern "C" BOOL __cdecl GetThreadContext64(HANDLE hThread, _CONTEXT64* lpContext) | |
{ | |
static DWORD64 gtc = 0; | |
if (0 == gtc) | |
{ | |
gtc = GetProcAddress64(getNTDLL64(), "NtGetContextThread"); | |
if (0 == gtc) | |
return 0; | |
} | |
DWORD64 ret = X64Call(gtc, 2, (DWORD64)hThread, (DWORD64)lpContext); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
return TRUE; | |
} | |
extern "C" BOOL __cdecl SetThreadContext64(HANDLE hThread, _CONTEXT64* lpContext) | |
{ | |
static DWORD64 stc = 0; | |
if (0 == stc) | |
{ | |
stc = GetProcAddress64(getNTDLL64(), "NtSetContextThread"); | |
if (0 == stc) | |
return 0; | |
} | |
DWORD64 ret = X64Call(stc, 2, (DWORD64)hThread, (DWORD64)lpContext); | |
if (STATUS_SUCCESS != ret) | |
{ | |
SetLastErrorFromX64Call(ret); | |
return FALSE; | |
} | |
else | |
return TRUE; | |
} | |
enum THREADINFOCLASS; | |
extern "C" DWORD64 __cdecl NtQueryInformationThread64(HANDLE hThread, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength) | |
{ | |
static DWORD64 stc = 0; | |
if (0 == stc) | |
{ | |
stc = GetProcAddress64(getNTDLL64(), "NtQueryInformationThread"); | |
if (0 == stc) | |
return 0; | |
} | |
DWORD64 ret = X64Call(stc, 5, (DWORD64)hThread, (DWORD64)ThreadInformationClass, (DWORD64)ThreadInformation, (DWORD64)ThreadInformationLength, (DWORD64)ReturnLength); | |
if (STATUS_SUCCESS != ret) | |
SetLastErrorFromX64Call(ret); | |
return ret; | |
} | |
extern "C" DWORD64 __cdecl NtSetInformationThread64(HANDLE hThread, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength) | |
{ | |
static DWORD64 stc = 0; | |
if (0 == stc) | |
{ | |
stc = GetProcAddress64(getNTDLL64(), "NtSetInformationThread"); | |
if (0 == stc) | |
return 0; | |
} | |
DWORD64 ret = X64Call(stc, 4, (DWORD64)hThread, (DWORD64)ThreadInformationClass, (DWORD64)ThreadInformation, (DWORD64)ThreadInformationLength); | |
if (STATUS_SUCCESS != ret) | |
SetLastErrorFromX64Call(ret); | |
return ret; | |
} | |
typedef LONG KPRIORITY; | |
typedef struct _THREAD_BASIC_INFORMATION { | |
NTSTATUS ExitStatus; | |
DWORD64 TebBaseAddress; | |
_CLIENT_ID<DWORD64> ClientId; | |
DWORD64 AffinityMask; | |
KPRIORITY Priority; | |
KPRIORITY BasePriority; | |
} THREAD_BASIC_INFORMATION, *PTHREAD_BASIC_INFORMATION; | |
extern "C" DWORD64 __declspec(dllexport) __cdecl SetThreadAffinityMask64(HANDLE hThread, DWORD64 mask) | |
{ | |
THREAD_BASIC_INFORMATION oThreadData; | |
DWORD64 ret = NtQueryInformationThread64(hThread, (THREADINFOCLASS)0, &oThreadData, sizeof(oThreadData), 0); | |
if (ret != 0) | |
return 0; | |
__declspec(align(8)) DWORD64 mask2 = mask; | |
ret = NtSetInformationThread64(hThread, (THREADINFOCLASS)4, &mask2, 8); | |
if (ret != 0) | |
return 0; | |
return oThreadData.AffinityMask; | |
} | |
extern "C" DWORD __declspec(dllexport) __cdecl SetThreadIdealProcessor64(HANDLE hThread, DWORD mask) | |
{ | |
__declspec(align(8)) DWORD64 mask2 = mask; | |
DWORD64 ret = NtSetInformationThread64(hThread, (THREADINFOCLASS)13, &mask2, 4); | |
if ((ret & 0x80000000) == 0) | |
return (DWORD)ret; | |
return -1; | |
} | |
int main() | |
{ | |
HANDLE cTh = GetCurrentThread(); | |
SetThreadIdealProcessor64(cTh, 0); | |
printf("done.\n"); | |
return 0; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Windows Registry Editor Version 5.00 | |
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options] | |
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Wow64Ext_ShowSnap.exe] | |
"GlobalFlag"=dword:00000002 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment