14种DLL注入技术

本文是我搜集整理的DLL注入方法,有一些自己实现了,在文末的链接里有源码,其余没有实现的也有git链接或别的源码链接。


参考链接:https://bbs.pediy.com/thread-227075.htm

 

第一种  CreateRemoteThread远程线程调用
DLL注入是指向某一个特定的进程空间强制插入一个特定的
DLL文件映像,值得注意的是这种插入是强制性的插入,从技术层面来看,DLL注入是利用LoadLibrary()加载特定的DLL文件到进程的内存空间。注入的对象是可以是自身,也可以是远程进程。DLL注入技术实现主要分为5个部分,

1.打开进程,获取进程的句柄,

2.是在内存空间开辟一段内存空间

3.是向刚刚开辟的内存中写入需要注入DLL的路径,

4.是利用GetProcessAddree()获取LoadLibrary的地址。

5.是调用远程线程,利用LoadLibrary()去加载DLL。

 

第二种  RtlCreateUserThread创建用户线程
RtlCreateUserThread
CreateRemoteThread的底层实现,所以使用RtlCreateUserThread的原理是和使用CreateRemoteThread的原理是一样的。唯一的区别是使用CreateRemoteThread写入目标进程的是Dll的路径,而RtlCreateUserThread写入的是一段shellcode。因为直接调用了RtlCreateUserThread创建线程,绕过了win7/vista对这方面的检测所以可以注入到系统进程中(比如winlogon.exe),当然此方法也有缺陷,因为没有和csrss通讯,可能在线程体里面调用某些api函数可能会失败。还有需要注意的是因为RtlCreateUserThread没有经过系统封装所以线程体必须自己调用ExitThread来结束线程。

 

第三种 APC注入
通过CreateRemoteThread
函数创建一个线程就可以使用远程进程中的一个函数。然而,线程创建需要系统开销,所以调用一个现有的线程会更加高效。

Windows的异步过程调用(APC)可以满足这种要求

APC可以让一个线程在它正常的执行路径运行之前执行一些其他的代码。每个线程都有一个附加的APC队列,它们在线程处于可警告的等待状态时被处理。

所以只需要将目标进程的线程的APC队列里面添加APC过程,当然为了提高命中率可以向进程的所有线程中添加APC过程。然后促使线程从休眠中恢复就可以实现APC注入。

 

第四种 设置进程上下文

核心是通过函数SetContextThread

实现注入,像很多方法比如内存注入,通过傀儡进程注入核心都是这个思想。都是挂起线程,然后往内存中写入shellcode然后,重新设置Context,然后调用SetThreadContextResumeThread恢复线程执行。

 

第五种  反射式注入

反射式dll注入不需要dll文件落地,减少被查杀的风险。首先将需要注入的dll写入进程内存,然后为该dll添加一个导出函数,利用这个导出函数让其自动的装载dll。注射器是将DLL文件写入目标进程内存。反射装载器实现的就是模拟dll装载器装载dll文件的操作。

反射式注入流程如下:

1、读入原始DLL文件至内存缓冲区;
2、解析DLL标头并获取SizeOfImage;
3、为DLL分配新的内存空间,大小为SizeOfImage;
4、将DLL标头和PE节复制到步骤3中分配的内存空间;
5、执行重定位;
6、加载DLL导入的库;
7、解析导入地址表(IAT);
8、调用DLL的DLL_PROCESS_ATTACH;

 

第六种  输入法注入

切换输入法时候,输入法管理器imm32.dll就会加载IME模块,这样就形成了输入法注入的充要条件。而由于这个Ime文件本质上只是个存放在C:\WINDOWS\system32目录下的特殊的DLL文件,因此我们可以利用这个特性,在Ime文件中使用LoadLibrary()函数待注入的DLL文件。

 

第七种 全局钩子注入

利用windows的消息机制,可以在事件发送到os之间设置一条钩链,来钩取不同的消息,如以下代码,利用SetwindowsHookEx可以钩取一个键盘消息。并且调用钩子处理函数来处理这个消息,所达到的效果和dll注入是一样的(执行dll内部的代码)

 

第八种  DLL劫持

DLL劫持原理见链接  https://blog.csdn.net/liuhaidon1992/article/details/103815868

注入实现见文末链接

 

第九种  注册表注入

REG注入原理是利用在Windows 系统中,当REG以下键值中存在有DLL文件路径时,会跟随EXE文件的启动加载这个DLL文件路径中的DLL文件。当如果遇到有多个DLL文件时,需要用逗号或者空格隔开多个DLL文件的路径。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \Windows\AppInit_DLLs中,os就会自动去加载位于该注册表的有效的DLL。所以只需要在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \Windows\AppInit_DLLs中添加DLL的绝对路径,并把数值改为1,可以使得所有加载USER32.dll的进程全部加载目标路径的DLL
利用AppCertDlls注册表,将HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls下写入dll的路径,可以将此注册表项下的DLL加载到调用CreateProcessCreateProcessAsUserCreateProcessWithLogonWCreateProcessWithTokenWWinExec的每个进程中。值得注意的是win xp-win 10 默认不存在这个注册表项

 

第十种  挂起线程注入

OpenThread-->SuspendThread-->申请内存-->写入代码-->GetThreadContext-->获取EIP-->修改EIP-->SetThreadContext-->ResumeThread

 

第十一种  挂起进程注入

CreateProcess注入方法之一,CREATE_SUSPENDED以挂起的方式打开进程,后面方法与挂起线程注入相似。

 

第十二种  进程替换技术(进程hollowing)

除了注入之外,我们还可以使用进程替换技术将一个可执行文件写入一个运行的进程内部,这种技术让恶意代码拥有和被替换进程相同的特权,这个技术关键是:需要以挂起状态创建进程,也就是说,这个进程将会被载入内存,但是主线程过去,在外部程序恢复主线程之前,程序不会工作,恢复主线程之后,程序工作。如下代码是进程替换代码的伪代码.该程序通过调用CreateProcess并将进程创建标志设置为CREATE_SUSPENDED0x00000004)完成。新进程的主线程被创建为挂起状态,直到ResumeThread函数被调用才会运行。接下来,恶意软件需要用恶意的有效载荷来替换合法文件的内容。这可以通过调用ZwUnmapViewOfSectionNtUnmapViewOfSection来取消映射目标进程的内存(这一步的目的在于是重新写入傀儡进程的时候,进程对于这部分待注入的内存还有所有权,为了避免冲突,在重写入进程之前,需要取消进程对内存的映射)。这两个API基本上释放了一个部分指向的所有内存。现在内存被取消映射,加载器执行VirtualAllocEx为恶意软件分配新内存,并使用WriteProcessMemory将每个恶意软件的部分写入目标进程空间。恶意软件调用SetThreadContextentrypoint指向已编写的新代码段。最后,恶意软件通过调用ResumeThread来恢复挂起的线程。

CreateProcess(...,"目标进程.exe",CREATE_SUSPENDED);//以挂起状态创建进程

ZwUnmapviewOfSection()                             //释放内存,解除内存映射

VirtualAllocEx()                                   //为恶意代码分配新空间

writeProcessMenory(header)                         //写入数据(文件头)

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

    WriteProcessMenory(section)                    //写入节区数据

···

ResumeThread()                                     //重新启动主线程

 

第十三种方法  调试器注入

CreateProcess注入方法之二,DEBUG_ONLY_THIS_PROCESS以调试的方法打开进程,利用CREATE_PROCESS_DEBUG_EVENT,向目标程序中写入我们的ShellCode完成相应功能,并且我们的ShellCode中写入以CC断点,代码执行指令时触发EXCEPTION_DEBUG_EVENT事件,在EXCEPTION_DEBUG_EVENT的处理函数中回到原来的执行流程。

 

第十四种方法 导入表注入

静态注入的方法。修改PE文件,添加一个新节,修改导入表添加一个新的DLL实现注入。

源码链接: https://pan.baidu.com/s/1VeYcCcGvAOW8oxzclf6d4A 提取码: 9spe 

  • 4
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
远程注入DLL方法有很多,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
Delphi是一编程语言,而DLL(Dynamic-Link Library)是一模块化的文件格式,用于存储代码和数据,可以被多个应用程序共享。DLL注入是一技术,它允许将DLL文件加载到正在运行的进程中,并使得该进程能够调用DLL中的函数和使用其中的数据。 在Delphi中实现DLL注入的方法有很多。一常见的方法是使用Windows API函数LoadLibrary和GetProcAddress。通过调用LoadLibrary函数,将DLL文件加载到进程的虚拟地址空间中。然后使用GetProcAddress函数获取DLL中导出函数的地址,并将其传递给需要调用的函数。通过这方式,可以在运行时将DLL注入到目标进程中,并且通过调用DLL中的函数来扩展进程的功能。 DLL注入在实际应用中有多用途。例如,可以使用DLL注入来为某个程序添加额外的功能或修改程序的行为。DLL注入还可以用于实现一些调试和监控的功能。通过注入DLL,可以截获程序的输入和输出,或者在程序执行某些指定的操作时进行额外的处理。 在Delphi中实现DLL注入需要一定的编程知识和技巧。需要考虑目标进程的架构和权限限制,以及如何管理注入DLL的生命周期和资源管理。同时,还需要处理一些安全性和稳定性方面的问题,以确保注入过程不会对目标进程造成损害或崩溃。 总之,Delphi可以通过调用Windows API函数来实现DLL注入,从而扩展和修改进程的功能。但在实际应用中,需要考虑各方面的问题,并且遵守相关的法律和规定,以确保注入操作的安全性和合法性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值