├── .appveyor.yml ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── dll_load ├── CMakeLists.txt ├── LICENSE ├── README.md └── main.cpp ├── kdb_check ├── CMakeLists.txt ├── LICENSE └── main.cpp ├── pe_check ├── CMakeLists.txt ├── LICENSE └── main.cpp └── syscall_extractor ├── CMakeLists.txt ├── LICENSE ├── main.cpp ├── util.cpp └── util.h /.appveyor.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - Visual Studio 2015 3 | 4 | platform: x64 5 | - x64 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | install: 12 | - git submodule update --init --recursive 13 | - set PATH=C:\Program Files\CMake\bin;%PATH% 14 | 15 | build: 16 | verbosity: detailed 17 | 18 | configuration: 19 | - Release 20 | 21 | environment: 22 | artifactName: $(APPVEYOR_PROJECT_NAME)-$(APPVEYOR_REPO_COMMIT)-$(CONFIGURATION) 23 | matrix: 24 | - env_arch: "x64" 25 | - env_arch: "x86" 26 | 27 | before_build: 28 | - mkdir build 29 | - cd build 30 | - if [%env_arch%]==[x64] ( 31 | cmake .. -A x64 ) 32 | - if [%env_arch%]==[x86] ( 33 | cmake .. ) 34 | - cmake -DCMAKE_INSTALL_PREFIX:PATH=%APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT% .. 35 | 36 | build_script: 37 | - cmake --build . --config %CONFIGURATION% --target install 38 | 39 | after_build: 40 | - mkdir %artifactName% 41 | - cp %APPVEYOR_BUILD_FOLDER%/%APPVEYOR_REPO_COMMIT%/* %artifactName% 42 | 43 | artifacts: 44 | - path: build\%artifactName% 45 | 46 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libpeconv"] 2 | path = libpeconv 3 | url = https://github.com/hasherezade/libpeconv.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required ( VERSION 3.0 ) 2 | 3 | project ( pe_utils ) 4 | 5 | # libs 6 | # modules: 7 | set ( M_PARSER "libpeconv/libpeconv" ) 8 | 9 | # modules paths: 10 | set (PECONV_DIR "${CMAKE_SOURCE_DIR}/${M_PARSER}" CACHE PATH "PEConv main path") 11 | add_subdirectory ( ${PECONV_DIR} ) 12 | set ( PECONV_LIB $ CACHE PATH "PEConvLib library path" ) 13 | 14 | # Add sub-directories 15 | # 16 | add_subdirectory ( pe_check ) 17 | add_subdirectory ( dll_load ) 18 | add_subdirectory ( kdb_check ) 19 | add_subdirectory ( syscall_extractor ) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PE utils 2 | [![Build status](https://ci.appveyor.com/api/projects/status/0o7akheju8te49d6?svg=true)](https://ci.appveyor.com/project/hasherezade/pe-utils) 3 | 4 | Set of small, self-contained utilities to be used in other toolkits, i.e. as helpers for PIN Tools ([example](https://github.com/hasherezade/tiny_tracer/tree/master/install32_64)). 5 | 6 | + **syscall_extractor** - Extract syscalls from system DLLs (ntdll.dll, win32u.dll) into a CSV file 7 | + **dll_load** - Loads a given DLL. Calls exported functions if supplied. 8 | + **pe_check** - Checks the bitness of the PE and outputs it as a return value. 9 | + **kdb_check** - Checks if the Kernel Debugger is enabled (no elevation required). Outputs the status as a return value. 10 | 11 | You can display the returned values of **pe_check** and **kdb_check** by: 12 | 13 | ```cmd 14 | echo %errorlevel% 15 | ``` 16 | -------------------------------------------------------------------------------- /dll_load/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project ( dll_load ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | option(DLL_LOAD_PAUSE_ON_FINISH "dll_load: pause after execution" ON) 8 | 9 | if(DLL_LOAD_PAUSE_ON_FINISH) 10 | add_compile_definitions(PAUSE_AFTER) 11 | endif() 12 | 13 | set (srcs 14 | main.cpp 15 | ) 16 | 17 | # general headers - they will be used for both EXE and DLL: 18 | set (hdrs 19 | #put your headers here 20 | ) 21 | 22 | set (rsrc 23 | #put your resources here 24 | ) 25 | 26 | add_executable ( ${PROJECT_NAME} ${exe_hdrs} ${srcs} ${rsrc} main.cpp ) 27 | 28 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 29 | -------------------------------------------------------------------------------- /dll_load/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /dll_load/README.md: -------------------------------------------------------------------------------- 1 | # dll_load 2 | 3 | Custom DLL loader (similar to `rundll32`). It allows to: 4 | + run a DLL without any exports (only `DllMain` will be executed) 5 | + run multiple exports, one after another 6 | + pause execution after the DLL finished 7 | 8 | ``` 9 | Loads a given DLL. Calls exported functions if supplied. 10 | Args: [*exports] 11 | * - optional 12 | exports: a list of functions separated by ';'. Examples: 13 | DllRegisterServer;DllUnregisterServer 14 | #1;#2 15 | ``` 16 | -------------------------------------------------------------------------------- /dll_load/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | size_t split_list(const std::string &sline, const char delimiter, std::vector &args) 7 | { 8 | std::istringstream f(sline); 9 | std::string s; 10 | while (getline(f, s, delimiter)) { 11 | args.push_back(s); 12 | } 13 | return args.size(); 14 | } 15 | 16 | bool run_dll_with_args(const wchar_t* pe_path, std::vector &exports) 17 | { 18 | HMODULE lib = LoadLibraryW(pe_path); 19 | if (!lib) { 20 | return false; 21 | } 22 | std::vector::iterator itr; 23 | for (itr = exports.begin(); itr != exports.end(); itr++) { 24 | std::string func_name = *itr; 25 | FARPROC func = NULL; 26 | if (func_name[0] == '#') { 27 | int ordinal; 28 | std::string ord_str = func_name.substr(1); 29 | std::stringstream ss; 30 | ss << std::dec << ord_str; 31 | ss >> ordinal; 32 | func = GetProcAddress(lib, MAKEINTRESOURCE(ordinal)); 33 | } 34 | else { 35 | func = GetProcAddress(lib, func_name.c_str()); 36 | } 37 | 38 | if (!func) continue; 39 | 40 | std::cout << "Calling the export: " << func_name << "\n"; 41 | int(*exp_func)() = (int(*)())func; 42 | func(); 43 | } 44 | return true; 45 | } 46 | 47 | int wmain(int argc, wchar_t *argv[]) 48 | { 49 | int is_dll_executed = 0; 50 | #ifdef _WIN64 51 | int bitness = 64; 52 | #else 53 | int bitness = 32; 54 | #endif 55 | if (argc < 2) { 56 | std::cout << "Loads a given DLL. Calls exported functions if supplied.\n"; 57 | std::cout << std::dec << bitness << "-bit version\n"; 58 | std::cout << "Built on: " __DATE__ << "\n"; 59 | std::cout << std::endl; 60 | std::cout << "Args: [*exports]\n"; 61 | std::cout << "\t* - optional\n"; 62 | std::cout << "\texports: a list of functions separated by ';'. Examples:\n"; 63 | std::cout << "\t DllRegisterServer;DllUnregisterServer\n"; 64 | std::cout << "\t #1;#2\n"; 65 | system("pause"); 66 | return 0; 67 | } 68 | wchar_t* pe_path = argv[1]; 69 | 70 | std::vector exports; 71 | if (argc >= 3) { 72 | // load exports: 73 | std::wstring paramsl = argv[2]; 74 | std::string params(paramsl.begin(), paramsl.end()); 75 | split_list(params, ';', exports); 76 | } 77 | 78 | if (run_dll_with_args(pe_path, exports)) { 79 | is_dll_executed = 1; 80 | std::cout << "[+] The Dll was run! " << std::endl; 81 | #ifdef PAUSE_AFTER 82 | system("pause"); 83 | #endif 84 | } 85 | return is_dll_executed; 86 | } 87 | -------------------------------------------------------------------------------- /kdb_check/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project ( kdb_check ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | set (srcs 8 | main.cpp 9 | ) 10 | 11 | # general headers - they will be used for both EXE and DLL: 12 | set (hdrs 13 | #put your headers here 14 | ) 15 | 16 | set (rsrc 17 | #put your resources here 18 | ) 19 | 20 | add_executable ( ${PROJECT_NAME} ${exe_hdrs} ${srcs} ${rsrc} main.cpp ) 21 | 22 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 23 | -------------------------------------------------------------------------------- /kdb_check/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /kdb_check/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define KUSER_SHARED_VA 0x7FFE0000 5 | #define KUSER_SHARED_SIZE 0x3B8 6 | 7 | inline bool is_kuser_shared_mapped() 8 | { 9 | if (IsBadReadPtr((BYTE*)KUSER_SHARED_VA, KUSER_SHARED_SIZE)) { 10 | std::cerr << "KDB: Failed to retrieve KUSER_SHARED_DATA\n"; 11 | return false; 12 | } 13 | return true; 14 | } 15 | 16 | typedef enum kdb_mode { 17 | KDB_UNKNOWN = (-1), 18 | KDB_DISABLED = 0, 19 | KDB_LOCAL_ENABLED = 1, 20 | KDB_REMOTE_ENABLED = 3 21 | } t_kdb_mode; 22 | 23 | t_kdb_mode is_kernelmode_dbg_enabled() 24 | { 25 | const ULONGLONG KdDebuggerEnable_offset = 0x2d4; 26 | if (!is_kuser_shared_mapped()) { 27 | return KDB_UNKNOWN; 28 | } 29 | BYTE *KdDebuggerEnable = (BYTE*)(KUSER_SHARED_VA + KdDebuggerEnable_offset); 30 | if (*KdDebuggerEnable) { 31 | /* 32 | this flag is selected if: 33 | bcdedit /debug on 34 | */ 35 | if (*KdDebuggerEnable == 3) { 36 | std::cout << "KDB: Remote enabled!\n"; 37 | return KDB_REMOTE_ENABLED; 38 | } 39 | std::cout << "KDB: Local enabled!\n"; 40 | return KDB_LOCAL_ENABLED; 41 | } 42 | std::cout << "KDB: Disabled\n"; 43 | return KDB_DISABLED; 44 | } 45 | 46 | int main() 47 | { 48 | return is_kernelmode_dbg_enabled(); 49 | } 50 | -------------------------------------------------------------------------------- /pe_check/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project ( pe_check ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | # include libpeconv headers: 8 | include_directories ( ${PECONV_DIR}/include ) 9 | 10 | set (srcs 11 | main.cpp 12 | ) 13 | 14 | # general headers - they will be used for both EXE and DLL: 15 | set (hdrs 16 | #put your headers here 17 | ) 18 | 19 | set (rsrc 20 | #put your resources here 21 | ) 22 | 23 | add_executable ( ${PROJECT_NAME} ${exe_hdrs} ${srcs} ${rsrc} main.cpp ) 24 | 25 | # link with libpeconv.lib 26 | target_link_libraries ( ${PROJECT_NAME} ${PECONV_LIB} ) 27 | 28 | #dependencies: 29 | add_dependencies( ${PROJECT_NAME} libpeconv ) 30 | 31 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 32 | -------------------------------------------------------------------------------- /pe_check/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /pe_check/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include // include libPeConv header 5 | 6 | #ifndef COMIMAGE_FLAGS_32BITPREFERRED 7 | #define COMIMAGE_FLAGS_32BITPREFERRED 0x20000 8 | #endif 9 | 10 | #ifndef COMIMAGE_FLAGS_32BITREQUIRED 11 | #define COMIMAGE_FLAGS_32BITREQUIRED 0x00002 12 | #endif 13 | 14 | typedef enum { 15 | PE_UNKNOWN = 0, 16 | PE_32BIT = 32, 17 | PE_64BIT = 64 18 | } t_bitness; 19 | 20 | namespace util { 21 | bool is_wow_64(HANDLE process) 22 | { 23 | FARPROC procPtr = GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); 24 | if (!procPtr) { 25 | //this system does not have a function IsWow64Process 26 | return false; 27 | } 28 | BOOL(WINAPI * is_process_wow64)(IN HANDLE, OUT PBOOL) 29 | = (BOOL(WINAPI*)(IN HANDLE, OUT PBOOL))procPtr; 30 | 31 | BOOL isCurrWow64 = FALSE; 32 | if (!is_process_wow64(process, &isCurrWow64)) { 33 | return false; 34 | } 35 | return isCurrWow64 ? true : false; 36 | } 37 | 38 | BOOL wow64_disable_fs_redirection(OUT PVOID* OldValue) 39 | { 40 | FARPROC procPtr = GetProcAddress(GetModuleHandleA("kernel32"), "Wow64DisableWow64FsRedirection"); 41 | if (!procPtr) return FALSE; 42 | 43 | BOOL(WINAPI * _Wow64DisableWow64FsRedirection)(OUT PVOID*) = (BOOL(WINAPI*) (OUT PVOID*))procPtr; 44 | 45 | if (!_Wow64DisableWow64FsRedirection) { 46 | return FALSE; 47 | } 48 | return _Wow64DisableWow64FsRedirection(OldValue); 49 | } 50 | 51 | BOOL wow64_revert_fs_redirection(IN PVOID OldValue) 52 | { 53 | FARPROC procPtr = GetProcAddress(GetModuleHandleA("kernel32"), "Wow64RevertWow64FsRedirection"); 54 | if (!procPtr) return FALSE; 55 | 56 | BOOL(WINAPI * _Wow64RevertWow64FsRedirection)(IN PVOID) = (BOOL(WINAPI*) (IN PVOID))procPtr; 57 | 58 | if (!_Wow64RevertWow64FsRedirection) { 59 | return FALSE; 60 | } 61 | return _Wow64RevertWow64FsRedirection(OldValue); 62 | } 63 | }; 64 | 65 | 66 | t_bitness get_bitness(BYTE *buffer, size_t buffer_size) 67 | { 68 | if (!peconv::get_nt_hdrs(buffer)) { 69 | return PE_UNKNOWN; 70 | } 71 | bool is64 = peconv::is64bit(buffer); 72 | if (is64) { 73 | return PE_64BIT; 74 | } 75 | // in case of .NET files, a PE with a 32-bit header still may be loaded as 64, depending on the flags 76 | IMAGE_DATA_DIRECTORY* dotNetDir = peconv::get_directory_entry(buffer, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR); 77 | if (!dotNetDir) { 78 | #ifdef _DEBUG 79 | std::cout << "Not .NET\n"; 80 | #endif 81 | return PE_32BIT; 82 | } 83 | IMAGE_COR20_HEADER *dnet = peconv::get_dotnet_hdr(buffer, buffer_size, dotNetDir); 84 | if (!dnet) { 85 | return PE_32BIT; 86 | } 87 | bool is_real_32 = (dnet->Flags & COMIMAGE_FLAGS_32BITPREFERRED) || (dnet->Flags & COMIMAGE_FLAGS_32BITREQUIRED); 88 | #ifdef _WIN64 89 | bool is_on_64 = true; 90 | #else 91 | bool is_on_64 = util::is_wow_64(GetCurrentProcess()); 92 | #endif 93 | if (is_on_64 && !is_real_32) { 94 | // the system is 64 bit, so the .NET app got switched to the 64 bit mode 95 | #ifdef _DEBUG 96 | std::cout << "This is 32-bit .NET App that will be loaded as 64 bit...\n"; 97 | #endif 98 | return PE_64BIT; 99 | } 100 | return PE_32BIT; 101 | } 102 | 103 | int main(int argc, char *argv[]) 104 | { 105 | if (argc < 2) { 106 | std::cout << "Checks if the given PE will run as 32 or 64 bit.\n" 107 | << "Returns the number of bits.\n" 108 | "URL: https://github.com/hasherezade/pe_check\n" 109 | << std::endl; 110 | std::cout << "args: " << std::endl; 111 | 112 | system("pause"); 113 | return 0; 114 | } 115 | 116 | VOID* old_val = NULL; 117 | util::wow64_disable_fs_redirection(&old_val); 118 | LPCSTR pe_path = argv[1]; 119 | size_t bufsize = 0; 120 | BYTE *buffer = peconv::load_pe_module(pe_path, bufsize, false, false); 121 | util::wow64_revert_fs_redirection(&old_val); 122 | 123 | if (!buffer) { 124 | return 0; 125 | } 126 | 127 | t_bitness my_bitness = get_bitness(buffer, bufsize); 128 | peconv::free_pe_buffer(buffer); 129 | #ifdef _DEBUG 130 | std::cout << "Bitness: " << my_bitness << "\n"; 131 | #endif 132 | return (int) my_bitness; 133 | } 134 | -------------------------------------------------------------------------------- /syscall_extractor/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project ( syscall_extract ) 4 | 5 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") 6 | 7 | # include libpeconv headers: 8 | include_directories ( ${PECONV_DIR}/include ) 9 | 10 | set (srcs 11 | main.cpp 12 | util.cpp 13 | ) 14 | 15 | # general headers - they will be used for both EXE and DLL: 16 | set (hdrs 17 | util.h 18 | ) 19 | 20 | set (rsrc 21 | #put your resources here 22 | ) 23 | 24 | add_executable ( ${PROJECT_NAME} ${exe_hdrs} ${srcs} ${rsrc} main.cpp ) 25 | 26 | # link with libpeconv.lib 27 | target_link_libraries ( ${PROJECT_NAME} ${PECONV_LIB} ) 28 | 29 | #dependencies: 30 | add_dependencies( ${PROJECT_NAME} libpeconv ) 31 | 32 | INSTALL( TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT ${PROJECT_NAME} ) 33 | -------------------------------------------------------------------------------- /syscall_extractor/LICENSE: -------------------------------------------------------------------------------- 1 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 2 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 4 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 5 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 6 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 7 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 8 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 9 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 10 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 11 | -------------------------------------------------------------------------------- /syscall_extractor/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include // include libPeConv header 6 | #include "util.h" 7 | 8 | bool isSyscallFunc(const std::string &funcName) 9 | { 10 | std::string prefix("Nt"); 11 | if (funcName.size() < (prefix.size() + 1)) { 12 | return false; 13 | } 14 | if (funcName.compare(0, prefix.size(), prefix) != 0) { 15 | return false; 16 | } 17 | char afterPrefix = funcName.at(prefix.size()); 18 | if (afterPrefix >= 'A' && afterPrefix <= 'Z') { 19 | // the name of the function after the Nt prefix will start in uppercase, 20 | // syscalls are in functions like: NtUserSetWindowLongPtr, but not: NtdllDefWindowProc_A 21 | return true; 22 | } 23 | return false; 24 | } 25 | 26 | size_t extract_syscalls(BYTE* pe_buf, size_t pe_size, std::stringstream& outs, size_t startID = 0) 27 | { 28 | std::vector names_list; 29 | if (!peconv::get_exported_names(pe_buf, names_list)) { 30 | return 0; 31 | } 32 | 33 | std::map sys_functions; 34 | for (auto itr = names_list.begin(); itr != names_list.end(); ++itr) { 35 | std::string funcName = *itr; 36 | if (isSyscallFunc(funcName)) { 37 | ULONG_PTR va = (ULONG_PTR)peconv::get_exported_func(pe_buf, funcName.c_str()); 38 | if (!va) continue; 39 | 40 | DWORD rva = DWORD(va - (ULONG_PTR)pe_buf); 41 | sys_functions[rva] = funcName; 42 | } 43 | } 44 | size_t id = startID; 45 | for (auto itr = sys_functions.begin(); itr != sys_functions.end(); ++itr) { 46 | std::string funcName = itr->second; 47 | outs << std::hex << "0x" << id++ << "," << funcName << "\n"; 48 | } 49 | return id; 50 | } 51 | 52 | size_t extract_from_dll(IN const std::string &path, size_t startSyscallID, OUT std::stringstream &outs) 53 | { 54 | size_t bufsize = 0; 55 | BYTE* buffer = peconv::load_pe_module(path.c_str(), bufsize, false, false); 56 | 57 | if (!buffer) { 58 | std::cerr << "Failed to load the PE: " << path << "\n"; 59 | return 0; 60 | } 61 | 62 | size_t extracted_count = extract_syscalls(buffer, bufsize, outs, startSyscallID); 63 | peconv::free_pe_buffer(buffer); 64 | 65 | if (!extracted_count) { 66 | std::cerr << "No syscalls extracted from: " << path << "\n"; 67 | } 68 | return extracted_count; 69 | } 70 | 71 | int main(int argc, char *argv[]) 72 | { 73 | std::string outFileName = "syscalls.txt"; 74 | if (argc < 2) { 75 | std::cout << "Extract syscalls from system DLLs (ntdll.dll, win32u.dll)\n" 76 | << "Source: https://github.com/hasherezade/pe_utils\n" 77 | << "\tOptional arg: " 78 | << std::endl; 79 | } 80 | else { 81 | outFileName = argv[1]; 82 | } 83 | 84 | PVOID old_val = NULL; 85 | util::wow64_disable_fs_redirection(&old_val); 86 | 87 | std::stringstream outs; 88 | size_t extracted_count = 0; 89 | 90 | char ntdll_path[MAX_PATH] = { 0 }; 91 | ExpandEnvironmentStringsA("%SystemRoot%\\system32\\ntdll.dll", ntdll_path, MAX_PATH); 92 | extracted_count += extract_from_dll(ntdll_path, 0, outs); 93 | 94 | char win32u_path[MAX_PATH] = { 0 }; 95 | ExpandEnvironmentStringsA("%SystemRoot%\\system32\\win32u.dll", win32u_path, MAX_PATH); 96 | extracted_count += extract_from_dll(win32u_path, 0x1000, outs); 97 | 98 | util::wow64_revert_fs_redirection(&old_val); 99 | 100 | if (!extracted_count) { 101 | std::cerr << "Failed to extract syscalls.\n"; 102 | return (-1); 103 | } 104 | 105 | std::ofstream myfile; 106 | myfile.open(outFileName); 107 | myfile << outs.str(); 108 | myfile.close(); 109 | std::cout << "Saved to: " << outFileName << std::endl; 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /syscall_extractor/util.cpp: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | BOOL(WINAPI* g_Wow64DisableWow64FsRedirection) (OUT PVOID* OldValue) = nullptr; 4 | BOOL(WINAPI* g_Wow64RevertWow64FsRedirection) (IN PVOID OldValue) = nullptr; 5 | 6 | BOOL util::wow64_disable_fs_redirection(OUT PVOID* OldValue) 7 | { 8 | if (!g_Wow64DisableWow64FsRedirection) { 9 | HMODULE kernelLib = GetModuleHandleA("kernel32.dll"); 10 | if (!kernelLib) return FALSE; 11 | 12 | FARPROC procPtr = GetProcAddress(kernelLib, "Wow64DisableWow64FsRedirection"); 13 | if (!procPtr) return FALSE; 14 | 15 | g_Wow64DisableWow64FsRedirection = (BOOL(WINAPI*) (OUT PVOID*))procPtr; 16 | } 17 | if (!g_Wow64DisableWow64FsRedirection) { 18 | return FALSE; 19 | } 20 | return g_Wow64DisableWow64FsRedirection(OldValue); 21 | } 22 | 23 | BOOL util::wow64_revert_fs_redirection(IN PVOID OldValue) 24 | { 25 | if (!g_Wow64RevertWow64FsRedirection) { 26 | HMODULE kernelLib = GetModuleHandleA("kernel32.dll"); 27 | if (!kernelLib) return FALSE; 28 | 29 | FARPROC procPtr = GetProcAddress(kernelLib, "Wow64RevertWow64FsRedirection"); 30 | if (!procPtr) return FALSE; 31 | 32 | g_Wow64RevertWow64FsRedirection = (BOOL(WINAPI*) (IN PVOID))procPtr; 33 | } 34 | if (!g_Wow64RevertWow64FsRedirection) { 35 | return FALSE; 36 | } 37 | return g_Wow64RevertWow64FsRedirection(OldValue); 38 | } 39 | -------------------------------------------------------------------------------- /syscall_extractor/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace util { 6 | BOOL wow64_disable_fs_redirection(OUT PVOID* OldValue); 7 | BOOL wow64_revert_fs_redirection(IN PVOID OldValue); 8 | }; 9 | --------------------------------------------------------------------------------