前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL

PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL

作者头像
方亮
发布2019-01-16 10:26:48
7000
发布2019-01-16 10:26:48
举报
文章被收录于专栏:方亮方亮方亮

        通过导出表隐性加载DLL?导出表?加载DLL?还隐性?是的。如果觉得不可思议,可以先看《PE文件和COFF文件格式分析——导出表》中关于“导出地址表”的详细介绍。(转载请指明出于breaksoftware的csdn博客

        这儿再废话几句,导出地址表,可能保存两种信息:

        1 保存的函数入口的RVA

        2 保存的是指向函数真正实现的所在的DLL名和函数名字符串组合(NTDLL.RtlAddVectoredExceptionHandler)

        一般情况下,我们遇到的是1这种情况。这种场景没什么好说的。我也想不到这个有什么好利用的,那么2又如何可以被利用呢?

        Exe调用一个DLL中的方法,有两种方法:

        1 在Exe导入表中加入DLL中函数信息,例如我们程序中调用GetProcAddress这类的API就是因为我们程序默认的导入表中包含了Kernel32.dll导出函数GetProcAddress信息。

        2 在逻辑中通过LoadLibrary动态载入一个DLL,然后通过GetProcAddress获取函数地址。这样我们在Exe的导入表中是看不到这个DLL的信息的。

        那么我是怎么设计”通过导出表隐性加载DLL“方案的呢?

        我设计了三个文件:DllBase.dll是我们要隐性加载的DLL;DllTop.dll是我们将要修改的DLL文件,MainExe.exe直接加载这个DLL,从而实现隐性加载DllBase.dll并调用它的导出函数。

        DllBase导出两个函数

LIBRARY	"DllBase"
EXPORTS
	Ret1
	Ret2

        这两个函数的实现更简单

int Ret1() {
    return 1;
}

int Ret2() {
    return 2;
}

        DllTop导出三个函数

LIBRARY	"DllTop"
EXPORTS
	Ret0
	Occupying001
	Occupying002

        其实现是

int Ret0(){
    return 0;
};

void Occupying001(){
    return;
}

void Occupying002(){
    return;
}

         注意Occupying001和Occupying002这两个函数,他们的返回类型是void。

         注意上图和代码,可以发现DllTop.dll和DllBase.dll不存在任何关系。

         现在我们要对DllTop.dll文件动手术,我会分别将Occupying001和Occupying002的导出地址指向DllBase.dll中的Ret1和Ret2。我保留TopDll.dll的一个副本为DllTop_Real.dll。具体的修改请看下图

        修改后,我们将其命名为TopDll_Modify.dll。

        新修改的DLL文件,我们用View Denpendencies查看下

        可以看到修改后的DLL并没有使DllBase.dll出现在导入表中,我们还是好好的隐藏着。

        为了做出结果比较,我将在MainExe中分别对DllTop_Real.dll和DllTop_Modify.dll进行加载并调用其函数。

typedef int (WINAPI *PRetN)();

void TopDll( LPCSTR lpFileName ){
    PRetN pRet0 = NULL;
    PRetN pRet1 = NULL;
    PRetN pRet2 = NULL;
    HMODULE hDllTop = NULL;
    hDllTop = LoadLibraryA( lpFileName );
    if ( NULL != hDllTop ) {
        pRet0 = (PRetN)GetProcAddress( hDllTop, "Ret0" );
        if ( NULL != pRet0 ) {
            printf("DllTop Ret0:%d\n", pRet0());
        }
        pRet1 = (PRetN)GetProcAddress( hDllTop, "Occupying001" );
        if ( NULL != pRet1 ) {
            printf("DllTop Occupying001:%d\n", pRet1());
        }
        pRet2 = (PRetN)GetProcAddress( hDllTop, "Occupying002" );
        if ( NULL != pRet2 ) {
            printf("DllTop Occupying002:%d\n", pRet2());
        }
        FreeLibrary( hDllTop);
        hDllTop = NULL;
    }
}

void BaseDll( LPCSTR lpFileName ) {
    PRetN pRet0 = NULL;
    PRetN pRet1 = NULL;
    PRetN pRet2 = NULL;
    HMODULE hDllBase = NULL;
    hDllBase = LoadLibraryA( lpFileName );
    if ( NULL != hDllBase ) {
        pRet1 = (PRetN)GetProcAddress( hDllBase, "Ret1" );
        if ( NULL != pRet1 ) {
            printf("DllBase Ret1:%d\n", pRet1());
        }
        pRet2 = (PRetN)GetProcAddress( hDllBase, "Ret2" );
        if ( NULL != pRet2 ) {
            printf("DllBase Ret2:%d\n", pRet2());
        }
        FreeLibrary( hDllBase);
        hDllBase = NULL;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    TopDll( "DllTop_Real.dll" );
    TopDll( "DllTop_Modify.dll" );
    BaseDll( "DllBase.dll" );
    system("pause");
	return 0;
}

        其运行结果是

        看!我们调用DllTop_Real.dll中函数时,由于Occupying001和Occupying002都是无返回值的,所以返回的结果是乱乱的数据。 我们调用DllTop_Modify.dll中函数时,Occupying001和Occupying002分别返回了1和2,这两个结果是DllBase.dll中Ret1和Ret2的执行结果!这样我们就是实现了通过导出表隐性加载DLL的方法。是不是很有意思?

        最后我们看下3个DLL在内存中存在的情况

1

2

3

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012年09月06日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档