Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >运用Capstone实现64位进程钩子扫描

运用Capstone实现64位进程钩子扫描

原创
作者头像
王 瑞
发布于 2024-08-14 01:50:42
发布于 2024-08-14 01:50:42
23100
代码可运行
举报
运行总次数:0
代码可运行

进程钩子扫描是一种安全技术和分析方法,用于检测和分析进程内的指令是否被篡改或注入了恶意功能。钩子(Hook)技术允许开发人员在执行特定系统调用或函数时插入自定义代码。虽然进程钩子在调试和软件功能扩展中发挥了重要作用,但该技术也可以被恶意软件用来拦截和修改程序行为,从而隐藏其活动或进行其他恶意操作。本章将通过Capstone引擎实现64位进程钩子的扫描,读者可使用此段代码检测目标进程内是否被挂了钩子。

通过进程钩子扫描,安全研究人员和开发人员可以检测进程中是否存在未授权的钩子,并分析这些钩子的行为。这有助于识别和防止恶意软件的活动,确保系统和应用程序的完整性和安全性。

在编写代码之前,读者需要自行下载并配置Capstone反汇编引擎,配置参数如下所示;

在之前的PeView命令行解析工具中笔者介绍了如何扫描32位进程内的钩子,由于32位进程需要重定位所以在扫描时需要考虑到对内存地址的修正,而64位进程则无需考虑重定位的问题,其钩子扫描原理与32位保持一致,均通过将磁盘和内存中的代码段进行反汇编,并逐条比较它们的机器码和反汇编结果。如果存在差异,则表示该代码段在内存中被篡改或挂钩。

定义头文件

首先引入capstone.h头文件,并引用capstone64.lib静态库,通过定义PeTextInfo来存储每个PE文件中节的文件偏移及大小信息,通过ModuleInfo用于存放进程内的模块信息,而DisassemblyInfo则用来存放反汇编信息,底部则定义PE结构的全局变量用于存储头指针。

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
#include <windows.h>
#include <TlHelp32.h>
#include <tchar.h>
#include <iostream>
#include <atlconv.h>
#include <vector>
#include <inttypes.h>
#include <capstone/capstone.h>

#pragma comment(lib,"capstone64.lib")

using namespace std;

// 存放PE信息段
struct PeTextInfo
{
	DWORD64 virtualAddress;    // 节区在内存的偏移
	DWORD64 pointerToRawData;  // 节区在文件中的偏移
	DWORD64 size;              // 大小
};

// 存放进程内所有模块信息
typedef struct
{
	char modulePath[256];      // 模块路径
	char moduleName[128];      // 模块名
	long long moduleBase;      // 模块基址
}ModuleInfo;

// 存放反汇编数据
typedef struct
{
	int opCodeSize;            // 机器码长度
	int opStringSize;          // 反汇编长度
	unsigned long long address;// 相对地址
	unsigned char opCode[16];  // 机器码
	char opString[256];        // 反汇编
}DisassemblyInfo;

// 全局PE结构
IMAGE_DOS_HEADER* dosHeader;              // DOS头
IMAGE_NT_HEADERS* ntHeader;               // NT头
IMAGE_FILE_HEADER* fileHeader;            // 标准PE头
IMAGE_OPTIONAL_HEADER64* optionalHeader;  // 可选PE头
IMAGE_SECTION_HEADER* sectionHeader;      // 节表

进程与线程

在进程与线程处理模块中,我们定义了三个函数:GetProcessHandleByNameGetProcessIDByNameGetModuleInfoByProcessNameGetProcessHandleByName函数接收一个进程名并返回该进程的句柄,方便后续的进程操作;GetProcessIDByName函数通过进程名获取其对应的PID(进程标识符),用于标识特定进程;GetModuleInfoByProcessName函数接收一个进程名并返回该进程内所有模块的信息,包括模块路径、模块名和模块基址,便于对进程内的模块进行分析和处理。

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
// -----------------------------------------------------------------------------------
// 进程线程部分
// -----------------------------------------------------------------------------------

// 通过进程名获取进程句柄
// 参数: 
//   processName - 进程名
// 返回值:
//   进程句柄
HANDLE GetProcessHandleByName(PCHAR processName)
{
	// 初始化进程快照
	PROCESSENTRY32 processEntry;
	processEntry.dwSize = sizeof(PROCESSENTRY32);

	// 获得快照句柄
	HANDLE processSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

	// 获取第一个进程
	Process32First(processSnap, &processEntry);
	do
	{
		USES_CONVERSION;
		if (strcmp(processName, W2A(processEntry.szExeFile)) == 0)
		{
			// 关闭快照句柄,避免内存泄漏
			CloseHandle(processSnap);

			// 返回句柄
			return OpenProcess(PROCESS_ALL_ACCESS, FALSE, processEntry.th32ProcessID);
		}
	} while (Process32Next(processSnap, &processEntry));

	// 关闭快照句柄,避免内存泄漏
	CloseHandle(processSnap);
	return (HANDLE)NULL;
}

// 根据进程名获取PID
// 参数: 
//   processName - 进程名
// 返回值:
//   进程ID
DWORD64 GetProcessIDByName(LPCTSTR processName)
{
	DWORD64 processID = 0xFFFFFFFF;
	HANDLE snapshot = INVALID_HANDLE_VALUE;
	PROCESSENTRY32 processEntry;
	processEntry.dwSize = sizeof(PROCESSENTRY32);

	snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
	Process32First(snapshot, &processEntry);
	do
	{
		if (!_tcsicmp(processName, (LPCTSTR)processEntry.szExeFile))
		{
			processID = processEntry.th32ProcessID;
			break;
		}
	} while (Process32Next(snapshot, &processEntry));

	CloseHandle(snapshot);
	return processID;
}

// 获取进程内所有模块信息
// 参数: 
//   processName - 进程名
// 返回值:
//   包含模块信息的向量
std::vector<ModuleInfo> GetModuleInfoByProcessName(CHAR* processName)
{
	// 读取进程中的模块信息
	MODULEENTRY32 moduleEntry;
	USES_CONVERSION;
	DWORD64 processID = GetProcessIDByName(A2W(processName));

	// 存放模块路径
	std::vector<ModuleInfo> moduleInfos = {};

	// 在使用这个结构前,先设置它的大小
	moduleEntry.dwSize = sizeof(MODULEENTRY32);

	// 获取模块快照
	HANDLE moduleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);

	// INVALID_HANDLE_VALUE表示无效的句柄
	if (moduleSnap == INVALID_HANDLE_VALUE)
	{
		return{};
	}

	BOOL hasMoreModules = Module32First(moduleSnap, &moduleEntry);   // 获取第一个模块信息
	char* modulePath = NULL;                                         // 模块路径
	char* moduleName = NULL;                                         // 模块名
	DWORD64 moduleBase = NULL;                                       // 模块基址

	while (hasMoreModules)
	{
		ModuleInfo moduleInfo;

		USES_CONVERSION;

		// W2A 将wchar转ascii
		modulePath = W2A(moduleEntry.szExePath);
		moduleBase = (DWORD64)moduleEntry.modBaseAddr;
		moduleName = W2A(moduleEntry.szModule);

		// printf("模块路径: %s -> 模块基地址: %x -> 模块名: %s \n", ModulePath, ModuleBase, ModuleName);

		strcpy_s(moduleInfo.modulePath, modulePath);
		strcpy_s(moduleInfo.moduleName, moduleName);
		moduleInfo.moduleBase = moduleBase;

		// 放入容器内
		moduleInfos.push_back(moduleInfo);
		hasMoreModules = Module32Next(moduleSnap, &moduleEntry);
	}

	CloseHandle(moduleSnap);
	return moduleInfos;
}

PE文件操作

如下代码实现了PE(Portable Executable)文件的读取、解析和扩展功能。我们定义了三个主要函数:ReadPEFile用于从磁盘读取PE文件数据,ParsePEHeaders用于解析PE文件的头信息,ExpandPEImageBuffer用于将PE文件扩展为内存中加载后的形式,并复制文件中的各个节(section)到内存中。最后,GetCodeSectionInfo函数获取了PE文件中代码段的起始地址和大小信息。

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
// -----------------------------------------------------------------------------------
// PE文件读写部分
// -----------------------------------------------------------------------------------

// 读取硬盘PE文件数据
// 参数: 
//   filePath - 文件路径
//   fileBuffer - 文件缓冲区指针
// 返回值:
//   文件大小
DWORD64 ReadPEFile(LPSTR filePath, LPVOID* fileBuffer)
{
	FILE* file = NULL;

	fopen_s(&file, filePath, "rb");
	if (file == NULL)
	{
		return 0;
	}
	else
	{
		// 计算文件大小
		fseek(file, 0, SEEK_END);
		long long fileSize = ftell(file);
		fseek(file, 0, SEEK_SET);

		// 开辟指定大小的内存
		LPVOID buffer = malloc(sizeof(char) * fileSize);
		if (buffer == NULL)
		{
			fclose(file);
			return 0;
		}
		// 将文件数据拷贝到缓冲区
		size_t bytesRead = fread(buffer, sizeof(char), fileSize, file);
		if (!bytesRead)
		{
			free(buffer);
			fclose(file);
			return 0;
		}
		*fileBuffer = buffer;
		buffer = NULL;

		fclose(file);
		return fileSize;
	}
	return 0;
}

// 读取PE头信息
// 参数: 
//   fileBuffer - 文件缓冲区指针
// 返回值:
//   成功返回1,失败返回0
DWORD64 ParsePEHeaders(LPVOID fileBuffer)
{
	if (fileBuffer == NULL)
	{
		// 缓冲区指针无效
		return 0;
	}

	// 判断是否是有效的MZ标记
	if (*((PWORD)fileBuffer) != IMAGE_DOS_SIGNATURE)
	{
		return 0;
	}

	dosHeader = (IMAGE_DOS_HEADER*)fileBuffer;

	// 判断是否是有效的pe标志
	if (*((PDWORD)((DWORD64)fileBuffer + dosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
	{
		return 0;
	}

	ntHeader = (IMAGE_NT_HEADERS*)((DWORD64)fileBuffer + dosHeader->e_lfanew);                                       // NT头赋值
	fileHeader = (IMAGE_FILE_HEADER*)((DWORD64)ntHeader + 4);                                                        // 标准PE头赋值
	optionalHeader = (IMAGE_OPTIONAL_HEADER64*)((DWORD64)fileHeader + IMAGE_SIZEOF_FILE_HEADER);                     // 可选PE头赋值,标准PE头地址+标准PE头大小
	sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD64)optionalHeader + fileHeader->SizeOfOptionalHeader);             // 第一个节表 可选PE头地址+可选PE头大小
	return 1;
}

// 拉伸PE结构
// 参数: 
//   fileBuffer - 硬盘状态的PE数据指针
//   imageBuffer - 用来存放拉伸后的PE数据的指针
// 返回值:
//   PE镜像大小
DWORD64 ExpandPEImageBuffer(LPVOID fileBuffer, LPVOID* imageBuffer)
{
	if (fileBuffer == NULL)
	{
		return 0;
	}

	// 申请ImageBuffer所需的内存空间
	LPVOID buffer = malloc(sizeof(char) * optionalHeader->SizeOfImage);
	if (buffer == NULL)
	{
		return 0;
	}

	memset(buffer, 0, optionalHeader->SizeOfImage);              // 将空间初始化为0
	memcpy(buffer, fileBuffer, optionalHeader->SizeOfHeaders);  // 把头+节表+对齐的内存复制过去

	// 复制节
	for (int i = 0; i < fileHeader->NumberOfSections; i++)
	{
		buffer = (LPVOID)((DWORD64)buffer + (sectionHeader + i)->VirtualAddress);                     // 定位这个节内存中的偏移
		fileBuffer = (LPVOID)((DWORD64)fileBuffer + (sectionHeader + i)->PointerToRawData);           // 定位这个节在文件中的偏移
		memcpy(buffer, fileBuffer, (sectionHeader + i)->SizeOfRawData);                               // 复制节在文件中所占的内存过去
		buffer = (LPVOID)((DWORD64)buffer - (sectionHeader + i)->VirtualAddress);                     // 恢复到起始位置
		fileBuffer = (LPVOID)((DWORD64)fileBuffer - (sectionHeader + i)->PointerToRawData);           // 恢复到起始位置
	}

	*imageBuffer = buffer;
	buffer = NULL;
	return optionalHeader->SizeOfImage;
}

// 获取本程序代码段在内存中的起始地址和大小
// 参数: 
//   textInfo - 存放代码段信息的结构体指针
// 返回值:
//   代码段的数量
DWORD64 GetCodeSectionInfo(PeTextInfo* textInfo)
{
	int length = 0;
	for (int i = 0; i < fileHeader->NumberOfSections; i++)
	{
		// 判断是否是可执行的代码
		if (((sectionHeader + i)->Characteristics & 0x20000000) == 0x20000000)
		{
			(textInfo + length)->virtualAddress = (sectionHeader + i)->VirtualAddress;
			(textInfo + length)->pointerToRawData = (sectionHeader + i)->PointerToRawData;
			(textInfo + length)->size = (sectionHeader + i)->SizeOfRawData;
			length++;
		}
	}
	return length;
}

反汇编与扫描

反汇编部分通过定义DisassembleCode函数,该函数接收一个起始地址及代码长度,当执行结束后会将反汇编结果放入到DisassemblyInfo容器内返回给用户,具体的反汇编实现细节可自行参考代码学习。

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
// -----------------------------------------------------------------------------------
// 反汇编部分
// -----------------------------------------------------------------------------------

// 反汇编字符串
// 参数: 
//   startOffset - 起始地址
//   size - 代码大小
// 返回值:
//   包含反汇编信息的向量

std::vector<DisassemblyInfo> DisassembleCode(unsigned char *startOffset, int size)
{
	std::vector<DisassemblyInfo> disassemblyInfos = {};

	csh handle;
	cs_insn *insn;
	size_t count;

	// 打开句柄
	if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK)
	{
		return{};
	}

	// 反汇编代码,地址从0x0开始,返回总条数
	count = cs_disasm(handle, (unsigned char *)startOffset, size, 0x0, 0, &insn);

	if (count > 0)
	{
		DWORD index;

		// 循环反汇编代码
		for (index = 0; index < count; index++)
		{
			// 清空
			DisassemblyInfo disasmInfo;
			memset(&disasmInfo, 0, sizeof(DisassemblyInfo));

			// 循环拷贝机器码
			for (int x = 0; x < insn[index].size; x++)
			{
				disasmInfo.opCode[x] = insn[index].bytes[x];
			}

			// 拷贝地址长度
			disasmInfo.address = insn[index].address;
			disasmInfo.opCodeSize = insn[index].size;

			// 拷贝反汇编指令
			strcpy_s(disasmInfo.opString, insn[index].mnemonic);
			strcat_s(disasmInfo.opString, " ");
			strcat_s(disasmInfo.opString, insn[index].op_str);

			// 得到反汇编长度
			disasmInfo.opStringSize = (int)strlen(disasmInfo.opString);

			disassemblyInfos.push_back(disasmInfo);
		}
		cs_free(insn, count);
	}
	else
	{
		return{};
	}
	cs_close(&handle);
	return disassemblyInfos;
}

最后我们在主函数中来实现反汇编比对逻辑,首先我们分别指定一个磁盘文件路径并将其放入到fullPath变量内,然后通过GetModuleInfoByProcessName得到进程内的所有加载模块信息,并对比进程内模块是否为Win32Project.exe也就是进程自身,当然此处也可被替换为例如user32.dll等模块,当磁盘与内存被读入后,通过ParsePEHeaders解析PE头信息,并将PE文件通过ExpandPEImageBuffer拉伸到内存中模拟加载后的状态。

随后,通过GetCodeSectionInfo获取代码节的地址和大小,将磁盘和内存中的代码段数据分别读取到缓冲区中。最后,通过Capstone反汇编库对磁盘和内存中的代码段进行反汇编,并逐条memcmp对比反汇编指令,以检测代码是否被篡改。整个过程包括文件读取、内存解析、反汇编和数据对比,最后输出检测结果并释放分配的内存资源。

代码语言:c
代码运行次数:0
运行
AI代码解释
复制
int main(int argc, char *argv[])
{
	DWORD64 fileSize = 0;
	LPVOID fileBuffer = NULL;

	// 从完整路径中获取文件名
	CHAR fullPath[256] = { 0 };
	CHAR fileName[64] = { 0 }, *p = NULL;

	strcpy_s(fullPath, "d:\\Win32Project.exe");
	strcpy_s(fileName, (p = strrchr(fullPath, '\\')) ? p + 1 : fullPath);

	// 打开进程
	HANDLE processHandle = GetProcessHandleByName(fileName);

	// 循环输出所有模块信息
	std::vector<ModuleInfo> moduleInfos = GetModuleInfoByProcessName(fileName);

	for (int i = 0; i < moduleInfos.size(); i++)
	{
		if (strcmp(moduleInfos[i].moduleName, "Win32Project.exe") == 0)
		{
			printf("[*] 模块基地址: 0x%I64X | 模块路径: %s \n", moduleInfos[i].moduleBase, moduleInfos[i].modulePath);

			// 读取磁盘PE文件
			fileSize = ReadPEFile(moduleInfos[i].modulePath, &fileBuffer);

			// 解析PE头
			DWORD64 ref = ParsePEHeaders(fileBuffer);

			// 拉伸PE
			LPVOID imageBuffer = NULL;
			DWORD64 sizeOfImage = ExpandPEImageBuffer(fileBuffer, &imageBuffer);

			// 获取.text节地址
			PeTextInfo textInfo;
			DWORD64 textSectionCount = GetCodeSectionInfo(&textInfo);

			// 读入磁盘数据
			unsigned char *fileTextBuffer = NULL;
			fileTextBuffer = (unsigned char *)malloc((textInfo.size));
			memcpy(fileTextBuffer, (unsigned char *)((DWORD64)imageBuffer + textInfo.virtualAddress), textInfo.size);

			// 读入内存数据
			unsigned char *memoryTextBuffer = NULL;
			DWORD64 protectTemp = NULL;

			DWORD64 moduleBase = moduleInfos[i].moduleBase;

			memoryTextBuffer = (unsigned char *)malloc(textInfo.size);

			for (int j = 0; j < textInfo.size; j++)
			{
				ReadProcessMemory(processHandle, (LPVOID)(moduleBase + textInfo.virtualAddress), memoryTextBuffer, sizeof(char) * textInfo.size, NULL);
			}

			// 开始反汇编
			std::vector<DisassemblyInfo> fileDisassembly = DisassembleCode(fileTextBuffer, textInfo.size);
			std::vector<DisassemblyInfo> memoryDisassembly = DisassembleCode(memoryTextBuffer, textInfo.size);

			for (int k = 0; k < fileDisassembly.size(); k++)
			{
				printf("0x%I64X | ", moduleBase + memoryDisassembly[k].address);
				printf("文件汇编: %-45s | ", fileDisassembly[k].opString);
				printf("内存汇编: %-45s | ", memoryDisassembly[k].opString);

				// 开始对比
				if (memcmp(fileDisassembly[k].opCode, memoryDisassembly[k].opCode, fileDisassembly[k].opCodeSize) != 0)
				{
					// 被挂钩
					printf("文件=> ");
					for (int l = 0; l < fileDisassembly[k].opCodeSize; l++)
					{
						printf("0x%02X ", fileDisassembly[k].opCode[l]);
					}
					printf(" 内存=> ");
					for (int m = 0; m < memoryDisassembly[k].opCodeSize; m++)
					{
						printf("0x%02X ", memoryDisassembly[k].opCode[m]);
					}
				}
				printf("\n");
			}

			// 释放
			imageBuffer = NULL;
			free(fileBuffer);
			free(fileTextBuffer);
			free(memoryTextBuffer);
		}
	}

	system("pause");
	return 0;
}

为了测试扫描效果,我们可以启动一个64位应用程序,此处为Win32Project.exe进程,通过x64dbg附加,并跳转到Win32Project.exe的程序领空,如下图所示;

此时我们随意找一处位置,这里就选择00007FF6973110E6处,并将其原始代码由int3修改为nop长度为6字节,如下图所示;

至此,我们编译并运行lyshark.exe程序,此时则可输出Win32Project.exe进程中的第一个模块也就是Win32project.exe的挂钩情况,输出效果如下图所示;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
7.7 实现进程内存读写
内存进程读写可以让我们访问其他进程的内存空间并读取或修改其中的数据。这种技术通常用于各种调试工具、进程监控工具和反作弊系统等场景。在Windows系统中,内存进程读写可以通过一些API函数来实现,如OpenProcess、ReadProcessMemory和WriteProcessMemory等。这些函数提供了一种通用的方式来访问其他进程的内存,并且可以用来读取或写入不同类型的数据,例如整数、字节集、浮点数等。
王 瑞
2023/09/25
6010
7.7 实现进程内存读写
驱动开发:封装x64内核驱动读写
内核级别的内存读写可用于绕过各类驱动保护,从而达到强制读写对端内存的目的,本人闲暇之余封装了一个驱动级的内核读写接口,使用此接口可实现对远程字节,字节集,整数,浮点数,多级偏移读写等。
王 瑞
2022/11/20
2.8K0
驱动开发:封装x64内核驱动读写
C/C++ Capstone 引擎源码编译
Capstone 是一个轻量级的多平台、多架构的反汇编框架。Capstone 旨在成为安全社区中二进制分析和反汇编的终极反汇编引擎。Capstone的编译非常简单只需要一步即可轻松得到对应的Lib库文件,如下将介绍该引擎如何被编译,以及简单的测试编译。
王 瑞
2022/12/28
5790
C/C++ Capstone 引擎源码编译
简单的加密壳实现
用到的API: GetModuleHandle GetModuleFileName
HACK学习
2021/06/24
1.5K0
简单的加密壳实现
LyScript 实现应用层钩子扫描器
Capstone 是一个轻量级的多平台、多架构的反汇编框架,该模块支持目前所有通用操作系统,反汇编架构几乎全部支持,本篇文章将运用LyScript插件结合Capstone反汇编引擎实现一个钩子扫描器。
王 瑞
2022/12/28
2340
LyScript 实现应用层钩子扫描器
C/C++遍历某进程的模块
对于应用层,直接使用 fs/gs 寄存器获取 peb 地址,对于内核层,使用 _EPROCESS + 偏移的方式获取 peb:
王 瑞
2022/12/28
4440
C/C++遍历某进程的模块
10.4 认识Capstone反汇编引擎
Capstone 是一款开源的反汇编框架,目前该引擎支持的CPU架构包括x86、x64、ARM、MIPS、POWERPC、SPARC等,Capstone 的特点是快速、轻量级、易于使用,它可以良好地处理各种类型的指令,支持将指令转换成AT&T汇编语法或Intel汇编语法等多种格式。Capstone的库可以集成到许多不同的应用程序和工具中,因此被广泛应用于反汇编、逆向工程、漏洞分析和入侵检测等领域,著名的比如IDA Pro、Ghidra、Hopper Disassembler等调试器都在使用该引擎。
王 瑞
2023/10/06
7680
10.4 认识Capstone反汇编引擎
4.7 x64dbg 应用层的钩子扫描
所谓的应用层钩子(Application-level hooks)是一种编程技术,它允许应用程序通过在特定事件发生时执行特定代码来自定义或扩展其行为。这些事件可以是用户交互,系统事件,或者其他应用程序内部的事件。应用层钩子是在应用程序中添加自定义代码的一种灵活的方式。它们可以用于许多不同的用途,如安全审计、性能监视、访问控制和行为修改等。应用层钩子通常在应用程序的运行时被调用,可以执行一些预定义的操作或触发一些自定义代码。
王 瑞
2023/07/10
2250
4.7 x64dbg 应用层的钩子扫描
LyScript 实现应用层钩子扫描器
Capstone 是一个轻量级的多平台、多架构的反汇编框架,该模块支持目前所有通用操作系统,反汇编架构几乎全部支持,本篇文章将运用LyScript插件结合Capstone反汇编引擎实现一个钩子扫描器。
王 瑞
2022/12/22
2330
LyScript 实现应用层钩子扫描器
干货|Windows下进程操作的一些C++代码
0x01 进程遍历 因为进程是在随时进行变动的所以我们需要获取一张快照 1.1 CreateToolhelp32Snapshot HANDLE CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID); 因为要获取进程第一个参数选择TH32CS_SNAPPROCESS来获取系统中所有的进程,具体可以参考[CreateToolhelp32Snapshot]:https://docs.microsoft.com/zh-cn/windows/w
HACK学习
2021/08/13
1.5K0
内网渗透 | 横向移动中MSTSC的密码获取
④:For Every: cmd开3389 win08 win03 win7 win2012 winxp win08,三条命令即可:
HACK学习
2021/07/21
2.1K0
C/C++ 定位文件 .text 区段地址
首先声明.text区段的起始地址是需要计算的,无论是哪个结构体里都不会直接提供某个区段的直接地址(虚拟内存地址),我就是因为想偷懒所以翻了好久的结构体成员列表,结果头都翻炸了还是没找到。
王 瑞
2022/12/28
6940
C/C++ 定位文件 .text 区段地址
64位 & Windows 内核6
继续学习《逆向工程核心原理》,本篇笔记是第五部分:64位 & Windows 内核6
中龙技术
2022/09/29
7140
64位 & Windows 内核6
C/C++ 获取进程某模块入口地址
实现获取指定进程中特定模块的枚举以及得到该模块入口地址等信息。 实现代码: HMODULE GetProcessModuleHandle(DWORD pid, CONST TCHAR* moduleName){ // 根据 PID 、模块名(需要写后缀,如:".dll"),获取模块入口地址。 MODULEENTRY32 moduleEntry; HANDLE handle = NULL; handle = ::CreateToolhelp32Snapshot(
王 瑞
2022/12/28
7360
C/C++ 获取进程某模块入口地址
Windows环境下的调试器探究
1.CPU检测到INT 3指令 2.查IDT表找到对应的函数 3.CommonDispatchException 4.KiDispatchException 5.DbgkForwardException收集并发送调试事件
红队蓝军
2022/04/27
8040
Windows环境下的调试器探究
实战 | 进程启动技术的思路和研究
通过常用的api来创建进程是常规启动进程的方式,最常用的几个api有WinExec、ShellExecute、CreateProcess,我们一个一个来看一下
HACK学习
2021/11/12
1.2K0
进程管理器中隐藏进程
将其注入到进程管理器进程中,即可隐藏指定进程,当然在tasklist中依旧存在,win10 x86测试成功。用途可自行扩展。
鸿鹄实验室
2021/07/06
1.1K0
DLL注入explorer.exe进程[通俗易懂]
  最近一直在学习dll注入远程进程的相关知识,于是有了这篇文章。通过注入的方式会运行程序,在资源管理器中是看不到,相关的进程的,这为程序的隐藏提供了极大的便利。
全栈程序员站长
2022/08/29
2.4K1
最简单绕过ring3 hook的方式(bypass bitdefender)
本文已上传视频教学: https://www.bilibili.com/video/BV1Wy4y1X7Z5/?spm_id_from=333.999.0.0 ntdll.dll常常是被挂钩的主要模块
红队蓝军
2023/02/25
8710
最简单绕过ring3 hook的方式(bypass bitdefender)
驱动开发:内核实现进程汇编与反汇编
在笔者上一篇文章《驱动开发:内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作,本章将通过如上案例实现远程进程反汇编功能,此类功能也是ARK工具中最常见的功能之一,通常此类功能的实现分为两部分,内核部分只负责读写字节集,应用层部分则配合反汇编引擎对字节集进行解码,此处我们将运用capstone引擎实现这个功能。
王 瑞
2023/05/23
5330
驱动开发:内核实现进程汇编与反汇编
相关推荐
7.7 实现进程内存读写
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验