逆向入门-反调试

反调试

过滤器异常

什么是异常:
最简单的一句话:CPU遇到了无法执行的指令。

异常的处理过程:
CPU-操作系统-调试器(如果有)-VEH-SEH-过滤器异常SetUnhandledExceptionFilter

即过滤器异常就是前面都没有处理异常,那么我就处理。

#include <Windows.h>
#include <iostream>

/*
	@function 异常处理函数
	@param ExceptionInfo 异常信息
	@return 返回值是否处理
*/
LONG WINAPI UnhandledExceptionFilterProc(
	_EXCEPTION_POINTERS* ExceptionInfo
) {
	MessageBoxA(0,0,0,0);
	ExceptionInfo->ContextRecord->Eip += 3; // 跳过异常处
	return 1; // 1异常我自己处理了
}

int main() {
	// 1表示自己处理了 0表示继续搜索下一个异常处理 -1表示继续执行   
	SetUnhandledExceptionFilter(UnhandledExceptionFilterProc);
	__asm {
		mov eax,0
		mov [eax],1 // 往0地址写入数据 报错
	}
	system("pause");
	return 0;
}

异常的作用,前面我们也说了,在编写程序的时候有的会故意触发异常,然后在异常处理函数里面判断是否有调试器的存在。

就像PEB+0x2的位置就能判断是否有调试器存在

反调试1

调试器的工作流程:
  -1.创建进程,通过CreateProcess并设置DEBUG_PROCESS模式
  -2.附加进程,调用DebugActiveProcess函数
程序在被调试的时候,进程执行的一些操作会以事件的方式通知调试器。
当有事件需要通知调试器的时候,操作系统会挂起进程所有线程,然后把事件发送给调试器。

调试器通过WaitForDebugEvent来等待事件消息。


程序被调试后会有那些特征?
  -调试程序的时候,调试器会对被调试的程序设置标志。
BeginDebugged=1 API:isDebugPresent
BeginDebugged 存放着PE+02偏移
NtGlobalFlag 调试的时候值为0x70存放在PEB+0x68偏移处,附加进程的方式值不变
ProcessDebugPort API:CheckRemoteDebuggerPresent 不仅可以检测自己,还能检测别的进程

#include <Windows.h>
#include <iostream>

int main() {
	/*
		mov     eax, dword ptr fs:[18] // 拿到TEB
		mov     eax, dword ptr [eax+30] // 拿到PEB
		movzx   eax, byte ptr [eax+2] // 拿到isDebugged这个标志
		retn
	*/
	BOOL ret = IsDebuggerPresent();
	if (ret) {
		printf("存在调试器\n");
	}else {
		printf("不存在调试器\n");
	}
	DWORD isDebug = 1;
	__asm {
		mov eax, fs: [0x30] ;
		mov eax, [eax + 0x68];
		mov isDebug, eax;
	};
	if (isDebug == 0x70) {
		printf("isDebug 存在调试器\n");
	}else {
		printf("isDebug 不存在调试器\n");
	}
	system("pause");
	return 0;
}

#include <Windows.h>
#include <iostream>

int main() {
	BOOL ret;
	CheckRemoteDebuggerPresent(GetCurrentProcess(),&ret);
	if (ret) {
		printf("存在调试器\n");
	}else {
		printf("不存在调试器\n");
	}
	system("pause");
	return 0;
}

反调试2

使用NtQueryInfomationProcess来实现CheckRemoteDebuggerPresent函数的功能,检测某个进程是否被调试。

他们的本质就是调试端口。

__kernel_entry NTSTATUS NtQueryInformationProcess(
   HANDLE           ProcessHandle,  要查询的进程句柄
   PROCESSINFOCLASS ProcessInformationClass,  查询进程信息的类型
   PVOID            ProcessInformation, 输出参数 缓冲区
   ULONG            ProcessInformationLength, 缓冲区大小
   PULONG           ReturnLength 实际返回的大小
);

PROCESSINFOCLASS为枚举类型
enum PROCESSINFOCLASS{
  ...
  ProcessDebugPort // 7 调试端口
  ...
  ProcessDebugObjectHandle // 30 获取调试对象句柄,为空表示未处于调试状态
  ProcessDebugFlags // 31 检测调试标志为0 表示处于调试状态,1非调试状态
}

#include <Windows.h>
#include <iostream>

// 定义函数指针
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
	HANDLE           ProcessHandle, // 要查询的进程句柄
	DWORD ProcessInformationClass, // 查询进程信息的类型
	PVOID            ProcessInformation, // 输出参数 缓冲区
	ULONG            ProcessInformationLength, // 缓冲区大小
	PULONG           ReturnLength //实际返回的大小
);
// 定义函数指针对象
_NtQueryInformationProcess NtQueryInformationProcess;
int main() {
	// 这个函数需要动态调用 
	HMODULE hNtDll = LoadLibraryA("ntdll.dll");
	if (hNtDll == 0) {
		printf("加载dll失败");
		system("pause");
		return 0;
	}
	NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
	DWORD debugPort = 0;
	NtQueryInformationProcess(
		GetCurrentProcess(),
		7, // 获取调试端口信息
		&debugPort, // 输出参数
		sizeof(DWORD), // 输出参数大小
		NULL);
	if (debugPort != 0) {
		printf("当前程序正在被调戏\n");
	}else {
		printf("当前程序没有事情哩\n");
	}

	HANDLE debugHandle = 0;
	NtQueryInformationProcess(
		GetCurrentProcess(),
		30, // 获取句柄
		&debugHandle, // 输出参数 ProcessDebugObjectHandle
		sizeof(HANDLE), // 输出参数大小
		NULL);
	if (debugHandle != 0) {
		printf("当前程序正在被调戏\n");
	}else {
		printf("当前程序没有事情哩\n");
	}

	DWORD debugFlags = 0;
	NtQueryInformationProcess(
		GetCurrentProcess(),
		31, // 检测标志 ProcessDebugFlags 0为被调试
		&debugFlags, // 输出参数 ProcessDebugObjectHandle
		sizeof(DWORD), // 输出参数大小
		NULL);
	if (debugFlags == 0) {
		printf("当前程序正在被调戏\n");
	}else {
		printf("当前程序没有事情哩\n");
	}

	FreeLibrary(hNtDll);
	system("pause");
	return 0;
}

通过CloseHandle来实现反调试
CloseHandle试图关闭一个不存在的句柄,如果程序处于调试状态则会触发异常,否则没有反应。

__try {
	CloseHandle((HANDLE)0x1234);
}__except (1) {
	printf("CloseHandle 当前程序正在被调戏\n");
}

即如果是正常执行,是没有任何反应的,只有被调试了,才会报异常。

设置线程信息分离调试器:
通过为线程设置 ThreadHideFromDebugger,可以禁止线程产生调试事件。

typedef NTSTATUS(NTAPI* ZwSetInformationThread)(
	IN HANDLE ThreadHandle,
	IN THREAD_INFO_CLASS ThreadInformaitonClass,
	IN PVOID ThreadInformation,
	IN ULONG ThreadInformationLength
	);


typedef enum _THREADINFOCLASS {
	ThreadBasicInformation,
	ThreadTimes,
	ThreadPriority,
	ThreadBasePriority,
	ThreadAffinityMask,
	ThreadImpersonationToken,
	ThreadDescriptorTableEntry,
	ThreadEnableAlignmentFaultFixup,
	ThreadEventPair,
	ThreadQuerySetWin32StartAddress, 
	ThreadZeroTlsCell,
	ThreadPerformanceCount, 
	ThreadAmILastThread, 
	ThreadIdealProcessor,
	ThreadPriorityBoost,
	ThreadSetTlsArrayAddress,
	ThreadIsIoPending,
	ThreadHideFromDebugger // 17 
} THREAD_INFO_CLASS;

#include <Windows.h>
#include <iostream>

// 定义函数指针
typedef NTSTATUS(NTAPI* _ZwSetInformationThread)(
	HANDLE ThreadHandle, // 线程句柄
	DWORD ThreadInformaitonClass, // 枚举值类型 要设置的线程信息类型
	PVOID ThreadInformation, // 缓冲区
	ULONG ThreadInformationLength // 缓冲区大小
	);
_ZwSetInformationThread ZwSetInformationThread;
// 定义函数指针对象
int main() {
	// 这个函数需要动态调用 
	HMODULE hNtDll = LoadLibraryA("ntdll.dll");
	if (hNtDll == 0) {
		printf("加载dll失败");
		system("pause");
		return 0;
	}
	ZwSetInformationThread = (_ZwSetInformationThread)GetProcAddress(hNtDll,"ZwSetInformationThread");
	ZwSetInformationThread(GetCurrentThread(),17,NULL,NULL);
	printf("helloStarHook");
	FreeLibrary(hNtDll);
	system("pause");
	return 0;
}

反调试3

硬件断点反调试:

回顾硬件断点相关的寄存器
DR0~DR3保存硬件断点的地址

DR6为调试异常产生后显示的一些信息,DR7断点属性。

DR7解析:L0=1表示DR0这个地址断点有效,并且是一个局部断点。
L0~L3对应DR0~DR3的断点是否有效,L是局部断点。
G0~G3对应的DR0~DR3的断点是否有效,G是全局断点,Windows下没用
LEN0~LEN3对应的DR0~DR3的断点长度,00表示一个字节,01两个字节,11四个字节
RW0~RW3对应DR0~DR3断点的类型,00表示执行断点,01写入断点,11读写断点

#include <Windows.h>
#include <iostream>

EXCEPTION_DISPOSITION  WINAPI myExceptHandler(
	struct _EXCEPTION_RECORD* ExceptionRecord,
	PVOID EstablisherFrame,
	PCONTEXT pcontext,
	PVOID DispatcherContext
) {
	if (pcontext->Dr0 != 0 || pcontext->Dr1 != 0 || pcontext->Dr2 != 0 || pcontext->Dr3 != 0) {
		printf("当前程序被调戏了\n");
		ExitProcess(0);
	}
	return ExceptionContinueSearch;
}

int main() {
	CONTEXT context{0};
	context.ContextFlags = CONTEXT_DEBUG_REGISTERS; // 获取调试寄存器的信息
	GetThreadContext(GetCurrentThread(),&context);
	if (context.Dr0 != 0 || context.Dr1 != 0 || context.Dr2 != 0 || context.Dr3 != 0) {
		printf("当前程序被调戏了\n");
	}else {
		printf("当前程序没有被调试\n");
	}

	DWORD setProcAddr = (DWORD)myExceptHandler;
	__asm {
		push setProcAddr;
		mov eax, fs: [0] ;
		push eax;
		mov fs : [0] , esp;
	};

	system("pause");
	return 0;
}

反调试4-检测调试对象

我们可以通过查询父进程的ID判断是否被调试了
正常打开的程序的父进程是explorer.exe,通过查询父进程ID是否是explorer来判断是否被调试了。

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

typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation,
    ProcessQuotaLimits,
    ProcessIoCounters,
    ProcessVmCounters,
    ProcessTimes,
    ProcessBasePriority,
    ProcessRaisePriority,
    ProcessDebugPort,
    ProcessExceptionPort,
    ProcessAccessToken,
    ProcessLdtInformation,
    ProcessLdtSize,
    ProcessDefaultHardErrorMode,
    ProcessIoPortHandlers,          // Note: this is kernel mode only
    ProcessPooledUsageAndLimits,
    ProcessWorkingSetWatch,
    ProcessUserModeIOPL,
    ProcessEnableAlignmentFaultFixup,
    ProcessPriorityClass,
    ProcessWx86Information,
    ProcessHandleCount,
    ProcessAffinityMask,
    ProcessPriorityBoost,
    ProcessDeviceMap,
    ProcessSessionInformation,
    ProcessForegroundInformation,
    ProcessWow64Information,
    ProcessImageFileName,
    ProcessLUIDDeviceMapsEnabled,
    ProcessBreakOnTermination,
    ProcessDebugObjectHandle,
    ProcessDebugFlags,
    ProcessHandleTracing,
    ProcessIoPriority,
    ProcessExecuteFlags,
    ProcessResourceManagement,
    ProcessCookie,
    ProcessImageInformation,
    MaxProcessInfoClass             // MaxProcessInfoClass should always be the last enum
} PROCESSINFOCLASS;

typedef struct _PROCESS_BASIC_INFORMATION {
    NTSTATUS ExitStatus; 
    DWORD PebBaseAddress;
    ULONG_PTR AffinityMask;
    LONG BasePriority;
    ULONG_PTR UniqueProcessId;
    ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

// 定义函数指针
typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)(
	HANDLE  ProcessHandle, // 要查询的进程句柄
    PROCESSINFOCLASS	ProcessInformationClass, // 查询进程信息的类型
	PVOID   ProcessInformation, // 输出参数 缓冲区
	ULONG   ProcessInformationLength, // 缓冲区大小
	PULONG  ReturnLength //实际返回的大小
);

_NtQueryInformationProcess NtQueryInformationProcess;

int main() {
    // 所有程序都会加载ntdll.dll
    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
	NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(hNtDll,"NtQueryInformationProcess");
	// 查询ProcessBasicInformation可以拿到父进程 返回PROCESS_BASIC_INFORMATION
    PROCESS_BASIC_INFORMATION basicInfomation{0};
    NtQueryInformationProcess(
        GetCurrentProcess(),
        ProcessBasicInformation,
        &basicInfomation,
        sizeof(PROCESS_BASIC_INFORMATION),
        NULL);

    PROCESSENTRY32 pe32{ 0 };
    pe32.dwSize = sizeof(PROCESSENTRY32);
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
    if (Process32First(hSnapshot, &pe32)) { 
        do {
            if (wcscmp(pe32.szExeFile, L"explorer.exe")) {
                if (pe32.th32ProcessID != basicInfomation.InheritedFromUniqueProcessId) {
                    printf("当前进程被调戏了\n");
                }else {
                    printf("当前进程没被调戏\n");
                }
                break;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }
    CloseHandle(hSnapshot);
	system("pause");
	return 0;
}

当调试器调试某进程时会创建一个调试对象类型的内核对象,检测是否存在调试对象实现反调试。

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

//NtQueryObject  参数2
typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectTypesInformation,
    ObjectHandleFlagInformation,
    ObjectSessionInformation,
    MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
} OBJECT_INFORMATION_CLASS;

typedef NTSTATUS (NTAPI* _NtQueryObject)(
    __in HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,//查询对象类型枚举值
    __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,//输出结果缓冲区
    __in ULONG ObjectInformationLength,//缓冲区大小
    __out_opt PULONG ReturnLength//实际需要大小
);

_NtQueryObject NtQueryObject;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, * PUNICODE_STRING;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING TypeName;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    ULONG PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

typedef struct _OBJECT_TYPES_INFORMATION {
    ULONG numberOfTypesInfo; // 数组长度
    OBJECT_TYPE_INFORMATION typeinfo[1]; // 上面的结构体数组
}OBJECT_TYPES_INFORMATION,*POBJECT_TYPES_INFORMATION;

int main() {
    POBJECT_TYPES_INFORMATION typesInfo = NULL;

    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
    NtQueryObject = (_NtQueryObject)GetProcAddress(hNtDll, "NtQueryObject");
    // 查询所有内核对象类型信息
    CHAR* buff = new CHAR[0x4000];
    DWORD realWrite = 0;
    NTSTATUS ret = NtQueryObject(
        NULL,
        ObjectTypesInformation,
        buff,
        0x4000,
        &realWrite);
    if (ret == 0) {
        // 没有错误
        typesInfo = (POBJECT_TYPES_INFORMATION)buff;
        POBJECT_TYPE_INFORMATION typeInfo = typesInfo->typeinfo; // 默认指向首地址
        // 那么就拿到了所有的内核对象
        for (ULONG i = 0; i < typesInfo->numberOfTypesInfo; i++) {
            if (wcscmp(L"DebugObject", typeInfo->TypeName.Buffer)) {
                // 如果存在这样名称的内核对象 并且数量大于0
                if (typeInfo->TotalNumberOfObjects > 0) {
                    printf("当前进程被调戏了\n");
                }else {
                    printf("当前进程没被调戏\n");
                }
                break;
            }
            char* temp = (CHAR*)typeInfo->TypeName.Buffer; // 指向UNICODE_STRING最后一个字段
            temp += typeInfo->TypeName.MaximumLength; // buffer大小
            temp = temp + (DWORD)temp % 4; // 还要进行对齐
            // 这样就跳过了buffer
            DWORD data = *(DWORD*)temp;
            while (data == 0) { // 然后把接下来的0都跳过
                temp += 4;
                data = *(DWORD*)temp;
            }
            // 那么就找到了下一个结构体的首地址
            typeInfo = (POBJECT_TYPE_INFORMATION)temp;
        }
    }
	system("pause");
	return 0;
}

反调试5

附加进程反调试

附加进程流程:调试器调用DebugActiveProcess后,进程中有多少个线程创建,调试器会发送多少个CREATE_THREAD_DEBUG_EVENT事件给调试器.
加载了多少个dll会发送多少次LOAD_DLL_DEBUG_EVENT事件给调试器,调试器通过WaitForDebugEvent函数获取调试事件,并做相应处理。
处理完毕后调用ContinueDebugEvent使得程序继续执行,等待新的调试事件,操作系统恢复进程中的所有线程。
在第一个线程被恢复的时候会去调用Ntdll.dll中的DbgBreakPoint函数
而DbgBreakPoint其实就是一个int3,利用这一点可以hook掉DbgBreakPoint直接调用终止进程函数,使得附加进程的时候直接结束。

反调试6-CRC32

CRC32:
CRC的全称是循环冗余校验,作用是为了检测数据的准确性,完整性。

CRC32检测的原理:
程序被编译后,代码是固定的,我们在调试程序的时候,下段或者修改汇编指令都会影响它的CRC32值。
这个时候只需要检测最初的CRC32跟某一时刻的CRC32值是否一致就能判断出代码是否被修改了。

#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
const UINT32 table[] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

UINT32 make_crc32(unsigned char* buf, int nLength)
{
    if (nLength < 1)
        return 0xffffffff;

    UINT32 crc = 0;

    for (int i(0); i != nLength; ++i)
    {
        crc = table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
    }

    crc = crc ^ 0xffffffff;
    return crc;
}

void test1()
{
    int a = 0;
    a++;
    int b = a + 1;
    printf("11111111\n");
}
void test2()
{
    int a = 0;
    a++;
    int b = a + 1;
    printf("22222222\n");
}
void test3()
{
    int a = 0;
    a++;
    int b = a + 1;
    printf("33333333\n");
}
void test4()
{
    int a = 0;
    a++;
    int b = a + 1;
    printf("444444444\n");
}

int main() {
    // 那么我们肯定是对程序的代码段进行检测,代码段运行之后肯定就不会去修改了
    CHAR* buff = (CHAR*)GetModuleHandleA(NULL); // 获取当前的exe模块基址
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)buff;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + buff);
    PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
    PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)&pNtHeader->FileHeader;
    PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);

    // 前两个区段是代码段 直接偷懒
    UINT32 crc1 = make_crc32((unsigned char*)(pSectionHeader->VirtualAddress + buff), pSectionHeader->Misc.VirtualSize);
    pSectionHeader++;
    UINT32 crc2 = make_crc32((unsigned char*)(pSectionHeader->VirtualAddress + buff), pSectionHeader->Misc.VirtualSize);

    // 这样前两个区段都加密完毕
    printf("crc1=%x\n", crc1);
    printf("crc2=%x\n", crc2);
	system("pause");
	return 0;
}

反调试7-过CRC校验,虚拟机检测

过CRC检测思路:
CRC检测必然会取代码段数据进行计算CRC值,可以结合CE搜索哪里访问了该代码找到关键判断点。
找到计算crc值的call 修改为固定返回值,这样不管我们怎么改都是返回初始的值。

虚拟机的检测:
1.虚拟机环境检测:
检测wmtoolsd.exe进程
检测C:\\Program Files\\VMware\\VMware Tools\\wmtoolsd.exe文件是否存在
检测系统服务
2.in eax,dx指令
在真实的环境中会触发异常,而在虚拟机中不会
      push   edx
			push   ecx
			push   ebx				//保存环境
			mov    eax, 'VMXh'
			mov    ebx, 0			//将ebx清零
			mov    ecx, 10			//指定功能号,用于获取VMWare版本,为0x14时获取VM内存大小
			mov    edx, 'VX'		//端口号
			in     eax, dx			//从端口edx 读取VMware到eax
			cmp    ebx, 'VMXh'		//判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中
			setz [bResult]			//设置返回值
			pop    ebx				//恢复环境
			pop    ecx
			pop    edx
      
__try

	{

		__asm

		{

			push   edx

			push   ecx

			push   ebx				//保存环境

			mov    eax, 'VMXh'

			mov    ebx, 0			//将ebx清零

			mov    ecx, 10			//指定功能号,用于获取VMWare版本,为0x14时获取VM内存大小

			mov    edx, 'VX'		//端口号

			in     eax, dx			//从端口edx 读取VMware到eax

			cmp    ebx, 'VMXh'		//判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中

			setz [bResult]			//设置返回值

			pop    ebx				//恢复环境

			pop    ecx

			pop    edx

		}

	}__except (1)	//如果未处于VMware中,则触发此异常

	{

		printf("当前处于正常环境\n");
    return;
	}
  printf("当前处于虚拟机环境\n");

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

int main() {
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	PROCESSENTRY32 pe32{0};
	pe32.dwSize = sizeof(PROCESSENTRY32);
	if (Process32First(hSnapshot, &pe32)) {
		do {
			if (wcscmp(pe32.szExeFile, L"vmtoolsd.exe")) {
				printf("处于虚拟机环境\n");
				ExitProcess(0);
				break;
			}
		} while (Process32Next(hSnapshot, &pe32));
	}
	CloseHandle(hSnapshot);
	system("pause");
	return 0;
}

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

int main() {
	/*
		[in, optional] lpMachineName
		目标计算机的名称。如果指针为 NULL 或指向空字符串,则该函数将连接到本地计算机上的服务控制管理器。
		[in, optional] lpDatabaseName
		服务控制管理器数据库的名称。此参数应设置为 SERVICES_ACTIVE_DATABASE。如果为 NULL,则默认情况下打开SERVICES_ACTIVE_DATABASE数据库。
		[in] dwDesiredAccess
		对服务控制管理器的访问。有关访问权限的列表,请参阅服务安全性和访问权限。
	*/
	SC_HANDLE scScmanger = OpenSCManagerA(NULL, NULL ,SC_MANAGER_ALL_ACCESS);
	LPENUM_SERVICE_STATUSA lpService = NULL; // 查询结果
	lpService = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);
	DWORD pcbBytesNeeded = 0; // 如果缓冲区太小,这个保存还有多少没接收
	DWORD lpServicesReturned = 0; // 返回实际的服务数量
	DWORD resumeHanle = 0;
	// 遍历系统服务
	BOOL ret = EnumServicesStatusA(
		scScmanger,
		SERVICE_WIN32,// 要枚举的服务类型
		SERVICE_STATE_ALL, // 要枚举的服务状态 运行还是停止还说啥
		lpService, // 接收结果的缓冲区
		1024 * 64, // 缓冲区的大小
		&pcbBytesNeeded, // 如果缓冲区太小,这个保存还有多少没接收
		&lpServicesReturned, // 返回实际的服务数量
		&resumeHanle); // 首次调用此函数时,必须将此值设置为零。在输出时,如果函数成功,则此值为零。
	if (ret == FALSE)
	{
		return 0;
	}
	for (size_t i = 0; i < lpServicesReturned; i++) {
		if (strstr(lpService[i].lpDisplayName, "VMware") == 0) {
			// 如果包含 VMware
			MessageBoxA(0, "检测到虚拟机", "提示", MB_OK);
			break;
		}
	}
	if (scScmanger){
		CloseServiceHandle(scScmanger);
	}
	system("pause");
	return 0;
}

反调试-调试器原理

调试进程的两种情况:
1.打开进程
2.附加进程

打开进程的方式:
要调试进程需要调用CreateProcess和DEBUG_PROCESS标志来启动一个新进程作为调试目标。

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

int main(int argc,char *argv[]) {
	STARTUPINFOA si = {0};
	si.cb = sizeof(STARTUPINFOA);
	PROCESS_INFORMATION pi = {0};
	BOOL ret = CreateProcessA(NULL, argv[1], NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);

	system("pause");
	return 0;
}

附加方式:
通过DebugActiveProcess附加到进程
注意:无论是CreateProcess创建调试目标,还是通过DebugActiveProcess创建调试目标,在调试器与操作系统之间的交互方式都是相同的。

3.创建了调试进程后接下来就是死循环等待调试事件:
当调试进程的时候,被调试进程执行的一些操作将会通知给调试器,例如动态库的加载和卸载,线程创建与退出,异常信息等。
当这些事件需要被发送到调试器的时候,Windows内核将首先挂起进程中的所有线程,然后把发生的事件通知给调试器,并且等待来自调试器的处理。
调试器通过WaitForDebugEvent等待调试事件,调试事件被封装到DEBUG_EVENT结构体中,调试器需要做的就是循环接收调试事件,并处理结构体中传递过来的不同的调试信息。

这里有一点需要注意,当发送事件给调试器的时候,被调试进程会挂起。
直到调试器调用ContinueDebugEvent函数。

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

/*
	我们以调试的方式创建目标进程
	那么目标进程就不能再被其他调试器进程调试了
*/
int main() {
	// FateMouse.exe
	STARTUPINFOA si{0};
	si.cb = sizeof(STARTUPINFOA);
	PROCESS_INFORMATION pi{0};
	BOOL ret = CreateProcessA(
		"FateMouse.exe",
		NULL,
		NULL,
		FALSE,
		DEBUG_PROCESS,
		NULL,
		NULL,
		NULL,
		&si,
		&pi
	);
	if (!ret) {
		// 进程打开失败 
		MessageBoxA(NULL, "打开进程失败!", "提示", NULL);
	}
	while (true) {
		DEBUG_EVENT dev = {0};
		ret = WaitForDebugEvent(&dev,INFINITE); // 等待被调试进程发送事件
		if (ret) {
			// 有要处理的事件
			printf("EventCode=%d\n", dev.dwDebugEventCode); // 调试的事件类型
			// 处理完了,要恢复挂起的被调试线程
			ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId,DBG_CONTINUE);
		}
	}
	system("pause");
	return 0;
}

反调试9-过掉Strong Od反反附加

反附加:
DebugActiveProcess附加进程调试,最终会调用DebugBreakPoint使程序触发异常暂停下来。
这种方式很容易被干掉,我们来看一下在OD是无效的。
77294D10

DebugActiveProcess到底做了那些事情?
在目标进程创建一个线程,目标线程最后调用DebugBreakPoint触发异常暂停下来。
call ntdll.DbgUiIssueRemoteBreakin 这个远程断点是怎么实现的
创建一个远程线程完了之后,让这个线程从DbgUiRemoteBreakin的位置开始执行,远程线程去执行了一个功能,这个功能触发了int3断下来了。

//NtClose
DebugActiveProcess--->ntdll.DbgUiDebugActiveProcess-->call ntdll.DbgUiIssueRemoteBreakin
//push ntdll.DbgUiRemoteBreakin -->ntdll.ZwCreateThreadEx

DbgUiIssueRemoteBreakin//这个函数就相当于 创建了一个远程线程,完了之后呢,让这个线程从
DbgUiRemoteBreakin 这个函数执行。---》call ntdll.DbgBreakPoint DEBUG_Event

这就是为什么我们附加进程,可以让目标进程停下来。
调试器主要就是依靠DEBUG_EVENT


Strong OD 反反附加的原理:
LdrInitializeThunk

调用 NtCreateThreadEx 创建新线程,该函数最终执行会到ring0,ring0会先去调用LdrInitializeThunk
LdrInitializeThunk---》 call ntdll.ZwContinue重新回到内核,这时NtCreateThreadEx 才会返回。

Strong OD就是直接HOOK了LdrInitializeThunk,不让你进行返回到NtCreateThreadEx,后面的东西就不会执行了。

如何对抗?
调试器时附加到我们目标进程进行调试的,这也就意味着我们的目标进程时站先机的,我们可以在调试器hook之前先进行hook,这样达到反附加效果,轻松过掉strong od的反反附加

创建一个线程:这个线程做什么呢? 写一个循环 不停的检测,一旦检测到LdrInitializeThunk 这个函数的前五个字节被修改了,我们直接给他改回去。 

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

DWORD g_LdrInitializeFunAddr;
BYTE g_LdrInitializeFunCode[5];

/*
	调用 NtCreateThreadEx 创建新线程,该函数最终执行会到ring0,ring0会先去调用LdrInitializeThunk
	LdrInitializeThunk---》 call ntdll.ZwContinue重新回到内核,这时NtCreateThreadEx 才会返回。 

	而String OD就是HOOK了Ldr,让回不去NtCreateThreadEx。
	那么我们需要在Stoing OD HOOK之前先进行HOOK 保存,然后循环当检测到Strong OD HOOK了,我们马上修改
*/
BOOL SaveLdrInitializeCode() {
	DWORD funAddr = (DWORD)GetProcAddress(GetModuleHandleA("ntdll.dll"),"LdrInitializeThunk");
	g_LdrInitializeFunAddr = funAddr;
	DWORD oldProtect;
	VirtualProtect((LPVOID)funAddr, 0x5, PAGE_EXECUTE_READWRITE, &oldProtect);
	memcpy(g_LdrInitializeFunCode,(LPVOID)funAddr, 0x5); // 保存被HOOK前的 前5个字节
	VirtualProtect((LPVOID)funAddr, 0x5, oldProtect, &oldProtect);
	return TRUE;
}

DWORD WINAPI CheckLdrInitializeCodeProc(LPVOID args) {
	while (true) {
		DWORD newCode = *(DWORD*)g_LdrInitializeFunAddr; // 直接取四个字节出来比较 1个字节其实也行 5个也
		if (newCode != *(DWORD*)g_LdrInitializeFunCode) { // 然后进行四个字节的比较
			// 不同就表示当前被Strong od 给hook了,我们填充回去 原本的5个字节
			printf("发现Strong OD进行Hook操作\n");
			DWORD oldProtect;
			VirtualProtect((LPVOID)g_LdrInitializeFunAddr, 0x5, PAGE_EXECUTE_READWRITE, &oldProtect);
			memcpy((LPVOID)g_LdrInitializeFunAddr, g_LdrInitializeFunCode, 0x5);
			VirtualProtect((LPVOID)g_LdrInitializeFunAddr, 0x5, oldProtect, &oldProtect);
			break;
		}
	}
	return 0;
}

BOOL hookDebugBreakPoint() {
	PBYTE funAddr = (PBYTE)GetProcAddress(GetModuleHandleA("ntdll.dll"), "DbgBreakPoint");
	DWORD oldProtect;
	VirtualProtect(funAddr, 0x1, PAGE_EXECUTE_READWRITE, &oldProtect);
	funAddr[0] = 0xC3; // 把CC int 3 变为 C3 retn
	VirtualProtect(funAddr, 0x1, oldProtect, &oldProtect);
	return TRUE;
}

int main() {
	SaveLdrInitializeCode();
	hookDebugBreakPoint();

	HANDLE hThread = CreateThread(NULL, 0, CheckLdrInitializeCodeProc, NULL, 0, NULL);
	while (true)
	{
		printf("hello star hook\n");
		Sleep(3000); // 测试
	}
	system("pause");
	return 0;
}

流程:
  -修改了DbgBreakPoint这样就附加不上了
  -然后在Strong Od进行HOOK之前我们进行保存
  -开启一个线程监听Strong Od的Hook操作。发现被Hook掉了,修改回去
  -这样就可以重新执行到DbgBreakPoint继续附加不上

反调试11-环境检测-TF标志位

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

// 检测调试器窗口
BOOL checkDbgWindow() {
    HWND hWin = FindWindowA(NULL, "xdbg32");
	if (hWin) {
		printf("打开了x32Dbg\n");
		return TRUE;
	}
	return FALSE;
}

// 检测进程
BOOL checkDbgProcess() {
	HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
	if (!hSnapShot) {
		return FALSE;
	}
	PROCESSENTRY32 pe32{ 0 };
	pe32.dwSize = sizeof(PROCESSENTRY32);
	if (Process32First(hSnapShot, &pe32)) {
		do {
			if (wcscmp(pe32.szExeFile, L"xdbg32.exe") == 0) {
				printf("打开了x32Dbg\n");
				CloseHandle(hSnapShot);
				return TRUE;
			}
		}while(Process32Next(hSnapShot, &pe32));
	}
	CloseHandle(hSnapShot);
	return FALSE;
}

DWORD WINAPI CheckDbgProc(LPVOID args) {
	while (true) {
		if (checkDbgWindow()) {
			break;
		}
		if (checkDbgProcess()) {
			break;
		}
	}
	return 0;
}

int main(){
	HANDLE hThread = CreateThread(NULL, 0, CheckDbgProc, NULL, 0, NULL);
	while (true) {
		printf("test\n");
		Sleep(3000);
	}
	system("pause");
	return 0;
}

标志寄存器中的TF(Trap Flag)位,CPU在单步执行完一条指令后,如果检测到标志寄存器的TF位为1,则会产生一个int 1中断,然后再将TF置为0,后进行int 1中断后继续执行。

即TF为1,执行完我们完我们的nop指令以后,会产生异常,如果有调试器会交给调试器,调试器会继续执行。
如果没有调试器那么就发生异常了。

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

using namespace std;

void HaveStep()
{
	cout << "检测到了单步调试" << endl;
	ExitProcess(0);
}

void NoStep()
{
	cout << "没有检测到单步调试" << endl;
}

void checkTFflag() {
	DWORD addr = (DWORD)HaveStep;
	__try {
		__asm {
			pushfd; // 保存标志寄存器
			or dword ptr ss : [esp] , 0x100; // 把第八位TF标志位设置为1
			popfd; // 还原标志寄存器
			nop; // 设置为1就会进入单步执行, 单步执行我让他执行nop指令
			// 如果程序处于调试模式,直接退出进程
			jmp addr;
		};
	}__except (1) {
		NoStep();
	}
}

int main(){
	checkTFflag();
	system("pause");
	return 0;
}

只有单步执行才有效

反调试12-通过程序子窗口特点检测

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

using namespace std;

BOOL CALLBACK enumWindowProc(HWND hwnd, LPARAM lParam) {
	CHAR buff[0x100] = { 0 };
	SendMessageA(hwnd, WM_GETTEXT, 0x100, (LPARAM)buff);
	printf("%s\n",buff);
	return TRUE; // 继续枚举
}
int main(){
	// 获取桌面窗口
	HWND hDeskTop = GetDesktopWindow();

	HWND hDeskChild = GetWindow(hDeskTop,GW_CHILD); // 获取桌面窗口的子窗口
	while (hDeskChild) {
		EnumChildWindows(hDeskChild, enumWindowProc, NULL); // 枚举所有子窗口
		hDeskChild = GetWindow(hDeskChild, GW_HWNDNEXT); // 获取同级别的下一个窗口
	}
	system("pause");
	return 0;
}

关闭其他进程句柄【解决游戏限制多开之类的】

步骤:
查看某进程拥有那些句柄
1.Ntdll的NtQueryInformationProcess函数查询指定进程句柄信息。
2.NtQueryObject获取某个句柄信息,遍历句柄,判断句柄是否为有效句柄:DuplicaeHandle函数返回成功表示有效
3.获取句柄名称判断是否是我们要关闭的句柄
4.DulicateHandle复制句柄并关闭源句柄,最后关闭复制到句柄。

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

using namespace std;

typedef enum _PROCESSINFOCLASS {
    ProcessBasicInformation,
    ProcessQuotaLimits,
    ProcessIoCounters,
    ProcessVmCounters,
    ProcessTimes,
    ProcessBasePriority,
    ProcessRaisePriority,
    ProcessDebugPort,
    ProcessExceptionPort,
    ProcessAccessToken,
    ProcessLdtInformation,
    ProcessLdtSize,
    ProcessDefaultHardErrorMode,
    ProcessIoPortHandlers,          // Note: this is kernel mode only
    ProcessPooledUsageAndLimits,
    ProcessWorkingSetWatch,
    ProcessUserModeIOPL,
    ProcessEnableAlignmentFaultFixup,
    ProcessPriorityClass,
    ProcessWx86Information,
    ProcessHandleCount,
    ProcessAffinityMask,
    ProcessPriorityBoost,
    ProcessDeviceMap,
    ProcessSessionInformation,
    ProcessForegroundInformation,
    ProcessWow64Information,
    ProcessImageFileName,
    ProcessLUIDDeviceMapsEnabled,
    ProcessBreakOnTermination,
    ProcessDebugObjectHandle,
    ProcessDebugFlags,
    ProcessHandleTracing,
    ProcessIoPriority,
    ProcessExecuteFlags,
    ProcessResourceManagement,
    ProcessCookie,
    ProcessImageInformation,
    MaxProcessInfoClass
} PROCESSINFOCLASS;

typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectTypesInformation,
    ObjectHandleFlagInformation,
    ObjectSessionInformation,
    MaxObjectInfoClass
} OBJECT_INFORMATION_CLASS;

typedef struct _UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
#ifdef MIDL_PASS
    [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
#else // MIDL_PASS
    PWSTR  Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

typedef struct _OBJECT_NAME_INFORMATION {               // ntddk wdm nthal
    UNICODE_STRING Name;                                // ntddk wdm nthal
} OBJECT_NAME_INFORMATION, * POBJECT_NAME_INFORMATION;   // ntddk wdm nthal

typedef NTSTATUS(NTAPI* _NtQueryInformationProcess)(
    __in HANDLE ProcessHandle,
    __in PROCESSINFOCLASS ProcessInformationClass,
    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
    __in ULONG ProcessInformationLength,
    __out_opt PULONG ReturnLength
    );
typedef NTSTATUS(NTAPI* _NtQueryObject)(
    __in HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
    __in ULONG ObjectInformationLength,
    __out_opt PULONG ReturnLength
    );

_NtQueryInformationProcess NtQueryInformationProcess;
_NtQueryObject NtQueryObject;

int main(){
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 15192);

    HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
    NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
    NtQueryObject = (_NtQueryObject)GetProcAddress(hNtDll, "NtQueryObject");

    // 遍历所有的句柄信息
    DWORD handleCount = 0;
    NtQueryInformationProcess(
        hProcess, // 要查询的进程句柄
        ProcessHandleCount, // 查询句柄数量
        &handleCount,
        sizeof(DWORD),
        NULL
    );
    DWORD handleData = 4;
    HANDLE copyHandleData;
    OBJECT_NAME_INFORMATION *ni = (POBJECT_NAME_INFORMATION)malloc(0x1000);
    // 句柄他都是4的整数倍,一个进程的有效句柄,我们可以认为是4开始的。
    for (size_t i = 0; i < handleCount; handleData+=4) {
        // 判断句柄是否有效
        BOOL ret = DuplicateHandle(
            hProcess, // 从哪个进程复制
            (HANDLE)handleData,  // 要复制的进程句柄
            GetCurrentProcess(), // 复制到哪个进程
            &copyHandleData, // 复制到哪个变量去
            0, // 填0就好
            FALSE,// 是否可以被继承
            DUPLICATE_SAME_ACCESS // 原样复制
        );
        if (ret) {
            NtQueryObject(
                copyHandleData, // 要获取的句柄
                ObjectNameInformation, // 获取名称信息
                ni, // 缓冲区
                0x1000, // 缓冲区大小
                NULL
            );
            printf("句柄名称:%ls\n",ni->Name.Buffer);
            // 判断是不是我们要的句柄
            if (ni->Name.Length != 0 && wcscmp(ni->Name.Buffer, L"\\Sessions\\11\\BaseNamedObjects\\FateMouse")) {
                DuplicateHandle(
                    hProcess, // 从哪个进程复制
                    (HANDLE)handleData,  // 要复制的进程句柄
                    GetCurrentProcess(), // 复制到哪个进程
                    &copyHandleData, // 复制到哪个变量去
                    0, // 填0就好
                    FALSE,// 是否可以被继承
                    DUPLICATE_CLOSE_SOURCE // 关闭原句柄
                );
                CloseHandle(copyHandleData); // 关闭复制过来的句柄
                break;
            }
            CloseHandle(copyHandleData); // 关闭复制过来的句柄
            i++; // 如果是有效的句柄 我们才找下一个句柄
        }else {
            CloseHandle(copyHandleData);
            continue;
        }
    }

	system("pause");
	return 0;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言最近随着计算机的发展,软件日益更新,有很多公司发布的产品遭到篡改和破解,在总众多年的历史种逐渐形成了软件保护与软件破解之间的对抗产生了软件逆向工程这本门技术将在如下的课程讲解各种软件逆向知识,软件保护知识,已经破解脱壳调试知识,为初期学软件逆向不懂而又迷茫的同学门指明一条道路此套课堂能有效帮助同学们解决软件逆向中所遇到的大部分问题大纲软件逆向工程高级班分为调试篇汇编篇算法篇补丁篇HOOK篇将在如上这几篇对软件逆向的各个方面进行详解,包括网络验证的分析思路,封包算法的提取,以及各种软件保护技术,无论哪一篇都会从诸多个方面的细节进行详解调试篇:分为PEB,时间校验,CRC,NtQuery函数,内核过渡等知识要领与诸多方面的综合性详解,细节分为每一节课,每一节课目标清晰无比,每一节深入精髓进行讲解!汇编篇:一个程序编译完成之后是如何通过在计算机运行起来的,其中少不了底层知识的汇编指令,汇编篇中将深入浅出的带领同学们对MASM32SDK的一套汇编库中开发程序,熟悉汇编的原理,如何运用汇编写出一套花指令,并且去除指令,方便以后的算法学习以及为今后的学习打下坚实的基础算法篇:随着编程语言的发展,编程语言的标准也在发展,一些编译器善于运用数学的手法,对程序进行各种优化,然后我们进行分析,我们得需要一步步还原这个优化或清晰了解这个优化才有可能掌握这个数学模型优化,进一步还原代码,算法篇知识要领将在优化,技巧这方面表现的玲离尽致!此篇会带领同学们分析多个语言的算法,包括C/C++算法还原代码还原易语言代码还原 算分开库的实现,制作自己的第一个注册机等!补丁篇:说到补丁,同学们可能第一个想到的就是对方一些网络验证所用到的技巧,我的课程这一方面虽有涉及,但是补丁技术远远不止这一点,我的课程会详解更多的补丁知识原理,包括什么是补丁,补丁的真正概念,前辈们是如何善用补丁对程序的漏洞进行修补损坏的程序。将在此篇一一介绍HOOK篇:详细详解了各种HOOK的原理以及实现代码,包括网上流行所说的超级HOOK,COMHOOK,协议HOOK,代理中转等方法,怎么定位关键位置,环境的保存知识要领,hook关键的注意事项,为自己以后做hook行业打下坚实的基础课程每一个细节讲究的深入骨髓,通俗易懂的学习方式,全程贯彻原理,软件逆向中必不可少少的教程!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值