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 ...
随机推荐
- linux统计文件夹大小
统计总大小: du -sh dirname 统计文件夹内部各文件大小及总大小: du -h dirname
- 「美团外卖APP签约快捷支付」流程体验
§1 添加银行卡 新用户在美团外卖APP订餐支付时,首先要绑定银行卡.如下是“添加银行卡”页,输入卡号后,系统自动调用卡bin库校验卡号的有效性,如果有效会显示发卡行和卡类型(借记卡/贷记卡). 这 ...
- hadoop 遇到java.net.ConnectException: to 0.0.0.0:10020 failed on connection
hadoop 遇到java.net.ConnectException: to 0.0.0.0:10020 failed on connection 这个问题一般是在hadoop2.x版本里会出 ...
- Oracle中找出用户的上次登录时间
可以使用如下sql语句: select t1.username,t1.logon_time last_logon_time,t2.account_status,created 账号创建时间 from ...
- Python全栈-库的操作
一.系统数据库 安装数据库系统后,系统自带的数据库.通过mysql客户端连接数据库系统后,使用show命令可查看系统中存在的所有库: mysql> show databases; +------ ...
- Orangegreenworks封装rpgmakermv
You’ll get a zip file with a folder called “lib” and a file called greenworks.js. Put both of them o ...
- ==与Equals的作用
string str1 = "Blackteeth"; string str2 = str1; string str3 = "Blackteeth"; Cons ...
- BR(BoomerangRobot)机器人项目
项目宗旨:推动机器人技术及相关知识的普及,增进广大机器人DIYer们的交流,提高爱好者们自身的专业水平,项目提供以机器人BR(boomerangrobot)为硬件平台,ROS(robot operat ...
- php实现多进程
转:http://www.jb51.net/article/71238.htm cd php-version/ext/pcntl phpize ./configure && make ...
- html5-fieldset和legend和keygen元素的用法
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...