├── docs ├── CNAME ├── _config.yml ├── README.md ├── vulnscan.sigs └── LICENSE.md ├── src ├── vuln_db.h ├── vulnerability.cpp ├── source_file.h ├── asm_scanner.h ├── string_scanner.h ├── disassembler.h ├── file_typer.h ├── disassembler.cpp ├── test_signature.cpp ├── linux_binary.cpp ├── scan_target.h ├── file_typer.cpp ├── string_scanner.cpp ├── scan_target.cpp ├── utils │ ├── thread_pool.cpp │ ├── thread_pool.h │ ├── thread_task.h │ └── task_queue.h ├── scan_engine.h ├── vulnscan.cpp ├── vuln_report.h ├── vulnerability.h ├── symbols.h ├── scan_results.h ├── vuln_report.cpp ├── scanner.h ├── signature.h ├── windows_binary.cpp ├── scan_engine.cpp ├── signature.cpp ├── binary_file.h ├── asm_scanner.cpp └── windows_symbols.cpp ├── dependencies ├── strings2 │ ├── strings │ │ ├── basics.h │ │ ├── module.h │ │ ├── process_strings.h │ │ ├── print_buffer.h │ │ ├── targetver.h │ │ ├── print_buffer.cpp │ │ ├── string_parser.h │ │ ├── string_hashes.h │ │ ├── DynArray.h │ │ ├── string_parser.cpp │ │ └── dirent.h │ ├── CMakeLists.txt │ ├── license.txt │ └── README.md ├── CMakeLists.capstone.txt ├── CMakeLists.peparse.txt ├── CMakeLists.gtest.txt └── CMakeLists.curl.txt ├── .gitignore ├── test ├── test_windows_binary.cpp ├── test_scan_engine.cpp ├── main.cpp ├── test_vulnerability.cpp ├── test_disassembler.cpp ├── test_file_typer.cpp └── test_signature.cpp └── CMakeLists.txt /docs/CNAME: -------------------------------------------------------------------------------- 1 | vulnscan.us -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /src/vuln_db.h: -------------------------------------------------------------------------------- 1 | 2 | class VulnerablityDatabase { 3 | public: 4 | CVE search(const Codesnippet &cs); 5 | } -------------------------------------------------------------------------------- /dependencies/strings2/strings/basics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //#include 4 | //#include 5 | 6 | void PrintLastError(LPTSTR lpszFunction); -------------------------------------------------------------------------------- /src/vulnerability.cpp: -------------------------------------------------------------------------------- 1 | #include "vulnerability.h" 2 | 3 | CVulnerablity::CVulnerablity(std::string sCVE) : sCVE(sCVE) { 4 | status = DETECTION_NOMATCH; 5 | } 6 | -------------------------------------------------------------------------------- /src/source_file.h: -------------------------------------------------------------------------------- 1 | #include "clang/Frontend/FrontendOptions.h" 2 | 3 | class SourceFile: clang::FrontendInputFile { 4 | 5 | public: 6 | llvm::MemoryBuffer* getCodeSection() const; 7 | }; 8 | -------------------------------------------------------------------------------- /src/asm_scanner.h: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | 3 | class CASMScanner : public IScanner { 4 | 5 | public: 6 | CASMScanner(); 7 | 8 | public: 9 | SCAN_STATUS initialize(std::shared_ptr SignatureLoader); 10 | SCAN_STATUS scan(std::shared_ptr spBinaryFile, std::unique_ptr& spVulnReport); 11 | }; -------------------------------------------------------------------------------- /src/string_scanner.h: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | 3 | class CStringScanner : public IScanner { 4 | public: 5 | CStringScanner(); 6 | 7 | public: 8 | SCAN_RESULT initialize(std::shared_ptr SignatureLoader); 9 | SCAN_RESULT scan(std::shared_ptr spBinaryFile, std::unique_ptr& spVulnReport); 10 | 11 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.out 31 | *.app 32 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //#include 3 | //#include 4 | #include 5 | #include 6 | #include "DynArray.h" 7 | 8 | class module 9 | { 10 | MODULEENTRY32W moduleDetails; 11 | public: 12 | bool contains(unsigned int address); 13 | 14 | module(MODULEENTRY32W details); 15 | ~module(void); 16 | bool operator== (const module &other) const; 17 | }; 18 | -------------------------------------------------------------------------------- /dependencies/CMakeLists.capstone.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(capstone-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(capstone 7 | GIT_REPOSITORY https://github.com/aquynh/capstone.git 8 | GIT_TAG 4.0.1 9 | SOURCE_DIR ${CMAKE_BINARY_DIR}/capstone-src 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/capstone-build 11 | CONFIGURE_COMMAND 12 | BUILD_COMMAND 13 | INSTALL_COMMAND 14 | TEST_COMMAND 15 | ) -------------------------------------------------------------------------------- /dependencies/CMakeLists.peparse.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(pe-parse-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(pe-parse 7 | GIT_REPOSITORY https://github.com/trailofbits/pe-parse.git 8 | GIT_TAG master 9 | SOURCE_DIR ${CMAKE_BINARY_DIR}/pe-parse-src 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/pe-parse-build 11 | CONFIGURE_COMMAND 12 | BUILD_COMMAND 13 | INSTALL_COMMAND 14 | TEST_COMMAND 15 | ) -------------------------------------------------------------------------------- /test/test_windows_binary.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "binary_file.h" 3 | 4 | /* 5 | TEST(BinaryFile, printResult) 6 | { 7 | std::string test("test"); 8 | IBinaryFile *pBinaryFile = nullptr; 9 | SCAN_RESULT sr = BinaryFactory::GetBinary(test, &pBinaryFile); 10 | ASSERT_EQ(sr, SCAN_RESULT_SUCCESS); 11 | 12 | ASSERT_NE(pBinaryFile, nullptr); 13 | VulnReport *pReport; 14 | sr = pBinaryFile->scan(&pReport); 15 | ASSERT_EQ(sr, SCAN_RESULT_SUCCESS); 16 | 17 | } 18 | */ -------------------------------------------------------------------------------- /src/disassembler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scan_results.h" 4 | #include "capstone\capstone.h" 5 | 6 | #include 7 | 8 | namespace Disassembler { 9 | 10 | typedef struct cs_insn Instruction, *PInstruction; 11 | 12 | typedef struct _InstructionSet { 13 | cs_insn* pInsn; 14 | size_t count; 15 | 16 | ~_InstructionSet() { cs_free(pInsn, count); } 17 | } InstructionSet; 18 | 19 | SCAN_RESULT Disassembly(const uint8_t* code, size_t size, uint64_t baseAddress, bool b64bit, InstructionSet& instructions); 20 | }; -------------------------------------------------------------------------------- /dependencies/CMakeLists.gtest.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG release-1.8.1 9 | SOURCE_DIR ${CMAKE_BINARY_DIR}/googletest-src 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/googletest-build 11 | CONFIGURE_COMMAND 12 | CMAKE_ARGS -DGTEST_CREATE_SHARED_LIBRARY=1 13 | BUILD_COMMAND 14 | INSTALL_COMMAND 15 | TEST_COMMAND 16 | ) -------------------------------------------------------------------------------- /dependencies/CMakeLists.curl.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(curl-download NONE) 4 | include(ExternalProject) 5 | 6 | ExternalProject_Add(curl 7 | GIT_REPOSITORY https://github.com/curl/curl.git 8 | GIT_TAG curl-7_62_0 9 | SOURCE_DIR ${CMAKE_BINARY_DIR}/curl-src 10 | BINARY_DIR ${CMAKE_BINARY_DIR}/curl-build 11 | CONFIGURE_COMMAND 12 | CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DBUILD_CURL_TESTS=OFF -DCURL_STATICLIB=ON 13 | BUILD_COMMAND 14 | INSTALL_COMMAND 15 | TEST_COMMAND 16 | ) -------------------------------------------------------------------------------- /test/test_scan_engine.cpp: -------------------------------------------------------------------------------- 1 | #include "scan_engine.h" 2 | #include "scanner.h" 3 | #include "gtest\gtest.h" 4 | #include "gmock\gmock.h" 5 | //#include 6 | 7 | 8 | class CMockScanner : public IScanner { 9 | public: 10 | MOCK_METHOD1(scan, SCAN_RESULT(std::shared_ptr& spVulnReport)); 11 | MOCK_METHOD0(getType, std::string()); 12 | }; 13 | 14 | 15 | TEST(CScanEngine, scanFile) { 16 | std::shared_ptr spMockScanner = std::make_shared(); 17 | CScanEngine engine(spMockScanner, spMockScanner); 18 | engine.scanFile("."); 19 | } -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "gtest/gtest.h" 3 | 4 | int main(int argc, char** argv) 5 | { 6 | int tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); 7 | tmpDbgFlag |= _CRTDBG_ALLOC_MEM_DF; 8 | tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; 9 | _CrtSetDbgFlag(tmpDbgFlag); 10 | //_CrtMemState memoryState = { 0 }; 11 | //_CrtMemCheckpoint(&memoryState); 12 | ::testing::InitGoogleTest(&argc, argv); 13 | 14 | 15 | int retval = RUN_ALL_TESTS(); 16 | //_CrtDumpMemoryLeaks(); 17 | // Check for leaks after tests have run 18 | //_CrtMemDumpAllObjectsSince(&memoryState); 19 | return retval; 20 | } 21 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/process_strings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //#include "windows.h" 3 | #include "module.h" 4 | //#include 5 | #include "string_parser.h" 6 | //#include 7 | #include "basics.h" 8 | #pragma comment(lib, "Psapi") 9 | 10 | class process_strings 11 | { 12 | DynArray modules; 13 | string_parser* parser; 14 | 15 | void generateModuleList(HANDLE hSnapshot); 16 | bool processAllHeaps(HANDLE ph, char* process_name); 17 | public: 18 | process_strings(string_parser* parser); 19 | bool dump_process(DWORD pid); 20 | bool dump_system(); 21 | ~process_strings(void); 22 | }; 23 | -------------------------------------------------------------------------------- /src/file_typer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gtest/gtest_prod.h" 4 | #include 5 | 6 | class FileTyper { 7 | 8 | 9 | public: 10 | enum class TYPE { 11 | EXE, //windows executable 12 | ELF, //linux executable 13 | BIN, //other binaries 14 | TEXT, 15 | UNKNOWN 16 | }; 17 | 18 | private: 19 | std::string sFilePath; 20 | TYPE type; 21 | 22 | public: 23 | FileTyper(std::string& sFilePath); 24 | 25 | 26 | public: 27 | inline bool isBinary() {return type < TYPE::TEXT;}; 28 | inline bool isEXE() {return type == TYPE::EXE;}; 29 | inline bool isELF() {return type == TYPE::ELF;}; 30 | 31 | private: 32 | FRIEND_TEST(FileTyper, typing); 33 | void typing(); 34 | }; -------------------------------------------------------------------------------- /src/disassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "disassembler.h" 2 | #include "capstone\capstone.h" 3 | #include "binary_file.h" 4 | 5 | namespace Disassembler { 6 | 7 | SCAN_RESULT Disassembly(const uint8_t* code, size_t size, 8 | uint64_t baseAddress, bool b64bit, InstructionSet& instructions) { 9 | csh handle; 10 | if (cs_open(CS_ARCH_X86, b64bit ? 11 | CS_MODE_64 : CS_MODE_32, &handle) != CS_ERR_OK) { 12 | return SCAN_RESULT_NOT_FOUND; 13 | } 14 | cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); 15 | instructions.count = cs_disasm(handle, code, size, 16 | baseAddress, 0, &(instructions.pInsn)); 17 | cs_close(&handle); 18 | if (instructions.count <= 0) { 19 | return SCAN_RESULT_NOT_SUPPORT; 20 | } 21 | return SCAN_RESULT_SUCCESS; 22 | } 23 | }; -------------------------------------------------------------------------------- /dependencies/strings2/strings/print_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class print_buffer 7 | { 8 | int buffer_size; 9 | int space_used; 10 | char* buffer; 11 | public: 12 | void addStrings(char* string1, char* string2, char* string3, char* string4, char* string5); 13 | void addStrings(char* string1, char* string2, char* string3, char* string4); 14 | void addStrings(char* string1, char* string2, char* string3); 15 | void addStrings(char* string1, char* string2); 16 | void addString(char* string, int length); 17 | void addString(char* string); 18 | void addLine(char* string, int length); 19 | void addLine(char* string); 20 | void digest(); 21 | print_buffer(int buffer_size); 22 | ~print_buffer(void); 23 | }; 24 | -------------------------------------------------------------------------------- /src/test_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "signature.h" 3 | 4 | TEST(SignatureLoader, loadSigs) 5 | { 6 | std::istringstream is("CVE-2017-9502\n\ 7 | STRING:\n\ 8 | + expected localhost or 127.0.0.1 or none\n\ 9 | ASM:\n\ 10 | - parseurlandfillconn strlen strlen strlen curl_strmequal Curl_failf curl_strmequal curl_strmequal Curl_failf Curl_cmalloc\n\ 11 | \n\ 12 | CVE-2017-1000100\n\ 13 | STRING:\n\ 14 | + TFTP: Unknown transfer ID\n\ 15 | + Disk full or allocation exceeded\n\ 16 | + tftp_rx: giving up waiting for block\n\ 17 | - TFTP file name too long"); 18 | 19 | SignatureLoader loader; 20 | loader.loadSigs(is); 21 | EXPECT_EQ(loader.getSize(), 2); 22 | EXPECT_EQ(loader.getSignature(0)->getCVE(), "CVE-2017-9502"); 23 | EXPECT_EQ(loader.getSignature(1)->getCVE(), "CVE-2017-1000100"); 24 | 25 | } -------------------------------------------------------------------------------- /dependencies/strings2/strings/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The following macros define the minimum required platform. The minimum required platform 4 | // is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 5 | // your application. The macros work by enabling all features available on platform versions up to and 6 | // including the version specified. 7 | 8 | // Modify the following defines if you have to target a platform prior to the ones specified below. 9 | // Refer to MSDN for the latest info on corresponding values for different platforms. 10 | #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. 11 | #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. 12 | #endif 13 | 14 | -------------------------------------------------------------------------------- /test/test_vulnerability.cpp: -------------------------------------------------------------------------------- 1 | #include "vulnerability.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include 6 | 7 | TEST(CVulnerablity, getsetDetectStatus) 8 | { 9 | CVulnerablity vuln("CVE-test"); 10 | 11 | ASSERT_EQ(vuln.getDetectStatus(), DETECTION_NOMATCH); 12 | vuln.setDetectStatus(DETECTION_ASM_MATCH | DETECTION_STRING_MATCH); 13 | ASSERT_EQ(vuln.getDetectStatus(), DETECTION_ASM_MATCH | DETECTION_STRING_MATCH); 14 | } 15 | 16 | TEST(CVulnerablity, appendDetectStatus) 17 | { 18 | CVulnerablity vuln("CVE-test"); 19 | ASSERT_EQ(vuln.getDetectStatus(), DETECTION_NOMATCH); 20 | vuln.appendDetectStatus(DETECTION_ASM_MATCH); 21 | ASSERT_EQ(vuln.getDetectStatus(), DETECTION_ASM_MATCH); 22 | vuln.appendDetectStatus(DETECTION_STRING_MATCH); 23 | ASSERT_EQ(vuln.getDetectStatus(), DETECTION_ASM_MATCH | DETECTION_STRING_MATCH); 24 | } -------------------------------------------------------------------------------- /src/linux_binary.cpp: -------------------------------------------------------------------------------- 1 | #include "binary_file.h" 2 | 3 | LinuxBinary::LinuxBinary(std::string sFilePath) { 4 | this->sFilePath = sFilePath; 5 | } 6 | 7 | 8 | SCAN_RESULT LinuxBinary::analyze() { 9 | return SCAN_RESULT_NOT_SUPPORT; 10 | } 11 | 12 | uint64_t LinuxBinary::getCodeSectionBase() { 13 | return 0; 14 | } 15 | size_t LinuxBinary::getCodeSectionSize() { 16 | return 0; 17 | } 18 | 19 | SCAN_RESULT LinuxBinary::getInstFromAddress(uint64_t ullAddress, size_t iLength, 20 | std::unique_ptr& spInstSet) { 21 | return SCAN_RESULT_NOT_SUPPORT; 22 | } 23 | 24 | SCAN_RESULT LinuxBinary::getCodeSection(std::vector& vCode) { 25 | return SCAN_RESULT_NOT_SUPPORT; 26 | } 27 | 28 | SCAN_RESULT LinuxBinary::readStrings() { 29 | return SCAN_RESULT_NOT_SUPPORT; 30 | } 31 | 32 | bool LinuxBinary::searchStrings(std::string sSearch) { 33 | return false; 34 | } -------------------------------------------------------------------------------- /test/test_disassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "disassembler.h" 3 | 4 | #include 5 | 6 | TEST(Disassembler, Disassembly) 7 | { 8 | // CODE = "\x55\x48\x8b\x05\xb8\x13\x00\x00" 9 | std::vector vCode; 10 | vCode.push_back(0x55); 11 | vCode.push_back(0x48); 12 | vCode.push_back(0x8b); 13 | vCode.push_back(0x05); 14 | vCode.push_back(0xb8); 15 | vCode.push_back(0x13); 16 | vCode.push_back(0x00); 17 | vCode.push_back(0x00); 18 | 19 | Disassembler::InstructionSet instructions; 20 | SCAN_RESULT sr = Disassembler::Disassembly(vCode.data(), vCode.size(), 0, false, instructions); 21 | ASSERT_EQ(sr, SCAN_RESULT_SUCCESS); 22 | ASSERT_EQ(instructions.count, 3); 23 | 24 | Disassembler::InstructionSet instructions64; 25 | sr = Disassembler::Disassembly(vCode.data(), vCode.size(), 0, true, instructions64); 26 | ASSERT_EQ(sr, SCAN_RESULT_SUCCESS); 27 | ASSERT_EQ(instructions64.count, 2); 28 | } -------------------------------------------------------------------------------- /dependencies/strings2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8 FATAL_ERROR) 2 | 3 | project(strings2) 4 | 5 | enable_language(C) 6 | enable_language(CXX) 7 | 8 | if(CMAKE_CXX_COMPILER_ID MATCHES GNU) 9 | set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wno-sign-compare -Woverloaded-virtual -Wwrite-strings -Wno-unused") 10 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") 11 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") 13 | endif() 14 | 15 | 16 | ######################################################################### 17 | # Build main project 18 | ######################################################################### 19 | include_directories( 20 | ${PROJECT_SOURCE_DIR}/strings 21 | ) 22 | 23 | 24 | add_library( 25 | ${PROJECT_NAME} STATIC 26 | strings/print_buffer.cpp 27 | strings/string_parser.cpp 28 | ) -------------------------------------------------------------------------------- /src/scan_target.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class IScanTarget { 7 | public: 8 | /** 9 | * get next file to scan 10 | * @return NULL if no more files to scan 11 | */ 12 | virtual std::string getNextFile() = 0; 13 | 14 | /** 15 | * validate and initialize the scan target, collect all scanable files 16 | */ 17 | virtual bool initialize() = 0; 18 | 19 | }; 20 | 21 | class CScanTarget : public IScanTarget { 22 | public: 23 | CScanTarget(std::string sTargetPath); 24 | bool initialize(); 25 | std::string getNextFile(); 26 | 27 | private: 28 | void collectFile(); 29 | 30 | private: 31 | std::string sTargetPath; 32 | std::queue qScanList; 33 | }; 34 | 35 | class CScanTargetFactory { 36 | public: 37 | static std::unique_ptr createScanTarget(std::string sTargetPath) { 38 | return std::make_unique(sTargetPath); 39 | } 40 | }; -------------------------------------------------------------------------------- /src/file_typer.cpp: -------------------------------------------------------------------------------- 1 | #include "file_typer.h" 2 | #include 3 | 4 | FileTyper::FileTyper(std::string& sFilePath) : sFilePath(sFilePath), type(TYPE::UNKNOWN){ 5 | typing(); 6 | } 7 | 8 | void FileTyper::typing() { 9 | FILE *pFile = fopen(sFilePath.c_str(), "rb"); 10 | if (!pFile) { 11 | perror("Failed to read file\n"); 12 | return; 13 | } 14 | uint8_t firstByte = getc(pFile); 15 | if (firstByte == 'M') { 16 | if (getc(pFile) == 'Z') { 17 | type = TYPE::EXE; 18 | } 19 | } 20 | else if (firstByte == 0x7f) { 21 | if ((getc(pFile) == 'E') && (getc(pFile) == 'L' && (getc(pFile) == 'F'))) { 22 | type = TYPE::ELF; 23 | } 24 | } 25 | else { 26 | // a quick dirty way to check if file is binary 27 | // check if there is any NUL character in the first 100 bytes 28 | size_t checkLength = 100; 29 | for (size_t i = 0; i < checkLength; i++) { 30 | if (getc(pFile) == '\0') { 31 | type = TYPE::BIN; 32 | break; 33 | } 34 | } 35 | if (type != TYPE::BIN) { 36 | type = TYPE::TEXT; 37 | } 38 | } 39 | fclose(pFile); 40 | } -------------------------------------------------------------------------------- /test/test_file_typer.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include "file_typer.h" 3 | #include 4 | #include 5 | 6 | TEST(FileTyper, typing) 7 | { 8 | std::string tempFile("typing_test.temp"); 9 | std::ofstream out; 10 | out.open(tempFile, std::ios::out); 11 | 12 | out << "This is a text file.\n"; 13 | out.close(); 14 | FileTyper typer(tempFile); 15 | typer.typing(); 16 | EXPECT_EQ(typer.isBinary(), false); 17 | 18 | out.open(tempFile, std::ios::out | std::ios::binary); 19 | out << "MZ"; 20 | out.close(); 21 | typer.typing(); 22 | EXPECT_EQ(typer.isEXE(), true); 23 | 24 | out.open(tempFile, std::ios::out | std::ios::binary); 25 | char bytes1[4] = { 0x7f, 'E','L','F' }; 26 | out.write(bytes1, 4); 27 | out.close(); 28 | typer.typing(); 29 | EXPECT_EQ(typer.isELF(), true); 30 | 31 | out.open(tempFile, std::ios::out | std::ios::binary); 32 | char bytes2[4] = { 'A', 'B','C','\0' }; 33 | out.write(bytes2, 4); 34 | out.close(); 35 | typer.typing(); 36 | EXPECT_EQ(typer.isBinary(), true); 37 | 38 | std::remove(tempFile.c_str()); 39 | } -------------------------------------------------------------------------------- /src/string_scanner.cpp: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | #include 3 | 4 | CStringScanner::CStringScanner(std::shared_ptr spSigLoader, 5 | std::shared_ptr spBinaryFile) 6 | : spSigLoader(spSigLoader){ 7 | assert(spBinaryFile != nullptr); 8 | this->spBinaryFile = spBinaryFile; 9 | } 10 | 11 | SCAN_RESULT CStringScanner::scan(std::shared_ptr& spVulnReport) { 12 | SCAN_RESULT sr = spBinaryFile->analyze(); 13 | if (SCAN_FAILED(sr)) { 14 | std::cout << "Failed to analyze binary" << std::endl; 15 | return sr; 16 | } 17 | 18 | if (spVulnReport == nullptr) 19 | spVulnReport = CVulnReportFactory::createReport(); 20 | 21 | auto vStrings = spBinaryFile->getStrings(); 22 | for (size_t i = 0; i < spSigLoader->getSize(); i++) { 23 | auto spSignature = spSigLoader->getSignature(i); 24 | if (spSignature->hasStringSig()) { 25 | DETECTION_STATUS status = spSignature->stringMatch(vStrings); 26 | if (status != DETECTION_NOMATCH) 27 | spVulnReport->addDetection(spSignature->getCVE(), status); 28 | } 29 | 30 | } 31 | 32 | return SCAN_RESULT_SUCCESS; 33 | } -------------------------------------------------------------------------------- /src/scan_target.cpp: -------------------------------------------------------------------------------- 1 | #include "scan_target.h" 2 | #include 3 | 4 | namespace fs = std::experimental::filesystem; 5 | 6 | CScanTarget::CScanTarget(std::string sTargetPath) : sTargetPath (sTargetPath){ 7 | qScanList = std::queue(); 8 | } 9 | 10 | bool CScanTarget::initialize() { 11 | collectFile(); 12 | return !qScanList.empty(); 13 | } 14 | 15 | std::string CScanTarget::getNextFile() { 16 | if (qScanList.empty()) return ""; 17 | 18 | std::string sNextFile = qScanList.front(); 19 | qScanList.pop(); 20 | return sNextFile; 21 | } 22 | 23 | void CScanTarget::collectFile() { 24 | 25 | if (fs::is_regular_file(sTargetPath)) { 26 | qScanList.push(sTargetPath); 27 | return; 28 | } 29 | std::vector vExtension = { ".exe", ".dll", ".EXE", ".DLL" }; 30 | for (auto& each : fs::recursive_directory_iterator(sTargetPath)) { 31 | std::string sExtension = each.path().extension().string(); 32 | if (std::find(vExtension.begin(), vExtension.end(), sExtension) != vExtension.end()) 33 | qScanList.push(std::string(each.path().string())); 34 | } 35 | } -------------------------------------------------------------------------------- /src/utils/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "thread_pool.h" 5 | 6 | CThreadPool::CThreadPool() : 7 | CThreadPool (std::max(std::thread::hardware_concurrency(), 2u) - 1u){ 8 | 9 | } 10 | 11 | CThreadPool::CThreadPool(uint32_t iNumThreads) : 12 | m_done(false), 13 | m_workQueue{}, 14 | m_threads{} { 15 | try 16 | { 17 | for (std::uint32_t i = 0u; i < iNumThreads; ++i) 18 | { 19 | m_threads.emplace_back(&CThreadPool::worker, this); 20 | } 21 | } 22 | catch (...) 23 | { 24 | destroy(); 25 | throw; 26 | } 27 | } 28 | 29 | CThreadPool::~CThreadPool() { 30 | destroy(); 31 | } 32 | 33 | void CThreadPool::worker() { 34 | while (!m_done) { 35 | std::unique_ptr spThreadTask; 36 | if (m_workQueue.waitPop(spThreadTask)) { 37 | assert(spThreadTask != nullptr); 38 | spThreadTask->execute(); 39 | } 40 | } 41 | } 42 | 43 | void CThreadPool::destroy() { 44 | m_done = true; 45 | for (auto& thread : m_threads) { 46 | if (thread.joinable()) { 47 | thread.join(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/scan_engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "binary_file.h" 4 | #include "scanner.h" 5 | #include "vuln_report.h" 6 | #include "utils\thread_pool.h" 7 | #include 8 | #include 9 | 10 | class IScanEngine { 11 | public: 12 | 13 | /** 14 | * scan target path, could be a folder or file 15 | */ 16 | virtual bool scanPath(std::string sTargetPath) = 0; 17 | 18 | /** 19 | * print scan results 20 | */ 21 | virtual void printResults() = 0; 22 | }; 23 | 24 | 25 | class CScanEngine : public IScanEngine { 26 | 27 | public: 28 | CScanEngine(); 29 | CScanEngine(std::shared_ptr spASMScanner, std::shared_ptr spStringScanner); 30 | 31 | public: 32 | bool scanPath(std::string sTargetPath); 33 | void scanFile(std::string sTargetPath); 34 | void printResults(); 35 | 36 | 37 | private: 38 | std::mutex m_mutex; 39 | std::shared_ptr spSigLoader; 40 | std::map> mSucceedScans; 41 | std::vector vFailedScans; 42 | std::unique_ptr m_threadPool; 43 | }; 44 | 45 | 46 | class CScanEngineFactory { 47 | public: 48 | static std::unique_ptr getScanEgnine() { 49 | return std::make_unique(); 50 | } 51 | }; -------------------------------------------------------------------------------- /src/vulnscan.cpp: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | // Name : vulnscan.cpp 3 | // Author : 4 | // Version : 5 | // Copyright : zhutoulala@gmail.com 6 | // Description : a static binary vulnerability scanner 7 | //============================================================================ 8 | 9 | #include "binary_file.h" 10 | #include "scan_engine.h" 11 | #include "vuln_report.h" 12 | #include 13 | #include 14 | 15 | void printInfo() { 16 | std::cout << "vulnscan (v0.1) - A static binary vulnerability scanner" << std::endl; 17 | std::cout << "Visit http://vulnscan.us/ for more details" << std::endl; 18 | } 19 | 20 | int main(int argc, char **argv) { 21 | printInfo(); 22 | if (argc < 2) { 23 | std::cout << "Usage: vulnscan [target path]\n"; 24 | return -1; 25 | } 26 | 27 | auto spScanEngine = CScanEngineFactory::getScanEgnine(); 28 | if (spScanEngine == nullptr) { 29 | std::cout << "Failed to initialize ScanEngine." << std::endl; 30 | return -1; 31 | } 32 | 33 | std::string sTargetPath(argv[1]); 34 | 35 | if (!spScanEngine->scanPath(sTargetPath)) { 36 | std::cout << "Failed to scan." << std::endl; 37 | return -1; 38 | } 39 | spScanEngine->printResults(); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/vuln_report.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vulnerability.h" 4 | #include "disassembler.h" 5 | #include "signature.h" 6 | #include "symbols.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | class IVulnReport { 12 | public: 13 | virtual bool isVulnerablityFound() = 0; 14 | virtual void addVulnerablity(std::shared_ptr spVulnerablity) = 0; 15 | virtual std::string toString() = 0; 16 | virtual std::shared_ptr getVulnerablity(std::string sCVE) = 0; 17 | virtual void addDetection(std::string sCVE, DETECTION_STATUS status) = 0; 18 | virtual size_t numberOfVuln() = 0; 19 | }; 20 | 21 | class CVulnReport : public IVulnReport { 22 | private: 23 | std::vector> vspVulnerablities; 24 | 25 | public: 26 | CVulnReport(); 27 | 28 | public: 29 | inline bool isVulnerablityFound() { return vspVulnerablities.size() > 0; }; 30 | inline size_t numberOfVuln() { return vspVulnerablities.size(); }; 31 | void addVulnerablity(std::shared_ptr spVulnerablity); 32 | std::string toString(); 33 | std::shared_ptr getVulnerablity(std::string sCVE); 34 | void addDetection(std::string sCVE, DETECTION_STATUS status); 35 | }; 36 | 37 | 38 | class CVulnReportFactory { 39 | public: 40 | static std::unique_ptr createReport() { 41 | return std::make_unique(); 42 | } 43 | }; -------------------------------------------------------------------------------- /dependencies/strings2/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Geoff McDonald 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | * Neither the name split-code nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /src/utils/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "task_queue.h" 6 | #include "thread_task.h" 7 | 8 | class CThreadPool { 9 | public: 10 | CThreadPool(); 11 | CThreadPool(uint32_t iNumThreads); 12 | 13 | /** 14 | * Non-copyable. 15 | */ 16 | CThreadPool(const CThreadPool& rhs) = delete; 17 | 18 | /** 19 | * Non-assignable. 20 | */ 21 | CThreadPool& operator=(const CThreadPool& rhs) = delete; 22 | ~CThreadPool(); 23 | 24 | template 25 | auto submit(Func&& func, Args&&... args) 26 | { 27 | auto boundTask = std::bind(std::forward(func), std::forward(args)...); 28 | using ResultType = std::result_of_t; 29 | using PackagedTask = std::packaged_task; 30 | using CTaskType = CThreadTask; 31 | 32 | PackagedTask task{ std::move(boundTask) }; 33 | CTaskFuture result{ task.get_future() }; 34 | m_workQueue.push(std::make_unique(std::move(task))); 35 | return result; 36 | } 37 | 38 | 39 | private: 40 | /** 41 | * Constantly running function each thread uses to acquire work items from the queue. 42 | */ 43 | void worker(); 44 | 45 | /** 46 | * Invalidates the queue and joins all running threads. 47 | */ 48 | void destroy(); 49 | 50 | private: 51 | std::atomic_bool m_done; 52 | CTaskQueue> m_workQueue; 53 | std::vector m_threads; 54 | }; 55 | 56 | class CThreadPoolFactory { 57 | public: 58 | static std::unique_ptr getThreadPool() { 59 | return std::make_unique(); 60 | } 61 | }; -------------------------------------------------------------------------------- /src/vulnerability.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "scan_results.h" 7 | 8 | class IVulnerablity { 9 | public: 10 | /** 11 | * get CVE name 12 | */ 13 | virtual std::string getCVE() = 0; 14 | 15 | /** 16 | * get confidence level, higher level means more confident 17 | * low confidence level 18 | // identified positive by assembly engine 19 | // might be a false positive 20 | * median confidence leve 21 | // identified positive by string engines 22 | // might be a false postive 23 | * high confident level 24 | // identified by both engine 25 | 26 | FIXED // the vulnerability has been patched 27 | */ 28 | virtual std::string getConfidenceLevel() = 0; 29 | 30 | /** 31 | * set detection status 32 | */ 33 | virtual void setDetectStatus(DETECTION_STATUS status) = 0; 34 | 35 | /** 36 | * get detection status 37 | */ 38 | virtual DETECTION_STATUS getDetectStatus() = 0; 39 | 40 | /** 41 | * append addtional detection status 42 | */ 43 | virtual void appendDetectStatus(DETECTION_STATUS status) = 0; 44 | }; 45 | 46 | 47 | class CVulnerablity : public IVulnerablity { 48 | 49 | public: 50 | CVulnerablity(std::string sCVE); 51 | 52 | 53 | public: 54 | inline std::string getCVE() { return sCVE; } 55 | 56 | inline std::string getConfidenceLevel() { 57 | return detectionToConfidence(status); 58 | } 59 | 60 | inline void setDetectStatus(DETECTION_STATUS status) { 61 | this->status = status; 62 | } 63 | 64 | inline DETECTION_STATUS getDetectStatus() { return status; } 65 | 66 | inline void appendDetectStatus(DETECTION_STATUS status) { 67 | this->status |= status; 68 | } 69 | 70 | private: 71 | 72 | 73 | private: 74 | std::string sCVE; 75 | DETECTION_STATUS status; 76 | }; 77 | 78 | 79 | class CVulnerablityFactory { 80 | public: 81 | static std::shared_ptr getVulnerablity(std::string sCVE) { 82 | return std::make_shared(sCVE); 83 | } 84 | }; -------------------------------------------------------------------------------- /test/test_signature.cpp: -------------------------------------------------------------------------------- 1 | #include "signature.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include 6 | 7 | TEST(CSignature, isSubSet) { 8 | CSignature signature("CVE-test"); 9 | std::vector vStr = {"a", "bb", "ccc"}; 10 | std::vector vSubSet = { "a", "bb" }; 11 | 12 | ASSERT_TRUE(signature.isSubSet(vSubSet, vStr)); 13 | ASSERT_FALSE(signature.isSubSet(vStr, vSubSet)); 14 | 15 | std::vector vIncorrectOder = { "bb", "a" }; 16 | ASSERT_FALSE(signature.isSubSet(vIncorrectOder, vStr)); 17 | 18 | std::vector vUpperStr = { "A", "bB", "cCC" }; 19 | std::vector vUpperSubSet = { "a", "BB" }; 20 | ASSERT_TRUE(signature.isSubSet(vUpperSubSet, vUpperStr)); 21 | } 22 | 23 | TEST(CSignature, hasFunctionSig) { 24 | CSignature signature("CVE-test"); 25 | std::string sFunction("test-func"); 26 | signature.setFunctionName(sFunction); 27 | 28 | ASSERT_FALSE(signature.hasFunctionSig()); 29 | signature.setPostiveCalls({ "abc", "ef" }); 30 | ASSERT_TRUE(signature.hasFunctionSig()); 31 | ASSERT_EQ(signature.getFunctionName(), sFunction); 32 | ASSERT_EQ(signature.getCVE(), "CVE-test"); 33 | } 34 | 35 | TEST(CSignature, stringMatch) { 36 | CSignature signature("CVE-test"); 37 | signature.addPostiveString("abc"); 38 | signature.addPostiveString("ef"); 39 | signature.addNegativeString("qwe"); 40 | ASSERT_EQ(signature.stringMatch({"abc", "ef", "qwe", "123"}), 41 | DETECTION_STRING_MATCH | DETECTION_POSITIVE_MATCH | DETECTION_NEGATIVE_MATCH); 42 | } 43 | 44 | TEST(CSignature, callSequenceMatch) { 45 | CSignature signature("CVE-test"); 46 | std::vector vStr = { "a", "bb", "ccc" }; 47 | signature.setFunctionName("test_func"); 48 | signature.setPostiveCalls(vStr); 49 | ASSERT_EQ(signature.callSequenceMatch({ "a", "bb", "ccc", "d" }), 50 | DETECTION_ASM_MATCH | DETECTION_POSITIVE_MATCH); 51 | signature.setNegativeCalls(vStr); 52 | ASSERT_EQ(signature.callSequenceMatch({ "a", "bb", "ccc", "d" }), 53 | DETECTION_ASM_MATCH | DETECTION_POSITIVE_MATCH | DETECTION_NEGATIVE_MATCH); 54 | } -------------------------------------------------------------------------------- /dependencies/strings2/README.md: -------------------------------------------------------------------------------- 1 | # strings2 2 | Strings2 is a Windows 32bit and 64bit command-line tool for extracting strings from binary data. On top of the classical Sysinternals strings approach, this improved version is also able to dump strings from process address spaces and also reconstructs hidden assembly local variable assignment ascii/unicode strings. Currently, the ASM-string extracting approach only supports the x86 instruction set. 3 | 4 | I am maintaining a public binary release download page for this project at: 5 | http://split-code.com/strings2.html 6 | 7 | 8 | ## Flags 9 | The command-line flags for strings2 are as follows: 10 | 11 | -f 12 | Prints the filename/processname before each string. 13 | 14 | -r 15 | Recursively process subdirectories. 16 | 17 | -t 18 | Prints the type before each string. Unicode, 19 | ascii, or assembly unicode/ascii stack push. 20 | 21 | -asm 22 | Only prints the extracted ascii/unicode 23 | assembly stack push-hidden strings. 24 | 25 | -raw 26 | Only prints the regular ascii/unicode strings. 27 | 28 | -l [numchars] 29 | Minimum number of characters that is 30 | a valid string. Default is 4. 31 | 32 | -nh 33 | No header is printed in the output. 34 | 35 | -pid 36 | The strings from the process address space for the 37 | specified PID will be dumped. Use a '0x' prefix to 38 | specify a hex PID. 39 | 40 | -system 41 | Dumps strings from all accessible processes on the 42 | system. This takes awhile. 43 | 44 | 45 | ## Example Usage 46 | From the command prompt: 47 | * strings2 malware.exe 48 | * strings2 *.exe > strings.txt 49 | * strings2 *.exe -nh -f -t -asm > strings.txt 50 | * strings2 -pid 419 > process_strings.txt 51 | * strings2 -pid 0x1a3 > process_strings.txt 52 | * strings2 -system > all_process_strings.txt 53 | * cat abcd.exe | strings2 > out.txt 54 | 55 | 56 | ## Contributing 57 | Contributions are welcome. Some possible contribution directions are as follows: 58 | * Only print unique strings. 59 | * Add flag support for dumping process strings by process/window title matching. 60 | * Add x64 assembly support for extracting ASM stack pushed strings. 61 | -------------------------------------------------------------------------------- /src/utils/thread_task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class IThreadTask 8 | { 9 | public: 10 | IThreadTask(void) = default; 11 | virtual ~IThreadTask(void) = default; 12 | IThreadTask(const IThreadTask& rhs) = delete; 13 | IThreadTask& operator=(const IThreadTask& rhs) = delete; 14 | IThreadTask(IThreadTask&& other) = default; 15 | IThreadTask& operator=(IThreadTask&& other) = default; 16 | 17 | /** 18 | * Run the task. 19 | */ 20 | virtual void execute() = 0; 21 | }; 22 | 23 | template 24 | class CThreadTask : public IThreadTask 25 | { 26 | public: 27 | CThreadTask(Func&& func) 28 | :m_func{ std::move(func) } 29 | { 30 | } 31 | 32 | ~CThreadTask(void) override = default; 33 | CThreadTask(const CThreadTask& rhs) = delete; 34 | CThreadTask& operator=(const CThreadTask& rhs) = delete; 35 | CThreadTask(CThreadTask&& other) = default; 36 | CThreadTask& operator=(CThreadTask&& other) = default; 37 | 38 | /** 39 | * Run the task. 40 | */ 41 | void execute() override 42 | { 43 | m_func(); 44 | } 45 | 46 | private: 47 | Func m_func; 48 | }; 49 | 50 | 51 | /** 52 | * A wrapper around a std::future that adds the behavior of futures returned from std::async. 53 | * Specifically, this object will block and wait for execution to finish before going out of scope. 54 | */ 55 | template 56 | class CTaskFuture 57 | { 58 | public: 59 | CTaskFuture(std::future&& future) 60 | :m_future{ std::move(future) } 61 | { 62 | } 63 | 64 | CTaskFuture(const CTaskFuture& rhs) = delete; 65 | CTaskFuture& operator=(const CTaskFuture& rhs) = delete; 66 | CTaskFuture(CTaskFuture&& other) = default; 67 | CTaskFuture& operator=(CTaskFuture&& other) = default; 68 | ~CTaskFuture(void) 69 | { 70 | if (m_future.valid()) 71 | { 72 | m_future.get(); 73 | } 74 | } 75 | 76 | auto get(void) 77 | { 78 | return m_future.get(); 79 | } 80 | 81 | 82 | private: 83 | std::future m_future; 84 | }; -------------------------------------------------------------------------------- /src/symbols.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scan_results.h" 4 | #include 5 | 6 | typedef void *HANDLE; 7 | typedef uint64_t DWORD64; 8 | 9 | typedef struct _SYMBOLMAP { 10 | int64_t iAddress; 11 | int64_t iDisplacement; 12 | std::string sName; 13 | } SYMBOLMAP, *PSYMBOLMAP; 14 | 15 | class ISymbols { 16 | public: 17 | /** 18 | * set current symbol file path 19 | */ 20 | virtual void setSymbolsPath(std::string sSymbolFile) = 0; 21 | 22 | /** 23 | * load symbols from current symbol file path 24 | */ 25 | virtual void loadSymbols() = 0; 26 | 27 | /** 28 | * unload current symbols 29 | * this method has to be called before trying to load another symbol file 30 | */ 31 | virtual void unloadSymbols() = 0; 32 | 33 | /** 34 | * get symbol from given address 35 | */ 36 | virtual SCAN_RESULT getSymbolFromAddress(PSYMBOLMAP pSymbolMap) = 0; 37 | 38 | /** 39 | * get symbol file loaded address 40 | */ 41 | virtual uint64_t getLoadedAddress() = 0; 42 | }; 43 | 44 | 45 | class CPDBSymbols : public ISymbols { 46 | 47 | public: 48 | CPDBSymbols(); 49 | ~CPDBSymbols(); 50 | public: 51 | void setSymbolsPath(std::string sSymbolFile) { sCurrentSymbol = sSymbolFile; }; 52 | void loadSymbols(); 53 | void unloadSymbols(); 54 | SCAN_RESULT getSymbolFromAddress(PSYMBOLMAP pSymbolMap); 55 | SCAN_RESULT enumSymbols(DWORD64 ModBase); 56 | bool ShowSymbolInfo(DWORD64 ModBase); 57 | inline uint64_t getLoadedAddress() { return dwLoadedAddr; } 58 | 59 | private: 60 | std::string sCurrentSymbol; 61 | bool bSymbolLoaded; 62 | HANDLE hProcess; 63 | DWORD64 dwLoadedAddr; 64 | }; 65 | 66 | 67 | class CSymbolsFactory { 68 | public: 69 | static std::shared_ptr getSymbols(std::string sSymbolFile) { 70 | /*std::string sExtension = ".pdb"; 71 | if (sSymbolFile.compare(sSymbolFile.length() - sExtension.length(), 72 | sExtension.length(), sExtension) == 0) { 73 | return std::make_unique(sSymbolFile); 74 | } 75 | return nullptr;*/ 76 | if (g_spSymbols == nullptr) { 77 | g_spSymbols = std::make_shared(); 78 | } 79 | g_spSymbols->setSymbolsPath(sSymbolFile); 80 | g_spSymbols->loadSymbols(); 81 | return g_spSymbols; 82 | } 83 | private: 84 | static std::shared_ptr g_spSymbols; 85 | }; -------------------------------------------------------------------------------- /src/scan_results.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | typedef uint32_t SCAN_RESULT; 5 | 6 | #define SCAN_RESULT_SUCCESS 0x00000000 7 | #define SCAN_RESULT_NOT_FOUND 0x00000001 8 | #define SCAN_RESULT_NOT_BINARY 0x00000002 9 | #define SCAN_RESULT_NOT_SUPPORT 0x00000004 10 | #define SCAN_RESULT_PE_PARSE_ERROR 0x00000008 11 | #define SCAN_RESULT_NO_SIGS 0x0000000F 12 | #define SCAN_RESULT_SYMBOL_NOT_LOADED 0x00000010 13 | #define SCAN_RESULT_SYMBOL_NOT_FOUND 0x00000020 14 | #define SCAN_RESULT_OUT_OF_BUFFER 0x00000040 15 | 16 | #define SCAN_SUCCEED(x) (((SCAN_RESULT)(x)) == 0) 17 | #define SCAN_FAILED(x) (((SCAN_RESULT)(x)) > 0) 18 | 19 | 20 | static std::string scanResultToString(SCAN_RESULT sr) { 21 | switch(sr) { 22 | case SCAN_RESULT_SUCCESS: return "Scan succeed"; 23 | case SCAN_RESULT_NOT_FOUND: return "Could not find file to scan"; 24 | case SCAN_RESULT_NOT_BINARY: return "Target file is not binary"; 25 | case SCAN_RESULT_NOT_SUPPORT: return "Target file is not supported"; 26 | case SCAN_RESULT_PE_PARSE_ERROR: return "PE parsing error"; 27 | case SCAN_RESULT_NO_SIGS: return "Signatures are not loaded"; 28 | case SCAN_RESULT_SYMBOL_NOT_LOADED: return "Symbols are not loaded"; 29 | case SCAN_RESULT_SYMBOL_NOT_FOUND: return "Symbol could not be found for given address"; 30 | case SCAN_RESULT_OUT_OF_BUFFER: return "Scan is running of out buffer memory"; 31 | default: return "Invalid scan result"; 32 | } 33 | } 34 | 35 | 36 | typedef uint32_t DETECTION_STATUS; 37 | 38 | #define DETECTION_NOMATCH 0x00000000 39 | #define DETECTION_STRING_MATCH 0x00000001 40 | #define DETECTION_ASM_MATCH 0x00000010 41 | #define DETECTION_POSITIVE_MATCH 0x00000100 42 | #define DETECTION_NEGATIVE_MATCH 0x00001000 43 | 44 | static std::string detectionToConfidence(DETECTION_STATUS status) { 45 | if ((DETECTION_ASM_MATCH & status) && 46 | (DETECTION_STRING_MATCH & status) && 47 | (DETECTION_POSITIVE_MATCH & status)) 48 | 49 | return " (confidence : high)"; 50 | 51 | if ((DETECTION_STRING_MATCH & status) && 52 | (DETECTION_POSITIVE_MATCH & status)) 53 | return " (confidence : median)"; 54 | 55 | if ((DETECTION_ASM_MATCH & status) && 56 | (DETECTION_POSITIVE_MATCH & status)) 57 | return " (confidence : low)"; 58 | 59 | if (DETECTION_NEGATIVE_MATCH & status) 60 | return " (patched, no worry)"; 61 | return ""; 62 | } -------------------------------------------------------------------------------- /src/vuln_report.cpp: -------------------------------------------------------------------------------- 1 | #include "vuln_report.h" 2 | #include 3 | #include 4 | 5 | CVulnReport::CVulnReport() { 6 | vspVulnerablities = std::vector>(); 7 | 8 | } 9 | 10 | std::string CVulnReport::toString() { 11 | if (vspVulnerablities.size() == 0) { 12 | return "No vulnerability found\n"; 13 | } 14 | 15 | std::string sReport = "Found vulnerability: \n\n"; 16 | for (auto it : vspVulnerablities) { 17 | sReport += it->getCVE(); 18 | sReport += it->getConfidenceLevel(); 19 | sReport += "\n"; 20 | } 21 | return sReport; 22 | } 23 | 24 | std::shared_ptr CVulnReport::getVulnerablity(std::string sCVE) { 25 | for (auto it : vspVulnerablities) { 26 | if (it->getCVE() == sCVE) { 27 | return it; 28 | } 29 | } 30 | return nullptr; 31 | } 32 | 33 | 34 | void CVulnReport::addVulnerablity(std::shared_ptr spVulnerablity){ 35 | auto spExistingVul = getVulnerablity(spVulnerablity->getCVE()); 36 | 37 | if(spExistingVul == nullptr) 38 | vspVulnerablities.push_back(spVulnerablity); 39 | else { 40 | spExistingVul = spVulnerablity; 41 | } 42 | }; 43 | 44 | 45 | void CVulnReport::addDetection(std::string sCVE, DETECTION_STATUS status) { 46 | auto spVulnerablity = getVulnerablity(sCVE); 47 | if (spVulnerablity == nullptr) 48 | spVulnerablity = CVulnerablityFactory::getVulnerablity(sCVE); 49 | spVulnerablity->appendDetectStatus(status); 50 | addVulnerablity(spVulnerablity); 51 | } 52 | /* 53 | SCAN_RESULT CVulnReport::SearchForCVEbyCode(Disassembler::InstructionSet& instructionSet, std::unique_ptr& spSymbols) { 54 | assert(spSymbols != nullptr); 55 | 56 | 57 | for (size_t i = 0; i < spSigLoader->getSize(); i++) { 58 | Signature *pSignature = spSigLoader->getSignature(i); 59 | if ((pSignature != nullptr) && (pSignature->asmCodeMatch(instructionSet))) { 60 | vspVulnerablities.push_back(std::make_shared(pSignature->getCVE())); 61 | } 62 | } 63 | 64 | return SCAN_RESULT_SUCCESS; 65 | } 66 | 67 | SCAN_RESULT CVulnReport::SearchForCVEbyString(std::vector& vLookupStrings) { 68 | for (size_t i = 0; i < spSigLoader->getSize(); i++) { 69 | Signature *pSignature = spSigLoader->getSignature(i); 70 | if ((pSignature != nullptr) && (pSignature->stringMatch(vLookupStrings))) { 71 | vspVulnerablities.push_back(std::make_shared(pSignature->getCVE())); 72 | } 73 | } 74 | 75 | return SCAN_RESULT_SUCCESS; 76 | }*/ -------------------------------------------------------------------------------- /src/scanner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scan_results.h" 4 | #include "binary_file.h" 5 | #include 6 | #include 7 | 8 | enum ScannerType { 9 | EStringScanner = 0, 10 | EASMScanner, 11 | }; 12 | 13 | class IScanner { 14 | public: 15 | 16 | virtual SCAN_RESULT scan(std::shared_ptr& spVulnReport) = 0; 17 | 18 | virtual std::string getType() = 0; 19 | }; 20 | 21 | class CStringScanner : public IScanner { 22 | public: 23 | CStringScanner(std::shared_ptr spSigLoader, 24 | std::shared_ptr spBinaryFile); 25 | 26 | public: 27 | 28 | SCAN_RESULT scan(std::shared_ptr& spVulnReport); 29 | 30 | std::string getType() { return "String Scanner"; } 31 | 32 | private: 33 | std::shared_ptr spSigLoader; 34 | std::shared_ptr spBinaryFile; 35 | 36 | }; 37 | 38 | 39 | class CASMScanner : public IScanner { 40 | 41 | public: 42 | CASMScanner(std::shared_ptr spSigLoader, 43 | std::shared_ptr spBinaryFile); 44 | 45 | public: 46 | SCAN_RESULT scan(std::shared_ptr& spVulnReport); 47 | 48 | std::string getType() { return "Assembly Scanner"; } 49 | 50 | private: 51 | 52 | 53 | bool getNextFunction(std::string& sFunction); 54 | bool getFunctionEnding(const std::string sFunction); 55 | bool getCallSequence(std::string sFunction, std::vector& vCalls); 56 | 57 | private: 58 | size_t seekToInstruction(uint64_t iStartPos, size_t iBatchSize, 59 | std::string sMnemonic, const std::vector& vsOperand); 60 | /** 61 | * get function at current offset 62 | */ 63 | std::string getCurrentSymbol(); 64 | bool getSymbolAtAddress(uint64_t ullAddress, std::string& sSymbol); 65 | bool getSymbolAtOffset(size_t iOffset, std::string& sSymbol); 66 | 67 | private: 68 | std::shared_ptr spSigLoader; 69 | std::shared_ptr spBinaryFile; 70 | std::shared_ptr spSymbols; 71 | size_t iCurrentOffset; // current scan offset of target binary file 72 | uint64_t ullCodeSectionBase; 73 | size_t iCodeSectionLength; 74 | 75 | size_t iBatchSize; // default scan batch size, read how many bits each time 76 | }; 77 | 78 | class CScannerFactory { 79 | public: 80 | static std::shared_ptr getScanner(ScannerType scannerType, 81 | std::shared_ptr spSigLoader, 82 | std::shared_ptr spBinaryFile) { 83 | switch (scannerType) 84 | { 85 | case EStringScanner: 86 | return std::make_shared(spSigLoader, spBinaryFile); 87 | case EASMScanner: 88 | return std::make_shared(spSigLoader, spBinaryFile); 89 | default: 90 | return nullptr; 91 | } 92 | } 93 | }; -------------------------------------------------------------------------------- /dependencies/strings2/strings/print_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "print_buffer.h" 2 | 3 | print_buffer::print_buffer(int buffer_size) 4 | { 5 | this->buffer_size = buffer_size; 6 | this->buffer = new char[buffer_size]; 7 | this->space_used = 0; 8 | } 9 | 10 | void print_buffer::addString(char* string, int length) 11 | { 12 | // Digest the buffer if it is full 13 | if( space_used + length + 1 >= buffer_size ) 14 | digest(); 15 | 16 | // Copy the string if there is room 17 | if( space_used + length + 1 >= buffer_size ) 18 | { 19 | // Digest this string without buffering it 20 | fwrite(string, length, 1, stdout); 21 | }else{ 22 | // Add it to the buffer 23 | memcpy( buffer + space_used, string, length ); 24 | space_used += length; 25 | buffer[space_used] = 0; 26 | } 27 | } 28 | 29 | void print_buffer::addString(char* string) 30 | { 31 | int length = strlen(string); 32 | addString(string, length); 33 | } 34 | 35 | void print_buffer::addStrings(char* string1, char* string2, char* string3, char* string4, char* string5) 36 | { 37 | addString(string1); 38 | addString(string2); 39 | addString(string3); 40 | addString(string4); 41 | addString(string5); 42 | } 43 | 44 | void print_buffer::addStrings(char* string1, char* string2, char* string3, char* string4) 45 | { 46 | addString(string1); 47 | addString(string2); 48 | addString(string3); 49 | addString(string4); 50 | } 51 | 52 | void print_buffer::addStrings(char* string1, char* string2, char* string3) 53 | { 54 | addString(string1); 55 | addString(string2); 56 | addString(string3); 57 | } 58 | 59 | void print_buffer::addStrings(char* string1, char* string2) 60 | { 61 | addString(string1); 62 | addString(string2); 63 | } 64 | 65 | void print_buffer::addLine(char* string, int length) 66 | { 67 | // Digest the buffer if it is full 68 | if( space_used + length + 3 >= buffer_size ) 69 | digest(); 70 | 71 | // Copy the string if there is room 72 | if( space_used + length + 3 >= buffer_size ) 73 | { 74 | // Digest this string without buffering it 75 | //printf( "%s", string ); 76 | }else{ 77 | // Add it to the buffer 78 | memcpy( buffer + space_used, string, length ); 79 | space_used += length + 2; 80 | buffer[space_used - 2] = '\r'; 81 | buffer[space_used - 1] = '\n'; 82 | buffer[space_used] = 0; 83 | } 84 | } 85 | 86 | void print_buffer::addLine(char* string) 87 | { 88 | int length = strlen(string); 89 | addLine(string, length); 90 | } 91 | 92 | void print_buffer::digest() 93 | { 94 | if( space_used > 0 ) 95 | { 96 | // Print the current buffer 97 | fwrite( buffer, 1, space_used, stdout); 98 | fflush( stdout ); 99 | buffer[0] = 0; 100 | space_used = 0; 101 | } 102 | } 103 | 104 | print_buffer::~print_buffer(void) 105 | { 106 | digest(); 107 | delete[] buffer; 108 | } 109 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # All data is bad, stay safe with vulnscan 2 | vulnscan is a static binary vulnerablity scanner. It could be used to detect if target executable files contain any known vulnerability, that potentially comes from popular 3rd party libraries in use. 3 | 4 | This tool is designed to be cross-platformed. It could be compiled and run on both Windows and Linux. Also it could be used to scan Windows executables and Linux executables. The currently supported scan targets include: 5 | 6 | Windows executables: `exe dll` 7 | 8 | Linux executables: `elf` 9 | 10 | ## Download 11 | Download vulnscan version 0.1 from [here](https://github.com/zhutoulala/vulnscan/releases/download/0.1/vulnscan.exe) 12 | 13 | ## How to run this tool 14 | ``` 15 | vulnscan [path to target binary file] 16 | vulnscan [path to target folder] 17 | ``` 18 | The output would be like: 19 | ``` 20 | C:\Users\peter\github\vulnscan\build\Release>vulnscan.exe vulnscan.exe 21 | vulnscan (v0.1) - A static binary vulnerability scanner 22 | Visit http://vulnscan.us/ for more details 23 | Scanning ===> vulnscan.exe 24 | ...... 25 | No symbols available for the module. 26 | Image name: vulnscan.exe 27 | Loaded image name: C:\Users\peter\github\vulnscan\build\Release\vulnscan.exe 28 | Line numbers: Not available 29 | Global symbols: Not available 30 | Type information: Not available 31 | Source indexing: No 32 | Public symbols: Not available 33 | No more code to scan 34 | Couldn't get next function 35 | 36 | 37 | ================================================== 38 | Scan Summary 39 | -------------------------------------------------- 40 | Total to scan: 1 41 | Successfully scanned: 1 42 | Vulnerability found: 1 43 | -------------------------------------------------- 44 | Detailed Report 45 | -------------------------------------------------- 46 | vulnscan.exe - Found vulnerability: 47 | 48 | CVE-2018-1000122 (confidence : median) 49 | 50 | ================================================== 51 | ``` 52 | 53 | 54 | ## How does it work 55 | vulnscan is consisted of 2 types of scan engine, the string scanner and disassembly scanner. 56 | 57 | String scanner looks through all human readable strings in the target file and match them against predefined signatures of each known vulnerability. 58 | 59 | Disassembly scanner uses [capstone](https://www.capstone-engine.org/) to disassemble the whole code section of target file. By examining the call sequence pattern of the potential vulnerable functions, it would be able to tell if those functions contains certain known vulnerabilities or not. 60 | 61 | For developers, visit its [github repo](https://github.com/zhutoulala/vulnscan/) 62 | 63 | ## How to build on Linux 64 | ``` 65 | mkdir build 66 | cd build 67 | cmake .. 68 | make 69 | ``` 70 | 71 | ## How to build on Windows 72 | ``` 73 | mkdir build 74 | cd 75 | cmake .. 76 | ``` 77 | Then open the solution vulnscan.sln and build project vulnscan -------------------------------------------------------------------------------- /src/utils/task_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * A thread safe task queue 10 | */ 11 | template 12 | class CTaskQueue { 13 | 14 | public: 15 | /** 16 | * attempt to get the first value in the queue. 17 | * @return true if succeed, false otherwise. 18 | */ 19 | bool tryPop(T& out) { 20 | std::lock_guard lock{ m_mutex }; 21 | if (m_queue.empty() || !m_valid) { 22 | return false; 23 | } 24 | out = std::move(m_queue.front()); 25 | m_queue.pop(); 26 | return true; 27 | } 28 | 29 | /** 30 | * wait to get the first value in the queue. 31 | * will block until a value is available unless clear is called or the instance is destructed. 32 | * @return true if succeed, false otherwise. 33 | */ 34 | bool waitPop(T& out) { 35 | std::unique_lock lock{ m_mutex }; 36 | m_condition.wait(lock, [this]() { 37 | return !m_queue.empty() || !m_valid; 38 | }); 39 | 40 | if (!m_valid) { 41 | return false; 42 | } 43 | 44 | out = std::move(m_queue.front()); 45 | m_queue.pop(); 46 | return true; 47 | } 48 | 49 | /** 50 | * Push a new value onto the queue. 51 | */ 52 | void push(T value) { 53 | std::lock_guard lock{ m_mutex }; 54 | m_queue.push(std::move(value)); 55 | m_condition.notify_one(); 56 | } 57 | 58 | 59 | /** 60 | * Check whether or not the queue is empty. 61 | */ 62 | bool empty(void) const { 63 | std::lock_guard lock{ m_mutex }; 64 | return m_queue.empty(); 65 | } 66 | 67 | /** 68 | * Clear all items from the queue. 69 | */ 70 | void clear(void) { 71 | std::lock_guard lock{ m_mutex }; 72 | while (!m_queue.empty()) 73 | { 74 | m_queue.pop(); 75 | } 76 | m_condition.notify_all(); 77 | } 78 | 79 | /** 80 | * Invalidate the queue. 81 | * Used to ensure no conditions are being waited on in waitPop when 82 | * a thread or the application is trying to exit. 83 | * The queue is invalid after calling this method and it is an error 84 | * to continue using a queue after this method has been called. 85 | */ 86 | void invalidate(void) { 87 | std::lock_guard lock{ m_mutex }; 88 | m_valid = false; 89 | m_condition.notify_all(); 90 | } 91 | 92 | /** 93 | * Returns whether or not this queue is valid. 94 | */ 95 | bool isValid(void) const { 96 | std::lock_guard lock{ m_mutex }; 97 | return m_valid; 98 | } 99 | 100 | private: 101 | std::atomic_bool m_valid{true}; 102 | mutable std::mutex m_mutex; 103 | std::queue m_queue; 104 | std::condition_variable m_condition; 105 | }; -------------------------------------------------------------------------------- /src/signature.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "gtest/gtest_prod.h" 7 | #include "disassembler.h" 8 | #include "scan_results.h" 9 | 10 | class ISignature { 11 | public: 12 | /** 13 | * does this signature contain a function signature 14 | */ 15 | virtual bool hasFunctionSig() = 0; 16 | 17 | /** 18 | * does this signature contain any string signature 19 | */ 20 | virtual bool hasStringSig() = 0; 21 | 22 | /** 23 | * get the CVE name for which this signature is 24 | */ 25 | virtual std::string getCVE() = 0; 26 | 27 | /** 28 | * get the function name if there is a function signature 29 | */ 30 | virtual std::string getFunctionName() = 0; 31 | 32 | /** 33 | * for function signature, does it match the given call sequence inside the function 34 | */ 35 | virtual DETECTION_STATUS callSequenceMatch(const std::vector& vCallSequence) = 0; 36 | 37 | /** 38 | * for string signature, does all given strings match the signature string list 39 | */ 40 | virtual DETECTION_STATUS stringMatch(const std::vector& vLookupStrings) = 0; 41 | }; 42 | 43 | class CSignature : public ISignature{ 44 | public: 45 | CSignature(std::string sCVE); 46 | 47 | public: 48 | inline bool hasFunctionSig() { 49 | return !sFunction.empty() && (vPostiveCalls.size() > 0 || vNegativeCalls.size() > 0); 50 | } 51 | 52 | inline bool hasStringSig() { 53 | return vPostiveStrings.size() > 0; 54 | } 55 | inline std::string getCVE() { return sCVE; } 56 | inline std::string getFunctionName() { return sFunction; } 57 | DETECTION_STATUS callSequenceMatch(const std::vector& vCallSequence); 58 | DETECTION_STATUS stringMatch(const std::vector& vLookupStrings); 59 | 60 | public: 61 | 62 | inline void setFunctionName(std::string sFunctionName) { 63 | sFunction = sFunctionName; 64 | } 65 | 66 | inline void setPostiveCalls(const std::vector& vCallSequence) { 67 | vPostiveCalls = vCallSequence; 68 | } 69 | 70 | inline void setNegativeCalls(const std::vector& vCallSequence) { 71 | vNegativeCalls = vCallSequence; 72 | } 73 | 74 | inline void addPostiveString(const std::string& sPostiveString) { 75 | vPostiveStrings.push_back(sPostiveString); 76 | } 77 | 78 | inline void addNegativeString(const std::string& sNegativeString) { 79 | vNegativeStrings.push_back(sNegativeString); 80 | } 81 | 82 | /** 83 | * check if v1 is a subset of v2 84 | */ 85 | template 86 | bool isSubSet(const std::vector& v1, const std::vector& v2); 87 | 88 | private: 89 | std::string sCVE; 90 | std::string sFunction; 91 | std::vector vPostiveStrings; 92 | std::vector vNegativeStrings; 93 | std::vector vPostiveCalls; 94 | std::vector vNegativeCalls; 95 | }; 96 | 97 | class SignatureLoader { 98 | public: 99 | SignatureLoader() {}; 100 | 101 | public: 102 | bool load(); 103 | inline size_t getSize() { 104 | return vspSignatures.size(); 105 | } 106 | 107 | inline std::shared_ptr getSignature(size_t index) { 108 | return vspSignatures.at(index); 109 | }; 110 | 111 | private: 112 | FRIEND_TEST(SignatureLoader, loadSigs); 113 | bool loadSigs(); 114 | bool download(); 115 | 116 | private: 117 | std::vector> vspSignatures; 118 | std::string sSigContent; 119 | }; -------------------------------------------------------------------------------- /dependencies/strings2/strings/string_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | //#include "windows.h" 5 | #include "DynArray.h" 6 | #include "print_buffer.h" 7 | //#include "string_hashes.h" 8 | 9 | using namespace std; 10 | 11 | #define MAX_STRING_SIZE 0x2000 12 | #define BLOCK_SIZE 0x50000 13 | 14 | 15 | // Quick way of checking if a character value is displayable ascii 16 | static bool isAscii[0x100] = 17 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ 18 | /* 0x00 */ {false,false,false,false, false,false,false,false, false,true ,true ,false, false,true ,false,false, 19 | /* 0x10 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 20 | /* 0x20 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , 21 | /* 0x30 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , 22 | /* 0x40 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , 23 | /* 0x50 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , 24 | /* 0x60 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , 25 | /* 0x70 */ true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,true , true ,true ,true ,false, 26 | /* 0x80 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 27 | /* 0x90 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 28 | /* 0xA0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 29 | /* 0xB0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 30 | /* 0xC0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 31 | /* 0xD0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 32 | /* 0xE0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false, 33 | /* 0xF0 */ false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false}; 34 | 35 | struct STRING_OPTIONS 36 | { 37 | bool printAsciiOnly; 38 | bool printUnicodeOnly; 39 | bool printFile; 40 | bool printType; 41 | bool printNormal; 42 | bool printASM; 43 | bool printUniqueLocal; 44 | bool printUniqueGlobal; 45 | bool escapeNewLines; 46 | int minCharacters; 47 | }; 48 | 49 | class string_parser 50 | { 51 | // Maybe add XOR methods for extracting strings? 52 | enum EXTRACT_TYPE 53 | { 54 | EXTRACT_RAW, 55 | EXTRACT_ASM 56 | }; 57 | 58 | enum STRING_TYPE 59 | { 60 | TYPE_UNDETERMINED, 61 | TYPE_ASCII, 62 | TYPE_UNICODE 63 | }; 64 | 65 | STRING_OPTIONS options; 66 | print_buffer* printer; 67 | std::vector vBuffer; 68 | 69 | int extractImmediate( char* immediate, int immediateSize, STRING_TYPE &stringType, unsigned char* outputString ); 70 | int extractString( unsigned char* buffer, long bufferSize, long offset, unsigned char* outputString, int outputStringSize, int &outputStringLength, EXTRACT_TYPE &extractType, STRING_TYPE & stringType); 71 | bool processContents( unsigned char* buffer, long bufferSize, const char* filepath ); 72 | public: 73 | string_parser( STRING_OPTIONS options ); 74 | bool parse_block( unsigned char* buffer, unsigned int buffer_length, const char* datasource ); 75 | bool parse_stream( FILE* fh, const char* datasource ); 76 | std::vector getBuffer() { return vBuffer; }; 77 | ~string_parser(void); 78 | }; -------------------------------------------------------------------------------- /src/windows_binary.cpp: -------------------------------------------------------------------------------- 1 | #include "binary_file.h" 2 | #include "file_typer.h" 3 | #include "disassembler.h" 4 | 5 | #include "string_parser.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | uint64_t WindowsBinary::codeSectionBase; 16 | uint32_t WindowsBinary::codeSectionSize; 17 | 18 | WindowsBinary::WindowsBinary(std::string sFilePath){ 19 | this->sFilePath = sFilePath; 20 | vCode = std::vector(); 21 | vStrings = std::vector(); 22 | bAnalyzed = false; 23 | } 24 | 25 | SCAN_RESULT WindowsBinary::analyze() { 26 | if (bAnalyzed) 27 | return SCAN_RESULT_SUCCESS; 28 | SCAN_RESULT sr = readStrings(); 29 | if (SCAN_FAILED(sr)) 30 | return sr; 31 | sr = readCodeSection(); // read code section into memory 32 | if (SCAN_FAILED(sr)) 33 | return sr; 34 | bAnalyzed = true; 35 | return SCAN_RESULT_SUCCESS; 36 | } 37 | 38 | 39 | uint64_t WindowsBinary::getCodeSectionBase() { 40 | return WindowsBinary::codeSectionBase; 41 | } 42 | 43 | size_t WindowsBinary::getCodeSectionSize() { 44 | return WindowsBinary::codeSectionSize; 45 | } 46 | 47 | SCAN_RESULT WindowsBinary::getInstFromAddress(uint64_t ullAddress, size_t iLength, 48 | std::unique_ptr& spInstSet) { 49 | assert(spInstSet == nullptr); 50 | 51 | spInstSet = std::make_unique(); 52 | if (ullAddress - codeSectionBase > vCode.size()) { 53 | return SCAN_RESULT_OUT_OF_BUFFER; 54 | } 55 | return Disassembler::Disassembly(vCode.data() + ullAddress - codeSectionBase, 56 | iLength, ullAddress, is64bit(), *spInstSet); 57 | } 58 | 59 | SCAN_RESULT WindowsBinary::readCodeSection() { 60 | 61 | if (vCode.size() > 0) { 62 | return SCAN_RESULT_SUCCESS; // already loaded 63 | } 64 | peparse::parsed_pe *pParsedPE = peparse::ParsePEFromFile(sFilePath.c_str()); 65 | if (pParsedPE == nullptr) { 66 | std::cout << "Error: " << peparse::GetPEErr() << " (" << 67 | peparse::GetPEErrString() << ")" << endl; 68 | std::cout << "Location: " << peparse::GetPEErrLoc() << endl; 69 | return SCAN_RESULT_PE_PARSE_ERROR; 70 | } 71 | IterSec(pParsedPE, WindowsBinary::locateCodeSection, NULL); 72 | 73 | for (size_t i = 0; i < WindowsBinary::codeSectionSize; i++) { 74 | uint8_t b; 75 | peparse::ReadByteAtVA(pParsedPE, i + WindowsBinary::codeSectionBase, b); 76 | vCode.push_back(b); 77 | } 78 | return SCAN_RESULT_SUCCESS; 79 | } 80 | 81 | int WindowsBinary::locateCodeSection(void *N, 82 | peparse::VA secBase, 83 | std::string &secName, 84 | peparse::image_section_header s, 85 | peparse::bounded_buffer *data) { 86 | static_cast(N); 87 | static_cast(s); 88 | if (secName == ".text") { 89 | WindowsBinary::codeSectionBase = secBase; 90 | WindowsBinary::codeSectionSize = data->bufLen; 91 | return 1; // to break the callback loop 92 | } 93 | return 0; 94 | } 95 | 96 | SCAN_RESULT WindowsBinary::readStrings() { 97 | STRING_OPTIONS options; 98 | options.printUniqueGlobal = false; 99 | options.printUniqueLocal = false; 100 | options.printAsciiOnly = false; 101 | options.printUnicodeOnly = false; 102 | options.printASM = true; 103 | options.printNormal = true; 104 | options.minCharacters = 4; 105 | 106 | string_parser* parser = new string_parser(options); 107 | FILE *pFile = fopen(sFilePath.c_str(), "rb"); 108 | if (!pFile) { 109 | perror("Failed to read file\n"); 110 | return SCAN_RESULT_NOT_FOUND; 111 | } 112 | bool bResult = parser->parse_stream(pFile, sFilePath.c_str()); 113 | fclose(pFile); 114 | vStrings = parser->getBuffer(); 115 | delete parser; 116 | if (bResult) 117 | return SCAN_RESULT_SUCCESS; 118 | else 119 | return SCAN_RESULT_PE_PARSE_ERROR; 120 | } 121 | 122 | bool WindowsBinary::searchStrings(std::string sSearch) { 123 | return std::find(vStrings.begin(), vStrings.end(), sSearch) != vStrings.end(); 124 | } -------------------------------------------------------------------------------- /src/scan_engine.cpp: -------------------------------------------------------------------------------- 1 | #include "scan_engine.h" 2 | #include "scan_target.h" 3 | #include 4 | 5 | #include 6 | 7 | CScanEngine::CScanEngine() { 8 | spSigLoader = std::make_shared(); 9 | mSucceedScans = std::map>(); 10 | m_threadPool = CThreadPoolFactory::getThreadPool(); 11 | } 12 | 13 | bool CScanEngine::scanPath(std::string sTargetPath) { 14 | 15 | if (!spSigLoader->load()) { 16 | std::cout << "Failed to load signatures. Does vulnscan.sigs exist?" << std::endl; 17 | return false; 18 | } 19 | 20 | std::unique_ptr spScanTarget = CScanTargetFactory::createScanTarget(sTargetPath); 21 | std::cout << "Inspecting scan target path..." << std::endl; 22 | if (!spScanTarget->initialize()) { 23 | std::cout << "Failed to initalize scan target." << std::endl; 24 | return false; 25 | } 26 | 27 | std::vector> vTasks; 28 | std::string sNextFile = spScanTarget->getNextFile(); 29 | while (!sNextFile.empty()) { 30 | vTasks.push_back(m_threadPool->submit([this](std::string sFilePath) { 31 | scanFile(sFilePath); 32 | }, sNextFile)); 33 | sNextFile = spScanTarget->getNextFile(); 34 | } 35 | for (auto& item : vTasks) 36 | { 37 | item.get(); 38 | } 39 | return true; 40 | } 41 | 42 | void CScanEngine::scanFile(std::string sTargetPath) { 43 | 44 | std::shared_ptr spBinaryFile; 45 | SCAN_RESULT sr = BinaryFactory::GetBinary(sTargetPath, spBinaryFile); 46 | if (SCAN_FAILED(sr) || (spBinaryFile == nullptr)) { 47 | std::cout << "GetBinary failed: " << scanResultToString(sr) << std::endl; 48 | return; 49 | } 50 | 51 | std::cout << "Scanning ===> " << spBinaryFile->getFilePath(); 52 | std::cout << " ......\n"; 53 | std::shared_ptr spVulnReport; 54 | 55 | auto spStringScanner = CScannerFactory::getScanner(EStringScanner, spSigLoader, spBinaryFile); 56 | assert(spStringScanner != nullptr); 57 | 58 | sr = spStringScanner->scan(spVulnReport); 59 | 60 | if (SCAN_FAILED(sr)) { 61 | std::cout << spStringScanner->getType() << " failed to scan: " << scanResultToString(sr) << std::endl; 62 | return; 63 | } 64 | 65 | /*auto spASMScanner = CScannerFactory::getScanner(EASMScanner, spSigLoader, spBinaryFile); 66 | assert(spASMScanner != nullptr); 67 | 68 | sr = spASMScanner->scan(spVulnReport); 69 | if (SCAN_FAILED(sr)) { 70 | std::cout << spASMScanner->getType() << " failed to scan: " << scanResultToString(sr) << std::endl; 71 | }*/ 72 | else { 73 | std::lock_guard lock{ m_mutex }; 74 | mSucceedScans.insert(std::make_pair(sTargetPath, spVulnReport)); 75 | } 76 | } 77 | 78 | void CScanEngine::printResults() { 79 | std::lock_guard lock{ m_mutex }; 80 | if (vFailedScans.size() + mSucceedScans.size() == 0) { 81 | std::cout << "No file is scanned\n"; 82 | return; 83 | } 84 | 85 | std::cout << "\n\n"; 86 | std::cout << "==================================================\n"; 87 | std::cout << "Scan Summary" << std::endl; 88 | std::cout << "--------------------------------------------------\n"; 89 | std::cout << "Total to scan: \t" << vFailedScans.size() + mSucceedScans.size() << std::endl; 90 | std::cout << "Successfully scanned: \t" << mSucceedScans.size() << std::endl; 91 | std::cout << "Vulnerability found: \t" << 92 | std::accumulate(mSucceedScans.begin(), mSucceedScans.end(), 0, 93 | [](size_t count, std::map>::value_type &v) { 94 | return count + v.second->numberOfVuln(); 95 | }) 96 | <toString();; 103 | } 104 | std::cout << "==================================================\n"; 105 | } 106 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/string_hashes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "DynArray.h" 6 | #include 7 | //#include 8 | 9 | using namespace std; 10 | using namespace std::tr1; 11 | 12 | // 10 mb 13 | #define START_SIZE 10000000 14 | 15 | // 1 mb 16 | #define HASH_SIZE 1000000 17 | 18 | struct unordered_eqstr 19 | { 20 | bool operator()(const char* s1, const char* s2) const 21 | { 22 | return strcmp(s1, s2) == 0; 23 | } 24 | }; 25 | 26 | struct unordered_deref 27 | { 28 | size_t operator()(const char* p) const 29 | { 30 | return hash()(p); 31 | } 32 | }; 33 | 34 | class string_hashes 35 | { 36 | // Create the hashsets 37 | unordered_set _GlobalHashset; 38 | unordered_set _LocalHashset; 39 | char* _data_block_start; 40 | char* _data_block_cur; 41 | char* _data_block_end; 42 | DynArray blocks; 43 | 44 | char* _clone_string(char* source, int length) 45 | { 46 | if( _data_block_cur + length + 1 >= _data_block_end ) 47 | { 48 | // Allocate a new block 49 | _data_block_start = new char[START_SIZE]; 50 | _data_block_cur = _data_block_start; 51 | _data_block_end = _data_block_start + START_SIZE - 1; 52 | blocks.Add( _data_block_start ); 53 | } 54 | 55 | // Copy the string 56 | char* result = _data_block_cur; 57 | memcpy( _data_block_cur, source, length + 1 ); 58 | _data_block_cur[length] = 0; 59 | _data_block_cur += length + 1; 60 | 61 | return result; 62 | } 63 | 64 | public: 65 | string_hashes(void) 66 | { 67 | _data_block_start = new char[START_SIZE]; 68 | _data_block_cur = _data_block_start; 69 | _data_block_end = _data_block_start + START_SIZE - 1; 70 | blocks.Add( _data_block_start ); 71 | 72 | _LocalHashset.rehash(HASH_SIZE); 73 | _GlobalHashset.rehash(HASH_SIZE); 74 | } 75 | 76 | string_hashes(char* file_globalhashes) 77 | { 78 | /*_data_block_start = new char[START_SIZE]; 79 | _data_block_cur = _data_block_start; 80 | _data_block_end = _data_block_start + START_SIZE - 1; 81 | blocks.Add( _data_block_start ); 82 | 83 | FILE* stream = fopen( file_globalhashes, "rb" ); 84 | if( stream != NULL ) 85 | { 86 | // Deserialize the file to rebuild the hashtable 87 | while( !feof(stream) ) 88 | { 89 | // Read the length of this string 90 | DWORD length = 0; 91 | if( fread( &length, 4, 1, stream ) > 0 ) 92 | { 93 | char* string = new char[length + 1]; 94 | if( fread( string, 1, length + 1, stream ) > 0 ) 95 | { 96 | // Add this to the hashtable 97 | Global_Insert( string(string, length ); 98 | } 99 | delete[] string; 100 | } 101 | } 102 | 103 | fclose(stream); 104 | }*/ 105 | } 106 | 107 | void Serialize(char* file_globalhashes) 108 | { 109 | /* 110 | FILE* stream = fopen( file_globalhashes, "wb" ); 111 | if( stream != NULL ) 112 | { 113 | for (unordered_set::const_iterator it = _GlobalHashset.begin(); 114 | it != _GlobalHashset.end(); ++it) 115 | { 116 | DWORD length = strlen(*it); 117 | fwrite( &length, 4, 1, stream ); 118 | fwrite( *it, 1, length + 1, stream); 119 | } 120 | 121 | fclose(stream); 122 | } 123 | */ 124 | } 125 | 126 | bool Contains(string value) 127 | { 128 | if( _LocalHashset.count( value ) > 0 || 129 | _GlobalHashset.count( value ) > 0 ) 130 | return true; 131 | return false; 132 | } 133 | 134 | void Local_Clear() 135 | { 136 | _LocalHashset.clear(); 137 | } 138 | 139 | void Local_Insert( string value ) 140 | { 141 | // Insert a copy of this string 142 | if( _LocalHashset.size() + 10 > _LocalHashset.max_size() ) 143 | _LocalHashset.rehash(_LocalHashset.max_size() + HASH_SIZE); 144 | _LocalHashset.insert( value ); 145 | } 146 | 147 | void Global_Clear() 148 | { 149 | _GlobalHashset.clear(); 150 | } 151 | 152 | void Global_Insert(string value) 153 | { 154 | // Insert a copy of this string 155 | if( _GlobalHashset.size() + 10 > _GlobalHashset.max_size() ) 156 | _GlobalHashset.rehash(_GlobalHashset.max_size() + HASH_SIZE); 157 | _GlobalHashset.insert( value ); 158 | } 159 | 160 | ~string_hashes(void) 161 | { 162 | Local_Clear(); 163 | Global_Clear(); 164 | 165 | // Clean up the allocated blocks 166 | 167 | } 168 | }; 169 | -------------------------------------------------------------------------------- /src/signature.cpp: -------------------------------------------------------------------------------- 1 | #include "signature.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | CSignature::CSignature(std::string sCVE) : sCVE(sCVE) { 8 | 9 | } 10 | 11 | DETECTION_STATUS CSignature::stringMatch(const std::vector& vLookupStrings) { 12 | size_t iPositiveCount = 0; 13 | size_t iNegativeCount = 0; 14 | for (auto eachLookup : vLookupStrings) { 15 | for (auto eachPositive : vPostiveStrings) { 16 | if (eachLookup.find(eachPositive) != std::string::npos) 17 | iPositiveCount++; 18 | } 19 | for (auto eachNegative : vNegativeStrings) { 20 | if (eachLookup.find(eachNegative) != std::string::npos) 21 | iNegativeCount++; 22 | } 23 | } 24 | 25 | DETECTION_STATUS status = DETECTION_NOMATCH; 26 | if (iPositiveCount == vPostiveStrings.size()) { 27 | status = DETECTION_STRING_MATCH | DETECTION_POSITIVE_MATCH; 28 | } 29 | 30 | if (iNegativeCount > 0) 31 | status |= DETECTION_STRING_MATCH | DETECTION_NEGATIVE_MATCH; 32 | return status; 33 | } 34 | 35 | DETECTION_STATUS CSignature::callSequenceMatch( 36 | const std::vector& vCallSequence) { 37 | 38 | DETECTION_STATUS status = DETECTION_NOMATCH; 39 | if (vCallSequence.empty()) { 40 | return status; 41 | } 42 | if (!vPostiveCalls.empty() && isSubSet(vPostiveCalls, vCallSequence)) { 43 | status = DETECTION_ASM_MATCH | DETECTION_POSITIVE_MATCH; 44 | } 45 | 46 | if (!vNegativeCalls.empty() && isSubSet(vNegativeCalls, vCallSequence)) { 47 | status |= DETECTION_ASM_MATCH | DETECTION_NEGATIVE_MATCH; 48 | } 49 | 50 | return status; 51 | } 52 | 53 | template 54 | bool CSignature::isSubSet(const std::vector& v1, 55 | const std::vector& v2) { 56 | auto it1 = v1.begin(); 57 | auto it2 = v2.begin(); 58 | 59 | while (it1 != v1.end()) { 60 | while (it2 != v2.end()) { 61 | std::string sLowerCase1(*it1), sLowerCase2(*it2); 62 | 63 | std::transform(sLowerCase1.begin(), sLowerCase1.end(), 64 | sLowerCase1.begin(), ::tolower); 65 | std::transform(sLowerCase2.begin(), sLowerCase2.end(), 66 | sLowerCase2.begin(), ::tolower); 67 | if (sLowerCase1 != sLowerCase2) { 68 | it2++; 69 | continue; 70 | } 71 | else { 72 | it2++; 73 | break; 74 | } 75 | } 76 | if (it2 == v2.end()) { 77 | return false; 78 | } 79 | else 80 | it1++; 81 | } 82 | return true; 83 | } 84 | 85 | bool SignatureLoader::load() { 86 | return loadSigs(); 87 | } 88 | 89 | bool SignatureLoader::loadSigs() 90 | { 91 | std::ifstream in("vulnscan.sigs"); 92 | if (!in) { 93 | return false; 94 | } 95 | std::string sLine; 96 | while (std::getline(in, sLine)) { 97 | if (sLine.compare(0, 3, "CVE-")) { 98 | std::shared_ptr spSignature = std::make_shared(sLine); 99 | vspSignatures.push_back(spSignature); 100 | if (std::getline(in, sLine) && (sLine == "STRING:")) { 101 | while (std::getline(in, sLine)) { 102 | if (sLine.empty()) break; 103 | else if (sLine.at(0) == '+') { 104 | std::string strSig = sLine.substr(2); 105 | spSignature->addPostiveString(strSig); 106 | } 107 | else if (sLine.at(0) == '-') { 108 | std::string strSig = sLine.substr(2); 109 | spSignature->addNegativeString(strSig); 110 | } 111 | else 112 | { 113 | // bad STRING 114 | break; 115 | } 116 | } 117 | if (sLine == "ASM:") { 118 | while (std::getline(in, sLine)) { 119 | if (sLine.empty()) break; 120 | else if (sLine.at(0) == '+') { 121 | std::string sFunctionCalls(sLine.substr(2)); 122 | size_t iPos = sFunctionCalls.find(':'); 123 | spSignature->setFunctionName(sFunctionCalls.substr(0, iPos)); 124 | std::istringstream iss(sFunctionCalls.substr(iPos+1)); 125 | std::vector vSplits(std::istream_iterator{iss}, 126 | std::istream_iterator()); 127 | spSignature->setPostiveCalls(vSplits); 128 | } 129 | else if (sLine.at(0) == '-') { 130 | std::string sFunctionCalls(sLine.substr(2)); 131 | size_t iPos = sFunctionCalls.find(':'); 132 | spSignature->setFunctionName(sFunctionCalls.substr(0, iPos)); 133 | std::istringstream iss(sFunctionCalls.substr(iPos + 1)); 134 | std::vector vSplits(std::istream_iterator{iss}, 135 | std::istream_iterator()); 136 | spSignature->setNegativeCalls(vSplits); 137 | } 138 | else 139 | { 140 | // bad STRING 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | } 148 | } 149 | return true; 150 | } 151 | -------------------------------------------------------------------------------- /src/binary_file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "file_typer.h" 3 | #include "scan_results.h" 4 | #include "symbols.h" 5 | #include "vuln_report.h" 6 | #include "parser-library/parse.h" 7 | #include 8 | #include 9 | 10 | class IBinaryFile { 11 | 12 | public: 13 | 14 | /** 15 | * Add addtioanal symbol file for the binary. 16 | * The default symbol info from binary will be used if this is not called. 17 | * @param sSymbolPath[in] - the path to addtional symbol file 18 | */ 19 | //virtual void addSymbols(std::string sSymbolPath) = 0; 20 | 21 | /** 22 | * Start analyzing the target binary file. This method needs to be called before rest methods 23 | */ 24 | virtual SCAN_RESULT analyze() = 0; 25 | 26 | /** 27 | * get binary file path 28 | */ 29 | virtual std::string getFilePath() = 0; 30 | 31 | /** 32 | * Get the base address of the code section 33 | */ 34 | virtual uint64_t getCodeSectionBase() = 0; 35 | 36 | /** 37 | * Get the length of code section 38 | */ 39 | virtual size_t getCodeSectionSize() = 0; 40 | 41 | /** 42 | * Get code instructions from given address 43 | * @param ullAddress[in] - address of the code instruction to get, must within code section address range 44 | * @param iLength[in] - the length of instructions 45 | * @param spInstSet[out] - pointer to the instruction set 46 | * @return SCAN_RESULT_SUCCESS if succeed 47 | */ 48 | virtual SCAN_RESULT getInstFromAddress(uint64_t ullAddress, size_t iLength, 49 | std::unique_ptr& spInstSet) = 0; 50 | 51 | /** 52 | * Find target string in the binary 53 | * @param[in] - string to search 54 | * @return true if found else false 55 | */ 56 | virtual bool searchStrings(std::string sSearch) = 0; 57 | 58 | /** 59 | * get the string list by reference 60 | */ 61 | virtual const std::vector& getStrings() = 0; 62 | 63 | /** 64 | * Get the symbol information for currect binary file 65 | */ 66 | //virtual std::shared_ptr getSymbols() = 0; 67 | 68 | /** 69 | * Check if the binary file 64-bit 70 | */ 71 | virtual bool is64bit() = 0; 72 | }; 73 | 74 | 75 | class WindowsBinary : public IBinaryFile { 76 | 77 | public: 78 | WindowsBinary(std::string sFilePath); 79 | 80 | public: 81 | SCAN_RESULT analyze(); 82 | 83 | inline std::string getFilePath() { 84 | return sFilePath; 85 | } 86 | 87 | uint64_t getCodeSectionBase(); 88 | size_t getCodeSectionSize(); 89 | 90 | SCAN_RESULT getInstFromAddress(uint64_t ullAddress, size_t iLength, 91 | std::unique_ptr& spInstSet); 92 | 93 | bool searchStrings(std::string sSearch); 94 | 95 | inline const std::vector& getStrings() { 96 | return vStrings; 97 | } 98 | 99 | inline bool is64bit() { 100 | return codeSectionBase > 0xffffffff; 101 | } 102 | 103 | public: 104 | static int locateCodeSection(void *N, 105 | peparse::VA secBase, 106 | std::string &secName, 107 | peparse::image_section_header s, 108 | peparse::bounded_buffer *data); 109 | 110 | public: 111 | static uint64_t codeSectionBase; 112 | static uint32_t codeSectionSize; 113 | 114 | private: 115 | SCAN_RESULT readCodeSection(); 116 | SCAN_RESULT readStrings(); 117 | 118 | private: 119 | bool bAnalyzed; 120 | std::string sFilePath; 121 | std::vector vCode; 122 | std::vector vStrings; 123 | }; 124 | 125 | 126 | class LinuxBinary : public IBinaryFile { 127 | public: 128 | LinuxBinary(std::string sFilePath); 129 | 130 | public: 131 | 132 | SCAN_RESULT analyze(); 133 | 134 | inline std::string getFilePath() { 135 | return sFilePath; 136 | } 137 | uint64_t getCodeSectionBase(); 138 | size_t getCodeSectionSize(); 139 | 140 | SCAN_RESULT getInstFromAddress(uint64_t ullAddress, size_t iLength, 141 | std::unique_ptr& spInstSet); 142 | 143 | SCAN_RESULT getCodeSection(std::vector& vCode); 144 | 145 | bool searchStrings(std::string sSearch); 146 | 147 | inline const std::vector& getStrings() { 148 | return vStrings; 149 | } 150 | 151 | bool is64bit() { return false; } 152 | 153 | private: 154 | SCAN_RESULT readStrings(); 155 | 156 | private: 157 | std::vector vStrings; 158 | std::string sFilePath; 159 | }; 160 | 161 | 162 | class BinaryFactory { 163 | public: 164 | static SCAN_RESULT GetBinary(std::string sFilePath, std::shared_ptr& spBinaryFile) { 165 | FileTyper typer(sFilePath); 166 | if (!typer.isBinary()) { 167 | return SCAN_RESULT_NOT_BINARY; 168 | } 169 | else if (typer.isEXE()) { 170 | spBinaryFile = std::make_shared(sFilePath); 171 | return SCAN_RESULT_SUCCESS; 172 | } 173 | else if (typer.isELF()) { 174 | spBinaryFile = std::make_shared(sFilePath); 175 | } 176 | 177 | return SCAN_RESULT_NOT_SUPPORT; 178 | } 179 | }; 180 | 181 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/DynArray.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // DynArray.h: interface&implementation for the DynArray class 3 | ////////////////////////////////////////////////////////////////////// 4 | 5 | #ifndef _AE_DYNARRAY_H_INCLUDED_ 6 | #define _AE_DYNARRAY_H_INCLUDED_ 7 | 8 | #include 9 | 10 | template 11 | class DynArray 12 | { 13 | public: 14 | DynArray(); // constructor 15 | DynArray(const DynArray &a); // copy constructor 16 | ~DynArray(); // distructor 17 | DynArray& operator = (const DynArray &a); // assignment operator 18 | 19 | el& operator [] (unsigned int index); // get array item 20 | void Add(const el &item); // Add item to the end of array 21 | 22 | unsigned int GetSize(); // get size of array (elements) 23 | void SetSize(unsigned int newsize); // set size of array (elements) 24 | void Clear(); // clear array 25 | void Delete(unsigned int pos); // delete array item 26 | void* getptr(); // get void* pointer to array data 27 | 28 | enum exception { MEMFAIL }; // exception enum 29 | 30 | private: 31 | el *array; // pointer for array's memory 32 | unsigned int size; // size of array (elemets) 33 | unsigned int realsize; // actual size of allocated memory 34 | 35 | const static int dyn_array_step = 128; // initial size of array memory (elements) 36 | const static int dyn_array_mult = 2; // multiplier (enlarge array memory 37 | // dyn_array_mult times ) 38 | }; 39 | 40 | ////////////////////////////////////////////////////////////////////// 41 | 42 | template 43 | DynArray::DynArray() 44 | { 45 | realsize = dyn_array_step; // First, allocate step 46 | // for dyn_array_step items 47 | size = 0; 48 | array = (el *)malloc(realsize*sizeof(el)); 49 | 50 | if (array == NULL) 51 | throw MEMFAIL; 52 | } 53 | 54 | 55 | template 56 | DynArray::~DynArray() 57 | { 58 | if (array) 59 | { 60 | free(array); 61 | array = NULL; 62 | } 63 | } 64 | 65 | 66 | template 67 | DynArray::DynArray(const DynArray &a) 68 | { 69 | array = (el *)malloc(sizeof(el)*a.realsize); 70 | if (array == NULL) 71 | throw MEMFAIL; 72 | 73 | memcpy(array, a.array, sizeof(el)*a.realsize); 74 | realsize = a.realsize; 75 | size = a.size; 76 | } 77 | 78 | 79 | template 80 | DynArray& DynArray::operator = (const DynArray &a) 81 | { 82 | if (this == &a) // in case somebody tries assign array to itself 83 | return *this; 84 | 85 | if (a.size == 0) // is other array is empty -- clear this array 86 | Clear(); 87 | 88 | SetSize(a.size); // set size 89 | 90 | memcpy(array, a.array, sizeof(el)*a.size); 91 | 92 | return *this; 93 | } 94 | 95 | template 96 | unsigned int DynArray::GetSize() 97 | { 98 | return size; // simply return size 99 | } 100 | 101 | 102 | template 103 | void DynArray::SetSize(unsigned int newsize) 104 | { 105 | size = newsize; 106 | 107 | if (size != 0) 108 | { 109 | // change array memory size 110 | // if new size is larger than current 111 | // or new size is less then half of the current 112 | if ((size > realsize) || (size < realsize/2)) 113 | { 114 | realsize = size; 115 | array = (el *)realloc(array, sizeof(el)*size); 116 | 117 | if (array == NULL) 118 | throw MEMFAIL; 119 | } 120 | } 121 | else 122 | Clear(); 123 | } 124 | 125 | template 126 | void DynArray::Delete(unsigned int pos) 127 | { 128 | if (size == 1) // If array has only one element 129 | Clear(); // than we clear it, since it will be deleted 130 | else 131 | { 132 | // otherwise, shift array elements 133 | for(unsigned int i=pos; i 142 | void DynArray::Clear() // clear array memory 143 | { 144 | size = 0; 145 | array = (el *)realloc(array, sizeof(el)*dyn_array_step); 146 | // set initial memory size again 147 | realsize = dyn_array_step; 148 | } 149 | 150 | template 151 | void* DynArray::getptr() 152 | { 153 | return array; // return void* pointer 154 | } 155 | 156 | template 157 | el& DynArray::operator [] (unsigned int index) 158 | { 159 | return array[index]; // return array element 160 | } 161 | 162 | template 163 | void DynArray::Add(const el &item) 164 | { 165 | size++; 166 | 167 | if (size > realsize) 168 | { 169 | realsize *= dyn_array_mult; 170 | 171 | array = (el *)realloc(array, sizeof(el)*realsize); 172 | 173 | if (array == NULL) 174 | throw MEMFAIL; 175 | } 176 | 177 | array[size-1] = item; 178 | } 179 | 180 | #endif // ifndef _AE_DYNARRAY_H_INCLUDED_ -------------------------------------------------------------------------------- /src/asm_scanner.cpp: -------------------------------------------------------------------------------- 1 | #include "scanner.h" 2 | #include 3 | 4 | CASMScanner::CASMScanner(std::shared_ptr spSigLoader, 5 | std::shared_ptr spBinaryFile) 6 | : spSigLoader(spSigLoader), iCurrentOffset(0){ 7 | assert(spBinaryFile != nullptr); 8 | this->spBinaryFile = spBinaryFile; 9 | spSymbols = CSymbolsFactory::getSymbols(spBinaryFile->getFilePath()); 10 | 11 | ullCodeSectionBase = spBinaryFile->getCodeSectionBase(); 12 | iCodeSectionLength = spBinaryFile->getCodeSectionSize(); 13 | 14 | iBatchSize = 1024; //disassemble 1k instructions each time 15 | } 16 | 17 | 18 | SCAN_RESULT CASMScanner::scan(std::shared_ptr& spVulnReport) { 19 | 20 | SCAN_RESULT sr = spBinaryFile->analyze(); 21 | if (SCAN_FAILED(sr)) { 22 | std::cout << "Failed to analyze binary" << std::endl; 23 | return sr; 24 | } 25 | 26 | if (spVulnReport == nullptr) 27 | spVulnReport = CVulnReportFactory::createReport(); 28 | 29 | std::string sFunction; 30 | while (getNextFunction(sFunction)) { 31 | if (sFunction.empty()) continue; 32 | for (size_t i = 0; i < spSigLoader->getSize(); i++) { 33 | auto spSignature = spSigLoader->getSignature(i); 34 | if (spSignature->getFunctionName() == sFunction) { 35 | std::vector sCallSequence; 36 | if (getCallSequence(sFunction, sCallSequence)) { 37 | DETECTION_STATUS status = spSignature->callSequenceMatch(sCallSequence); 38 | if (status != DETECTION_NOMATCH) 39 | spVulnReport->addDetection(spSignature->getCVE(), status); 40 | } 41 | } 42 | } 43 | } 44 | return sr; 45 | } 46 | 47 | 48 | bool CASMScanner::getNextFunction(std::string& sFunction) { 49 | iCurrentOffset = seekToInstruction(iCurrentOffset + 1, 50 | iBatchSize, "push", { "ebp", "rbp" }); 51 | 52 | if (iCurrentOffset == 0) { 53 | std::cout << "Couldn't get next function" << std::endl; 54 | return false; 55 | } 56 | 57 | sFunction = getCurrentSymbol(); 58 | return true; 59 | } 60 | 61 | size_t CASMScanner::seekToInstruction(uint64_t iStartPos, size_t iBatchSize, 62 | std::string sMnemonic, const std::vector& vsOperand) { 63 | if (iStartPos >= iCodeSectionLength) { 64 | std::cout << "No more code to scan" << std::endl; 65 | return 0; 66 | } 67 | 68 | while (iStartPos < iCodeSectionLength) { 69 | 70 | std::unique_ptr spInstSet; 71 | 72 | if (SCAN_FAILED(spBinaryFile->getInstFromAddress( 73 | ullCodeSectionBase + iStartPos, iBatchSize, spInstSet))) { 74 | std::cout << "Failed to get instruction from address: " << iStartPos << std::endl; 75 | return 0; 76 | } 77 | 78 | for (size_t i = 0; i < spInstSet->count; i++) { 79 | //printf("0x%" PRIx64 ":\t%s\t\t%s", spInstSet->pInsn[i].address, 80 | //spInstSet->pInsn[i].mnemonic, spInstSet->pInsn[i].op_str); 81 | 82 | if (sMnemonic.compare(spInstSet->pInsn[i].mnemonic) == 0) { 83 | for (auto sOperand : vsOperand) { 84 | if (sOperand.compare(spInstSet->pInsn[i].op_str) == 0) { 85 | uint64_t ullOffset = spInstSet->pInsn[i].address - ullCodeSectionBase; 86 | assert(ullOffset < SIZE_MAX); 87 | return (size_t)ullOffset; 88 | } //if 89 | } //for 90 | } //if 91 | } //for 92 | iStartPos += iBatchSize; 93 | } //while 94 | 95 | std::cout << "No more code to scan" << std::endl; 96 | return 0; 97 | } 98 | 99 | bool CASMScanner::getFunctionEnding(const std::string sFunction) { 100 | iCurrentOffset = seekToInstruction(iCurrentOffset, iBatchSize, "pop", { "ebp", "rbp" }); 101 | if (iCurrentOffset == 0) { 102 | std::cout << "Couldn't find function ending" << std::endl; 103 | return false; 104 | } 105 | 106 | 107 | if (getCurrentSymbol() != sFunction) { 108 | std::cout << "Symbol for function ending doesn't match given function" << std::endl; 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | bool CASMScanner::getSymbolAtAddress(uint64_t ullAddress, std::string& sSymbol) { 115 | SYMBOLMAP symbolMap; 116 | symbolMap.iAddress = ullAddress; 117 | if (SCAN_FAILED(spSymbols->getSymbolFromAddress(&symbolMap))) { 118 | return false; 119 | } 120 | sSymbol = symbolMap.sName; 121 | return true; 122 | } 123 | 124 | bool CASMScanner::getSymbolAtOffset(size_t iOffset, std::string& sSymbol) { 125 | return getSymbolAtAddress(iOffset + ullCodeSectionBase, sSymbol); 126 | } 127 | 128 | std::string CASMScanner::getCurrentSymbol() { 129 | std::string sSymbol; 130 | getSymbolAtOffset(iCurrentOffset, sSymbol); 131 | return sSymbol; 132 | } 133 | 134 | bool CASMScanner::getCallSequence(std::string sFunction, std::vector& vCalls) { 135 | 136 | std::string sCurrentFunction; 137 | if (getCurrentSymbol() != sFunction) { 138 | std::cout << "Function name doesn't match current offset" << std::endl; 139 | return false; 140 | } 141 | 142 | size_t iFunctionStart = iCurrentOffset; 143 | if (!getFunctionEnding(sFunction)) { 144 | std::cout << "Couldn't get function end" << std::endl; 145 | return false; 146 | } 147 | 148 | std::unique_ptr spInstSet; 149 | if (SCAN_FAILED(spBinaryFile->getInstFromAddress( 150 | ullCodeSectionBase + iFunctionStart, 151 | iCurrentOffset - iFunctionStart, spInstSet))) { 152 | std::cout << "Failed to get instructions from function: " << sFunction << std::endl; 153 | return false; 154 | } 155 | 156 | 157 | for (size_t i = 0; i < spInstSet->count; i++) { 158 | std::string sMnemonic(spInstSet->pInsn[i].mnemonic); 159 | std::string sOperand(spInstSet->pInsn[i].op_str); 160 | if ((sMnemonic == "jmp") || (sMnemonic == "call") 161 | || (sMnemonic == "je") || (sMnemonic == "jne")) { 162 | 163 | uint64_t ullAddress = std::strtoull(sOperand.c_str(), nullptr, 0); 164 | if (ullAddress != 0) { 165 | std::string sSymbol; 166 | if (getSymbolAtAddress(ullAddress, sSymbol)) 167 | vCalls.push_back(sSymbol); 168 | } 169 | } 170 | 171 | else if (sMnemonic.find("dword ptr [") != std::string::npos) { 172 | size_t iLeft = sOperand.find("["); 173 | std::string sAddress = sOperand.substr(iLeft + 1, sOperand.find("]") - iLeft - 1); 174 | uint64_t ullAddress = std::strtoull(sAddress.c_str(), nullptr, 0); 175 | if (ullAddress != 0) { 176 | std::string sSymbol; 177 | if (getSymbolAtAddress(ullAddress, sSymbol)) 178 | vCalls.push_back(sSymbol); 179 | } 180 | } 181 | 182 | } 183 | return true; 184 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8 FATAL_ERROR) 2 | 3 | project(vulnscan) 4 | 5 | enable_language(C) 6 | enable_language(CXX) 7 | 8 | if(CMAKE_CXX_COMPILER_ID MATCHES GNU) 9 | set(CMAKE_CXX_FLAGS "-std=c++14 -Wall -Wno-unknown-pragmas -Wno-sign-compare -Woverloaded-virtual -Wwrite-strings -Wno-unused") 10 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3") 11 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") 13 | endif() 14 | 15 | ########################################################################## 16 | # Download and unpack googletest at configure time 17 | ######################################################################### 18 | configure_file(dependencies/CMakeLists.gtest.txt googletest-download/CMakeLists.txt) 19 | execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . 20 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download" ) 21 | execute_process(COMMAND "${CMAKE_COMMAND}" --build . 22 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download" ) 23 | 24 | # Prevent GoogleTest from overriding our compiler/linker options 25 | # when building with Visual Studio 26 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 27 | 28 | # Add googletest directly to our build. This adds 29 | # the following targets: gtest, gtest_main, gmock 30 | # and gmock_main 31 | add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src" EXCLUDE_FROM_ALL 32 | "${CMAKE_BINARY_DIR}/googletest-build" EXCLUDE_FROM_ALL) 33 | 34 | include_directories( 35 | ${CMAKE_BINARY_DIR}/googletest-src/googletest/include 36 | ${CMAKE_BINARY_DIR}/googletest-src/googlemock/include 37 | ) 38 | 39 | ######################################################################### 40 | # Download and unpack capstone at configure time 41 | ######################################################################### 42 | configure_file(dependencies/CMakeLists.capstone.txt capstone-download/CMakeLists.txt) 43 | execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . 44 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/capstone-download" ) 45 | execute_process(COMMAND "${CMAKE_COMMAND}" --build . 46 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/capstone-download" ) 47 | 48 | 49 | # Add capstone directly to our build. This adds 50 | # the following targets: capstone 51 | add_subdirectory("${CMAKE_BINARY_DIR}/capstone-src" EXCLUDE_FROM_ALL 52 | "${CMAKE_BINARY_DIR}/capstone-build" EXCLUDE_FROM_ALL) 53 | 54 | include_directories( 55 | ${CMAKE_BINARY_DIR}/capstone-src/include 56 | ) 57 | 58 | ######################################################################### 59 | # Download and unpack pe-parse at configure time 60 | ######################################################################### 61 | configure_file(dependencies/CMakeLists.peparse.txt pe-parse-download/CMakeLists.txt) 62 | execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" . 63 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/pe-parse-download" ) 64 | execute_process(COMMAND "${CMAKE_COMMAND}" --build . 65 | WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/pe-parse-download" ) 66 | 67 | # Add pe-parse directly to our build. This adds 68 | # the following targets: pe-parse 69 | add_subdirectory("${CMAKE_BINARY_DIR}/pe-parse-src" EXCLUDE_FROM_ALL 70 | "${CMAKE_BINARY_DIR}/pe-parse-build" EXCLUDE_FROM_ALL) 71 | 72 | include_directories( 73 | ${CMAKE_BINARY_DIR}/pe-parse-src/pe-parser-library/include 74 | ) 75 | 76 | ######################################################################### 77 | # other dependencies 78 | ######################################################################### 79 | 80 | add_subdirectory(${PROJECT_SOURCE_DIR}/dependencies/strings2) 81 | 82 | include_directories( 83 | ${PROJECT_SOURCE_DIR}/dependencies/strings2/strings 84 | ) 85 | 86 | ######################################################################### 87 | # Build main project 88 | ######################################################################### 89 | 90 | include_directories( 91 | ${PROJECT_SOURCE_DIR}/src 92 | ) 93 | 94 | add_executable( 95 | ${PROJECT_NAME} 96 | src/file_typer.cpp 97 | src/windows_binary.cpp 98 | src/linux_binary.cpp 99 | src/vuln_report.cpp 100 | src/vulnerability.cpp 101 | src/vulnscan.cpp 102 | src/disassembler.cpp 103 | src/signature.cpp 104 | src/windows_symbols.cpp 105 | src/asm_scanner.cpp 106 | src/string_scanner.cpp 107 | src/scan_engine.cpp 108 | src/scan_target.cpp 109 | src/utils/thread_pool.cpp 110 | ) 111 | 112 | target_link_libraries( 113 | ${PROJECT_NAME} 114 | capstone-static 115 | pe-parser-library 116 | strings2 117 | ) 118 | 119 | add_executable( 120 | unit_tests 121 | src/windows_binary.cpp 122 | src/linux_binary.cpp 123 | src/file_typer.cpp 124 | src/vuln_report.cpp 125 | src/vulnerability.cpp 126 | src/disassembler.cpp 127 | src/signature.cpp 128 | src/windows_symbols.cpp 129 | src/string_scanner.cpp 130 | src/asm_scanner.cpp 131 | src/scan_engine.cpp 132 | src/scan_target.cpp 133 | src/utils/thread_pool.cpp 134 | test/main.cpp 135 | test/test_windows_binary.cpp 136 | test/test_file_typer.cpp 137 | test/test_disassembler.cpp 138 | test/test_signature.cpp 139 | test/test_vulnerability.cpp 140 | ) 141 | 142 | target_link_libraries( 143 | unit_tests 144 | gtest 145 | gmock 146 | capstone-static 147 | pe-parser-library 148 | strings2 149 | ) 150 | 151 | 152 | include(CTest) 153 | enable_testing() 154 | 155 | add_test(unit ${PROJECT_BINARY_DIR}/unit_tests) 156 | 157 | add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD 158 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/docs/vulnscan.sigs ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/vulnscan.sigs 159 | ) 160 | 161 | message("PROJECT_NAME ${PROJECT_NAME}") 162 | message("PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}") 163 | message("CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}") 164 | message("CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}") 165 | message("PROJECT_BINARY_DIR ${PROJECT_BINARY_DIR}") 166 | message("CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}") 167 | message("CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}") 168 | message("CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}") 169 | message("CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") 170 | message("CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}") -------------------------------------------------------------------------------- /src/windows_symbols.cpp: -------------------------------------------------------------------------------- 1 | #include "symbols.h" 2 | 3 | #ifdef _WIN32 // will need DbgHelp 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #pragma comment(lib, "dbghelp.lib") 10 | #endif // _WIN32 11 | 12 | std::shared_ptr CSymbolsFactory::g_spSymbols; 13 | 14 | CPDBSymbols::CPDBSymbols(){ 15 | sCurrentSymbol = std::string(); 16 | bSymbolLoaded = false; 17 | hProcess = nullptr; 18 | } 19 | 20 | CPDBSymbols::~CPDBSymbols() { 21 | 22 | } 23 | 24 | void CPDBSymbols::loadSymbols() { 25 | if (sCurrentSymbol.empty()) { 26 | std::cout << "symbol file is not specified\n"; 27 | return; 28 | } 29 | if (bSymbolLoaded) unloadSymbols(); 30 | #ifdef _WIN32 // will need DbgHelp 31 | 32 | DWORD error; 33 | 34 | SymSetOptions(SYMOPT_UNDNAME); 35 | 36 | hProcess = GetCurrentProcess(); 37 | 38 | if (!SymInitialize(hProcess, NULL, TRUE)) 39 | { 40 | // SymInitialize failed 41 | error = GetLastError(); 42 | printf("SymInitialize returned error : %d\n", error); 43 | } 44 | 45 | dwLoadedAddr = SymLoadModuleEx(hProcess, // target process 46 | NULL, // handle to image - not used 47 | sCurrentSymbol.c_str(), // name of image file 48 | NULL, // name of module - not required 49 | 0, // can't be zero if loading from .pdb 50 | 0, // size of image - not required 51 | NULL, // MODLOAD_DATA used for special cases 52 | 0); // flags - not required 53 | 54 | if (!dwLoadedAddr){ 55 | DWORD error = GetLastError(); 56 | printf("SymLoadModuleEx returned error : %d\n", error); 57 | return; 58 | } 59 | 60 | if (!ShowSymbolInfo(dwLoadedAddr)) { 61 | return; 62 | } 63 | //enumSymbols(dwLoadedAddr); 64 | bSymbolLoaded = true; 65 | #endif // _WIN32 66 | } 67 | 68 | #ifdef _WIN32 // will need DbgHelp 69 | 70 | BOOL CALLBACK EnumSymProc( 71 | PSYMBOL_INFO pSymInfo, 72 | ULONG SymbolSize, 73 | PVOID UserContext) 74 | { 75 | UNREFERENCED_PARAMETER(UserContext); 76 | std::string sSymbolName(pSymInfo->Name, pSymInfo->NameLen); 77 | printf("0x%llx\t", pSymInfo->Address); 78 | std::cout << sSymbolName << std::endl; 79 | return TRUE; 80 | } 81 | 82 | #endif // _WIN32 83 | 84 | SCAN_RESULT CPDBSymbols::enumSymbols(DWORD64 ModBase) { 85 | #ifdef _WIN32 // will need DbgHelp 86 | 87 | if (!bSymbolLoaded) 88 | return SCAN_RESULT_SYMBOL_NOT_LOADED; 89 | 90 | if (!SymEnumSymbols(hProcess, // Process handle from SymInitialize. 91 | ModBase, // Base address of module. 92 | NULL, // Name of symbols to match. 93 | EnumSymProc, // Symbol handler procedure. 94 | NULL)) // User context. 95 | { 96 | // SymEnumSymbols failed 97 | printf("SymEnumSymbols failed: %d\n", GetLastError()); 98 | } 99 | 100 | #endif // _WIN32 101 | return SCAN_RESULT_SUCCESS; 102 | } 103 | 104 | SCAN_RESULT CPDBSymbols::getSymbolFromAddress(PSYMBOLMAP pSymbolMap) { 105 | 106 | #ifdef _WIN32 // will need DbgHelp 107 | 108 | assert(pSymbolMap != nullptr); 109 | assert(pSymbolMap->iAddress != 0); 110 | 111 | if (!bSymbolLoaded) 112 | return SCAN_RESULT_SYMBOL_NOT_LOADED; 113 | 114 | DWORD64 dwDisplacement = 0; 115 | 116 | char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; 117 | PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; 118 | 119 | pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); 120 | pSymbol->MaxNameLen = MAX_SYM_NAME; 121 | PDWORD64 pDisplacement = reinterpret_cast(&pSymbolMap->iDisplacement); 122 | if (!SymFromAddr(hProcess, pSymbolMap->iAddress, pDisplacement, pSymbol)) 123 | { 124 | // SymFromAddr failed 125 | DWORD error = GetLastError(); 126 | //printf("SymFromAddr returned error : %d\n", error); 127 | return SCAN_RESULT_SYMBOL_NOT_FOUND; 128 | } 129 | pSymbolMap->sName = std::string(pSymbol->Name, pSymbol->NameLen); 130 | #endif // _WIN32 131 | 132 | return SCAN_RESULT_SUCCESS; 133 | } 134 | 135 | void CPDBSymbols::unloadSymbols() { 136 | #ifdef _WIN32 // will need DbgHelp 137 | 138 | if (!bSymbolLoaded) { 139 | std::cout << "Symbol needs to be loaded before unloading\n"; 140 | return; 141 | } 142 | 143 | 144 | assert(hProcess != nullptr); 145 | if (!SymUnloadModule64(hProcess, dwLoadedAddr)) 146 | { 147 | // SymUnloadModule64 failed 148 | DWORD error = GetLastError(); 149 | printf("SymUnloadModule64 returned error : %d\n", error); 150 | } 151 | #endif // _WIN32 152 | } 153 | 154 | bool CPDBSymbols::ShowSymbolInfo(DWORD64 ModBase) 155 | { 156 | // Get module information 157 | #ifdef _WIN32 // will need DbgHelp 158 | 159 | IMAGEHLP_MODULE64 ModuleInfo; 160 | 161 | memset(&ModuleInfo, 0, sizeof(ModuleInfo)); 162 | 163 | ModuleInfo.SizeOfStruct = sizeof(ModuleInfo); 164 | 165 | BOOL bRet = ::SymGetModuleInfo64(GetCurrentProcess(), ModBase, &ModuleInfo); 166 | 167 | if (!bRet) 168 | { 169 | _tprintf(_T("Error: SymGetModuleInfo64() failed. Error code: %u \n"), ::GetLastError()); 170 | return false; 171 | } 172 | 173 | 174 | // Display information about symbols 175 | 176 | // Kind of symbols 177 | 178 | switch (ModuleInfo.SymType) 179 | { 180 | case SymNone: 181 | _tprintf(_T("No symbols available for the module.\n")); 182 | break; 183 | 184 | case SymExport: 185 | _tprintf(_T("Loaded symbols: Exports\n")); 186 | break; 187 | 188 | case SymCoff: 189 | _tprintf(_T("Loaded symbols: COFF\n")); 190 | break; 191 | 192 | case SymCv: 193 | _tprintf(_T("Loaded symbols: CodeView\n")); 194 | break; 195 | 196 | case SymSym: 197 | _tprintf(_T("Loaded symbols: SYM\n")); 198 | break; 199 | 200 | case SymVirtual: 201 | _tprintf(_T("Loaded symbols: Virtual\n")); 202 | break; 203 | 204 | case SymPdb: 205 | _tprintf(_T("Loaded symbols: PDB\n")); 206 | break; 207 | 208 | case SymDia: 209 | _tprintf(_T("Loaded symbols: DIA\n")); 210 | break; 211 | 212 | case SymDeferred: 213 | _tprintf(_T("Loaded symbols: Deferred\n")); // not actually loaded 214 | break; 215 | 216 | default: 217 | _tprintf(_T("Loaded symbols: Unknown format.\n")); 218 | break; 219 | } 220 | 221 | // Image name 222 | 223 | if (_tcslen(ModuleInfo.ImageName) > 0) 224 | { 225 | _tprintf(_T("Image name: %s \n"), ModuleInfo.ImageName); 226 | } 227 | 228 | // Loaded image name 229 | 230 | if (_tcslen(ModuleInfo.LoadedImageName) > 0) 231 | { 232 | _tprintf(_T("Loaded image name: %s \n"), ModuleInfo.LoadedImageName); 233 | } 234 | 235 | // Loaded PDB name 236 | 237 | if (_tcslen(ModuleInfo.LoadedPdbName) > 0) 238 | { 239 | _tprintf(_T("PDB file name: %s \n"), ModuleInfo.LoadedPdbName); 240 | } 241 | 242 | // Is debug information unmatched ? 243 | // (It can only happen if the debug information is contained 244 | // in a separate file (.DBG or .PDB) 245 | 246 | if (ModuleInfo.PdbUnmatched || ModuleInfo.DbgUnmatched) 247 | { 248 | _tprintf(_T("Warning: Unmatched symbols. \n")); 249 | } 250 | 251 | // Contents 252 | 253 | // Line numbers available ? 254 | 255 | _tprintf(_T("Line numbers: %s \n"), ModuleInfo.LineNumbers ? _T("Available") : _T("Not available")); 256 | 257 | // Global symbols available ? 258 | 259 | _tprintf(_T("Global symbols: %s \n"), ModuleInfo.GlobalSymbols ? _T("Available") : _T("Not available")); 260 | 261 | // Type information available ? 262 | 263 | _tprintf(_T("Type information: %s \n"), ModuleInfo.TypeInfo ? _T("Available") : _T("Not available")); 264 | 265 | // Source indexing available ? 266 | 267 | _tprintf(_T("Source indexing: %s \n"), ModuleInfo.SourceIndexed ? _T("Yes") : _T("No")); 268 | 269 | // Public symbols available ? 270 | 271 | _tprintf(_T("Public symbols: %s \n"), ModuleInfo.Publics ? _T("Available") : _T("Not available")); 272 | 273 | return ModuleInfo.GlobalSymbols; 274 | #endif // _WIN32 275 | } 276 | 277 | -------------------------------------------------------------------------------- /docs/vulnscan.sigs: -------------------------------------------------------------------------------- 1 | CVE-2010-5327 2 | STRING: 3 | - This method must have at least one argument as the name of 4 | - the class to instantiate 5 | - template for security reasons 6 | 7 | 8 | CVE-2011-1019 9 | STRING: 10 | - Loading kernel module for a network device 11 | - with CAP_SYS_MODULE (deprecated). Use CAP_NET_ADMIN and alias netdev 12 | 13 | 14 | CVE-2011-1021 15 | STRING: 16 | - Allow ACPI methods to be inserted/replaced at run time 17 | 18 | 19 | CVE-2011-1476 20 | STRING: 21 | + MIDI Error: Invalid patch format (key) 22 | + MIDI Error: Patch header too short 23 | + MIDI Warning: Sysex record too short 24 | 25 | 26 | CVE-2011-1833 27 | STRING: 28 | - Mount of device (uid: 29 | - not owned by requested user (uid: 30 | 31 | 32 | CVE-2011-3178 33 | STRING: 34 | - Invalid scheduler type, check mkdiststats docu - aehm, source 35 | 36 | CVE-2012-1836 37 | STRING: 38 | - DN label decompression header is bogus 39 | - DN label decompression is impossible -- malformed/hostile packet? 40 | - rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet? 41 | - rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet? 42 | - don't know how to handle undefined type ( 43 | 44 | CVE-2012-2139 45 | STRING: 46 | - should use the base name of the file name to prevent file system traversal 47 | 48 | CVE-2012-2140 49 | STRING: 50 | - should send an email using exim 51 | - should send an email with a return-path using exim 52 | - Can't set the return-path 53 | - should use the sender address is no return path is specified 54 | - should use the from address is no return path or sender are specified 55 | - should escape the return path address 56 | - should still send an email if the settings have been set to nil 57 | - should escape evil haxxor attemptes 58 | 59 | CVE-2012-2395 60 | STRING: 61 | - no power type set for system 62 | + Power management is not enabled for this system 63 | + Invalid power management type for this system 64 | 65 | CVE-2012-3400 66 | STRING: 67 | - error loading logical volume descriptor: 68 | - Invalid packet length 69 | - Too many sparing tables 70 | 71 | 72 | CVE-2012-3412 73 | STRING: 74 | + TX and RX queues cannot be smaller than 75 | - RX queues cannot be smaller than 76 | - increasing TX queue size to minimum of 77 | 78 | CVE-2012-4520 79 | STRING: 80 | - If the reset view is marked as being for admin, the HTTP_HOST header is used for a domain override. 81 | - Poisoned HTTP_HOST headers can't be used for reset emails 82 | - Poisoned HTTP_HOST headers can't be used for reset emails on admin views 83 | 84 | 85 | CVE-2012-6697 86 | STRING: 87 | - Invalid decompression pointer 88 | 89 | CVE-2013-1770 90 | STRING: 91 | + You do not have access to view views. 92 | + No graphs defined for this view. Please add some 93 | 94 | CVE-2013-2021 95 | STRING: 96 | - pdf_extract_obj: obj 97 | - cli_pdf: R value out of range 98 | - cli_pdf: R value outside supported range [2..5] 99 | 100 | CVE-2013-4136 101 | STRING: 102 | - Server instance directory 103 | - Cannot create server instance directory 104 | 105 | CVE-2013-4492 106 | STRING: 107 | - MissingTranslationData html_message html escapes key names 108 | 109 | CVE-2013-6370 110 | STRING: 111 | - a-=c; a^=rot(c,k); c+=b; 112 | - racy random seed initializtion if used by multiple threads 113 | - error CryptAcquireContextW 114 | 115 | CVE-2013-7225 116 | STRING: 117 | + should not display completed tasks 118 | - should collapse all comments and emails on a specific contact 119 | - should expand all comments and emails on a specific contact 120 | - should not do anything when state neither Expanded nor Collapsed 121 | - should collapse all comments and emails on Contact 122 | - should not allow an arbitary state (sanitizes input) 123 | - should not update an arbitary model (sanitizes input) 124 | 125 | CVE-2013-7441 126 | STRING: 127 | - Failed to accept socket connection: %m 128 | - Spawned a child process 129 | - Failed to spawn a child process 130 | - Modern initial negotiation failed 131 | - Failed to get socket flags 132 | - Failed to set socket to blocking mode 133 | - Failed to set peername 134 | 135 | CVE-2013-7459 136 | STRING: 137 | - ECB mode does not use IV 138 | - CTR mode needs counter parameter, not IV 139 | 140 | 141 | CVE-2014-0237 142 | STRING: 143 | + Unpack summary info loop limit 144 | 145 | 146 | CVE-2014-0238 147 | STRING: 148 | - CDF_VECTOR with nelements == 0 149 | 150 | CVE-2014-2208 151 | STRING: 152 | + Light Process failed chdir to %s. 153 | + LightProcess got invalid command: %.20s 154 | + Failed to send command proc_open 155 | 156 | 157 | CVE-2014-2567 158 | STRING: 159 | - Configuration requires sending STARTTLS, but the IMAP server greets us with PREAUTH. 160 | - Encryption cannot be established. If this configuration worked previously, someone 161 | - is after your data and they are pretty smart. 162 | - * PREAUTH hi there\r\n 163 | 164 | CVE-2014-5472 165 | STRING: 166 | - ISOFS: Recursive directory relocation 167 | - ISOFS: Directory relocation points to 168 | 169 | CVE-2014-8122 170 | STRING: 171 | - Invalid state detected after 172 | 173 | CVE-2014-9620 174 | STRING: 175 | - elf_notes 256 max ELF notes processed 176 | 177 | CVE-2014-9721 178 | STRING: 179 | - Received unauthenticated message: 180 | 181 | CVE-2015-1335 182 | STRING: 183 | - Out of memory checking for symbolic link 184 | - this is a relative bind mount 185 | 186 | CVE-2015-2304 187 | STRING: 188 | - Extracting an absolute path should fail here. 189 | 190 | CVE-2015-2311 191 | STRING: 192 | - Text blob missing NUL terminator. 193 | 194 | CVE-2015-2312 195 | STRING: 196 | - Message contains amplified list pointer. 197 | 198 | CVE-2015-2922 199 | STRING: 200 | - RA: Got route advertisement with lower hop_limit than current 201 | 202 | CVE-2015-3220 203 | STRING: 204 | - No data left after decryption and IV removal 205 | 206 | 207 | CVE-2015-4054 (1) 208 | STRING: 209 | - Password packet before auth packet? 210 | 211 | 212 | CVE-2015-4054 213 | STRING: 214 | - client password pkt before startup packet 215 | 216 | CVE-2015-7566 217 | STRING: 218 | - missing bulk out endpoints\n 219 | 220 | 221 | CVE-2015-8013 222 | STRING: 223 | - GNU s2k type not supported. 224 | 225 | 226 | CVE-2015-8747 227 | STRING: 228 | - Can't tranlate name safely to filesystem, 229 | - skipping component: %s 230 | 231 | 232 | CVE-2015-8896 233 | STRING: 234 | - DPC HDRI Cipher OpenMP 235 | 236 | CVE-2016-10248 237 | STRING: 238 | - warning: forcing negative ROI shift to zero 239 | - (bitstream is probably corrupt)\n 240 | 241 | CVE-2016-2385 242 | STRING: 243 | - not enough space to encode sip message\n 244 | 245 | 246 | CVE-2016-3136 247 | STRING: 248 | - expected endpoint missing\n 249 | 250 | CVE-2016-3140 251 | STRING: 252 | - OOB endpoints missing\n 253 | - bulk-in endpoint missing\n 254 | - bulk-out endpoint missing\n 255 | 256 | 257 | CVE-2016-3178 258 | STRING: 259 | - Response received %d bytes\n 260 | 261 | 262 | CVE-2016-3728 263 | STRING: 264 | - Unrecognized pxeboot config type: Server 265 | 266 | 267 | CVE-2016-4302 268 | STRING: 269 | - Invalid zero dictionary size 270 | 271 | 272 | CVE-2016-4414 273 | STRING: 274 | - Received invalid handshake data from client 275 | 276 | CVE-2016-4809 277 | STRING: 278 | - Rejecting malformed cpio archive: symlink contents exceed 1 megabyte 279 | 280 | CVE-2016-5009 281 | STRING: 282 | - command prefix not found 283 | - command prefix must not be empty 284 | - command requires a prefix to be valid 285 | 286 | CVE-2016-6873 287 | STRING: 288 | - compact(): recursion detected 289 | 290 | 291 | CVE-2016-6912 292 | STRING: 293 | + Paletter image not supported by webp 294 | - Palette image not supported by webp 295 | 296 | 297 | CVE-2016-7115 298 | STRING: 299 | - Invalid salt length: %d (instead of 16) received from server %s\n 300 | 301 | 302 | CVE-2016-8339 303 | STRING: 304 | + Unrecognized client limit class 305 | - Unrecognized client limit class: the user specified 306 | - an invalid one, or 'master' which has no buffer limits. 307 | 308 | 309 | CVE-2016-9123 310 | STRING: 311 | - ECDH-ES output size too large, must be less than 1<<16 312 | 313 | 314 | CVE-2016-9317 315 | STRING: 316 | - product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully 317 | 318 | CVE-2017-0896 319 | STRING: 320 | - Must be a realm administrator 321 | 322 | CVE-2017-0900 323 | STRING: 324 | - max_length must be positive 325 | - Truncating desc to 2 characters:\nab 326 | 327 | 328 | CVE-2017-0903 329 | STRING: 330 | - YAML safe loading is not available. Please upgrade psych to a version that supports safe loading (>= 2.0). 331 | 332 | 333 | CVE-2017-1000198 334 | STRING: 335 | + tcmu_create_glfs_object failed\n 336 | + glfs_access file not present, or not writable 337 | 338 | CVE-2017-13053 339 | STRING: 340 | - field inside this NLRI 341 | 342 | 343 | CVE-2017-15102 344 | STRING: 345 | + Not able to get a minor for this device.\n 346 | + LEGO USB Tower #%d now attached to major 347 | - Not able to get a minor for this device.\n 348 | - LEGO USB Tower #%d now attached to major 349 | 350 | 351 | CVE-2017-15385 352 | STRING: 353 | - Warning: Invalid vd_next in the ELF version\n 354 | 355 | CVE-2017-16529 356 | STRING: 357 | - invalid control header\n 358 | - too short v1 buffer descriptor\n 359 | - invalid buffer length (v1)\n 360 | 361 | 362 | CVE-2017-16533 363 | STRING: 364 | - hid descriptor is too short\n 365 | 366 | 367 | CVE-2017-16534 368 | STRING: 369 | - invalid descriptor buffer length\n 370 | 371 | CVE-2017-16645 372 | STRING: 373 | - Too large descriptor\n 374 | - Union descriptor to short (%d vs %zd\n) 375 | 376 | CVE-2017-2298 377 | STRING: 378 | - Identity returned by server would result in directory traversal. Not writing key to disk. 379 | 380 | 381 | CVE-2017-2599 382 | STRING: 383 | + rename as an overwrite should have failed 384 | - worked as expected on a nonexistent target 385 | - was not supposed to work against 386 | - CLI command failed with status 387 | 388 | 389 | CVE-2017-5225 390 | STRING: 391 | - Error, can only handle BitsPerSample=8 in %s 392 | 393 | 394 | CVE-2017-5345 395 | STRING: 396 | - blog-post magazine col-md-12 clearfix 397 | 398 | 399 | CVE-2017-5545 400 | STRING: 401 | - ERROR: Input file is too small to contain valid plist data.\n 402 | 403 | 404 | CVE-2017-5581 405 | STRING: 406 | - Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d 407 | 408 | CVE-2017-6199 409 | STRING: 410 | - invalid e-mail address 411 | 412 | CVE-2017-6420 413 | STRING: 414 | - WWPack: unpack memory address out of bounds.\n 415 | 416 | 417 | CVE-2017-6430 418 | STRING: 419 | + Cannot write output file (%s) 420 | - Cannot write output file (%s): the filter is not correctly handled. 421 | - Cannot write output file (%s): the filter format is not correct. 422 | 423 | 424 | CVE-2017-7524 425 | STRING: 426 | - Algorithm not supported for hmac: 427 | - HMAC Update failed: 428 | - HMAC Final failed: 429 | 430 | CVE-2017-9502 431 | STRING: 432 | + expected localhost or 127.0.0.1 or none 433 | ASM: 434 | - parseurlandfillconn: strlen strlen strlen curl_strnequal Curl_failf curl_strnequal curl_strnequal Curl_failf Curl_cmalloc 435 | 436 | CVE-2017-8816 437 | STRING: 438 | + NTLM handshake failure (bad type-2 message) 439 | ASM: 440 | - Curl_ntlm_core_mk_ntlmv2_hash: Curl_cmalloc 441 | 442 | CVE-2017-1000100 443 | STRING: 444 | + TFTP: Unknown transfer ID 445 | + Disk full or allocation exceeded 446 | + tftp_rx: giving up waiting for block 447 | - TFTP file name too long 448 | 449 | CVE-2018-1000120 450 | ASM: 451 | + Curl_ftp_done: curl_unescape 452 | + ftp_parse_url_path: curl_unescape 453 | 454 | CVE-2018-1000121 455 | STRING: 456 | + LDAP local: bind ldap_parse_result 457 | + LDAP local: ldap_search_ext 458 | ASM: 459 | - ldap_recv: ldap_get_attribute_ber ldap_get_attribute_ber 460 | 461 | CVE-2018-1000122 462 | STRING: 463 | + RTSP CSeq mismatch or invalid CSeq 464 | 465 | CVE-2018-1000300 466 | STRING: 467 | - cached response data too big to handle -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 zhutoulala@gmail.com 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/string_parser.cpp: -------------------------------------------------------------------------------- 1 | //#include "StdAfx.h" 2 | #include "string_parser.h" 3 | 4 | int string_parser::extractImmediate( char* immediate, int immediateSize, STRING_TYPE &stringType, unsigned char* outputString ) 5 | { 6 | // Extract unicode or ascii from the immediate constant. 7 | // Assumes: outputString + 4 is a valid address. 8 | int i = 0; 9 | switch(stringType) 10 | { 11 | case TYPE_ASCII: 12 | // Parse the immediate as ascii 13 | while( i < immediateSize && isAscii[immediate[i]] ) 14 | { 15 | *outputString = immediate[i]; 16 | outputString++; 17 | i++; 18 | } 19 | return i; 20 | 21 | case TYPE_UNICODE: 22 | // Parse the immediate as unicode 23 | while( i+1 < immediateSize && isAscii[immediate[i]] && immediate[i+1] == 0 ) 24 | { 25 | *outputString = immediate[i]; 26 | outputString++; 27 | i+=2; 28 | } 29 | return i/2; 30 | 31 | case TYPE_UNDETERMINED: 32 | // Determine if this is ascii or unicode 33 | if( !isAscii[immediate[0]] ) 34 | { 35 | // Not unicode or ascii, return. 36 | return 0; 37 | }else if( immediateSize > 1 && immediate[1] == 0 ) 38 | { 39 | // Recurse as Unicode 40 | stringType = TYPE_UNICODE; 41 | return extractImmediate( immediate, immediateSize, stringType, outputString ); 42 | }else{ 43 | // Recurse as Ascii 44 | stringType = TYPE_ASCII; 45 | return extractImmediate( immediate, immediateSize, stringType, outputString ); 46 | } 47 | 48 | default: 49 | break; 50 | } 51 | return 0; 52 | } 53 | 54 | int string_parser::extractString( unsigned char* buffer, long bufferSize, long offset, unsigned char* outputString, int outputStringSize, int &outputStringLength, EXTRACT_TYPE & extractType, STRING_TYPE & stringType) 55 | { 56 | // Process the string as either: 57 | // 1. ascii 58 | // 2. unicode 59 | // 3. x86 ASM stack pushes 60 | // TODO: 4. x64 ASM stack pushes 61 | // 62 | // To improve performance: 63 | // Assumes MAX_STRING_SIZE > 1 64 | // Assumes MinStringSize > 1 65 | // Assumes offset + 3 < bufferSize 66 | // These assumptions must be validated by the calling function. 67 | 68 | // Supported string push formats 69 | // C6 45 mov byte [ebp+imm8], imm8 70 | // C6 85 mov byte [ebp+imm32], imm8 71 | // 66 C7 45 mov word [ebp+imm8], imm16 72 | // 66 C7 85 mov word [ebp+imm32], imm16 73 | // C7 45 mov dword [ebp+imm8], imm32 74 | // C7 85 mov dword [ebp+imm32], imm32 75 | 76 | // Set unknown string type 77 | extractType = EXTRACT_RAW; 78 | outputStringLength = 0; 79 | int i = 0; 80 | int instSize; 81 | int immSize; 82 | int immOffset; 83 | int maxStringSize; 84 | int size; 85 | 86 | 87 | uint16_t value = *((uint16_t*) (buffer+offset)); 88 | // Switch on the first two bytes 89 | switch( value ) 90 | { 91 | case 0x45C6: 92 | // 0 1 0 [0] 93 | // C6 45 mov byte [ebp+imm8], imm8 94 | instSize = 4; 95 | immSize = 1; 96 | immOffset = instSize - immSize; 97 | maxStringSize = 1; 98 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 99 | && buffer[offset+i] == 0xC6 && buffer[offset+i+1] == 0x45 ) 100 | { 101 | 102 | // Process this immediate 103 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 104 | outputString += size; 105 | outputStringLength += size; 106 | 107 | i+=instSize; 108 | 109 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 110 | || (stringType == TYPE_ASCII && size < immSize ) ) 111 | break; 112 | } 113 | extractType = EXTRACT_ASM; 114 | return i; 115 | 116 | case 0x85C6: 117 | // 0 1 0 1 2 3 4 [0] 118 | // C6 85 mov byte [ebp+imm32], imm8 119 | instSize = 8; 120 | immSize = 1; 121 | immOffset = instSize - immSize; 122 | maxStringSize = 1; 123 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 124 | && buffer[offset+i] == 0xC6 && buffer[offset+i+1] == 0x85 ) 125 | { 126 | 127 | // Process this immediate 128 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 129 | outputString += size; 130 | outputStringLength += size; 131 | 132 | i+=instSize; 133 | 134 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 135 | || (stringType == TYPE_ASCII && size < immSize ) ) 136 | break; 137 | } 138 | extractType = EXTRACT_ASM; 139 | return i; 140 | 141 | case 0x45C7: 142 | // 0 1 0 [0 1 2 3] 143 | // C7 45 mov dword [ebp+imm8], imm32 144 | instSize = 7; 145 | immSize = 4; 146 | immOffset = instSize - immSize; 147 | maxStringSize = 4; 148 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 149 | && buffer[offset+i] == 0xC7 && buffer[offset+i+1] == 0x45 ) 150 | { 151 | 152 | // Process this immediate 153 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 154 | outputString += size; 155 | outputStringLength += size; 156 | 157 | i+=instSize; 158 | 159 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 160 | || (stringType == TYPE_ASCII && size < immSize ) ) 161 | break; 162 | } 163 | extractType = EXTRACT_ASM; 164 | return i; 165 | 166 | case 0x85C7: 167 | // 0 1 0 1 2 3 [0 1 2 3] 168 | // C7 85 mov dword [ebp+imm32], imm32 169 | instSize = 10; 170 | immSize = 4; 171 | immOffset = instSize - immSize; 172 | maxStringSize = 4; 173 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 174 | && buffer[offset+i] == 0xC7 && buffer[offset+i+1] == 0x85 ) 175 | { 176 | 177 | // Process this immediate 178 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 179 | outputString += size; 180 | outputStringLength += size; 181 | 182 | i+=instSize; 183 | 184 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 185 | || (stringType == TYPE_ASCII && size < immSize ) ) 186 | break; 187 | } 188 | extractType = EXTRACT_ASM; 189 | return i; 190 | 191 | case 0xC766: 192 | if( buffer[offset+2] == 0x45 ) 193 | { 194 | // 0 1 2 0 [0 1] 195 | // 66 C7 45 mov word [ebp+imm8], imm16 196 | instSize = 6; 197 | immSize = 2; 198 | immOffset = instSize - immSize; 199 | maxStringSize = 2; 200 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 201 | && buffer[offset+i] == 0x66 && buffer[offset+i+1] == 0xC7 && buffer[offset+i+2] == 0x45 ) 202 | { 203 | 204 | // Process this immediate 205 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 206 | outputString += size; 207 | outputStringLength += size; 208 | 209 | i+=instSize; 210 | 211 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 212 | || (stringType == TYPE_ASCII && size < immSize ) ) 213 | break; 214 | } 215 | extractType = EXTRACT_ASM; 216 | return i; 217 | }else if( buffer[offset+2] == 0x85 ) 218 | { 219 | // 0 1 2 0 1 2 3 [0 1] 220 | // 66 C7 85 mov word [ebp+imm32], imm16 221 | i = 0; 222 | instSize = 9; 223 | immSize = 2; 224 | immOffset = instSize - immSize; 225 | maxStringSize = 2; 226 | while( offset+i+instSize < bufferSize && outputStringLength + maxStringSize < outputStringSize 227 | && buffer[offset+i] == 0x66 && buffer[offset+i+1] == 0xC7 && buffer[offset+i+2] == 0x85 ) 228 | { 229 | 230 | // Process this immediate 231 | size = this->extractImmediate( (char*) (buffer + offset + immOffset + i), immSize, stringType, outputString ); 232 | outputString += size; 233 | outputStringLength += size; 234 | 235 | i+=instSize; 236 | 237 | if( (stringType == TYPE_UNICODE && size < ((immSize + 1) / 2) ) 238 | || (stringType == TYPE_ASCII && size < immSize ) ) 239 | break; 240 | } 241 | extractType = EXTRACT_ASM; 242 | return i; 243 | } 244 | break; 245 | 246 | default: 247 | // Try to parse as ascii or unicode 248 | if( isAscii[buffer[offset]] ) 249 | { 250 | // Consider unicode case 251 | if( buffer[offset+1] == 0 ) // No null dereference by assumptions 252 | { 253 | // Parse as unicode 254 | while( offset+i+1 < bufferSize && i/2 < outputStringSize && isAscii[buffer[offset+i]] && buffer[offset+i+1] == 0 && i/2 + 1 < outputStringSize ) 255 | { 256 | // Copy this character 257 | outputString[i/2] = buffer[offset+i]; 258 | 259 | i+=2; 260 | } 261 | outputStringLength = i / 2; 262 | stringType = TYPE_UNICODE; 263 | return i; 264 | }else 265 | { 266 | // Parse as ascii 267 | i = offset; 268 | while( i < bufferSize && isAscii[buffer[i]] ) 269 | i++; 270 | outputStringLength = i - offset; 271 | if( outputStringLength > outputStringSize ) 272 | outputStringLength = outputStringSize; 273 | 274 | // Copy this string to the output 275 | memcpy( outputString, buffer + offset, outputStringLength ); 276 | stringType = TYPE_ASCII; 277 | return outputStringLength; 278 | } 279 | } 280 | } 281 | 282 | outputStringLength = 0; 283 | return 0; 284 | } 285 | 286 | 287 | bool string_parser::processContents( unsigned char* filecontents, long bufferSize, const char* filename ) 288 | { 289 | // Process the contents of the specified file, and build the list of strings 290 | unsigned char* outputString = new unsigned char[MAX_STRING_SIZE+1]; 291 | int outputStringSize = 0; 292 | 293 | long offset = 0; 294 | EXTRACT_TYPE extractType; 295 | while( offset + options.minCharacters < bufferSize ) 296 | { 297 | // Process this offset 298 | STRING_TYPE stringType = TYPE_UNDETERMINED; 299 | int stringDiskSpace = extractString( filecontents, bufferSize, offset, outputString, MAX_STRING_SIZE, outputStringSize, extractType, stringType ); 300 | 301 | if( outputStringSize >= options.minCharacters ) 302 | { 303 | // Print the resulting string 304 | outputString[outputStringSize] = 0; 305 | 306 | 307 | // Decide if we should print this 308 | bool print = false; 309 | if( options.printNormal && extractType == EXTRACT_RAW ) 310 | print = true; 311 | else if( options.printASM && extractType == EXTRACT_ASM ) 312 | print = true; 313 | 314 | if( options.printUnicodeOnly && stringType != TYPE_UNICODE ) 315 | print = false; 316 | if( options.printAsciiOnly && stringType != TYPE_ASCII ) 317 | print = false; 318 | 319 | if( print ) 320 | { 321 | // Replace \n with "\\n" and \r with "\\r" 322 | if( options.escapeNewLines ) 323 | { 324 | int i = 0; 325 | while( i < outputStringSize && outputStringSize + 2 < MAX_STRING_SIZE ) 326 | { 327 | if( outputString[i] == '\n' ) 328 | { 329 | // Replace with "\\n" 330 | // "\n\x00" to "\\n\x00" 331 | memmove(outputString+i+2, outputString+i+1, (outputStringSize) - i); 332 | outputString[i] = '\\'; 333 | outputString[i+1] = 'n'; 334 | outputStringSize++; 335 | }else if( outputString[i] == '\r' ) 336 | { 337 | // Replace with "\\r" 338 | memmove(outputString+i+2, outputString+i+1, (outputStringSize) - i); 339 | outputString[i] = '\\'; 340 | outputString[i+1] = 'r'; 341 | outputStringSize++; 342 | } 343 | i++; 344 | } 345 | } 346 | 347 | string tmpString( (char*) outputString, outputStringSize); 348 | /*if( (!options.printUniqueLocal && !options.printUniqueGlobal)/* || 349 | !hashes.Contains( tmpString ) ) 350 | { 351 | // Add this string has as necessary 352 | /* 353 | if( options.printUniqueGlobal ) 354 | hashes.Global_Insert( tmpString ); 355 | else if( options.printUniqueLocal ) 356 | hashes.Local_Insert( tmpString ); 357 | 358 | 359 | if( options.printType && options.printFile ) 360 | { 361 | printer->addStrings((char*)filename, ",", (char*)(extractType == EXTRACT_RAW ? (stringType == TYPE_UNICODE ? "UNICODE: " : (stringType == TYPE_ASCII ? "ASCII: " : "UNDETERMINED: ")) : "ASM: "), (char*)outputString, "\n"); 362 | } 363 | else if( options.printType ) 364 | printer->addStrings((char*)(extractType == EXTRACT_RAW ? (stringType == TYPE_UNICODE ? "UNICODE: " : (stringType == TYPE_ASCII ? "ASCII: " : "UNDETERMINED: ")) : "ASM: "), (char*)outputString, "\n"); 365 | else if( options.printFile ) 366 | printer->addStrings((char*)filename, ": ", (char*)outputString, "\n"); 367 | else 368 | printer->addStrings((char*)outputString, "\n"); 369 | }*/ 370 | std::string s((char*)outputString); 371 | vBuffer.push_back(s); 372 | } 373 | 374 | // Advance the offset 375 | offset += stringDiskSpace; 376 | }else{ 377 | // Advance the offset by 1 378 | offset += 1; 379 | } 380 | } 381 | 382 | delete[] outputString; 383 | return true; 384 | } 385 | 386 | 387 | bool string_parser::parse_block(unsigned char* buffer, unsigned int buffer_length, const char* datasource) 388 | { 389 | if( buffer != NULL && buffer_length > 0) 390 | { 391 | // Process this buffer 392 | return this->processContents( buffer, buffer_length, datasource ); 393 | } 394 | return false; 395 | } 396 | 397 | string_parser::string_parser(STRING_OPTIONS options) 398 | { 399 | printer = new print_buffer(0x100000); 400 | this->options = options; 401 | } 402 | 403 | bool string_parser::parse_stream(FILE* fh, const char* datasource) 404 | { 405 | if( fh != NULL ) 406 | { 407 | 408 | unsigned char* buffer; 409 | int numRead; 410 | 411 | // Allocate the buffer 412 | buffer = new unsigned char[BLOCK_SIZE]; 413 | 414 | do 415 | { 416 | // Read the stream in blocks of 0x50000, assuming that a string does not border the regions. 417 | numRead = fread( buffer, 1, BLOCK_SIZE, fh); 418 | 419 | if( numRead > 0 ) 420 | { 421 | // We have read in the full contents now, lets process it. 422 | this->processContents( buffer, numRead, datasource ); 423 | } 424 | }while( numRead == BLOCK_SIZE ); 425 | 426 | // Clean up 427 | delete[] buffer; 428 | return true; 429 | }else{ 430 | // Failed to open file 431 | fprintf(stderr,"Invalid stream: %s.\n", strerror(errno)); 432 | return false; 433 | } 434 | } 435 | 436 | string_parser::~string_parser(void) 437 | { 438 | delete printer; 439 | } 440 | -------------------------------------------------------------------------------- /dependencies/strings2/strings/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Dirent interface for Microsoft Visual Studio 5 | * Version 1.21 6 | * 7 | * Copyright (C) 2006-2012 Toni Ronkko 8 | * This file is part of dirent. Dirent may be freely distributed 9 | * under the MIT license. For all details and documentation, see 10 | * https://github.com/tronkko/dirent 11 | */ 12 | #ifndef DIRENT_H 13 | #define DIRENT_H 14 | 15 | /* 16 | * Include windows.h without Windows Sockets 1.1 to prevent conflicts with 17 | * Windows Sockets 2.0. 18 | */ 19 | #ifndef WIN32_LEAN_AND_MEAN 20 | # define WIN32_LEAN_AND_MEAN 21 | #endif 22 | //#include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | /* Indicates that d_type field is available in dirent structure */ 35 | #define _DIRENT_HAVE_D_TYPE 36 | 37 | /* Indicates that d_namlen field is available in dirent structure */ 38 | #define _DIRENT_HAVE_D_NAMLEN 39 | 40 | /* Entries missing from MSVC 6.0 */ 41 | #if !defined(FILE_ATTRIBUTE_DEVICE) 42 | # define FILE_ATTRIBUTE_DEVICE 0x40 43 | #endif 44 | 45 | /* File type and permission flags for stat(), general mask */ 46 | #if !defined(S_IFMT) 47 | # define S_IFMT _S_IFMT 48 | #endif 49 | 50 | /* Directory bit */ 51 | #if !defined(S_IFDIR) 52 | # define S_IFDIR _S_IFDIR 53 | #endif 54 | 55 | /* Character device bit */ 56 | #if !defined(S_IFCHR) 57 | # define S_IFCHR _S_IFCHR 58 | #endif 59 | 60 | /* Pipe bit */ 61 | #if !defined(S_IFFIFO) 62 | # define S_IFFIFO _S_IFFIFO 63 | #endif 64 | 65 | /* Regular file bit */ 66 | #if !defined(S_IFREG) 67 | # define S_IFREG _S_IFREG 68 | #endif 69 | 70 | /* Read permission */ 71 | #if !defined(S_IREAD) 72 | # define S_IREAD _S_IREAD 73 | #endif 74 | 75 | /* Write permission */ 76 | #if !defined(S_IWRITE) 77 | # define S_IWRITE _S_IWRITE 78 | #endif 79 | 80 | /* Execute permission */ 81 | #if !defined(S_IEXEC) 82 | # define S_IEXEC _S_IEXEC 83 | #endif 84 | 85 | /* Pipe */ 86 | #if !defined(S_IFIFO) 87 | # define S_IFIFO _S_IFIFO 88 | #endif 89 | 90 | /* Block device */ 91 | #if !defined(S_IFBLK) 92 | # define S_IFBLK 0 93 | #endif 94 | 95 | /* Link */ 96 | #if !defined(S_IFLNK) 97 | # define S_IFLNK 0 98 | #endif 99 | 100 | /* Socket */ 101 | #if !defined(S_IFSOCK) 102 | # define S_IFSOCK 0 103 | #endif 104 | 105 | /* Read user permission */ 106 | #if !defined(S_IRUSR) 107 | # define S_IRUSR S_IREAD 108 | #endif 109 | 110 | /* Write user permission */ 111 | #if !defined(S_IWUSR) 112 | # define S_IWUSR S_IWRITE 113 | #endif 114 | 115 | /* Execute user permission */ 116 | #if !defined(S_IXUSR) 117 | # define S_IXUSR 0 118 | #endif 119 | 120 | /* Read group permission */ 121 | #if !defined(S_IRGRP) 122 | # define S_IRGRP 0 123 | #endif 124 | 125 | /* Write group permission */ 126 | #if !defined(S_IWGRP) 127 | # define S_IWGRP 0 128 | #endif 129 | 130 | /* Execute group permission */ 131 | #if !defined(S_IXGRP) 132 | # define S_IXGRP 0 133 | #endif 134 | 135 | /* Read others permission */ 136 | #if !defined(S_IROTH) 137 | # define S_IROTH 0 138 | #endif 139 | 140 | /* Write others permission */ 141 | #if !defined(S_IWOTH) 142 | # define S_IWOTH 0 143 | #endif 144 | 145 | /* Execute others permission */ 146 | #if !defined(S_IXOTH) 147 | # define S_IXOTH 0 148 | #endif 149 | 150 | /* Maximum length of file name */ 151 | #if !defined(PATH_MAX) 152 | # define PATH_MAX MAX_PATH 153 | #endif 154 | #if !defined(FILENAME_MAX) 155 | # define FILENAME_MAX MAX_PATH 156 | #endif 157 | #if !defined(NAME_MAX) 158 | # define NAME_MAX FILENAME_MAX 159 | #endif 160 | 161 | /* File type flags for d_type */ 162 | #define DT_UNKNOWN 0 163 | #define DT_REG S_IFREG 164 | #define DT_DIR S_IFDIR 165 | #define DT_FIFO S_IFIFO 166 | #define DT_SOCK S_IFSOCK 167 | #define DT_CHR S_IFCHR 168 | #define DT_BLK S_IFBLK 169 | #define DT_LNK S_IFLNK 170 | 171 | /* Macros for converting between st_mode and d_type */ 172 | #define IFTODT(mode) ((mode) & S_IFMT) 173 | #define DTTOIF(type) (type) 174 | 175 | /* 176 | * File type macros. Note that block devices, sockets and links cannot be 177 | * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are 178 | * only defined for compatibility. These macros should always return false 179 | * on Windows. 180 | */ 181 | #if !defined(S_ISFIFO) 182 | # define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) 183 | #endif 184 | #if !defined(S_ISDIR) 185 | # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) 186 | #endif 187 | #if !defined(S_ISREG) 188 | # define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) 189 | #endif 190 | #if !defined(S_ISLNK) 191 | # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) 192 | #endif 193 | #if !defined(S_ISSOCK) 194 | # define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) 195 | #endif 196 | #if !defined(S_ISCHR) 197 | # define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) 198 | #endif 199 | #if !defined(S_ISBLK) 200 | # define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) 201 | #endif 202 | 203 | /* Return the exact length of d_namlen without zero terminator */ 204 | #define _D_EXACT_NAMLEN(p) ((p)->d_namlen) 205 | 206 | /* Return number of bytes needed to store d_namlen */ 207 | #define _D_ALLOC_NAMLEN(p) (PATH_MAX) 208 | 209 | 210 | #ifdef __cplusplus 211 | extern "C" { 212 | #endif 213 | 214 | 215 | /* Wide-character version */ 216 | struct _wdirent { 217 | /* Always zero */ 218 | long d_ino; 219 | 220 | /* Structure size */ 221 | unsigned short d_reclen; 222 | 223 | /* Length of name without \0 */ 224 | size_t d_namlen; 225 | 226 | /* File type */ 227 | int d_type; 228 | 229 | /* File name */ 230 | wchar_t d_name[PATH_MAX]; 231 | }; 232 | typedef struct _wdirent _wdirent; 233 | 234 | struct _WDIR { 235 | /* Current directory entry */ 236 | struct _wdirent ent; 237 | 238 | /* Private file data */ 239 | WIN32_FIND_DATAW data; 240 | 241 | /* True if data is valid */ 242 | int cached; 243 | 244 | /* Win32 search handle */ 245 | HANDLE handle; 246 | 247 | /* Initial directory name */ 248 | wchar_t *patt; 249 | }; 250 | typedef struct _WDIR _WDIR; 251 | 252 | static _WDIR *_wopendir(const wchar_t *dirname); 253 | static struct _wdirent *_wreaddir(_WDIR *dirp); 254 | static int _wclosedir(_WDIR *dirp); 255 | static void _wrewinddir(_WDIR* dirp); 256 | 257 | 258 | /* For compatibility with Symbian */ 259 | #define wdirent _wdirent 260 | #define WDIR _WDIR 261 | #define wopendir _wopendir 262 | #define wreaddir _wreaddir 263 | #define wclosedir _wclosedir 264 | #define wrewinddir _wrewinddir 265 | 266 | 267 | /* Multi-byte character versions */ 268 | struct dirent { 269 | /* Always zero */ 270 | long d_ino; 271 | 272 | /* Structure size */ 273 | unsigned short d_reclen; 274 | 275 | /* Length of name without \0 */ 276 | size_t d_namlen; 277 | 278 | /* File type */ 279 | int d_type; 280 | 281 | /* File name */ 282 | char d_name[PATH_MAX]; 283 | }; 284 | typedef struct dirent dirent; 285 | 286 | struct DIR { 287 | struct dirent ent; 288 | struct _WDIR *wdirp; 289 | }; 290 | typedef struct DIR DIR; 291 | 292 | static DIR *opendir(const char *dirname); 293 | static struct dirent *readdir(DIR *dirp); 294 | static int closedir(DIR *dirp); 295 | static void rewinddir(DIR* dirp); 296 | 297 | 298 | /* Internal utility functions */ 299 | static WIN32_FIND_DATAW *dirent_first(_WDIR *dirp); 300 | static WIN32_FIND_DATAW *dirent_next(_WDIR *dirp); 301 | 302 | static int dirent_mbstowcs_s( 303 | size_t *pReturnValue, 304 | wchar_t *wcstr, 305 | size_t sizeInWords, 306 | const char *mbstr, 307 | size_t count); 308 | 309 | static int dirent_wcstombs_s( 310 | size_t *pReturnValue, 311 | char *mbstr, 312 | size_t sizeInBytes, 313 | const wchar_t *wcstr, 314 | size_t count); 315 | 316 | static void dirent_set_errno(int error); 317 | 318 | /* 319 | * Open directory stream DIRNAME for read and return a pointer to the 320 | * internal working area that is used to retrieve individual directory 321 | * entries. 322 | */ 323 | static _WDIR* 324 | _wopendir( 325 | const wchar_t *dirname) 326 | { 327 | _WDIR *dirp = NULL; 328 | int error; 329 | 330 | /* Must have directory name */ 331 | if (dirname == NULL || dirname[0] == '\0') { 332 | dirent_set_errno(ENOENT); 333 | return NULL; 334 | } 335 | 336 | /* Allocate new _WDIR structure */ 337 | dirp = (_WDIR*)malloc(sizeof(struct _WDIR)); 338 | if (dirp != NULL) { 339 | DWORD n; 340 | 341 | /* Reset _WDIR structure */ 342 | dirp->handle = INVALID_HANDLE_VALUE; 343 | dirp->patt = NULL; 344 | dirp->cached = 0; 345 | 346 | /* Compute the length of full path plus zero terminator 347 | * 348 | * Note that on WinRT there's no way to convert relative paths 349 | * into absolute paths, so just assume its an absolute path. 350 | */ 351 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 352 | n = wcslen(dirname); 353 | # else 354 | n = GetFullPathNameW(dirname, 0, NULL, NULL); 355 | # endif 356 | 357 | /* Allocate room for absolute directory name and search pattern */ 358 | dirp->patt = (wchar_t*)malloc(sizeof(wchar_t) * n + 16); 359 | if (dirp->patt) { 360 | 361 | /* 362 | * Convert relative directory name to an absolute one. This 363 | * allows rewinddir() to function correctly even when current 364 | * working directory is changed between opendir() and rewinddir(). 365 | * 366 | * Note that on WinRT there's no way to convert relative paths 367 | * into absolute paths, so just assume its an absolute path. 368 | */ 369 | # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) 370 | wcsncpy_s(dirp->patt, n + 1, dirname, n); 371 | # else 372 | n = GetFullPathNameW(dirname, n, dirp->patt, NULL); 373 | # endif 374 | if (n > 0) { 375 | wchar_t *p; 376 | 377 | /* Append search pattern \* to the directory name */ 378 | p = dirp->patt + n; 379 | if (dirp->patt < p) { 380 | switch (p[-1]) { 381 | case '\\': 382 | case '/': 383 | case ':': 384 | /* Directory ends in path separator, e.g. c:\temp\ */ 385 | /*NOP*/; 386 | break; 387 | 388 | default: 389 | /* Directory name doesn't end in path separator */ 390 | *p++ = '\\'; 391 | } 392 | } 393 | *p++ = '*'; 394 | *p = '\0'; 395 | 396 | /* Open directory stream and retrieve the first entry */ 397 | if (dirent_first(dirp)) { 398 | /* Directory stream opened successfully */ 399 | error = 0; 400 | } 401 | else { 402 | /* Cannot retrieve first entry */ 403 | error = 1; 404 | dirent_set_errno(ENOENT); 405 | } 406 | 407 | } 408 | else { 409 | /* Cannot retrieve full path name */ 410 | dirent_set_errno(ENOENT); 411 | error = 1; 412 | } 413 | 414 | } 415 | else { 416 | /* Cannot allocate memory for search pattern */ 417 | error = 1; 418 | } 419 | 420 | } 421 | else { 422 | /* Cannot allocate _WDIR structure */ 423 | error = 1; 424 | } 425 | 426 | /* Clean up in case of error */ 427 | if (error && dirp) { 428 | _wclosedir(dirp); 429 | dirp = NULL; 430 | } 431 | 432 | return dirp; 433 | } 434 | 435 | /* 436 | * Read next directory entry. The directory entry is returned in dirent 437 | * structure in the d_name field. Individual directory entries returned by 438 | * this function include regular files, sub-directories, pseudo-directories 439 | * "." and ".." as well as volume labels, hidden files and system files. 440 | */ 441 | static struct _wdirent* 442 | _wreaddir( 443 | _WDIR *dirp) 444 | { 445 | WIN32_FIND_DATAW *datap; 446 | struct _wdirent *entp; 447 | 448 | /* Read next directory entry */ 449 | datap = dirent_next(dirp); 450 | if (datap) { 451 | size_t n; 452 | DWORD attr; 453 | 454 | /* Pointer to directory entry to return */ 455 | entp = &dirp->ent; 456 | 457 | /* 458 | * Copy file name as wide-character string. If the file name is too 459 | * long to fit in to the destination buffer, then truncate file name 460 | * to PATH_MAX characters and zero-terminate the buffer. 461 | */ 462 | n = 0; 463 | while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { 464 | entp->d_name[n] = datap->cFileName[n]; 465 | n++; 466 | } 467 | dirp->ent.d_name[n] = 0; 468 | 469 | /* Length of file name excluding zero terminator */ 470 | entp->d_namlen = n; 471 | 472 | /* File type */ 473 | attr = datap->dwFileAttributes; 474 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 475 | entp->d_type = DT_CHR; 476 | } 477 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 478 | entp->d_type = DT_DIR; 479 | } 480 | else { 481 | entp->d_type = DT_REG; 482 | } 483 | 484 | /* Reset dummy fields */ 485 | entp->d_ino = 0; 486 | entp->d_reclen = sizeof(struct _wdirent); 487 | 488 | } 489 | else { 490 | 491 | /* Last directory entry read */ 492 | entp = NULL; 493 | 494 | } 495 | 496 | return entp; 497 | } 498 | 499 | /* 500 | * Close directory stream opened by opendir() function. This invalidates the 501 | * DIR structure as well as any directory entry read previously by 502 | * _wreaddir(). 503 | */ 504 | static int 505 | _wclosedir( 506 | _WDIR *dirp) 507 | { 508 | int ok; 509 | if (dirp) { 510 | 511 | /* Release search handle */ 512 | if (dirp->handle != INVALID_HANDLE_VALUE) { 513 | FindClose(dirp->handle); 514 | dirp->handle = INVALID_HANDLE_VALUE; 515 | } 516 | 517 | /* Release search pattern */ 518 | if (dirp->patt) { 519 | free(dirp->patt); 520 | dirp->patt = NULL; 521 | } 522 | 523 | /* Release directory structure */ 524 | free(dirp); 525 | ok = /*success*/0; 526 | 527 | } 528 | else { 529 | /* Invalid directory stream */ 530 | dirent_set_errno(EBADF); 531 | ok = /*failure*/-1; 532 | } 533 | return ok; 534 | } 535 | 536 | /* 537 | * Rewind directory stream such that _wreaddir() returns the very first 538 | * file name again. 539 | */ 540 | static void 541 | _wrewinddir( 542 | _WDIR* dirp) 543 | { 544 | if (dirp) { 545 | /* Release existing search handle */ 546 | if (dirp->handle != INVALID_HANDLE_VALUE) { 547 | FindClose(dirp->handle); 548 | } 549 | 550 | /* Open new search handle */ 551 | dirent_first(dirp); 552 | } 553 | } 554 | 555 | /* Get first directory entry (internal) */ 556 | static WIN32_FIND_DATAW* 557 | dirent_first( 558 | _WDIR *dirp) 559 | { 560 | WIN32_FIND_DATAW *datap; 561 | 562 | /* Open directory and retrieve the first entry */ 563 | dirp->handle = FindFirstFileExW( 564 | dirp->patt, FindExInfoStandard, &dirp->data, 565 | FindExSearchNameMatch, NULL, 0); 566 | if (dirp->handle != INVALID_HANDLE_VALUE) { 567 | 568 | /* a directory entry is now waiting in memory */ 569 | datap = &dirp->data; 570 | dirp->cached = 1; 571 | 572 | } 573 | else { 574 | 575 | /* Failed to re-open directory: no directory entry in memory */ 576 | dirp->cached = 0; 577 | datap = NULL; 578 | 579 | } 580 | return datap; 581 | } 582 | 583 | /* Get next directory entry (internal) */ 584 | static WIN32_FIND_DATAW* 585 | dirent_next( 586 | _WDIR *dirp) 587 | { 588 | WIN32_FIND_DATAW *p; 589 | 590 | /* Get next directory entry */ 591 | if (dirp->cached != 0) { 592 | 593 | /* A valid directory entry already in memory */ 594 | p = &dirp->data; 595 | dirp->cached = 0; 596 | 597 | } 598 | else if (dirp->handle != INVALID_HANDLE_VALUE) { 599 | 600 | /* Get the next directory entry from stream */ 601 | if (FindNextFileW(dirp->handle, &dirp->data) != FALSE) { 602 | /* Got a file */ 603 | p = &dirp->data; 604 | } 605 | else { 606 | /* The very last entry has been processed or an error occured */ 607 | FindClose(dirp->handle); 608 | dirp->handle = INVALID_HANDLE_VALUE; 609 | p = NULL; 610 | } 611 | 612 | } 613 | else { 614 | 615 | /* End of directory stream reached */ 616 | p = NULL; 617 | 618 | } 619 | 620 | return p; 621 | } 622 | 623 | /* 624 | * Open directory stream using plain old C-string. 625 | */ 626 | static DIR* 627 | opendir( 628 | const char *dirname) 629 | { 630 | struct DIR *dirp; 631 | int error; 632 | 633 | /* Must have directory name */ 634 | if (dirname == NULL || dirname[0] == '\0') { 635 | dirent_set_errno(ENOENT); 636 | return NULL; 637 | } 638 | 639 | /* Allocate memory for DIR structure */ 640 | dirp = (DIR*)malloc(sizeof(struct DIR)); 641 | if (dirp) { 642 | wchar_t wname[PATH_MAX]; 643 | size_t n; 644 | 645 | /* Convert directory name to wide-character string */ 646 | error = dirent_mbstowcs_s(&n, wname, PATH_MAX, dirname, PATH_MAX); 647 | if (!error) { 648 | 649 | /* Open directory stream using wide-character name */ 650 | dirp->wdirp = _wopendir(wname); 651 | if (dirp->wdirp) { 652 | /* Directory stream opened */ 653 | error = 0; 654 | } 655 | else { 656 | /* Failed to open directory stream */ 657 | error = 1; 658 | } 659 | 660 | } 661 | else { 662 | /* 663 | * Cannot convert file name to wide-character string. This 664 | * occurs if the string contains invalid multi-byte sequences or 665 | * the output buffer is too small to contain the resulting 666 | * string. 667 | */ 668 | error = 1; 669 | } 670 | 671 | } 672 | else { 673 | /* Cannot allocate DIR structure */ 674 | error = 1; 675 | } 676 | 677 | /* Clean up in case of error */ 678 | if (error && dirp) { 679 | free(dirp); 680 | dirp = NULL; 681 | } 682 | 683 | return dirp; 684 | } 685 | 686 | /* 687 | * Read next directory entry. 688 | * 689 | * When working with text consoles, please note that file names returned by 690 | * readdir() are represented in the default ANSI code page while any output to 691 | * console is typically formatted on another code page. Thus, non-ASCII 692 | * characters in file names will not usually display correctly on console. The 693 | * problem can be fixed in two ways: (1) change the character set of console 694 | * to 1252 using chcp utility and use Lucida Console font, or (2) use 695 | * _cprintf function when writing to console. The _cprinf() will re-encode 696 | * ANSI strings to the console code page so many non-ASCII characters will 697 | * display correcly. 698 | */ 699 | static struct dirent* 700 | readdir( 701 | DIR *dirp) 702 | { 703 | WIN32_FIND_DATAW *datap; 704 | struct dirent *entp; 705 | 706 | /* Read next directory entry */ 707 | datap = dirent_next(dirp->wdirp); 708 | if (datap) { 709 | size_t n; 710 | int error; 711 | 712 | /* Attempt to convert file name to multi-byte string */ 713 | error = dirent_wcstombs_s( 714 | &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); 715 | 716 | /* 717 | * If the file name cannot be represented by a multi-byte string, 718 | * then attempt to use old 8+3 file name. This allows traditional 719 | * Unix-code to access some file names despite of unicode 720 | * characters, although file names may seem unfamiliar to the user. 721 | * 722 | * Be ware that the code below cannot come up with a short file 723 | * name unless the file system provides one. At least 724 | * VirtualBox shared folders fail to do this. 725 | */ 726 | if (error && datap->cAlternateFileName[0] != '\0') { 727 | error = dirent_wcstombs_s( 728 | &n, dirp->ent.d_name, PATH_MAX, 729 | datap->cAlternateFileName, PATH_MAX); 730 | } 731 | 732 | if (!error) { 733 | DWORD attr; 734 | 735 | /* Initialize directory entry for return */ 736 | entp = &dirp->ent; 737 | 738 | /* Length of file name excluding zero terminator */ 739 | entp->d_namlen = n - 1; 740 | 741 | /* File attributes */ 742 | attr = datap->dwFileAttributes; 743 | if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { 744 | entp->d_type = DT_CHR; 745 | } 746 | else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { 747 | entp->d_type = DT_DIR; 748 | } 749 | else { 750 | entp->d_type = DT_REG; 751 | } 752 | 753 | /* Reset dummy fields */ 754 | entp->d_ino = 0; 755 | entp->d_reclen = sizeof(struct dirent); 756 | 757 | } 758 | else { 759 | /* 760 | * Cannot convert file name to multi-byte string so construct 761 | * an errornous directory entry and return that. Note that 762 | * we cannot return NULL as that would stop the processing 763 | * of directory entries completely. 764 | */ 765 | entp = &dirp->ent; 766 | entp->d_name[0] = '?'; 767 | entp->d_name[1] = '\0'; 768 | entp->d_namlen = 1; 769 | entp->d_type = DT_UNKNOWN; 770 | entp->d_ino = 0; 771 | entp->d_reclen = 0; 772 | } 773 | 774 | } 775 | else { 776 | /* No more directory entries */ 777 | entp = NULL; 778 | } 779 | 780 | return entp; 781 | } 782 | 783 | /* 784 | * Close directory stream. 785 | */ 786 | static int 787 | closedir( 788 | DIR *dirp) 789 | { 790 | int ok; 791 | if (dirp) { 792 | 793 | /* Close wide-character directory stream */ 794 | ok = _wclosedir(dirp->wdirp); 795 | dirp->wdirp = NULL; 796 | 797 | /* Release multi-byte character version */ 798 | free(dirp); 799 | 800 | } 801 | else { 802 | 803 | /* Invalid directory stream */ 804 | dirent_set_errno(EBADF); 805 | ok = /*failure*/-1; 806 | 807 | } 808 | return ok; 809 | } 810 | 811 | /* 812 | * Rewind directory stream to beginning. 813 | */ 814 | static void 815 | rewinddir( 816 | DIR* dirp) 817 | { 818 | /* Rewind wide-character string directory stream */ 819 | _wrewinddir(dirp->wdirp); 820 | } 821 | 822 | /* Convert multi-byte string to wide character string */ 823 | static int 824 | dirent_mbstowcs_s( 825 | size_t *pReturnValue, 826 | wchar_t *wcstr, 827 | size_t sizeInWords, 828 | const char *mbstr, 829 | size_t count) 830 | { 831 | int error; 832 | 833 | #if defined(_MSC_VER) && _MSC_VER >= 1400 834 | 835 | /* Microsoft Visual Studio 2005 or later */ 836 | error = mbstowcs_s(pReturnValue, wcstr, sizeInWords, mbstr, count); 837 | 838 | #else 839 | 840 | /* Older Visual Studio or non-Microsoft compiler */ 841 | size_t n; 842 | 843 | /* Convert to wide-character string (or count characters) */ 844 | n = mbstowcs(wcstr, mbstr, sizeInWords); 845 | if (!wcstr || n < count) { 846 | 847 | /* Zero-terminate output buffer */ 848 | if (wcstr && sizeInWords) { 849 | if (n >= sizeInWords) { 850 | n = sizeInWords - 1; 851 | } 852 | wcstr[n] = 0; 853 | } 854 | 855 | /* Length of resuting multi-byte string WITH zero terminator */ 856 | if (pReturnValue) { 857 | *pReturnValue = n + 1; 858 | } 859 | 860 | /* Success */ 861 | error = 0; 862 | 863 | } 864 | else { 865 | 866 | /* Could not convert string */ 867 | error = 1; 868 | 869 | } 870 | 871 | #endif 872 | 873 | return error; 874 | } 875 | 876 | /* Convert wide-character string to multi-byte string */ 877 | static int 878 | dirent_wcstombs_s( 879 | size_t *pReturnValue, 880 | char *mbstr, 881 | size_t sizeInBytes, /* max size of mbstr */ 882 | const wchar_t *wcstr, 883 | size_t count) 884 | { 885 | int error; 886 | 887 | #if defined(_MSC_VER) && _MSC_VER >= 1400 888 | 889 | /* Microsoft Visual Studio 2005 or later */ 890 | error = wcstombs_s(pReturnValue, mbstr, sizeInBytes, wcstr, count); 891 | 892 | #else 893 | 894 | /* Older Visual Studio or non-Microsoft compiler */ 895 | size_t n; 896 | 897 | /* Convert to multi-byte string (or count the number of bytes needed) */ 898 | n = wcstombs(mbstr, wcstr, sizeInBytes); 899 | if (!mbstr || n < count) { 900 | 901 | /* Zero-terminate output buffer */ 902 | if (mbstr && sizeInBytes) { 903 | if (n >= sizeInBytes) { 904 | n = sizeInBytes - 1; 905 | } 906 | mbstr[n] = '\0'; 907 | } 908 | 909 | /* Length of resulting multi-bytes string WITH zero-terminator */ 910 | if (pReturnValue) { 911 | *pReturnValue = n + 1; 912 | } 913 | 914 | /* Success */ 915 | error = 0; 916 | 917 | } 918 | else { 919 | 920 | /* Cannot convert string */ 921 | error = 1; 922 | 923 | } 924 | 925 | #endif 926 | 927 | return error; 928 | } 929 | 930 | /* Set errno variable */ 931 | static void 932 | dirent_set_errno( 933 | int error) 934 | { 935 | #if defined(_MSC_VER) && _MSC_VER >= 1400 936 | 937 | /* Microsoft Visual Studio 2005 and later */ 938 | _set_errno(error); 939 | 940 | #else 941 | 942 | /* Non-Microsoft compiler or older Microsoft compiler */ 943 | errno = error; 944 | 945 | #endif 946 | } 947 | 948 | 949 | #ifdef __cplusplus 950 | } 951 | #endif 952 | #endif /*DIRENT_H*/ 953 | --------------------------------------------------------------------------------