From bc07b20950b80489347f90622504653187c40d31 Mon Sep 17 00:00:00 2001 From: Huoji's <1296564236@qq.com> Date: Sat, 3 Aug 2024 19:27:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=8132=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- white_patch_detect/pe/pe.cpp | 8 +- white_patch_detect/pe/pe.h | 2 + white_patch_detect/white_patch_detect.cpp | 406 ++++++++++++++-------- 3 files changed, 278 insertions(+), 138 deletions(-) diff --git a/white_patch_detect/pe/pe.cpp b/white_patch_detect/pe/pe.cpp index 224f6c5..132d33d 100644 --- a/white_patch_detect/pe/pe.cpp +++ b/white_patch_detect/pe/pe.cpp @@ -28,8 +28,8 @@ pe64::pe64(std::string binary_path) { PIMAGE_NT_HEADERS nt = reinterpret_cast(temp_buffer.data() + dos->e_lfanew); - if (nt->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) - throw std::runtime_error("huoji doesn't support 32bit binaries!"); + //if (nt->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) + // throw std::runtime_error("huoji doesn't support 32bit binaries!"); this->buffer.resize(nt->OptionalHeader.SizeOfImage); @@ -47,6 +47,10 @@ pe64::pe64(std::string binary_path) { } this->buffer_not_relocated = temp_buffer; } + +bool pe64::is_32_pe() { + return get_nt()->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64; +} bool pe64::delete_section(std::string section_name) { PIMAGE_SECTION_HEADER section = get_section(section_name); PIMAGE_NT_HEADERS nt_headers = get_nt(); diff --git a/white_patch_detect/pe/pe.h b/white_patch_detect/pe/pe.h index 880f306..3938ded 100644 --- a/white_patch_detect/pe/pe.h +++ b/white_patch_detect/pe/pe.h @@ -14,6 +14,8 @@ public: pe64(std::string binary_path); + bool is_32_pe(); + bool delete_section(std::string section_name); uint32_t align(uint32_t address, uint32_t alignment); diff --git a/white_patch_detect/white_patch_detect.cpp b/white_patch_detect/white_patch_detect.cpp index 0a2ef7b..5fd4a76 100644 --- a/white_patch_detect/white_patch_detect.cpp +++ b/white_patch_detect/white_patch_detect.cpp @@ -16,7 +16,6 @@ struct _functionDetail { uint64_t end_address; size_t size; }; -csh capstone_handle; auto calculateEntropy(void* data, size_t size) -> double { if (data == nullptr || size == 0) { return 0.0; @@ -38,35 +37,29 @@ auto calculateEntropy(void* data, size_t size) -> double { return entropy; } -auto Init() -> bool { - bool status = false; +auto buildFunctionMaps(pe64* pe) + -> std::vector> { + std::vector> functionList; + cs_insn* insn = nullptr; + size_t disasmCount = 0; + csh capstone_handle; + do { - // 打开句柄 - if (cs_open(CS_ARCH_X86, CS_MODE_64, &capstone_handle) != CS_ERR_OK) { + if (cs_open(CS_ARCH_X86, pe->is_32_pe() ? CS_MODE_32 : CS_MODE_64, + &capstone_handle) != CS_ERR_OK) { break; } cs_option(capstone_handle, CS_OPT_DETAIL, CS_OPT_ON); cs_option(capstone_handle, CS_OPT_SKIPDATA, CS_OPT_ON); - status = true; - } while (false); - return status; -} -auto buildFunctionMaps(pe64* pe) -> std::vector> { - std::vector> functionList; - cs_insn* insn = nullptr; - size_t disasmCount = 0; - - do { - auto textSection = pe->get_section(".text"); const auto codeAddressInMemory = reinterpret_cast( pe->get_buffer()->data() + textSection->VirtualAddress); disasmCount = cs_disasm(capstone_handle, - reinterpret_cast(codeAddressInMemory), - textSection->Misc.VirtualSize, 0, 0, &insn); + reinterpret_cast(codeAddressInMemory), + textSection->Misc.VirtualSize, 0, 0, &insn); if (disasmCount == 0) { break; } @@ -87,32 +80,36 @@ auto buildFunctionMaps(pe64* pe) -> std::vector backTrackCodeList.push_back(codeMnemonic); if ((codeMnemonic != "int3" && codeMnemonic != "nop") && ((backTrackCodeList.size() > 2) && - (backTrackCodeList[0] == "int3" || - backTrackCodeList[0] == "nop") && - (backTrackCodeList[1] == "int3" || - backTrackCodeList[1] == "nop") && - (backTrackCodeList[2] == "int3" || - backTrackCodeList[2] == "nop")) && + (backTrackCodeList[0] == "int3" || + backTrackCodeList[0] == "nop") && + (backTrackCodeList[1] == "int3" || + backTrackCodeList[1] == "nop") && + (backTrackCodeList[2] == "int3" || + backTrackCodeList[2] == "nop")) && isEnterFunction == false) { - // printf("进入函数 开始地址: %llx\n", codeAddressInMemory + offset); - // printf("address: 0x%llx | size: %d code: %s %s \n", - // code.address, code.size, code.mnemonic, code.op_str); + // printf("进入函数 开始地址: %llx\n", codeAddressInMemory + + // offset); printf("address: 0x%llx | size: %d code: %s %s \n", + // code.address, code.size, code.mnemonic, code.op_str); currentFuncAddress = codeAddressInMemory + offset; isEnterFunction = true; backTrackCodeList.clear(); - } - else if ((codeMnemonic == "int3" || codeMnemonic == "nop") && - ((backTrackCodeList.size() > 2) && - (backTrackCodeList[0] != "int3" && - backTrackCodeList[0] != "nop")) && - isEnterFunction) { - //printf("退出函数 结束地址: %llx 当前大小: %d \n", codeAddressInMemory + code.address, currentFuncAddress - codeAddressInMemory); + } else if ((codeMnemonic == "int3" || codeMnemonic == "nop") && + ((backTrackCodeList.size() > 2) && + (backTrackCodeList[0] != "int3" && + backTrackCodeList[0] != "nop")) && + isEnterFunction) { + // printf("退出函数 结束地址: %llx 当前大小: %d \n", + // codeAddressInMemory + code.address, currentFuncAddress - + // codeAddressInMemory); - auto func = _functionDetail{ .start_address = currentFuncAddress, - .end_address = codeAddressInMemory + code.address, - .size = (codeAddressInMemory + code.address) - currentFuncAddress }; + auto func = _functionDetail{ + .start_address = currentFuncAddress, + .end_address = codeAddressInMemory + code.address, + .size = (codeAddressInMemory + code.address) - + currentFuncAddress}; functionList.push_back(std::make_shared<_functionDetail>(func)); - //printf("退出函数 结束地址: %llx 当前大小: %d \n", func.end_address, func.size); + // printf("退出函数 结束地址: %llx 当前大小: %d \n", + // func.end_address, func.size); isFirst = false; isEnterFunction = false; @@ -128,48 +125,56 @@ auto buildFunctionMaps(pe64* pe) -> std::vector .start_address = static_cast(codeAddressInMemory), .end_address = static_cast( codeAddressInMemory + textSection->Misc.VirtualSize), - .size = textSection->Misc.VirtualSize })); + .size = textSection->Misc.VirtualSize})); } } while (false); cs_free(insn, disasmCount); + if (capstone_handle) { + cs_close(&capstone_handle); + } return functionList; } -class super_huoji_tracker -{ -public: +class super_huoji_tracker { + public: auto print_asm(const cs_insn* code) -> void; - super_huoji_tracker(uint64_t startAddr, size_t sizeOfCode, uint64_t current_function_rva); + super_huoji_tracker(uint64_t startAddr, size_t sizeOfCode, + uint64_t current_function_rva, bool is_32_pe); ~super_huoji_tracker(); + auto track_gs_access_64_i() -> void; + auto track_gs_access_32_i() -> void; auto track_gs_access() -> void; -private: + private: + bool is_x32 = false; std::vector> ins_list; cs_insn* insn = nullptr; size_t disasmCount = 0; csh capstone_handle_i; uint64_t ins_ip, ins_ip_address, current_function_rva; - auto get_next_ins()->std::shared_ptr; - template - auto match_code(T match_fn, B process_fn, std::optional num_operands, std::vector> operand_types) -> bool; + auto get_next_ins() -> std::shared_ptr; + template + auto match_code( + T match_fn, B process_fn, std::optional num_operands, + std::vector> operand_types) -> bool; }; auto super_huoji_tracker::print_asm(const cs_insn* code) -> void { printf("0x%08X :\t\t%s\t%s\t\n", code->address, code->mnemonic, - code->op_str); + code->op_str); } -super_huoji_tracker::super_huoji_tracker(uint64_t startAddr, size_t sizeOfCode, uint64_t current_function_rva) -{ - if (cs_open(CS_ARCH_X86, CS_MODE_64, &capstone_handle_i) != CS_ERR_OK) { +super_huoji_tracker::super_huoji_tracker(uint64_t startAddr, size_t sizeOfCode, + uint64_t current_function_rva, + bool is_32_pe) { + if (cs_open(CS_ARCH_X86, is_32_pe ? CS_MODE_32 : CS_MODE_64, + &capstone_handle_i) != CS_ERR_OK) { __debugbreak(); } cs_option(capstone_handle_i, CS_OPT_DETAIL, CS_OPT_ON); cs_option(capstone_handle_i, CS_OPT_SKIPDATA, CS_OPT_ON); - - do - { - disasmCount = - cs_disasm(capstone_handle_i, - reinterpret_cast(startAddr), - sizeOfCode, 0, 0, &insn); + is_x32 = is_32_pe; + do { + disasmCount = cs_disasm(capstone_handle_i, + reinterpret_cast(startAddr), + sizeOfCode, 0, 0, &insn); if (disasmCount == 0) { break; } @@ -181,8 +186,7 @@ super_huoji_tracker::super_huoji_tracker(uint64_t startAddr, size_t sizeOfCode, this->current_function_rva = current_function_rva; } -super_huoji_tracker::~super_huoji_tracker() -{ +super_huoji_tracker::~super_huoji_tracker() { if (insn) { cs_free(insn, disasmCount); } @@ -198,8 +202,7 @@ auto super_huoji_tracker::get_next_ins() -> std::shared_ptr { } template auto super_huoji_tracker::match_code( - T match_fn, B process_fn, - std::optional num_operands, + T match_fn, B process_fn, std::optional num_operands, std::vector> operand_types) -> bool { while (auto instruction = get_next_ins()) { if (&process_fn != nullptr) { @@ -222,31 +225,35 @@ auto super_huoji_tracker::match_code( } return false; } +auto super_huoji_tracker::track_gs_access_64_i() -> void { + // const auto matched_gs_access = match_code([&](cs_insn* instruction) {}, + // [&](cs_insn* instruction) {}, {}, {}); + const auto isGsRegAccess = match_code( + [&](cs_insn* instruction) { + //@todo: other access gs reg code... + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } -auto super_huoji_tracker::track_gs_access() -> void -{ - //const auto matched_gs_access = match_code([&](cs_insn* instruction) {}, [&](cs_insn* instruction) {}, {}, {}); - const auto isGsRegAccess = match_code([&](cs_insn* instruction) { - //@todo: other access gs reg code... - if (instruction->id != X86_INS_MOV && instruction->id != X86_INS_MOVZX) { - return false; - } - - if (instruction->detail->x86.operands[1].mem.segment != X86_REG_GS) { - return false; - } - /* - gs:[0x30] TEB - gs:[0x40] Pid - gs:[0x48] Tid - gs:[0x60] PEB - gs:[0x68] LastError - */ - if (instruction->detail->x86.operands[1].mem.disp != 0x30 && instruction->detail->x86.operands[1].mem.disp != 0x60) { - return false; - } - return true; - }, [&](cs_insn* instruction) {}, {}, {}); + if (instruction->detail->x86.operands[1].mem.segment != + X86_REG_GS) { + return false; + } + /* + gs:[0x30] TEB + gs:[0x40] Pid + gs:[0x48] Tid + gs:[0x60] PEB + gs:[0x68] LastError + */ + if (instruction->detail->x86.operands[1].mem.disp != 0x30 && + instruction->detail->x86.operands[1].mem.disp != 0x60) { + return false; + } + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); if (isGsRegAccess == false) { return; } @@ -255,86 +262,213 @@ auto super_huoji_tracker::track_gs_access() -> void x86_reg ldrAccessReg; bool isPebAccess = false; if (currentIns->detail->x86.operands[1].mem.disp == 0x30) { - //从TEB访问的PEB->ldr - isPebAccess = match_code([&](cs_insn* instruction) { - //@todo: other access gs reg code... - if (instruction->id != X86_INS_MOV && instruction->id != X86_INS_MOVZX) { - return false; - } + // 从TEB访问的PEB->ldr + isPebAccess = match_code( + [&](cs_insn* instruction) { + //@todo: other access gs reg code... + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } - if (instruction->detail->x86.operands[1].mem.base != gsAccessReg) { - return false; - } - if (instruction->detail->x86.operands[1].mem.disp != 0x60) { - return false; - } - ldrAccessReg = instruction->detail->x86.operands[0].reg; - return true; - }, [&](cs_insn* instruction) {}, {}, {}); - } - else { - //直接访问的GS->peb + if (instruction->detail->x86.operands[1].mem.base != + gsAccessReg) { + return false; + } + if (instruction->detail->x86.operands[1].mem.disp != 0x60) { + return false; + } + ldrAccessReg = instruction->detail->x86.operands[0].reg; + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); + } else { + // 直接访问的GS->peb isPebAccess = true; ldrAccessReg = gsAccessReg; } - if (isPebAccess == false){ + if (isPebAccess == false) { return; } - //访问了PEB的ldr - const auto isPebLdrAccess = match_code([&](cs_insn* instruction) { - //@todo: other access gs reg code... - if (instruction->id != X86_INS_MOV && instruction->id != X86_INS_MOVZX) { - return false; - } - if (instruction->detail->x86.operands[1].mem.base != ldrAccessReg) { - return false; - } - if (instruction->detail->x86.operands[1].mem.disp != 0x18) { - return false; - } - return true; - }, [&](cs_insn* instruction) {}, {}, {}); + // 访问了PEB的ldr + const auto isPebLdrAccess = match_code( + [&](cs_insn* instruction) { + //@todo: other access gs reg code... + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } + if (instruction->detail->x86.operands[1].mem.base != ldrAccessReg) { + return false; + } + if (instruction->detail->x86.operands[1].mem.disp != 0x18) { + return false; + } + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); if (isPebLdrAccess == false) { return; } - printf("mawlare function detected at address: 0x%llx by gs access peb->ldr \n", this->current_function_rva); + printf( + "malware function detected at address: 0x%llx by gs access peb->ldr \n", + this->current_function_rva); this->print_asm(currentIns); } -auto functionAnalysis(std::vector> functionlist, pe64* peFileObject) -> void { +auto super_huoji_tracker::track_gs_access_32_i() -> void { + bool isMalwareDetect = false; + cs_insn* currentIns; + do { + const auto isFsRegAccess = match_code( + [&](cs_insn* instruction) { + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } + + if (instruction->detail->x86.operands[1].mem.segment != + X86_REG_FS) { + return false; + } + // todo: SEH(FS:[00]) + if (instruction->detail->x86.operands[1].mem.disp != 0x30 && + instruction->detail->x86.operands[1].mem.disp != 0x18) { + return false; + } + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); + if (isFsRegAccess == false) { + return; + } + currentIns = this->ins_list[this->ins_ip - 1].get(); + const auto fsAccessReg = currentIns->detail->x86.operands[0].reg; + if (currentIns->detail->x86.operands[1].mem.disp == 0x18) { + /* + 只是其中一个经典款,还有其他的款式,懒得做了 + xor esi , esi + mov esi , fs :[ esi + 0x18 ] // TEB + mov eax , [ esi + 4 ] // 这个是需要的栈顶 + mov eax , [ eax - 0x1c ] // 指向Kernel32.dll内部 + find_kernel32_base : + dec eax // 开始地毯式搜索Kernel32空间 + xor ax , ax + cmp word ptr [ eax ], 0x5a4d // "MZ" + jne find_kernel32_base // 循 环遍 历 ,找到 则 返回 eax + */ + const auto isTebAccess = match_code( + [&](cs_insn* instruction) { + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } + + if (instruction->detail->x86.operands[1].mem.base != + fsAccessReg) { + return false; + } + if (instruction->detail->x86.operands[1].mem.disp != 0x4) { + return false; + } + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); + + if (isTebAccess) { + isMalwareDetect = true; + break; + } else { + // todo , teb获取PEB然后访问ldr... + DebugBreak(); + } + } else if (currentIns->detail->x86.operands[1].mem.disp == 0x30) { + /* + mov eax,fs:[30h] ;得到PEB结构地址 + mov eax,[eax + 0ch] ;得到PEB_LDR_DATA结构地址 + mov esi,[eax + 1ch] + lodsd ; 得到KERNEL32.DLL所在LDR_MODULE结构的 + ; InInitializationOrderModuleList地址 + mov eax,[eax];win7要加 + mov edx,[eax + 8h] ;得到BaseAddress,既Kernel32.dll基址 + */ + const auto isPebLdrAccess = match_code( + [&](cs_insn* instruction) { + if (instruction->id != X86_INS_MOV && + instruction->id != X86_INS_MOVZX) { + return false; + } + + if (instruction->detail->x86.operands[1].mem.base != + fsAccessReg) { + return false; + } + if (instruction->detail->x86.operands[1].mem.disp != 0xc) { + return false; + } + return true; + }, + [&](cs_insn* instruction) {}, {}, {}); + isMalwareDetect = isPebLdrAccess; + break; + } else { + // todo: fs:00 SEH访问 + //__debugbreak(); + } + } while (false); + if (isMalwareDetect) { + printf( + "malware function detected at address: 0x%llx by gs access " + "peb->ldr \n", + this->current_function_rva); + this->print_asm(currentIns); + } +} + +auto super_huoji_tracker::track_gs_access() -> void { + this->is_x32 ? this->track_gs_access_32_i() : this->track_gs_access_64_i(); +} +auto functionAnalysis( + std::vector> functionlist, + pe64* peFileObject) -> void { double maxEntropy = -1.0; uint64_t maxEntropyAddress = 0; for (auto& func : functionlist) { - - auto entropy = calculateEntropy(reinterpret_cast(func.get()->start_address), func.get()->size); + auto entropy = + calculateEntropy(reinterpret_cast(func.get()->start_address), + func.get()->size); if (entropy > maxEntropy) { maxEntropy = entropy; - maxEntropyAddress = func.get()->start_address - reinterpret_cast(peFileObject->get_buffer()->data()); + maxEntropyAddress = + func.get()->start_address - + reinterpret_cast(peFileObject->get_buffer()->data()); } - auto tracker = new super_huoji_tracker(func.get()->start_address, func.get()->size, func.get()->start_address - reinterpret_cast(peFileObject->get_buffer()->data())); + auto tracker = new super_huoji_tracker( + func.get()->start_address, func.get()->size, + func.get()->start_address - + reinterpret_cast(peFileObject->get_buffer()->data()), + peFileObject->is_32_pe()); tracker->track_gs_access(); delete tracker; } if (maxEntropy > 7.0f) { - printf("mawlare function detected at address: 0x%08x + 0x%llx = 0x%llx entropy %f \n", maxEntropyAddress, peFileObject->get_image_base(), (peFileObject->get_image_base() + maxEntropyAddress), maxEntropy); + printf( + "malware function detected at address: 0x%08x + 0x%llx = 0x%llx " + "entropy %f \n", + maxEntropyAddress, peFileObject->get_image_base(), + (peFileObject->get_image_base() + maxEntropyAddress), maxEntropy); } } -int main() -{ +int main() { const std::string filePath = "z:\\huoji.bin"; pe64* peFileObject = NULL; - do - { - if (Init() == false) { - break; - } + do { try { srand(time(NULL)); peFileObject = new pe64(filePath); - } - catch (std::runtime_error e) { + } catch (std::runtime_error e) { std::cout << "Runtime error: " << e.what() << std::endl; break; } @@ -351,6 +485,6 @@ int main() functionAnalysis(functionlist, peFileObject); } - } while (false); + } while (false); return 0; }