This commit is contained in:
Huoji's
2025-07-13 18:47:55 +08:00
parent 5eccbbb5e5
commit 3a01b7dc39
5 changed files with 173 additions and 105 deletions

View File

@@ -69,29 +69,42 @@ auto DoCFTrackX64(HANDLE hProcess,
for (size_t i = stackArrays.size() - 1; i > 0; i--) { for (size_t i = stackArrays.size() - 1; i > 0; i--) {
auto ripAddr = stackArrays[i].first; auto ripAddr = stackArrays[i].first;
auto retAddr = stackArrays[i].second; auto retAddr = stackArrays[i].second;
//printf("stack walk: %p\n", ripAddr);
if (retAddr == 0) { if (retAddr == 0) {
continue; continue;
} }
auto rawAddress = ripAddr - 0x16; auto rawAddress = ripAddr - 0x20;
StackTracker stackTrack(hProcess, rawAddress, 0x30, false); StackTracker stackTrack(hProcess, rawAddress, 0x28, false);
if (stackTrack.TryFindValidDisasm(rawAddress, 0x30) == false) { if (stackTrack.TryFindValidDisasm(rawAddress, 0x28) == false) {
printf("\nSleepMask Encryption Memory Detected: %p\n\t", rawAddress); printf("\nSleepMask Encryption Memory Detected: %p\n\t",
rawAddress);
PrintProcessInfoFromHandle(hProcess); PrintProcessInfoFromHandle(hProcess);
stackTrack.PrintAsm();
continue; continue;
} }
auto [successTrack, nextJmpAddress] = stackTrack.CalcNextJmpAddress(); auto [successTrack, nextJmpAddress] = stackTrack.CalcNextJmpAddress();
if (successTrack == false && if (successTrack == false) {
stackTrack.feature != _features::kCallRip && // very perfer lazy method
stackTrack.feature != _features::kCallReg && static const std::string WaitonAddressGate = "52 10 47 AE";
stackTrack.feature != _features::kSyscall) { if (Tools::FindPatternInMemory(
printf("\nNon-integrity Stack Detect: %p\n\t", rawAddress); (uint64_t)stackTrack.SuccessReadedBuffer.data(),
PrintProcessInfoFromHandle(hProcess); 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; break;
} }
} }
return; return;
} }
@@ -101,7 +114,7 @@ auto DoX64StackDetect(HANDLE hProcess, HANDLE hThread) -> void {
context.ContextFlags = CONTEXT_ALL; context.ContextFlags = CONTEXT_ALL;
std::vector<std::pair<uint64_t, uint64_t>> stackArrays; std::vector<std::pair<uint64_t, uint64_t>> stackArrays;
SymInitialize(hProcess, nullptr, TRUE); SymInitialize(hProcess, nullptr, TRUE);
//printf("scan tid: %d \n", GetThreadId(hThread)); printf("scan tid: %d \n", GetThreadId(hThread));
do { do {
if (GetThreadContext(hThread, &context) == false) { if (GetThreadContext(hThread, &context) == false) {
break; break;
@@ -126,26 +139,30 @@ auto DoX64StackDetect(HANDLE hProcess, HANDLE hThread) -> void {
} }
if (SimpleCheckIn2020(hProcess, StackFarmeEx.AddrPC.Offset)) { if (SimpleCheckIn2020(hProcess, StackFarmeEx.AddrPC.Offset)) {
detect = true; detect = true;
//break; // break;
} }
stackArrays.push_back( stackArrays.push_back(
{StackFarmeEx.AddrPC.Offset, StackFarmeEx.AddrReturn.Offset}); {StackFarmeEx.AddrPC.Offset, StackFarmeEx.AddrReturn.Offset});
} }
//if (detect) { // if (detect) {
// break; // break;
//} // }
DoCFTrackX64(hProcess, stackArrays); DoCFTrackX64(hProcess, stackArrays);
} while (false); } while (false);
SymCleanup(hProcess); SymCleanup(hProcess);
} }
// 主扫描函数 // 主扫描函数
auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void { auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false)
HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 所有线程 -> void {
HANDLE hThreadSnap =
CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); // 所有线程
THREADENTRY32 te32 = {}; THREADENTRY32 te32 = {};
te32.dwSize = sizeof(THREADENTRY32); te32.dwSize = sizeof(THREADENTRY32);
if (hThreadSnap == INVALID_HANDLE_VALUE || !Thread32First(hThreadSnap, &te32)) if (hThreadSnap == INVALID_HANDLE_VALUE ||
!Thread32First(hThreadSnap, &te32))
return; return;
do { do {
@@ -158,7 +175,8 @@ auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void
if (!scanAll && pidFilter != 0 && te32.th32OwnerProcessID != pidFilter) if (!scanAll && pidFilter != 0 && te32.th32OwnerProcessID != pidFilter)
continue; continue;
if (!scanAll && pidFilter == 0 && te32.th32OwnerProcessID != GetCurrentProcessId()) if (!scanAll && pidFilter == 0 &&
te32.th32OwnerProcessID != GetCurrentProcessId())
continue; continue;
auto handleDeleter = [](HANDLE h) { auto handleDeleter = [](HANDLE h) {
@@ -172,12 +190,11 @@ auto DoLittleHackerMemeDetect(DWORD pidFilter = 0, bool scanAll = false) -> void
OpenProcess(PROCESS_ALL_ACCESS, FALSE, te32.th32OwnerProcessID), OpenProcess(PROCESS_ALL_ACCESS, FALSE, te32.th32OwnerProcessID),
handleDeleter); handleDeleter);
if (!hProcess || hProcess.get() == INVALID_HANDLE_VALUE || if (!hProcess || hProcess.get() == INVALID_HANDLE_VALUE || !hThread ||
!hThread || hThread.get() == INVALID_HANDLE_VALUE) hThread.get() == INVALID_HANDLE_VALUE)
continue; continue;
if (!Tools::Is64BitPorcess(hProcess.get())) if (!Tools::Is64BitPorcess(hProcess.get())) continue;
continue;
DoX64StackDetect(hProcess.get(), hThread.get()); DoX64StackDetect(hProcess.get(), hThread.get());
} while (Thread32Next(hThreadSnap, &te32)); } while (Thread32Next(hThreadSnap, &te32));
@@ -194,12 +211,10 @@ int main(int argc, char* argv[]) {
if (arg == "-all") { if (arg == "-all") {
scanAll = true; scanAll = true;
} } else if (arg == "-pid" && i + 1 < argc) {
else if (arg == "-pid" && i + 1 < argc) {
scanAll = false; scanAll = false;
targetPid = static_cast<DWORD>(std::stoul(argv[++i])); targetPid = static_cast<DWORD>(std::stoul(argv[++i]));
} } else {
else {
std::cerr << "[!] Unknown argument ,go scan all: " << arg << "\n"; std::cerr << "[!] Unknown argument ,go scan all: " << arg << "\n";
scanAll = true; scanAll = true;
} }

View File

@@ -1,4 +1,5 @@
#include "stack_tracker.h" #include "stack_tracker.h"
auto StackTracker::rpm(uintptr_t address, size_t readSize) auto StackTracker::rpm(uintptr_t address, size_t readSize)
-> std::vector<char> { -> std::vector<char> {
size_t NumOfRead = 0; size_t NumOfRead = 0;
@@ -12,8 +13,7 @@ auto StackTracker::rpm(uintptr_t address, size_t readSize)
return buffer; return buffer;
} }
auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool { auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool {
if (insn == nullptr || count == 0) if (insn == nullptr || count == 0) return false;
return false;
int threshold_score = 2; int threshold_score = 2;
int score = 0; int score = 0;
@@ -25,39 +25,36 @@ auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool {
const cs_insn& inst = insn[i]; const cs_insn& inst = insn[i];
switch (inst.id) { switch (inst.id) {
case X86_INS_PUSH: case X86_INS_PUSH:
if (strcmp(inst.mnemonic, "push") == 0) 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++; score++;
break; break;
case X86_INS_MOV: case X86_INS_NOP:
if (strstr(inst.op_str, "rbp") != nullptr || strstr(inst.op_str, "rsp") != nullptr) break; // 忽略
score++; default:
break; if (score == 0) score -= 1; // 杂指令降低一点分数
case X86_INS_SUB: break;
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;
} }
if (score >= threshold_score) { if (score >= threshold_score) {
@@ -66,24 +63,31 @@ auto StackTracker::LookslikeValidEntry(cs_insn* insn, size_t count) -> bool {
} }
return score >= threshold_score; 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) { for (size_t i = 0; i < maxOffset; ++i) {
auto buf = this->rpm(baseAddr + i, this->trackSize); auto buf = this->rpm(baseAddr + i, this->trackSize);
if (buf.size() != this->trackSize) continue; if (buf.size() != this->trackSize) continue;
cs_insn* testInsn = nullptr; cs_insn* testInsn = nullptr;
auto cnt = cs_disasm(this->capstoneHandle, this->disasmCount = cs_disasm(this->capstoneHandle,
reinterpret_cast<const uint8_t*>(buf.data()), reinterpret_cast<const uint8_t*>(buf.data()),
this->trackSize, baseAddr + i, 0, &testInsn); this->trackSize, baseAddr + i, 0, &testInsn);
if (cnt > 0 && LookslikeValidEntry(testInsn, cnt)) { // this->PrintAsm(testInsn);
if (this->disasmCount > 0) {
this->insn = testInsn;
}
if (this->disasmCount > 0 && LookslikeValidEntry(testInsn, this->disasmCount)) {
this->baseAddr += i; this->baseAddr += i;
if (this->insn != nullptr) { if (this->insn != nullptr) {
cs_free(this->insn, this->disasmCount); cs_free(this->insn, this->disasmCount);
} }
this->insn = testInsn; for (size_t j = 0; j < this->disasmCount; ++j) {
this->disasmCount = cnt; // this->PrintAsm(&this->insn[j]);
for (size_t j = 0; j < cnt; ++j) {
this->insList.push_back(std::make_shared<cs_insn>(this->insn[j])); this->insList.push_back(
std::make_shared<cs_insn>(this->insn[j]));
} }
this->SuccessReadedBuffer = buf;
this->readSuccess = true; this->readSuccess = true;
return true; return true;
} }
@@ -104,10 +108,14 @@ StackTracker::StackTracker(HANDLE hProcess, uint64_t StartAddress,
cs_option(capstoneHandle, CS_OPT_SKIPDATA, CS_OPT_ON); cs_option(capstoneHandle, CS_OPT_SKIPDATA, CS_OPT_ON);
/* /*
do { do {
// 1.读取
auto bufferArrays = this->rpm(StartAddress, trackSize); auto bufferArrays = this->rpm(StartAddress, trackSize);
if (bufferArrays.size() != trackSize) { if (bufferArrays.size() != trackSize) {
break; break;
} }
// 2. 反过来
std::reverse(bufferArrays.begin(), bufferArrays.end());
// 3. 这里就是向上的了.指令是对的上的
disasmCount = disasmCount =
cs_disasm(capstoneHandle, cs_disasm(capstoneHandle,
reinterpret_cast<const uint8_t*>(bufferArrays.data()), reinterpret_cast<const uint8_t*>(bufferArrays.data()),
@@ -115,8 +123,10 @@ StackTracker::StackTracker(HANDLE hProcess, uint64_t StartAddress,
if (disasmCount == 0) { if (disasmCount == 0) {
break; break;
} }
for (size_t index = 0; index < disasmCount; index++) { // 4. 再反过来
for (size_t index = disasmCount; index > 0; index--) {
const auto code = insn[index]; const auto code = insn[index];
this->PrintAsm(&code);
this->insList.push_back(std::make_shared<cs_insn>(code)); this->insList.push_back(std::make_shared<cs_insn>(code));
} }
this->readSuccess = true; this->readSuccess = true;
@@ -135,7 +145,7 @@ auto StackTracker::getNextIns() -> std::shared_ptr<cs_insn> {
} }
StackTracker::~StackTracker() { StackTracker::~StackTracker() {
if (insn) { if (insn) {
cs_free(insn, disasmCount); //cs_free(insn, disasmCount);
cs_close(&capstoneHandle); cs_close(&capstoneHandle);
} }
} }
@@ -168,16 +178,26 @@ auto StackTracker::matchCode(
inline auto StackTracker::is_call(cs_insn* ins) -> bool { inline auto StackTracker::is_call(cs_insn* ins) -> bool {
return ins->id == X86_INS_CALL; 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<bool, uint64_t> { auto StackTracker::CalcNextJmpAddress() -> std::pair<bool, uint64_t> {
if (this->readSuccess == false) { if (this->readSuccess == false) {
return {false , 0}; return {false, 0};
} }
this->feature = _features::kNonCallOnly; this->feature = _features::kNonCallOnly;
uint64_t callAddress = 0; uint64_t callAddress = 0;
auto isMatchCall = matchCode( auto isMatchCall = matchCode(
[&](cs_insn* instruction) { [&](cs_insn* instruction) {
if (instruction->id != X86_INS_CALL) { if (instruction->id != X86_INS_CALL) {
if (instruction->id == X86_INS_SYSCALL) { if (instruction->id == X86_INS_SYSCALL) {
this->feature = _features::kSyscall; this->feature = _features::kSyscall;
@@ -192,35 +212,39 @@ auto StackTracker::CalcNextJmpAddress() -> std::pair<bool, uint64_t> {
callAddress = callAddress =
instruction->address + instruction->size + operand.imm; instruction->address + instruction->size + operand.imm;
return true; return true;
} } else if (operand.type == X86_OP_MEM) {
else if (operand.type == X86_OP_MEM) {
const x86_op_mem& mem = operand.mem; const x86_op_mem& mem = operand.mem;
// 我们只处理可以静态计算的 RIP 相对寻址 // 我们只处理可以静态计算的 RIP 相对寻址
if (mem.base == X86_REG_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; size_t pointerSize = this->isWow64 ? 4 : 8;
std::vector<char> pointerBuffer = this->rpm(pointerAddress, pointerSize); std::vector<char> pointerBuffer =
this->rpm(pointerAddress, pointerSize);
if (pointerBuffer.empty()) { 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; return false;
} }
if (pointerSize == 8) { if (pointerSize == 8) {
callAddress = *reinterpret_cast<uint64_t*>(pointerBuffer.data()); callAddress =
} *reinterpret_cast<uint64_t*>(pointerBuffer.data());
else { // 32位 } else { // 32位
callAddress = *reinterpret_cast<uint32_t*>(pointerBuffer.data()); callAddress =
*reinterpret_cast<uint32_t*>(pointerBuffer.data());
} }
//std::cout << "Found RIP-relative call at 0x" << std::hex << instruction->address // std::cout << "Found RIP-relative call at 0x" << std::hex
// << ". Pointer at 0x" << pointerAddress // << instruction->address
// << ". Final Target: 0x" << callAddress << std::endl; // << ". Pointer at 0x" << pointerAddress
// << ". Final Target: 0x" << callAddress << std::endl;
return true; 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; this->feature = _features::kCallRip;
return false; return false;
} } else if (operand.type == X86_OP_REG) {
else if (operand.type == X86_OP_REG) {
this->feature = _features::kCallReg; this->feature = _features::kCallReg;
return false; return false;
} }

View File

@@ -1,25 +1,18 @@
#pragma once #pragma once
#include "head.h" #include "head.h"
enum class _features { enum class _features { kNone, kNonCallOnly, kCallRip, kCallReg, kSyscall };
kNone,
kNonCallOnly,
kCallRip,
kCallReg,
kSyscall
};
class StackTracker { class StackTracker {
private: private:
bool readSuccess; bool readSuccess;
bool isWow64; bool isWow64;
HANDLE targetProcess; HANDLE targetProcess;
std::vector<std::shared_ptr<cs_insn>> insList; std::vector<std::shared_ptr<cs_insn>> insList;
cs_insn* insn = nullptr;
size_t disasmCount = 0;
csh capstoneHandle; csh capstoneHandle;
uint64_t ins_ip, ins_ip_address, baseAddr, trackSize; uint64_t ins_ip, ins_ip_address, baseAddr, trackSize;
auto getNextIns() -> std::shared_ptr<cs_insn>; auto getNextIns() -> std::shared_ptr<cs_insn>;
auto LookslikeValidEntry(cs_insn* insn, size_t count) -> bool; auto LookslikeValidEntry(cs_insn* insn, size_t count) -> bool;
inline auto is_call(cs_insn* ins) -> bool; inline auto is_call(cs_insn* ins) -> bool;
template <typename T, typename B> template <typename T, typename B>
auto matchCode(T match_fn, B process_fn, auto matchCode(T match_fn, B process_fn,
std::optional<uint32_t> num_operands, std::optional<uint32_t> num_operands,
@@ -27,13 +20,15 @@ class StackTracker {
-> bool; -> bool;
auto rpm(uintptr_t address, size_t readSize) -> std::vector<char>; auto rpm(uintptr_t address, size_t readSize) -> std::vector<char>;
public: public:
cs_insn* insn = nullptr;
size_t disasmCount = 0;
std::vector<char> SuccessReadedBuffer;
_features feature; _features feature;
StackTracker(HANDLE hProcess, uint64_t StartAddress, size_t trackSize, StackTracker(HANDLE hProcess, uint64_t StartAddress, size_t trackSize,
bool isX32); bool isX32);
~StackTracker(); ~StackTracker();
auto PrintAsm() -> void;
auto CalcNextJmpAddress() -> std::pair<bool, uint64_t>; auto CalcNextJmpAddress() -> std::pair<bool, uint64_t>;
auto TryFindValidDisasm(uint64_t baseAddr, size_t maxOffset) -> bool; auto TryFindValidDisasm(uint64_t baseAddr, size_t maxOffset) -> bool;
}; };

View File

@@ -1,5 +1,37 @@
#include "tools.h" #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 { 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 { auto Is64BitPorcess(HANDLE hProcess) -> bool {
BOOL bIsWow64 = false; BOOL bIsWow64 = false;
IsWow64Process(hProcess, &bIsWow64); IsWow64Process(hProcess, &bIsWow64);

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include "head.h" #include "head.h"
namespace Tools { namespace Tools {
auto EnableDebugPrivilege(bool bEnable) -> bool; auto EnableDebugPrivilege(bool bEnable) -> bool;
auto Is64BitPorcess(HANDLE hProcess) -> bool; auto Is64BitPorcess(HANDLE hProcess) -> bool;
}; auto FindPatternInMemory(uint64_t StartAddress, size_t MemorySize,
std::string pattern) -> uint64_t;
}; // namespace Tools