admin 管理员组文章数量: 887021
前言:最近为了完成课题,需要写系统API的钩子程序。然而系统API种类繁多,手动调配是不可能的。而只改写一些API来应付课题又不甘心,想起之前读某位大佬的论文,提到了写爬虫程序并改写的思路,遗憾的是并没有代码。今天仿照大佬的思路,自己动手了一遍,遇到了诸多问题,以这篇博客记录之。
一、爬取程序
首先,我进入了MSDN的网站(https://docs.microsoft/zh-cn/windows/desktop/api/ ),仔细的考量了一下其网站结构,可以发现,想要进入详细的API介绍页面需要三步:
- 选择头文件
- 选择详细的API
- 查看API详细信息
这里以Readfile为例,如果我们想要看到readfile这个API的详细情况,我们需要做三步:点击fileapi.h头文件->找到API->爬取所需要的信息,网站结构如下图:
于是,我们的爬取路线就很明朗了:
- 爬取头文件网址作为下级线索
- 爬API网址作为下级线索
- 爬你所需要的信息
完成这三步后便得到我们所需要的信息了,我在这里选择了爬取API名称、API原型、API所在的DLL这三条数据。
二、处理原型函数
为了写我们的hook程序,我们应用了easyhook这个开源工具。为此,我们需要编写自己的钩子函数。以WriteFile为例,我们需要将以下我们爬到的函数原型:
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
改写成我们需要的钩子函数:
BOOL WINAPI new_WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
)
{
senddata("Writefile");
return WriteFile(
hFile,
lpBuffer,
nNumberOfBytesToWrite,
lpNumberOfBytesWritten,
lpOverlapped
);
}
其中senddata(string str)函数是向我们的监控进程发送数据,这里可以改写成你自己函数,改写完了后再返回原API调用,这样我们就相当于写了一个新的系统API,这个API看起来似乎没什么不同,但其实中间中转过了我们的程序。
除了新的API,我们还要写对应的钩子语句,方便easyhook将我们的API覆盖原来的API:
HOOK_TRACE_INFO hHook_WriteFile = { NULL };
LhInstallHook(
GetProcAddress(GetModuleHandle(TEXT("kernel32")), "WriteFile"),
new_WriteFile,
NULL,
&hHook_WriteFile);
ULONG ACLEntries_WriteFile[1] = { 0 };
LhSetExclusiveACL(ACLEntries_WriteFile, 1, &hHook_WriteFile);
好了,将我们爬取到的API改写成这两部分,并放入我们的DLL就可以开始注入工作了。然而,这只是几百个系统调用中的一个而已。为了完成自动化配接任务,我写了以下一段代码,这段代码的作用是读取API.txt中的内容,即我们爬取的函数原型,改写成两部分,分别至Hook.txt(我们的钩子代码)和API_New.txt(新的API)。
#include "pch.h"
#include <iostream>
#include<fstream>
#include<string>
#include<vector>
#include <regex>
using namespace std;
int main()
{
int lastpos = 0;
int i = 0;
ifstream API_Origin("D:\API.txt");
ofstream API_Hook("Hook.txt");//储存钩子
ofstream API_New("API_New.txt");//储存新的API
//函数原型
string API_now;
//用于获得参数的正则表达式
regex regPere("[^ ][_a-zA-Z]+(?=,|\n)");
//储存参数 0号位置为返回类型,1号位置为函数名称,后面的为参数
vector<string> peres;//用于储存参数
if (!API_Origin.is_open())
{
cout << "can not open this file" << endl;
return 0;
}
//读取API文件中的一个API至我们的缓冲区,即‘;’前的内容
getline(API_Origin, API_now, ';');
cout << API_now;
while (API_now[i] != ' ') i++;
peres.insert(peres.end(),API_now.substr(lastpos, i-lastpos));
lastpos = i + 1;
while (API_now[i] != '(') i++;
peres.insert(peres.end(), API_now.substr(lastpos, i-lastpos));
//写我们的新函数
API_New << peres[0] << " WINAPI new_" << peres[1]<<API_now.substr(i,API_now.size()-i)
<<"{\n"<<" senddat(\""+peres[1]+"\");\n"<<" return "<<peres[1]<<"(\n\t";
sregex_iterator it(API_now.begin(), API_now.end(), regPere);
sregex_iterator end;
for (; it != end; ++it)
{
API_New << it->str() << ",\n\t";
}
API_New << ");\n}";
return cin.get();
}
先来看看效果吧:
API果然被改写成了我们所需要的形式,总结一下,这段代码主要干了几件事:
- 在函数返回值和函数名间加上了WINAPI
- 改写了新的函数名
- 增添了函数内容
- 攥写返回值
因为比较晚了,代码比较粗糙,有空再改进,这里原本想得是利用一个vector来存储各种参数信息,后来写着写着觉得太麻烦,于是想尝试着用正则表示式,来获取各个参数,由于以前没用过正则,加上C++的正则机制又比较坑,没想到踩了更多坑。于是把这里的正则单独领出来讲讲。
三、正则的使用
先阐述一下目的吧,我们的函数原型如下:
BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
而返回值需要如下:
return WriteFile(
hFile,
lpBuffer,
nNumberOfBytesToWrite,
lpNumberOfBytesWritten,
lpOverlapped
);
所以我们得单独把原型里的每个参数拎出来,这种提取子字符串显然是用正则比较方便。细化一下需求,即:提取空格与逗号或换行符(最后一个参数)之间的字符串,但不包括空格与逗号或换行符。
一开始时我用了以下的形式:
“(?<= )[a-zA-Z_]+(?=,|\n)”
并在网上通过了测试:
当我得意洋洋的放在VS里跑时,却得到了下面的错误:
网上并找不到合理的解释,经过多次调试后我认为是C++中正则版本不太一样。这时候经过室友提示,可以在regex后加basic参数,然而试了还是不行。后来我改成了一下形式:
[^ ][_a-zA-Z]+(?=,|\n)
成功的过了调试,总结原因就是C++并不支持“?<=”这种语法(也可能是我没找对版本),以上的模板用于匹配两个特定字符间的字符串均可适用。
版权声明:本文标题:hook系统调用(一):爬取MSDN官网上的API调用并改为自己的API(c++正则表达式的应用) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1715833142h653586.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论