手动修改PE加载DLL

selph
selph
发布于 2020-10-11 / 1048 阅读
0
0

手动修改PE加载DLL

原理介绍

通过修改PE来加载DLL,相比之前了解的远程线程注入,这种方法只用注入一次,以后每次启动都会加载

用到的程序介绍

书上用的DLL会发起网络请求,下载一个html到本地,这里学习的主要目的是修改PE加载DLL,所以就用简单的dll来代替进行

DLL源代码

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) VOID helloworld() {
    MessageBox(NULL, L"Hello World", NULL, MB_OK);
}

目标程序

这里也简单选用一个普通的程序来进行演示:

#include<windows.h>
int main() {
	MessageBoxW(NULL,L"Hello Excel!",L"ERROR",MB_OK);
	return 0;
}

修改前准备工作

PE文件中导入的DLL信息以结构体数组的形式存在导入地址表中,只要将我们要导入的dll加到尾部即可,但是需要先判断一下导入地址表尾部是否有空间让我们来添加

判断导入表是否有足够的空间

通过 010editor 打开exe文件,查看扩展头里数据目录表的导入表:

image-20201009110324292

大小是180字节,在文件中的位置是0x1764,跳转过去看看:

image-20201009110525958

一个dll的信息占用14h个字节,一共有180字节,也就是有8个dll(剩下一个为NULL,来表示结束)

image-20201009120015005

这180个字节之后,没有空的位置,所以导入表没有足够的空间去装下新的一个dll的信息

查找空白区域

这种情况下,要把整个导入表转移到更宽阔的地方,然后再添加新的dll信息

确定移动的目标位置可用以下三种方法:

  1. 查找文件中的空白区域
  2. 增加文件最后一个区段的大小
  3. 在文件末尾添加新的区段

关于区段的操作,之前的笔记里已经记录过了,这里以查找空白区域为例,来学习转移导入表的操作

经过大致浏览,发现rdata区段末尾存在挺多的空白区域:

image-20201010094543146

一般区段结尾都会有空白区域

因为我们只需要14h*(9+1)=200个字节的空间,这里足够了,那就这里了

手动修改PE加载DLL

移动导入表需要做的操作有:

  1. 修改数据目录表中导入表的数据(RVA和Size)
  2. 删除绑定导入表
  3. 将原来的导入表复制到新的位置
  4. 添加新的导入表内容,Name,IAT,INT
  5. 修改区段属性

修改数据目录表中的导入表信息

根据空白区域,我们选择1CF8作为导入表数组的起始位置,1CF8的RVA是2AF8

所以修改导入表信息RVA为2AF8,修改大小为200:

image-20201010095615690

删除绑定导入表

绑定导入表也在数据目录表里有,删除的操作就是把RVA和Size都改为0,这里我的绑定导入表默认就是0,所以就不用管:

image-20201010095738222

移动导入表

将原本的导入表数组复制到我们新定义的导入表数组的位置:

image-20201010095839454

添加新的导入表项

首先观察其他导入dll是怎么写的:

image-20201010100055412

这里第一个DWORD写指向导入名称表的RVA,FirstThunk是指向导入地址表的RVA,这里两个表可以是同一个地方,也可以不同,在硬盘中,这两个表的内容是相同的:

  • 前两个字节表示导出表的序号,不是必须的,可以填0
  • 后面的字节是导出函数的名称

那么只要让他们指向同一个地方就行了

然后name则指向填写dll名称的RVA即可

填写完成之后如下图所示:

image-20201010101449559

注意!!这里导入地址表和导入名称表RVA后面要空一个DWORD来表示结束,不然会出错(因为这个导入名称表和导入地址表都是数组,以空项结尾)

修改区段属性

因为PE装载器加载PE的时候,会向导入地址表写入重定位后的地址,所以这个区段得有写入权限才行

找到这个区段属性的位置:

image-20201010101628067

找到IMAGE_SCN_MEM_WRITE项,然后改成1:

image-20201010101714243

到这里修改就完成了,可以保存了

效果演示

双击打开我们刚刚修改过的文件:

image-20201010102057159

程序正常运行,然后用Process Explorer进行查看:

image-20201010102135199

我们手动写入的dll成功加载进来了


评论