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拦截技术的更多相关文章

  1. 20145319 《网络渗透》MS08_067安全漏洞

    20145319 <网络渗透>MS08_067安全漏洞 一 实验内容 了解掌握metasploit平台的一些基本操作,能学会利用已知信息完成简单的渗透操作 了解漏洞MS08_067的相关知 ...

  2. 20145319 《网络渗透》web安全基础实践

    20145319 <网络渗透>web安全基础实践 问题回答 Sql注入攻击原理,如何防御 攻击原理:由于对于用户输入并没做出相应限制,因此可以通过构造特定的sql语句,达到自身的一些非法目 ...

  3. 20145319 《网络渗透》web基础

    20145319 <网络渗透>web基础 实验要求 掌握网页编程的基本知识 html语法 javascript php 前端,后台,数据库之间如何建立连接 掌握数据库的使用 mysql的启 ...

  4. 20145319 《网络渗透》DNS欺骗

    20145319 <网络渗透>DNS欺骗 实验内容 dns欺骗实质上就是一种通过修改dns文件来改变目标想访问的域名和对应ip的攻击,使得目标在访问自己常用域名时不知不觉落入我们的圈套(可 ...

  5. 20145319 《网络渗透》URL攻击

    20145319 <网络渗透>URL攻击 实验步骤 首先启动apache2,打开我们的钓鱼网页,键入命令/etc/init.d/apache2 start 在浏览器中尝试着访问自己的ip地 ...

  6. 20145319 《网络渗透》MS12_020安全漏洞

    20145319 <网络渗透>MS12_020安全漏洞 一 实验内容 初步掌握平台matesploit辅助模块aux的使用 辅助模块包括扫描等众多辅助功能 本次展示DOS攻击的实现 有了初 ...

  7. 20145319 《网络渗透》MSF基础应用

    20145319 <网络渗透>MSF基础应用 一 实验链接 渗透实验一:MS08_067渗透实验 渗透实验二:MS11_050渗透实验 渗透实验三:Adobe阅读器渗透实验 渗透实验四:M ...

  8. 20145319 《网络渗透》Adobe阅读器渗透攻击

    20145319 <网络渗透>Adobe阅读器渗透攻击 一 实验内容 初步掌握平台matesploit的使用 有了初步完成渗透操作的思路 本次攻击对象:windows xp sp3  Ad ...

  9. 20145319 《网络渗透》MS11-050漏洞渗透

    20145319 <网络渗透>MS11-050漏洞渗透 一 实验内容 初步掌握平台matesploit的使用 有了初步完成渗透操作的思路 了解MS11_050相关知识: - 安全公告:KB ...

随机推荐

  1. spring注解式开发之视图解析器

    http://localhost:8089/springmvc-04-viewResovler/springmvc/hello

  2. 例子:动能并不是特别强(2-3)后,下M5的同时,也是恢复期到期的前一天

    动能并不是特别强(2-3)后,下M5的同时,但是恢复期到期 EG.002195 2017/06/23-->2017/06/29

  3. InstallShield2015制作安装包----------安装后实现自动运行

    安装向导完成后,自动运行. 实现的手段是:InstallScript脚本OnEnd()函数里面,调用可执行程序. 备注:INSTALLDIR预定义变量存放着程序的安装目录. //安装后运行dispat ...

  4. [IDE] ECLIPSE取消自动更新

    eclipse自动更新的取消方法: window --> preferences --> General --> Startup and Shutdown --> 在列表中找到 ...

  5. <6>Lua元表和冒号 self

    Lua中没有像C.C++.JAVA中的类概念,面向对象等 ,但我们可以模拟出来 1. Lua中有个很重要的概念元表 设置元表setmetatable()函数  获取元表getmetatable()函数 ...

  6. python django简单的登陆实现

    实现方法: 1,可以先定义一个基础的页面访问路径 例如:http://127.0.0.1:8000/index/  定义index路径 在urls urlpatterns = [ url(r'^ind ...

  7. pyinstaller将python脚本生成exe

    一.下载pyinstaller 二.生成exe 下载pyinstaller 1.在C:\python27\Scripts目录下打开cmd界面,执行命令:pip install PyInstaller ...

  8. hdu 5126 cdq+Treap+BIT

    这题说的是给了三维空间然后操作 寻求在 x1,y1,z1    x2, y2, z2; (x1<x2, y1<y2,z1<z2) 计算出在 以这两个端点为右下和左上端点的方体内的点的 ...

  9. SQl server更新某阶段的匹配关系。

    DECLARE @count INTEGERDECLARE @id INTEGERDECLARE @subjectID INTEGERSET @count=1SET @id =11894SET @su ...

  10. Java多线程-----volatile关键字详解

       volatile原理     Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程.当把变量声明为volatile类型后, 编译器与运行时都会注意 ...