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. (转) Awesome Deep Learning

    Awesome Deep Learning  Table of Contents Free Online Books Courses Videos and Lectures Papers Tutori ...

  2. app后端设计(12)--图片的处理

    app上线后,不断接受用户的反馈,于是,反馈非常差的情况下,都会有app的改版. 一旦app的改版,都会有比较大的UI改动,一改动UI,那么图片的尺寸也就必须要改变. 在app后端设计(1)—api( ...

  3. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  4. 【转】reduce端缓存数据过多出现FGC,导致reduce生成的数据无法写到hdfs

    转自  http://blog.csdn.net/bigdatahappy/article/details/41726389 转这个目的,是因为该贴子中调优思路不错,值得学习 搜索推荐有一个job,1 ...

  5. [转]SQL中char、varchar、nvarchar的区别

    char    char是定长的,也就是当你输入的字符小于你指定的数目时,char(8),你输入的字符小于8时,它会再后面补空值.当你输入的字符大于指定的数时,它会截取超出的字符.   nvarcha ...

  6. Unity3D 中材质球(Material)预制体打包成AB(AssetBundle)出现材质丢失问题的解决方案

    关于Unity3D中打AB(AssetBundle)资源包,默认是不连Shader一同打包进去的,所以得自行添加,添加方式比较简单,不需要往项目中添加Shader,只是点选一下就可以了,具体实现方式如 ...

  7. JavaWeb开发环境搭建Eclipse配置Tomcat

    转载请标明出处:http://blog.csdn.net/wu_wxc/article/details/48651251本文出自[吴孝城的CSDN博客] 工具: Eclipse官网下载:http:// ...

  8. CI表单验证

    =================Form辅助函数==================== 1.在视图文件中导入:$this->load->helper("form") ...

  9. 在Visual Studio 2017中安装bower

    在项目目录下添加一个文件.bowerrc { "directory": "wwwroot/lib" } JS包默认安装到webroot的lib文件夹,可以通过. ...

  10. ODAC(V9.5.15) 学习笔记(二)控件列表

    ODAC的控件有26个,简单介绍如下: TOraSession  管理Oracle的连接  TOraQuery  使用SQL进行数据获取,自动将更新提交数据库  TSmartQuery    在处理字 ...