欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]不用导出任何函数的DLL劫持注入,完美!

[原创]不用导出任何函数的DLL劫持注入,完美!

.

最近在写SOCKS5代理,用的是LSP.代理成功了,但是因为LSP模块没办法隐藏,各种三方模块啊

删除PE头,强制删除文件,游戏都会闪退


想来想去,就想到这个法子,HOOK LdrLoadDll返回的模块句柄

把返回句柄替换成真实的DLL句柄,这种劫持的方式好处就是不用任何导出函数

不管什么是DLL劫持都可以,只要你能替换原来的DLL,LSP也挺方便的,哈哈哈


不过这种lsp劫持注入,只能用API HOOK来完成 SOCKS5代理

基本框架已经测试通过,抽空来分享一下劫持注入部分的代码


首先是HOOK部分

PBYTE gethookadd(PBYTE pLdrLoad) //搜索LdrLoadDll返回

{

	BYTE bsearch[3] = {0xC2,0x10,0x00};

	while (TRUE)

	{

		if (memcmp(pLdrLoad, bsearch,3)==0)

		{

			break;

		}

		pLdrLoad++;

	}

	return pLdrLoad;

}

void hookLdrLoadDll(bool bHook)

{

	if (pLdrLoadDll==NULL)

	{

		pLdrLoadDll = (PBYTE)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "LdrLoadDll");

		pLdrLoadDll = gethookadd(pLdrLoadDll);

		VirtualProtect(pLdrLoadDll, 10, PAGE_EXECUTE_READWRITE, NULL);

	}

	if (bHook )

	{

		if (!pVEH1) //如果没注册VEH就注册一个,全局变量

			pVEH1 = AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)FirstVectExcepHandler);

		BYTE bNewcode[1] = { 0xCC };

		WriteProcessMemory(GetCurrentProcess(), pLdrLoadDll, bNewcode, 1, NULL);

	}

	else

	{

		BYTE bNewcode[1] = { 0xC2 };

		WriteProcessMemory(GetCurrentProcess(), pLdrLoadDll, bNewcode, 1, NULL);

		if (pVEH1)//删除VEH

		{

			RemoveVectoredExceptionHandler(pVEH1);//这里好像删除不成功,所以写道卸载模块的线程里了,删除不成功就卸载不了模块

		//	MessageBoxA(0, "删除VRH",0,0);

		}	

	}

}

本来之前用的是inlinehook,XP测试的,弄成功了发现XP WIN7 WIN10中的HOOK地址汇编代码不一样,

而HOOK地址中 只有三个字节,所以就想到了用VEH来HOOK,只需要修改一个字节就行了


然后是处理HOOK部分:

 NTSTATUS NTAPI NewLdrLoadDll(

	IN PWCHAR               PathToFile OPTIONAL,

	IN ULONG                Flags OPTIONAL,

	IN PUNICODE_STRING      ModuleFileName,

	OUT PHANDLE             ModuleHandle)

{

	 if (*ModuleHandle == m_Module)//如果返回的句柄是自身模块句柄

	 {

		 *ModuleHandle = LoadLibrary(L"NTDLL.DLL");//假设劫持NTDLL

		 hookLdrLoadDll(false);//结束HOOK	

//		 MessageBox(0, ModuleFileName->Buffer, 0, 0);

		 FreeLib();

	 }

	 return 0;

}

LONG CALLBACK FirstVectExcepHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo)//异常处理

{

	switch (ExceptionInfo->ExceptionRecord->ExceptionCode)

	{

	case STATUS_BREAKPOINT:

	{

		if (ExceptionInfo->ContextRecord->Eip==(DWORD)pLdrLoadDll)//如果是我们的HOOK地址 

		{

			ExceptionInfo->ContextRecord->Eip = (DWORD)NewLdrLoadDll;//跳转到处理函数

			return EXCEPTION_CONTINUE_EXECUTION;

		}

	}

	}

	return EXCEPTION_CONTINUE_SEARCH;

}


因为RET的时候堆栈各种参数跟函数入口的时候都是一样了,

所以这里的HOOK函数(NewLdrLoadDll)直接跟LdrLoadDll一样就行了

先判断LdrLoadDll返回的句柄是不是自己的,如果是,说明我们的DLLMain函数执行完成,要返回了

然后把返回的句柄替换成被劫持的DLL

然后还原HOOK地址,完成我们想要的操作(比如内存注入自己,创建独立的工作线程),

最后卸载自身模块,不留痕迹


卸载模块部分:

void FreeLib() //退出线程

{

	BYTE bCode[100] = { 106,80,184,0,0,16,0,255,208,104,0,0,1,0,184,0,16,0,0,255,208,194,4,0 };

	DWORD i=0;

	//删除VEH

	shellcodeAppend(bCode, 0x68, (LONG)pVEH1, 4, TRUE);//push pVEH1  

	shellcodeAppend(bCode, 0xB8, (LONG)RemoveVectoredExceptionHandler, 4, false);//mov eax,RemoveVectoredExceptionHandler

	shellcodeAppend(bCode, 0xFFD0, 0, 0, false);//call  eax

	//等待50毫秒

	shellcodeAppend(bCode, 0x68, 0x50, 4, false);//push 50 

	shellcodeAppend(bCode, 0xB8, (LONG)Sleep,4,false);//mov eax,sleep

	shellcodeAppend(bCode, 0xFFD0, 0,0,false);//call  eax

	//卸载自身模块

	shellcodeAppend(bCode, 0x68, (LONG)m_Module, 4, false);//push m_Module 

	shellcodeAppend(bCode, 0xB8, (LONG)FreeLibrary, 4, false);//mov eax,FreeLibrary

	shellcodeAppend(bCode, 0xFFD0, 0, 0, false);//call  eax

	//返回 

	LONG lCodeLen= shellcodeAppend(bCode, 0xC20400, 0, 0, false);//ret 04

	LPVOID lpADD = VirtualAlloc(NULL, lCodeLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	if (lpADD)

	{

	//	memLoadself(); //内存加载自身,论坛有源码,自己去弄一个,

	//	内存注入那通过DLLMain传入lpADD,DllMain(code,0,lpADD),然后再执行释放内存lpADD,哈哈,完美!

		RtlCopyMemory(lpADD, bCode, lCodeLen);

		CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)lpADD, 0, 0, NULL);

	}

}


因为DLL不能自己Free自己,所以申请一块独立的内存,使用线程来卸载自己

测试的时候,发现hookLdrLoadDll(false),竟然不能删除VEH,导致线程不能卸载模块

然后把删除VEH也添加到了线程中处理


写着写着shellcode,发现好烦啊,都是用od写好汇编代码,复制数据出来

再RtlCopyMemory修改shellcode参数

因为这个特别烦,就是撸了几把LOL,

回来就写了个shellcode组装的函数 shellcodeAppend()

看着用起来挺方便,拿出来个大家瞧瞧

/**

*  功能描述:拼接 shelllcode汇编指令

*   bCode shellcode指针

*   lHead 汇编指令头

*   lData 汇编指令数据

*   dLen  数据长度

*   bReset 重新设置指令指针

*

*  返回 shellcode 长度

*/

LONG shellcodeAppend(PBYTE bCode, LONG lHead, LONG lData, LONG dLen, BOOL bReset)

{

	static LONG lcurp = 0;//当前指针

	LONG i = 0;

	LONG hLen = 4;//最多4位

	if (bReset)//重置

		lcurp = 0;

	for (i = 1; i < 4; i++) //计算汇编头位数

	{

		if ((lHead >> (i * 8)) == 0)

		{

			hLen = i;

			break;

		}

	}

	for (i = 0; i <hLen; i++)

	{

		bCode[lcurp + i] = ((PBYTE)(&lHead))[hLen - i - 1];

	}

	lcurp += hLen;

	//	RtlCopyMemory(bCode + lcurp, &lData, dLen);

	for (i = 0; i <dLen; i++)

	{

		bCode[lcurp + i] = ((PBYTE)(&lData))[i];

	}

	lcurp += dLen;

	return lcurp;

}

 

哈哈,一下子心情又好了

我把一句汇编代码分为两部分,比如:

7700A65C    8B4C24 10       mov     ecx,dword ptr ss:[esp+0x10]

7700A660    68 58750700     push    0x77558

 mov   ecx,dword ptr ss:[esp+0x10] 我把数据 8B4C24 看做汇编指令头,数据 10 是汇编指令数据

 push    0x77558 我把数据 68 看做汇编指令头,数据 0x00077558 是汇编指令数据

调用的时候就写成:

// 最后一个参数=TRUE 初始化 bCode 当前指针为 0 

shellcodeAppend(bCode, 0x8B4C24, 0x10, 4, TRUE);

// 最后一个参数=TRUE 初始化 bCode 当前指针为 0 

shellcodeAppend(bCode, 0x68, 0x77558 , 4, TRUE);

一般头部最长不过4个字节,所以shellcodeAppend把头部写成 整数型 

数据段一般最长也不过4个字节,所以也定义成4字节,

少见的超过4字节的就是 

dword ptr ds:[0xXXXXXXX],0xXXXXXXXX

这种就不能直接用 shellcodeAppend 一次完成了,要分两次

不过,既然shellcode是我们自己编写的,干嘛非要用这种,哈哈,脑残


最后 DLLMain() 中调用就可以了 :

#include "stdafx.h"

#include <winternl.h>

void hookLdrLoadDll(bool bHook);

HMODULE m_Module = NULL;

PVOID pVEH1 = NULL;

PBYTE pLdrLoadDll = NULL;

BOOL APIENTRY DllMain( HMODULE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

					 )

{

	switch (ul_reason_for_call)

	{

	case DLL_PROCESS_ATTACH:

		if ((DWORD)lpReserved== 0)//lpReserved 内存注入的时候,我们给个参数传进来的是卸载模块线程地址

		{

			m_Module = hModule;

			hookLdrLoadDll(true);

			break;

		}

		else

		{

			//这里是内存注入

			//	VirtualFree(lpReserved, 0, MEM_RELEASE);

			//然后启动线程啊之类的

			//RtlZeroMemory(hModule, 0x1000);//清空PE头啊

			//CreateThread();

			break;

		}

	case DLL_THREAD_ATTACH:

	case DLL_THREAD_DETACH:

	case DLL_PROCESS_DETACH:

		break;

	}

	return TRUE;

}


一直没工作做,有公司招人吗.像俺这种没文凭,没各种证的人,会的东西挺多,不敢说精通.

.

.






上传的附件 main.png

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功