其他
手动打造应用层钩子扫描
>>>> 文章概述
文章概述
>>>> 所需知识
所需知识
>>>> 环境
环境
首先,要对某个程序下钩子,那肯定是要下在程序的代码段的,所以我们的关注点就是代码段(也就是可执行的区段),所以,我们先把该程序在硬盘中的代码段读出来,然后在读取内存中的代码段,然后逐字节进行对比,这样就可以把下钩子的地方找出来。大致的思路如下:
1、绑定进程
2、遍历该程序的所有模块
3、读取该模块在硬盘中的内容
4、确定该模块在硬盘中代码段的位置和大小
5、修复重定位表
6、读取该模块在内存中代码段内容
7、逐个指令进行比较
>>>> 1. 绑定进程
1. 绑定进程
HANDLE GetProcessHandle(PWCHAR name)
{
//初始化进程快照
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hProcessSanp = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //获得快照句柄
Process32First(hProcessSanp, &pe32); //获取第一个进程
do
{
if (wcscmp(name, pe32.szExeFile) == 0)
{
CloseHandle(hProcessSanp);//关闭快照句柄,避免内存泄漏
return OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); //注意这里使用的权限
}
} while (Process32Next(hProcessSanp, &pe32));
CloseHandle(hProcessSanp);//关闭快照句柄,避免内存泄漏
return (HANDLE)NULL;
}
>>>> 2. 映射PE文件数据到内存
2. 映射PE文件数据到内存
DWORD ReadPEFile(IN LPSTR file_path, OUT LPVOID* pFileBuffer)
{
FILE* fp = fopen(file_path, "rb");
if (fp == NULL)
{
printf("打开文件失败\n");
return 0;
}
fseek(fp, 0, SEEK_END);//移动文件位置到末尾
long long filesize = ftell(fp);//计算大小
fseek(fp, 0, SEEK_SET); //恢复到文件开始的位置
LPVOID pFileBuffer_temp = malloc(sizeof(char)*filesize); //开辟指定大小的内存
if (pFileBuffer_temp == NULL)
{
printf("开辟空间失败\n");
fclose(fp);
return 0;
}
size_t n = fread(pFileBuffer_temp, sizeof(char), filesize, fp); //将文件数据拷贝到缓冲区
if (!n)
{
printf("读取数据失败\n");
free(pFileBuffer_temp);
fclose(fp);
return 0;
}
*pFileBuffer = pFileBuffer_temp;
pFileBuffer_temp = NULL;
fclose(fp);
return filesize;
}
>>>> 3. 读取各个节表的信息
3. 读取各个节表的信息
IMAGE_NT_HEADERS* peNtHeader; //nt头
IMAGE_FILE_HEADER* peFileHeader; //标准pe头
IMAGE_OPTIONAL_HEADER32* peOptionalHeader; //可选pe头
IMAGE_SECTION_HEADER* peSectionHeader; //节表
DWORD PeHead_information(IN LPVOID pFileBuffer)
{
if (pFileBuffer == NULL)
{
printf("缓冲区指针无效\n");
return 0;
}
/*判断是否是有效的MZ标记*/
if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE) //PWORD就是word*类型,取头两个字节的内容, IMAGE_DOS_SIGNATURE就是MZ
{
printf("不是有效的MZ标记\n");
return 0;
}
peDosHeader = (IMAGE_DOS_HEADER*)pFileBuffer; //dos赋值,因为是结构体,所以只需要知道首地址就等于知道了dos头的所有信息
/*判断是否是有效的pe标志*/
if (*((PDWORD)((DWORD)pFileBuffer + peDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) //这里为什么要转成DOWRD?因为指针进行加法操作,要去掉一个*来算的,注意了
{
printf("不是有效的pe标记\n");
return 0;
}
peNtHeader = (IMAGE_NT_HEADERS*)((DWORD)pFileBuffer + peDosHeader->e_lfanew); //NT头赋值
peFileHeader = (IMAGE_FILE_HEADER*)((DWORD)peNtHeader + 4); //标准pe头赋值
peOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)((DWORD)peFileHeader + IMAGE_SIZEOF_FILE_HEADER);//可选pe头赋值,标准pe头地址+标准pe头大小
peSectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)peOptionalHeader + peFileHeader->SizeOfOptionalHeader); //第一个节表 可选pe头地址+可选pe头大小
}
>>>> 4.拉伸PE
4.拉伸PE
DWORD simulation_ImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pImageBuffer)
{
if (pFileBuffer == NULL)
{
//printf("缓冲区指针无效\n");
return 0;
}
LPVOID pImageBuffer_temp = malloc(sizeof(char)*peOptionalHeader->SizeOfImage);//申请ImageBuffer所需的内存空间
if (pImageBuffer_temp == NULL)
{
//printf("开辟ImageBuffer空间失败\n");
return 0;
}
memset(pImageBuffer_temp, 0, peOptionalHeader->SizeOfImage); //将空间初始化为0
memcpy(pImageBuffer_temp, pFileBuffer, peOptionalHeader->SizeOfHeaders); //把头+节表+对齐的内存复制过去
/*复制节*/
for (int i = 0; i < peFileHeader->NumberOfSections; i++)
{
pImageBuffer_temp = (LPVOID)((DWORD)pImageBuffer_temp + (peSectionHeader + i)->VirtualAddress);//定位这个节内存中的偏移
pFileBuffer = (LPVOID)((DWORD)pFileBuffer + (peSectionHeader + i)->PointerToRawData); //定位这个节在文件中的偏移
memcpy(pImageBuffer_temp, pFileBuffer, (peSectionHeader + i)->SizeOfRawData); //复制节在文件中所占的内存过去
pImageBuffer_temp = (LPVOID)((DWORD)pImageBuffer_temp - (peSectionHeader + i)->VirtualAddress);
pFileBuffer = (LPVOID)((DWORD)pFileBuffer - (peSectionHeader + i)->PointerToRawData); //恢复到起始位置
}
*pImageBuffer = pImageBuffer_temp;
pImageBuffer_temp = NULL;
return peOptionalHeader->SizeOfImage;
}
>>>> 5. 修复重定位表
5. 修复重定位表
VOID RepairRelocation(IN LPVOID pImageBuffer,DWORD NewImageBase)
{
if (pImageBuffer == NULL)
{
printf("指针无效\n");
return;
}
IMAGE_DOS_HEADER* pImageBuffer_peDosHeader = (IMAGE_DOS_HEADER*)pImageBuffer;
IMAGE_NT_HEADERS* pImageBuffer_peNtHeader = (IMAGE_NT_HEADERS*)((DWORD)pImageBuffer + pImageBuffer_peDosHeader->e_lfanew);
IMAGE_FILE_HEADER* pImageBuffer_peFileHeader = (IMAGE_FILE_HEADER*)((DWORD)pImageBuffer_peNtHeader + 4);
IMAGE_OPTIONAL_HEADER32* pImageBuffer_peOptionalHeader = (IMAGE_OPTIONAL_HEADER32*)((DWORD)pImageBuffer_peFileHeader + 20);
IMAGE_SECTION_HEADER* pImageBuffer_peSectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)pImageBuffer_peOptionalHeader + pImageBuffer_peFileHeader->SizeOfOptionalHeader);
/*定位重定位表地址*/
IMAGE_DATA_DIRECTORY* pImageBuffer_Data = (IMAGE_DATA_DIRECTORY*)((DWORD)(&(pImageBuffer_peOptionalHeader->NumberOfRvaAndSizes)) + 4);//数据目录起始位置
IMAGE_DATA_DIRECTORY* pImageBuffer_Relocation_temp = (IMAGE_DATA_DIRECTORY*)pImageBuffer_Data + 5;//数据目录的第6个就是重定位表
IMAGE_BASE_RELOCATION* pImageBuffer_Relocation = (IMAGE_BASE_RELOCATION*)((DWORD)pImageBuffer + pImageBuffer_Relocation_temp->VirtualAddress); //重定位表
/*计算块的总数*/
DWORD size_block = 0; //存放块的总数
while ( pImageBuffer_Relocation->SizeOfBlock != 0 && pImageBuffer_Relocation->VirtualAddress != 0)
{
size_block++;
pImageBuffer_Relocation = (IMAGE_BASE_RELOCATION*)( (DWORD)pImageBuffer_Relocation + pImageBuffer_Relocation->SizeOfBlock);
}
pImageBuffer_Relocation = (IMAGE_BASE_RELOCATION*)((DWORD)pImageBuffer + pImageBuffer_Relocation_temp->VirtualAddress); //恢复
/* 修复重定位表 */
LPWORD temp = (LPWORD)((DWORD)(&(pImageBuffer_Relocation->SizeOfBlock)) + 4); //存放具体项
DWORD size_temp = 0;//存放具体项的数目
LPDWORD dest = NULL; //要修复的地址
for (int i = 0; i < size_block; i++)
{
if (pImageBuffer_Relocation->SizeOfBlock <=8)
{
continue;
}
size_temp = (pImageBuffer_Relocation->SizeOfBlock - 8) / 2; //计算具体项的个数
for (int j = 0; j < size_temp; j++)
{
dest = (LPDWORD)( (temp[j] & 0x0FFF) + pImageBuffer_Relocation->VirtualAddress + (DWORD)pImageBuffer); //计算需要修改的地址
if (temp[j] >> 12 == 3) //只有当具体项的前四位是3的时候才需要修改
{
*dest = *dest + (NewImageBase- pImageBuffer_peOptionalHeader->ImageBase); //当前地址+(新的基址-旧的基址)
}
}
pImageBuffer_Relocation = (IMAGE_BASE_RELOCATION*)((DWORD)pImageBuffer_Relocation + pImageBuffer_Relocation->SizeOfBlock);
temp = (LPWORD)((DWORD)(&(pImageBuffer_Relocation->SizeOfBlock)) + 4);
}
}
>>>> 6. 获取模块代码段的位置和大小
6. 获取模块代码段的位置和大小
struct TestInformation
{
DWORD VirtualAddress; //节区在内存的偏移
DWORD PointerToRawData; //节区在文件中的偏移
DWORD SizeTest; //大小
};
DWORD GetTestInformation(TestInformation* te)
{
int len = 0;
for (int i = 0; i < peFileHeader->NumberOfSections; i++)
{
if (((peSectionHeader + i)->Characteristics & 0x20000000) == 0x20000000) //判断是否是可执行的代码
{
(te+len)->VirtualAddress = (peSectionHeader + i)->VirtualAddress;
(te + len)->PointerToRawData = (peSectionHeader + i)->PointerToRawData;
(te + len)->SizeTest = (peSectionHeader + i)->SizeOfRawData;
len++;
}
}
return len;
}
>>>> 7. 具体实现
7. 具体实现
me32.dwSize = sizeof(MODULEENTRY32); //在使用这个结构前,先设置它的大小
HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); //HANDLE也是属于句柄,是通用句柄 ,HWND是窗口句柄
if (hModuleSnap == INVALID_HANDLE_VALUE) //INVALID_HANDLE_VALUE表示无效的句柄
{
MessageBoxA(0, "模块读取失败", 0, 0);
return;
}
BOOL bMore = Module32First(hModuleSnap, &me32); //获取第一个模块信息
char* ModulePath = NULL; //模块路径
while (bMore)
{
........
}
USES_CONVERSION;
ModulePath = W2A(me32.szExePath); //获得模块路径
char ModulePath_big[MAX_PATH] = { 0 }; //存放大写的结果
Conversion_Big(ModulePath, ModulePath_big); //这里全部转换成了大写,防止不一致
ModuleBase = (DWORD)me32.modBaseAddr;
char* ModuleName = my_strstr(ModulePath, "\\");
while (my_strstr(ModuleName, "\\") != NULL)
{
ModuleName += 1;
}
/* 定位PE的代码节和内存的代码节的地址及大小 */
ReadPEFile(ModulePath, &FileBuffer); //映射PE文件
PeHead_information(FileBuffer); //读取各个表的信息
simulation_ImageBuffer(FileBuffer, &ImageBuffer); //模拟imagbebuffer
TestInformation performCode[10];
memset(performCode, 0, sizeof(TestInformation)* 10);
CodeLen = GetTestInformation(performCode); //获取代码段的结构信息 ------------------
if ((performCode + 0)->SizeTest != 0 )
{
for (int i = 0; i < CodeLen; i++)
{
PeText_temp[i] = (char*)malloc((performCode + i)->SizeTest);
}
RepairRelocation(ImageBuffer, ModuleBase); //修复重定位表
for (int i = 0; i < CodeLen; i++)
{
memcpy(PeText_temp[i], (char*)((DWORD)ImageBuffer + (performCode + i)->VirtualAddress), (performCode + i)->SizeTest);
}
}
if ((performCode + 0)->SizeTest != 0 )
{
for (int i = 0; i < CodeLen; i++)
{
MeText_temp[i] = (char*)malloc((performCode + i)->SizeTest);
}
DWORD ProtectTemp = NULL;
for (int i = 0; i < CodeLen; i++)
{
VirtualProtectEx(g_hProcess, (LPVOID)(ModuleBase + (performCode + i)->VirtualAddress), sizeof(char)*(performCode + i)->SizeTest, PAGE_READWRITE, &ProtectTemp); //可读写权限
ReadProcessMemory(g_hProcess, (void*)(ModuleBase + (performCode + i)->VirtualAddress), MeText_temp[i], sizeof(char)*(performCode + i)->SizeTest, NULL);
VirtualProtectEx(g_hProcess, (LPVOID)(ModuleBase + (performCode + i)->VirtualAddress), sizeof(char)*(performCode + i)->SizeTest, ProtectTemp, NULL); //恢复权限
}
}
if ((performCode + 0)->SizeTest != 0 )
{
char OpCode[2] = { 0 }; //存放OpCode
int indicators = 0; //指标
int Asmlen = 0; //存放指令长度
char OriginalCode[20] = { 0 }; //存放原始字节
char NowCode[20] = { 0 }; //存放现在字节
char ff = 0xff; //过滤IAT的
int List_line = 0; //list的当前行数
for (int s = 0; s < CodeLen; s++)
{
while (indicators < (performCode + s)->SizeTest)
{
OpCode[0] = *(LPBYTE)((DWORD)PeText_temp[s] + indicators);
OpCode[1] = *(LPBYTE)((DWORD)PeText_temp[s] + indicators + 1); //读取opcode
Asmlen = Decode_Size(OpCode); //解析指令长度
memcpy(OriginalCode, PeText_temp[s] + indicators, Asmlen); //读取原始字节
memcpy(NowCode, MeText_temp[s] + indicators, Asmlen); //读取当前字节
for (int i = 0; i < Asmlen; i++)
{
if (OriginalCode[i] != NowCode[i] /*&& memcmp(&OpCode[0], &ff, 1) != 0*/) //判断字节是否不相等
{
char Addres[30] = { 0 };
char arr1[30] = { 0 }; //原始字节字符串
char arr2[30] = { 0 }; //现在字节字符串
BytesToHexStr1((unsigned char*)OriginalCode, Asmlen, arr1);
BytesToHexStr1((unsigned char*)NowCode, Asmlen, arr2);
sprintf(Addres, "%s+%x", ModuleName, indicators + (performCode + s)->VirtualAddress);
//判断是否需要过滤
BOOL guolv = false;
if (SizeFilter > 0)
{
for (int j = 0; j < SizeFilter; j++)
{
if (strcmp(&Filter[j * 40], Addres) == 0)
{
guolv = true;
break;
}
}
}
if (guolv == true)
{
break;
}
USES_CONVERSION;
m_ListControl.InsertItem(List_line, A2W(Addres));
m_ListControl.SetItemText(List_line, 1, A2W(arr2));
m_ListControl.SetItemText(List_line, 2, A2W(arr1));
List_line++;
break;
}
}
memset(OriginalCode, 20, 0);
memset(NowCode, 20, 0);
indicators += Asmlen;
}
indicators = 0;
}
}
memset(ModulePath_big, 0, MAX_PATH);
for (int i = 0; i < CodeLen; i++)
{
free(PeText_temp[i]);
free(MeText_temp[i]);
}
free(FileBuffer);
free(ImageBuffer);
FileBuffer = NULL;
ImageBuffer = NULL;
bMore = Module32Next(hModuleSnap, &me32);
}
CloseHandle(hModuleSnap);
看雪ID:千音丶
https://bbs.pediy.com/user-840606.htm
推荐文章++++