zoukankan      html  css  js  c++  java
  • [DLL注入的方法]静态修改PE输入表法

    1.三种DLL加载时机:

    • 进程创建加载输入表中的DLL(静态输入)
    • 通过调用LoadLibrary主动加载(动态加载)
    • 系统预设加载

       通过干预输入表处理过程加载目标dll

    1.静态修改PE输入表法(测试程序 Notepad.exe)

    • 准备工作:自行编写一个MsgDLL,到处一个函数Msg();
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        if (ul_reason_for_call == DLL_PROCESS_ATTACH)                
        {
            CreateThread(NULL, 0, ThreadShow, NULL, 0, NULL);
        }
    
        return TRUE;
    }
    
    DWORD WINAPI ThreadShow(LPVOID lpParameter)
    {
        char szPath[MAX_PATH] = { 0 };
        char szBuf[1024] = { 0 };
        //获取宿主进程的路径
        GetModuleFileName(NULL, (LPWSTR)szPath, MAX_PATH);
        sprintf(szBuf, "DLL已经注入到进程:%s	[pid =%d]
    ", szPath, GetCurrentProcessId());
        //以三种方式显示自己的存在
            //1.
        MessageBox(NULL, (LPWSTR)szBuf, "DLL存在", MB_OK);
        
        //2.
        printf("%s
    ", szBuf);
        //3.
        OutputDebugString((LPWSTR)szBuf);
        return 0;
    }                

    参数意义: 

    ①hModule参数:指向DLL本身的实例句柄;

    ②ul_reason_for_call参数:指明了DLL被调用的原因,可以有以下4个取值:


    1. DLL_PROCESS_ATTACH:
    当DLL被进程 <<第一次>> 调用时,导致DllMain函数被调用,

    同时ul_reason_for_call的值为DLL_PROCESS_ATTACH,

    如果同一个进程后来再次调用此DLL时,操作系统只会增加DLL的使用次数,

    不会再用DLL_PROCESS_ATTACH调用DLL的DllMain函数。


    2.DLL_PROCESS_DETACH:
    当DLL被从进程的地址空间解除映射时,系统调用了它的DllMain,传递的ul_reason_for_call值是DLL_PROCESS_DETACH。
    ★如果进程的终结是因为调用了TerminateProcess,系统就不会用DLL_PROCESS_DETACH来调用DLL的DllMain函数。这就意味着DLL在进程结束前没有机会执行任何清理工作。


    3.DLL_THREAD_ATTACH:
    当进程创建一线程时,系统查看当前映射到进程地址空间中的所有DLL文件映像,

    并用值DLL_THREAD_ATTACH调用DLL的DllMain函数。 

    新创建的线程负责执行这次的DLL的DllMain函数,

    只有当所有的DLL都处理完这一通知后,系统才允许线程开始执行它的线程函数。


    4.DLL_THREAD_DETACH:
    如果线程调用了ExitThread来结束线程(线程函数返回时,系统也会自动调用ExitThread),

    系统查看当前映射到进程空间中的所有DLL文件映像,

    并用DLL_THREAD_DETACH来调用DllMain函数,

    通知所有的DLL去执行线程级的清理工作。
    ★注意:如果线程的结束是因为系统中的一个线程调用了TerminateThread,

    系统就不会用值DLL_THREAD_DETACH来调用所有DLL的DllMain函数。

    ③lpReserved参数:保留

    • 第二步:判断是否还有足够的空间存储我们的导出函数  -----》(PE格式知识)
      •  )如果空间不足,那么我们则需要采取扩大节或者新增节来进行位置存储;
      • Notepad无法存储我们的导出函数,那么我这里采取扩大最后一个节的方法

     根据PE格式可以看到再数据目录项中的导入表RVA为00007604

    那么我们这里讲RVA转成FOA文件偏移 :0x7604 -0x1000(Virtual Address) + 0x400 (Raw Address) =0x6a04     ---》PE知识,看不懂重学PE

    然后用16进制软件打开notepad,我这里使用的是010 editor:

    首先我们可以看到一个导入表的结构为20字节也就是16h,输入表中,每20个字节(一个Image_Import_Directory)对应一个动态链接库Dll的调用数据:

     并且导入表是连续的,直到它以一组0x14大小的全0的结束标记来结束

    定位到0x6a04,如图所示,后面则是0x14大小的结束标记

     

     接下来,我们将最后一个节区进行扩充:

    将原有导入表搬入新地址(直接复制粘贴到新地址):

     

     粘贴好后,在原有导入表区域构建新的OriginalFirstThunk、name和FirstThunk结构(注意我们粘贴后把原有导入表区域清零,腾出空间,做我们自己的结构)

      首先清零

     

    然后构建我们的OriginalFirstThunk、name和FirstThunk结构

    在PE文件被加载前 ,OriginalFirstThunk和FirstThunk都是指向IMPORT_BY_NAME

    根据结构我们可以知道:

      DLLName                     RawOffset =0x6A14          RVA= 0x6A14  -0x400(Raw Address)+0x1000(Virtual Address)  =0x7614

      IMPORT_BY_NAME    RawOffset =0x6A20          RVA= 0x6A20     -0x400(Raw Address)+0x1000(Virtual Address)  =0x7620 

    在手动填写数据的时候一定要注意字节顺序问题:

    然后根据刚填充的两个结构和Name的偏移,填写新的导入表结构

    OriginalFirstThunk :RawOffset =0x6a04            RVA =0x6a04  -0x400(Raw Address)+0x1000(Virtual Address)    =0x7604

    FirstThunk:RawOffset =0x6a0c          RVA =   0x6a0c -0x400(Raw Address)+0x1000(Virtual Address)   =0x7620

    DLLName                     RawOffset =0x6A14          RVA= 0x6A14  -0x400(Raw Address)+0x1000(Virtual Address)  =0x7614

     修改完成后,我们修正PE文件头信息:

    •   输入表目录指向位置
    •   FirstThunk -》可写属性

    首先修正输入表目录指向位置:

    在010中定位到导入表的RVA :

     将它原本指向的值修改成我们新替换的位置也就是最后一节的位置:

    内存偏移 = 0XB000 +0X8000 =0X13000

    文件偏移 = 0x8400+0x8000=0x10400

     

     由于使用了原来导入表数组的位置存放FirstThunk,而它原来的位置的RVA是0X7604,根据各节的起始位置和偏移量,可以确定该节属于text节,而该节原来的属性是0X60000020,写属性定义如下

    # define IMAGE_SCN_MEM_WRITE      0x800000000  //节是可写的

    0x 60000020+0x80000000 =0xE0000020

    然后把新节属性就是原属性加上这个值也就是0xE0000020

     至此我们的修改工作全部完成,保存修改结果

    接下来运行修改后的NotePad,结果MessageBox没有弹出来!

    这里是因为IMAGE_IMPORT_DESCRIPTOR中定义的TimeDateStamp为0xFFFFFFFF也就是-1,表示改输入项是原来预先Bound的,如果系统检测发现预绑定是有效的,那么就不会再去处理输入表加载了,所以我们只需把0x1B0到0x1B8 内容清零再次保存即可

    可以看到确实加载了我们的MsgDll.dll

  • 相关阅读:
    多产品代码架构
    PMBOK 指南 第四章 项目整合管理(4.1-4.3)
    PMBOK 指南 第三章 项目经理的角色
    PMBOK 指南 第二章 项目运行环境
    初识PMP PMBOK初解(指南第一章引论)
    c++ 拷贝构造函数(重点在内含指针的浅拷贝和深拷贝)
    C++中的try throw catch 异常处理
    source insight 3 常用设置
    C++ map insert 另一个map的子集
    PMP学习笔记--11项目干系人管理
  • 原文地址:https://www.cnblogs.com/hanhandaren/p/11447317.html
Copyright © 2011-2022 走看看