20145319 《网络渗透》免考—API拦截技术
20145319 《网络渗透》免考—API拦截技术
概述
- 本次实验在window环境下进行,主要通过编写hook代码和注入程序,将我们的hook代码通过dll文件的形式注入到目标中,拦截其系统函数使其跳转到我们编写好函数上,以此来达到我们的目的
- 我将给大家介绍目前的三种api hook技术(着重前两种)
- inline hook(内联hook)
- IAT hook(导入表Hook)
- windows消息hook
- 主要知识如下
- 远程注入
- Pe文件结构(主要是导入表相关结构)
- c++编程
实验内容
- API拦截技术,顾名思义,就是通过给API函数挂上钩子,拦截,控制相应API函数的调用,从而改变API执行结果的一种技术,在实际中API HOOK实现起来有两个难点,一是如何实现将自己的恶意代码注入到其他进程中,二则是如何给API函数挂钩
注入技术
HKLM/Software/Microsoft/Windows NT/CurrentVersion/Windows/AppInit_DLLs修该注册表键值,添加相应dll,那么程序在加载user32.dll文件的时候同时也会加载相应dll,但是相对,对于不调用user32.dll的程序,则无法实现注入- 使用
函数CreateRemoteThread创建远程线程,远程线程中可以执行任意代码,通过代码重定位技术就可以实现我们远程dll注入 - 当然注入技术还有很多种,但是在这里我们主要研究API HOOK技术,就不再详细介绍了
API HOOK(内联HOOK)
hook技术,最核心的功能就是拦截原本信息,转而执行我们想要系统执行的部分,最直接的手段呢,就是直接修改二进制代码为Jmp指令,直接跳转到我们指定的地址来达到目的
如何拦截API函数?只需要在二进制文件中找到相应API函数的地址,将该函数的头几个字节修改成JMP指令,跳转到我们想执行的函数地址,执行完毕之后,再次执行原API函数被修改的字节,并跳转到原API地址,完成原本函数流程,由于这种方法是直接在程序流程中嵌入jmp指令来改变流程的,所以把它叫做内联hook(Inline Hook)
首先,我们要弄清楚的是jmp指令的长度,即我们需要修改多少个字节,使用ollydbg打开任意一个程序,随便将其中的一条指令修改成Jmp指令


从修改结果来看,jmp指令二进制形式为E9,长度为5个字节
那么,我们函数的流程应该如下
- 找到目标api函数的地址,保存其地址和前五个字节
- 明确hook函数的地址,构造jmp指令
- 执行Hook函数
- 将保存的api函数前五个字节写入原地址中
- 跳转到原API函数,完成原本的函数流程
为了实现我们的目的,我们选择将相应功能的代码写成dll文件,这样更方便我们进行操作
首先我们构造一个类来管理我们的函数以及相应信息(本来是想直接写函数的,后来发现需要保存原函数的二进制指令,保存原函数地址,单纯通过函数实在是太麻烦了,不由得不感叹面对对象这种方法还是方便啊)
#include"windows.h" class CILHOOK
{
public:
CILHOOK(); //构造
~CILHOOK(); //解钩 BOOL Hook(LPSTR pszModuleName,
LPSTR pszFuncName,
PROC pfnHookFunc); //HOOK函数 VOID UnHook(); //取消HOOK函数 BOOL ReHook(); //重新进行HOOK,回到正常函数流程 private:
PROC m_pfnOrig; //函数原地址
BYTE m_bOldBytes[5]; //函数原前五个字节代码
BYTE m_bNewBytes[5]; //jmp指令构造
};
编写class中的hook函数,unhook函数(取下钩子),rehook(重新挂钩),其中的根本原理上面已经提到过了,就是读取其中前五个字节,相应地址,将其记录下来并改写成Jmp指令,最后再将其还原,回到原流程中
#include "windows.h"
#include "hookClass.h" CILHOOK::CILHOOK()
{
m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} CILHOOK::~CILHOOK()
{ UnHook(); //脱钩 m_pfnOrig = NULL;
ZeroMemory(m_bOldBytes, 5);
ZeroMemory(m_bNewBytes, 5);
} BOOL CILHOOK::Hook(LPSTR pszModuleName,LPSTR pszFuncName,PROC pfnHookFunc)/*对指定模块的函数进行挂钩,参数分别是模块名称,函数名称,钩子函数名*/
{
BOOL bRet = FALSE; //获取指定模块中函数的地址
m_pfnOrig = (PROC)GetProcAddress(GetModuleHandle(pszModuleName), pszFuncName); if (m_pfnOrig != NULL)
{
DWORD dwNum = 0;
//将原来的数据存起来
ReadProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum); //构造jmp指令
m_bNewBytes[0] = '\xe9'; //jmp Opcode
//pfnHookFunc是HOOK后的目标地址
//m_pfnOrig是原来的地址
//5是指令长度
*(DWORD *)(m_bNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)m_pfnOrig - 5; //将构造好的地址写入该地址处
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum); bRet = TRUE;
}
return bRet;
} VOID CILHOOK::UnHook() /*取消挂钩*/
{
if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
//将原来的内容写回hook地址
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bOldBytes, 5, &dwNum);
}
} BOOL CILHOOK::ReHook() /*重新挂钩*/
{
BOOL bRet = FALSE; if (m_pfnOrig != 0)
{
DWORD dwNum = 0;
//写入挂钩地址
WriteProcessMemory(GetCurrentProcess(), m_pfnOrig, m_bNewBytes, 5, &dwNum);
bRet = TRUE;
}
return bRet;
}
至此,我们的基本功能都已经完成了,只需要在dllmain中定义具体的钩子函数,已经将钩子挂到相应的函数上即可(这里,我们选择了对创建进程的函数creatProcessW进行挂钩,当dll文件被加载时,完成挂钩,并弹出对话框提示)当我们创建进程时,系统会弹出内容为进程路径的对话框提示,如果我们选择否,则进程创建失败
#include "windows.h"
#include "hookClass.h" CILHOOK CreateProcessHook; BOOL WINAPI MyCreateProcessW(LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation)
{ BOOL bRet = FALSE; //弹出被创建的进程名
if (MessageBoxW(NULL, lpApplicationName, lpCommandLine, MB_YESNO) == IDYES)
{ //自己调用函数前先去掉钩子,否则会进入死循环
CreateProcessHook.UnHook(); bRet = CreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation); CreateProcessHook.ReHook(); }
else{
MessageBox(NULL, "您启动的程序被拦截", "提示", MB_OK);
} return bRet; } BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
//HOOK CreateProcessW()函数
CreateProcessHook.Hook("kernel32.dll", "CreateProcessW", (PROC)MyCreateProcessW);
MessageBox(NULL, TEXT("hello"), TEXT("helloWindow"), MB_OK);
break;
}
case DLL_PROCESS_DETACH:
{
CreateProcessHook.UnHook();
MessageBox(NULL, TEXT("goodbye"), TEXT("goodbyeWindow"), MB_OK);
break;
}
}
return TRUE;
}
下面以qq为例,打开对话框,当我们想给对方发送文件时,就会弹出对话框
运行注入程序

程序被拦截


当我们选择是,则能继续正常进行文件发送
这个本来是想注入到windows资源管理器中,但是最后不知道是因为windows有自身的防护机制还是自己权限不够所以没有办法得到相应进程的权限完成注入,自己也搜到了一些提权的资料,说实话在这方面了解不多,代码也不是太懂,不过反正提权好像是失败了,查资料又有说xp之后就没有debug权限了,所以这个问题至今还是没弄清楚
提权代码:
int EnableDebugPrivilege(LPTSTR name)
{
HANDLE token;
TOKEN_PRIVILEGES tp;
//打开进程令牌环
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token))
{
printf("open process token error!\n");
return 0;
}
//获得进程本地唯一ID
LUID luid;
if (!LookupPrivilegeValue(NULL, name, &luid))
{
printf( "lookup privilege value error!\n");
return 0;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = luid;
//调整进程权限
if (!AdjustTokenPrivileges(token, 0, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
printf("adjust token privilege error!\n");
return 0;
}
return 1;
}
API HOOK(导入表HOOK)
通过修改PE拓展头中的导入地址表(IAT)中函数对应的地址,将原API函数地址修改为HOOK函数地址
这种hook技术就要求对于pe文件结构有一定的了解
导入表和导入地址表都存在于pe可选文件头中
IMAGE_OPTONAL_HEADER结构中,是IMAGE_DATA_DIRECTORY结构的数组导入表具体结构如下
Typedef struct _IMAGE_IMPORT_DESCRIPTOR{ Union{ DWORD Charatorics ;
DWORD OriginalFirstThunk ; } ; DWORD TimeDataStamp ;
DWORD ForwarderChain ;
DWORD Name ;
DWORD FirstThunk ;
} IMAGE_IMPORT_DESCRIPTOR ;
其中有三个字段比较重要(加载后)
- OriginFirstThunk:该字段指向了导入表的RVA,是一个IMAGE_THUNK_DATA的结构体
- name:加载的dll文件名
- FirstThunk:指向导入地址表(IAT)的RVA,是一个IMAGE_THUNK_DATA的结构体
IMAGE_THUNK_DATA结构体如下
Typedef struct _IMAGE_THUNK_DATA{ Union { BYTE ForwarderString ;
WORD Function ;
DWORD Ordinal ;
PIMAGE_IMPORT_BY_NAME AddressOfData ;
} u1 ;
} IMAGE_THUNK_DATA32 ;
这个虽然说是结构体,但其实也可以说只是一个联合体,其作用是决定了函数以函数序号导入,还是以函数名导入,这里对我们影响不大
导入表整体结构如下图:

在了解了Pe文件的相关结构之后,我想我们思路已经非常清晰了
- 一个PE文件映像,从偏移位置0开始就是Dos Header,在Dos头中通过指针e_lfanew跳转带PE文件头
- 通过PE文件头可以得到
IMAGE_DATA_DIRECTORY结构,而数据目录的第二个元素就是存储的导入表的信息 - 通过导入表的信息我们可以定位到导入表,其结构为
IMAGE_IMPORT_DESCRIPTOR,再通过遍历所有的IMAGE_IMPORT_DESCRIPTOR结构可以得到所有的DLL文件名,通过遍历每个结构的FirstThunk可以得到每个函数的地址,通过遍历每个结构的OriginalFirstThunk所指向的IMAGE_IMPORT_BY_NAME可以得到每个函数的导出函数名 - 我们要做的就是通过修改其中的地址和导出函数名即为我们自己的函数即可完成Hook
替换Windows消息处理函数实现Hook
- 这种方法主要是通过函数SetWindowLong来替换原来的消息处理函数,但是这个函数和具体技术我自己目前也不是很懂,所以就只做这个大概的介绍了
20145319 《网络渗透》免考—API拦截技术的更多相关文章
- 20145319 《网络渗透》MS08_067安全漏洞
20145319 <网络渗透>MS08_067安全漏洞 一 实验内容 了解掌握metasploit平台的一些基本操作,能学会利用已知信息完成简单的渗透操作 了解漏洞MS08_067的相关知 ...
- 20145319 《网络渗透》web安全基础实践
20145319 <网络渗透>web安全基础实践 问题回答 Sql注入攻击原理,如何防御 攻击原理:由于对于用户输入并没做出相应限制,因此可以通过构造特定的sql语句,达到自身的一些非法目 ...
- 20145319 《网络渗透》web基础
20145319 <网络渗透>web基础 实验要求 掌握网页编程的基本知识 html语法 javascript php 前端,后台,数据库之间如何建立连接 掌握数据库的使用 mysql的启 ...
- 20145319 《网络渗透》DNS欺骗
20145319 <网络渗透>DNS欺骗 实验内容 dns欺骗实质上就是一种通过修改dns文件来改变目标想访问的域名和对应ip的攻击,使得目标在访问自己常用域名时不知不觉落入我们的圈套(可 ...
- 20145319 《网络渗透》URL攻击
20145319 <网络渗透>URL攻击 实验步骤 首先启动apache2,打开我们的钓鱼网页,键入命令/etc/init.d/apache2 start 在浏览器中尝试着访问自己的ip地 ...
- 20145319 《网络渗透》MS12_020安全漏洞
20145319 <网络渗透>MS12_020安全漏洞 一 实验内容 初步掌握平台matesploit辅助模块aux的使用 辅助模块包括扫描等众多辅助功能 本次展示DOS攻击的实现 有了初 ...
- 20145319 《网络渗透》MSF基础应用
20145319 <网络渗透>MSF基础应用 一 实验链接 渗透实验一:MS08_067渗透实验 渗透实验二:MS11_050渗透实验 渗透实验三:Adobe阅读器渗透实验 渗透实验四:M ...
- 20145319 《网络渗透》Adobe阅读器渗透攻击
20145319 <网络渗透>Adobe阅读器渗透攻击 一 实验内容 初步掌握平台matesploit的使用 有了初步完成渗透操作的思路 本次攻击对象:windows xp sp3 Ad ...
- 20145319 《网络渗透》MS11-050漏洞渗透
20145319 <网络渗透>MS11-050漏洞渗透 一 实验内容 初步掌握平台matesploit的使用 有了初步完成渗透操作的思路 了解MS11_050相关知识: - 安全公告:KB ...
随机推荐
- 第一章入门篇CSS样式的分类、盒模型
1.CSS样式的分类 CSS样式分为一项4种: 1.内联样式表,直接写在元素style属性里面的样式,如 <p style="color:red;">内联样式</ ...
- jmeter压测之 监控--nmon
压测方法整理: 1. 写jmx脚本,整理csv数据文件: 2. 部署测试环境,把jmx和csv放在压测机,把监控脚本nmon放被压测机: 3. 安装nmon: a. w ...
- 关于Stuck Archiver的疑问
客户使用crsctl stat res -t命令去查看RAC集群状态时,发现异常,知晓Stuck Archiver代表归档满,问我们为什么RAC是同一个库,只有实例1显示Stuck Archiver, ...
- Anaconda 使用指南
Anaconda 使用指南 参考文章: 致Python初学者:Anaconda入门使用指南 Anaconda使用总结 概述 很多学习python的初学者甚至学了有一段时间的人接触到anaconda或者 ...
- [转-SSRF]什么是SSRF
这些天专注了解了SSRF攻击(Server-side Request Forgery,服务器端请求伪造),对这类攻击有了自己的一些总结与看法,老外取这个名字是挺不错的,我很喜欢,这让我想到了CSRF( ...
- PHP与JSP简单比较
比较PHP和JSP这两个Web开发技术,在目前的情况是其实是比较php和Java的Web开发.以下就几个主要方面进行的比较: 一. 语言比较 PHP是解释执行的服务器脚本语言,首先php有简单容易上手 ...
- Linux常用网络工具:Http压力测试之ab
ab的全称是Apache Bench,是Apache自带的网络压力测试工具,相比于LR.JMeter,是我所知道的 Http 压力测试工具中最简单.最通用的. ab命令对发出负载的计算机要求很低,不会 ...
- ruby自动化之selenium webGUI
1.下载ruby语言包,windows下需要安装rubyinstall http://railsinstaller.org/en 2.cmd命令下安装selenium-webdriver gem包 g ...
- 加快cin读取速度
cin在读取大量数据时会比C里的scanf慢很多,但这并不是cin"无能",而是C++为了兼容C,对cin做了scanf的同步,只要关闭这个同步,cin就会有不弱于scanf的速度 ...
- arc 093 D – Grid Components
题意: 给出A和B,要求构造出一个具有A个白色连通块和B个黑色连通块的矩阵. 这个矩阵的长和宽最多为100. 思路: 试想如果横着每个点同类的点隔着一个不同的点,竖着每个同类的点隔着一个不同的点,那么 ...