From 3a01b7dc3964ef14ade9615295dd124419df85c4 Mon Sep 17 00:00:00 2001 From: Huoji's <1296564236@qq.com> Date: Sun, 13 Jul 2025 18:47:55 +0800 Subject: [PATCH] update --- sleep_duck/sleep_duck.cpp | 73 ++++++++++------- sleep_duck/stack_tracker.cpp | 148 ++++++++++++++++++++--------------- sleep_duck/stack_tracker.h | 17 ++-- sleep_duck/tools.cpp | 32 ++++++++ sleep_duck/tools.h | 8 +- 5 files changed, 173 insertions(+), 105 deletions(-) diff --git a/sleep_duck/sleep_duck.cpp b/sleep_duck/sleep_duck.cpp index e55ac11..c4449d0 100644 --- a/sleep_duck/sleep_duck.cpp +++ b/sleep_duck/sleep_duck.cpp @@ -69,29 +69,42 @@ auto DoCFTrackX64(HANDLE hProcess, for (size_t i = stackArrays.size() - 1; i > 0; i--) { auto ripAddr = stackArrays[i].first; auto retAddr = stackArrays[i].second; - //printf("stack walk: %p\n", ripAddr); if (retAddr == 0) { continue; } - auto rawAddress = ripAddr - 0x16; - StackTracker stackTrack(hProcess, rawAddress, 0x30, false); - if (stackTrack.TryFindValidDisasm(rawAddress, 0x30) == false) { - printf("\nSleepMask Encryption Memory Detected: %p\n\t", rawAddress); + auto rawAddress = ripAddr - 0x20; + StackTracker stackTrack(hProcess, rawAddress, 0x28, false); + if (stackTrack.TryFindValidDisasm(rawAddress, 0x28) == false) { + printf("\nSleepMask Encryption Memory Detected: %p\n\t", + rawAddress); PrintProcessInfoFromHandle(hProcess); + stackTrack.PrintAsm(); continue; } auto [successTrack, nextJmpAddress] = stackTrack.CalcNextJmpAddress(); - if (successTrack == false && - stackTrack.feature != _features::kCallRip && - stackTrack.feature != _features::kCallReg && - stackTrack.feature != _features::kSyscall) { - printf("\nNon-integrity Stack Detect: %p\n\t", rawAddress); - PrintProcessInfoFromHandle(hProcess); + if (successTrack == false) { + // very perfer lazy method + static const std::string WaitonAddressGate = "52 10 47 AE"; + if (Tools::FindPatternInMemory( + (uint64_t)stackTrack.SuccessReadedBuffer.data(), + stackTrack.SuccessReadedBuffer.size(), + WaitonAddressGate) != 0) { + printf("skip waitonaddress, golang detect\n"); + continue; + } + if (stackTrack.feature != _features::kCallRip && + stackTrack.feature != _features::kCallReg && + stackTrack.feature != _features::kSyscall) { + printf("\nNon-integrity Stack Detect: %p ripAddr: %p \n\t", + rawAddress, ripAddr); + PrintProcessInfoFromHandle(hProcess); + stackTrack.PrintAsm(); + } + break; } - } return; } @@ -101,7 +114,7 @@ auto DoX64StackDetect(HANDLE hProcess, HANDLE hThread) -> void { context.ContextFlags = CONTEXT_ALL; std::vector> stackArrays; SymInitialize(hProcess, nullptr, TRUE); - //printf("scan tid: %d \n", GetThreadId(hThread)); + printf("scan tid: %d \n", GetThreadId(hThread)); do { if (GetThreadContext(hThread, &context) == false) { break; @@ -126,26 +139,30 @@ auto DoX64StackDetect(HANDLE hProcess, HANDLE hThread) -> void { } if (SimpleCheckIn2020(hProcess, StackFarmeEx.AddrPC.Offset)) { detect = true; - //break; + // break; } + stackArrays.push_back( {StackFarmeEx.AddrPC.Offset, StackFarmeEx.AddrReturn.Offset}); } - //if (detect) { - // break; - //} + // if (detect) { + // break; + // } DoCFTrackX64(hProcess, stackArrays); } while (false); SymCleanup(hProcess); } // 主扫描函数 -auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void { - HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 所有线程 +auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) + -> void { + HANDLE hThreadSnap = + CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 所有线程 THREADENTRY32 te32 = {}; te32.dwSize = sizeof(THREADENTRY32); - if (hThreadSnap == INVALID_HANDLE_VALUE || !Thread32First(hThreadSnap, &te32)) + if (hThreadSnap == INVALID_HANDLE_VALUE || + !Thread32First(hThreadSnap, &te32)) return; do { @@ -158,7 +175,8 @@ auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void if (!scanAll && pidFilter != 0 && te32.th32OwnerProcessID != pidFilter) continue; - if (!scanAll && pidFilter == 0 && te32.th32OwnerProcessID != GetCurrentProcessId()) + if (!scanAll && pidFilter == 0 && + te32.th32OwnerProcessID != GetCurrentProcessId()) continue; auto handleDeleter = [](HANDLE h) { @@ -172,12 +190,11 @@ auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void OpenProcess(PROCESS_ALL_ACCESS, FALSE, te32.th32OwnerProcessID), handleDeleter); - if (!hProcess || hProcess.get() == INVALID_HANDLE_VALUE || - !hThread || hThread.get() == INVALID_HANDLE_VALUE) + if (!hProcess || hProcess.get() == INVALID_HANDLE_VALUE || !hThread || + hThread.get() == INVALID_HANDLE_VALUE) continue; - if (!Tools::Is64BitPorcess(hProcess.get())) - continue; + if (!Tools::Is64BitPorcess(hProcess.get())) continue; DoX64StackDetect(hProcess.get(), hThread.get()); } while (Thread32Next(hThreadSnap, &te32)); @@ -194,12 +211,10 @@ int main(int argc, char* argv[]) { if (arg == "-all") { scanAll = true; - } - else if (arg == "-pid" && i + 1 < argc) { + } else if (arg == "-pid" && i + 1 < argc) { scanAll = false; targetPid = static_cast(std::stoul(argv[++i])); - } - else { + } else { std::cerr << "[!] Unknown argument ,go scan all: " << arg << "\n"; scanAll = true; } diff --git a/sleep_duck/stack_tracker.cpp b/sleep_duck/stack_tracker.cpp index 6942f7f..4b71519 100644 --- a/sleep_duck/stack_tracker.cpp +++ b/sleep_duck/stack_tracker.cpp @@ -1,4 +1,5 @@ #include "stack_tracker.h" + auto StackTracker::rpm(uintptr_t address, size_t readSize) -> std::vector { size_t NumOfRead = 0; @@ -12,8 +13,7 @@ auto StackTracker::rpm(uintptr_t address, size_t readSize) return buffer; } auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool { - if (insn == nullptr || count == 0) - return false; + if (insn == nullptr || count == 0) return false; int threshold_score = 2; int score = 0; @@ -25,39 +25,36 @@ auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool { const cs_insn& inst = insn[i]; switch (inst.id) { - case X86_INS_PUSH: - if (strcmp(inst.mnemonic, "push") == 0) + case X86_INS_PUSH: + if (strcmp(inst.mnemonic, "push") == 0) score++; + break; + case X86_INS_MOV: + if (strstr(inst.op_str, "rbp") != nullptr || + strstr(inst.op_str, "rsp") != nullptr) + score++; + break; + case X86_INS_SUB: + case X86_INS_ADD: + if (strstr(inst.op_str, "rsp") != nullptr) score++; + break; + case X86_INS_CALL: + score += 1; + break; + case X86_INS_LEA: + if (strstr(inst.op_str, "rip") != nullptr) score++; + break; + case X86_INS_TEST: + case X86_INS_CMP: + case X86_INS_JE: + case X86_INS_JNE: + case X86_INS_JMP: score++; - break; - case X86_INS_MOV: - if (strstr(inst.op_str, "rbp") != nullptr || strstr(inst.op_str, "rsp") != nullptr) - score++; - break; - case X86_INS_SUB: - case X86_INS_ADD: - if (strstr(inst.op_str, "rsp") != nullptr) - score++; - break; - case X86_INS_CALL: - score += 1; - break; - case X86_INS_LEA: - if (strstr(inst.op_str, "rip") != nullptr) - score++; - break; - case X86_INS_TEST: - case X86_INS_CMP: - case X86_INS_JE: - case X86_INS_JNE: - case X86_INS_JMP: - score++; - break; - case X86_INS_NOP: - break; // 忽略 - default: - if (score == 0) - score -= 1; //杂指令降低一点分数 - break; + break; + case X86_INS_NOP: + break; // 忽略 + default: + if (score == 0) score -= 1; // 杂指令降低一点分数 + break; } if (score >= threshold_score) { @@ -66,24 +63,31 @@ auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool { } return score >= threshold_score; } -auto StackTracker::TryFindValidDisasm(uint64_t baseAddr, size_t maxOffset) -> bool { +auto StackTracker::TryFindValidDisasm(uint64_t baseAddr, size_t maxOffset) + -> bool { for (size_t i = 0; i < maxOffset; ++i) { auto buf = this->rpm(baseAddr + i, this->trackSize); if (buf.size() != this->trackSize) continue; cs_insn* testInsn = nullptr; - auto cnt = cs_disasm(this->capstoneHandle, - reinterpret_cast(buf.data()), - this->trackSize, baseAddr + i, 0, &testInsn); - if (cnt > 0 && LookslikeValidEntry(testInsn, cnt)) { + this->disasmCount = cs_disasm(this->capstoneHandle, + reinterpret_cast(buf.data()), + this->trackSize, baseAddr + i, 0, &testInsn); + // this->PrintAsm(testInsn); + if (this->disasmCount > 0) { + this->insn = testInsn; + } + if (this->disasmCount > 0 && LookslikeValidEntry(testInsn, this->disasmCount)) { this->baseAddr += i; if (this->insn != nullptr) { cs_free(this->insn, this->disasmCount); } - this->insn = testInsn; - this->disasmCount = cnt; - for (size_t j = 0; j < cnt; ++j) { - this->insList.push_back(std::make_shared(this->insn[j])); + for (size_t j = 0; j < this->disasmCount; ++j) { + // this->PrintAsm(&this->insn[j]); + + this->insList.push_back( + std::make_shared(this->insn[j])); } + this->SuccessReadedBuffer = buf; this->readSuccess = true; return true; } @@ -104,10 +108,14 @@ StackTracker::StackTracker(HANDLE hProcess, uint64_t StartAddress, cs_option(capstoneHandle, CS_OPT_SKIPDATA, CS_OPT_ON); /* do { + // 1.读取 auto bufferArrays = this->rpm(StartAddress, trackSize); if (bufferArrays.size() != trackSize) { break; } + // 2. 反过来 + std::reverse(bufferArrays.begin(), bufferArrays.end()); + // 3. 这里就是向上的了.指令是对的上的 disasmCount = cs_disasm(capstoneHandle, reinterpret_cast(bufferArrays.data()), @@ -115,8 +123,10 @@ StackTracker::StackTracker(HANDLE hProcess, uint64_t StartAddress, if (disasmCount == 0) { break; } - for (size_t index = 0; index < disasmCount; index++) { + // 4. 再反过来 + for (size_t index = disasmCount; index > 0; index--) { const auto code = insn[index]; + this->PrintAsm(&code); this->insList.push_back(std::make_shared(code)); } this->readSuccess = true; @@ -135,7 +145,7 @@ auto StackTracker::getNextIns() -> std::shared_ptr { } StackTracker::~StackTracker() { if (insn) { - cs_free(insn, disasmCount); + //cs_free(insn, disasmCount); cs_close(&capstoneHandle); } } @@ -168,16 +178,26 @@ auto StackTracker::matchCode( inline auto StackTracker::is_call(cs_insn* ins) -> bool { return ins->id == X86_INS_CALL; } +auto StackTracker::PrintAsm() -> void { + for (size_t j = 0; j < this->disasmCount; ++j) { + for (int x = 0; x < this->insn[j].size; x++) { + printf("%02X ", this->insn[j].bytes[x]); + } + printf("0x%llx :\t\t%s\t%s\t\n", this->insn[j].address, + this->insn[j].mnemonic, this->insn[j].op_str); + + } + +} auto StackTracker::CalcNextJmpAddress() -> std::pair { if (this->readSuccess == false) { - return {false , 0}; + return {false, 0}; } this->feature = _features::kNonCallOnly; uint64_t callAddress = 0; auto isMatchCall = matchCode( [&](cs_insn* instruction) { - if (instruction->id != X86_INS_CALL) { if (instruction->id == X86_INS_SYSCALL) { this->feature = _features::kSyscall; @@ -192,35 +212,39 @@ auto StackTracker::CalcNextJmpAddress() -> std::pair { callAddress = instruction->address + instruction->size + operand.imm; return true; - } - else if (operand.type == X86_OP_MEM) { + } else if (operand.type == X86_OP_MEM) { const x86_op_mem& mem = operand.mem; // 我们只处理可以静态计算的 RIP 相对寻址 if (mem.base == X86_REG_RIP) { - uint64_t pointerAddress = instruction->address + instruction->size + mem.disp; + uint64_t pointerAddress = + instruction->address + instruction->size + mem.disp; size_t pointerSize = this->isWow64 ? 4 : 8; - std::vector pointerBuffer = this->rpm(pointerAddress, pointerSize); + std::vector pointerBuffer = + this->rpm(pointerAddress, pointerSize); if (pointerBuffer.empty()) { - std::cerr << "Failed to read pointer at 0x" << std::hex << pointerAddress << std::endl; + std::cerr << "Failed to read pointer at 0x" << std::hex + << pointerAddress << std::endl; return false; } if (pointerSize == 8) { - callAddress = *reinterpret_cast(pointerBuffer.data()); - } - else { // 32位 - callAddress = *reinterpret_cast(pointerBuffer.data()); + callAddress = + *reinterpret_cast(pointerBuffer.data()); + } else { // 32位 + callAddress = + *reinterpret_cast(pointerBuffer.data()); } - //std::cout << "Found RIP-relative call at 0x" << std::hex << instruction->address - // << ". Pointer at 0x" << pointerAddress - // << ". Final Target: 0x" << callAddress << std::endl; + // std::cout << "Found RIP-relative call at 0x" << std::hex + // << instruction->address + // << ". Pointer at 0x" << pointerAddress + // << ". Final Target: 0x" << callAddress << std::endl; return true; } - //std::cout << "Skipping non-RIP-relative memory call at 0x" << std::hex << instruction->address << std::endl; + // std::cout << "Skipping non-RIP-relative memory call at 0x" << + // std::hex << instruction->address << std::endl; this->feature = _features::kCallRip; return false; - } - else if (operand.type == X86_OP_REG) { + } else if (operand.type == X86_OP_REG) { this->feature = _features::kCallReg; return false; } diff --git a/sleep_duck/stack_tracker.h b/sleep_duck/stack_tracker.h index 1e21597..ba00a87 100644 --- a/sleep_duck/stack_tracker.h +++ b/sleep_duck/stack_tracker.h @@ -1,25 +1,18 @@ #pragma once #include "head.h" -enum class _features { - kNone, - kNonCallOnly, - kCallRip, - kCallReg, - kSyscall -}; +enum class _features { kNone, kNonCallOnly, kCallRip, kCallReg, kSyscall }; class StackTracker { private: bool readSuccess; bool isWow64; HANDLE targetProcess; std::vector> insList; - cs_insn* insn = nullptr; - size_t disasmCount = 0; csh capstoneHandle; uint64_t ins_ip, ins_ip_address, baseAddr, trackSize; auto getNextIns() -> std::shared_ptr; auto LookslikeValidEntry(cs_insn* insn, size_t count) -> bool; inline auto is_call(cs_insn* ins) -> bool; + template auto matchCode(T match_fn, B process_fn, std::optional num_operands, @@ -27,13 +20,15 @@ class StackTracker { -> bool; auto rpm(uintptr_t address, size_t readSize) -> std::vector; - - public: + cs_insn* insn = nullptr; + size_t disasmCount = 0; + std::vector SuccessReadedBuffer; _features feature; StackTracker(HANDLE hProcess, uint64_t StartAddress, size_t trackSize, bool isX32); ~StackTracker(); + auto PrintAsm() -> void; auto CalcNextJmpAddress() -> std::pair; auto TryFindValidDisasm(uint64_t baseAddr, size_t maxOffset) -> bool; }; diff --git a/sleep_duck/tools.cpp b/sleep_duck/tools.cpp index e4fb95c..6ebd1b0 100644 --- a/sleep_duck/tools.cpp +++ b/sleep_duck/tools.cpp @@ -1,5 +1,37 @@ #include "tools.h" +// 71 A3 52 10 47 AE A0 +#define INRANGE(x, a, b) (x >= a && x <= b) +#define getBits(x) \ + (INRANGE((x & (~0x20)), 'A', 'F') ? ((x & (~0x20)) - 'A' + 0xa) \ + : (INRANGE(x, '0', '9') ? x - '0' : 0)) +#define getByte(x) (getBits(x[0]) << 4 | getBits(x[1])) namespace Tools { +auto FindPatternInMemory(uint64_t StartAddress, size_t MemorySize, + std::string pattern) -> uint64_t { + const char* pat = pattern.c_str(); + uint64_t firstMatch = 0; + uint64_t rangeStart = StartAddress; + uint64_t rangeEnd = rangeStart + MemorySize; + for (uint64_t pCur = rangeStart; pCur < rangeEnd; pCur++) { + if (!*pat) return firstMatch; + + if (*(PBYTE)pat == '\?' || *(BYTE*)pCur == getByte(pat)) { + if (!firstMatch) firstMatch = pCur; + + if (!pat[2]) return firstMatch; + + if (*(PWORD)pat == '\?\?' || *(PBYTE)pat != '\?') + pat += 3; + + else + pat += 2; // one ? + } else { + pat = pattern.c_str(); + firstMatch = 0; + } + } + return 0; +} auto Is64BitPorcess(HANDLE hProcess) -> bool { BOOL bIsWow64 = false; IsWow64Process(hProcess, &bIsWow64); diff --git a/sleep_duck/tools.h b/sleep_duck/tools.h index 1b4b0bb..a474c86 100644 --- a/sleep_duck/tools.h +++ b/sleep_duck/tools.h @@ -1,6 +1,8 @@ #pragma once #include "head.h" namespace Tools { - auto EnableDebugPrivilege(bool bEnable) -> bool; - auto Is64BitPorcess(HANDLE hProcess) -> bool; -}; +auto EnableDebugPrivilege(bool bEnable) -> bool; +auto Is64BitPorcess(HANDLE hProcess) -> bool; +auto FindPatternInMemory(uint64_t StartAddress, size_t MemorySize, + std::string pattern) -> uint64_t; +}; // namespace Tools