博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
注入理解之挂起线程
阅读量:5982 次
发布时间:2019-06-20

本文共 4369 字,大约阅读时间需要 14 分钟。

0x00挂起线程注入原理

1.主要是shellcode注入进程后 要让他在获取线程上下文后,修改到我们写入的ShellCode处执行(修改Eip为ShellCode处的地址)
2.含义 A->B B做操作在给到A
//获得线程上下背景文
Ret = GetThreadContext(ThreadHandle, &OldContext);
if (Ret==FALSE)
{
MessageBox("GetThreadContext 失败");
return;
}
NewContext = OldContext;

//含义 A->B B做操作在给到A

#ifdef _WIN64
NewContext.Rip = (DWORD)AllocBuffer;
OldEip = NewContext.Rip;
#else
NewContext.Eip = (DWORD)AllocBuffer;//下一指令到申请的内存空间处
OldEip = NewContext.Eip;
#endif

//;将指针指向ShellCode第一句push 12345678h中的地址,写入返回地址

Ret = WriteProcessMemory(ProcessHandle, ((char*)AllocBuffer) + 1, &OldEip, sizeof(DWORD), NULL);
if (!Ret)
{RETURN;}
3.shellcode构造
这种注入方式的思路:首先先向目标程序中写入我们的ShellCode,比如说写入LoadLibry加载我们的DLL。然后把目标进程中当主线程挂起,然后获取线程上下文,修改线程上下文中的EIP到我们写入的ShellCode处执行完代码,然后再设置为线程原来的上下文·继续执行,执行完成过后在目标中释放申请的空间。

具体编程实现大致实现思路:

构造ShellCode
VirtualAllocEx在目标进程中申请空间,WriteProcessMemory写入ShellCode
通过线程快照获取目标主线程
OpenThread打开线程,SuspendThread挂起线程
GetThreadContext获取目标主线程线程上下文
SetThreadContext修改目标主线程上下文到我们写入的ShellCode处执行
ResumeThread恢复线程让ShellCode执行
VirtualFreeEx扫尾释放空间

其实挂起线程注入的思路其实也是挺简单的,而且这种方式相对于常规的DLL注入,隐蔽性更高,一些病毒也比较喜欢用这种方式。

下面介绍详细的实现步骤。

 

 

0x01 挂起线程注入详细的编程实现
由于这种注入方式要注入ShellCode,用C++实现比较麻烦一点因为要扣二进制,我也用汇编一个版本,在附件中,这里还是用C++
?ShellCode的构造,LoudLibrary加载DLL
//结构必须字节对齐1
#pragma pack(1)
typedef struct _INJECT_CODE
{
BYTE byPUSH;
DWORD dwPUSH_VALUE;
BYTE byPUSHFD;
BYTE byPUSHAD;
BYTE byMOV_EAX; //mov eax, addr szDllpath
DWORD dwMOV_EAX_VALUE;
BYTE byPUSH_EAX; //push eax
BYTE byMOV_ECX; //mov ecx, LoadLibrary
DWORD dwMOV_ECX_VALUE;
WORD wCALL_ECX; //call ecx
BYTE byPOPAD;
BYTE byPOPFD;
BYTE byRETN;
CHAR szDllPath[MAX_PATH];
}INJECT_CODE, *PINJECT_CODE;
#pragma pack()

利用C++注入不用考虑重定位的问题,因为C++中提供offsetof宏可以求出变量偏移,但是在汇编实现中就要考虑求变量的偏移了,如下图

其实在这注入方式中,最有学习意义的就是ShellCode的构造,我总结了一些我在构造ShellCode中学习到的知识点。

代码重定位
在目标进程中,要想实现API调用的难点其实就是传参数,因为我们的注入程序和目标进程中基址是不一样的,这样就注定了写入参数的地址不一样,这就涉及到重定位问题。
经典的重定位代码。
Call $+5 ;将下一行的地址入栈,获得目标进程下一行的地址
FIXADDR: ;这个是注入进程中地址Lable,用于求差值
Pop ebp ;弹出入栈的目标进程的地址
Sub ebp,FIXADDR ;求得目标进程与注入进程中的地址差值

求出了目标进程和注入进程之间的地址差,就可以访问我们写入变量的地址了。

 

释放问题

一定要等所有的ShellCode执行完成再释放空间,否则会有同步问题,导致目标程序崩溃。
?打开进程,写入ShellCode
//打开进程
g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_dwPid);
if (!g_hProcess)
{
MessageBox("OpenProcess 失败");
return;
}

g_lpBuffer=VirtualAllocEx(g_hProcess,NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if (!g_lpBuffer)
{
MessageBox("VirtualAllocEx 失败");
return;
}

//给ShellCode结构体赋值

ic.byPUSH = 0x68;
ic.dwPUSH_VALUE = 0x12345678;
ic.byPUSHFD = 0x9C;
ic.byPUSHAD = 0x60;
ic.byMOV_EAX = 0xB8;
ic.dwMOV_EAX_VALUE = (DWORD)g_lpBuffer + offsetof(INJECT_CODE, szDllPath);
ic.byPUSH_EAX = 0x50;
ic.byMOV_ECX = 0xB9;
ic.dwMOV_ECX_VALUE = (DWORD)&LoadLibrary;
ic.wCALL_ECX = 0xD1FF;
ic.byPOPAD = 0x61;
ic.byPOPFD = 0x9D;
ic.byRETN = 0xC3;
memcpy(ic.szDllPath, m_strDllPath.GetBuffer(0), m_strDllPath.GetLength());

//写入ShellCode

bRet = WriteProcessMemory(g_hProcess, g_lpBuffer, &ic, sizeof(ic), NULL);
if (!bRet)
{
MessageBox("写入内存失败");
return;
}

ƒ创建线程快照查找目标程序主线程

//创建线程快照查找目标程序主线程
te32.dwSize = sizeof(te32);
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
{
MessageBox("CreateToolhelp32Snapshot 失败");
return;
}

//遍历查询目标程序主线程ID

if (Thread32First(hThreadSnap, &te32))
{
do
{
if (m_dwPid == te32.th32OwnerProcessID)
{
dwThreadId = te32.th32ThreadID;
break;
}
} while (Thread32Next(hThreadSnap, &te32));
}

④打开并且挂起目标主线程,获取线程上下文,修改Eip为ShellCode处的地址

//挂起目标主线程
bRet = SuspendThread(hThread);

if (bRet == -1)

{
MessageBox("SuspendThread 失败");
return;
}

oldContext.ContextFlags = CONTEXT_FULL;

bRet = GetThreadContext(hThread, &oldContext);
if (!bRet)
{
MessageBox("GetThreadContext 失败");
return;
}
newContext = oldContext;

newContext.Eip = (DWORD)g_lpBuffer;

//;将指针指向ShellCode第一句push 12345678h中的地址,写入返回地址

bRet = WriteProcessMemory(g_hProcess, ((char*)g_lpBuffer) + 1, &oldContext.Eip, sizeof(DWORD), NULL);
if (!bRet)
{
MessageBox("写入内存失败");
return;
}

 

⑤设置上下文,恢复线程跑起来
bRet = SetThreadContext(hThread, &newContext);

if (!bRet)

{
MessageBox("SetThreadContext 失败");
return;
}

//然后把主线程跑起来

bRet = ResumeThread(hThread);
if (bRet == -1)
{
MessageBox("ResumeThread 失败");
return;
}

⑥扫尾工作,单独设了个函数清除目标进程中申请的空间,注意这个操作务必等待我们的ShellCode执行完再执行,否则会导致目标程序崩溃
if (!VirtualFreeEx(g_hProcess, g_lpBuffer, 0, MEM_RELEASE))
{
MessageBox("VirtualFreeEx 失败");
return;
}

MessageBox("释放对方空间成功");

详细源码见附件

 

实验效果如下,Dll注入成功

 

转载于:https://www.cnblogs.com/L-Sunny/p/8040519.html

你可能感兴趣的文章
Django 配置MySQL数据库 mysql
查看>>
27.读文件时通过两种方式判断文件结尾
查看>>
试水Proxmox最新版本PVE 5.3
查看>>
第7章核心代码《跟老男孩学习Linux运维:Shell编程实战》
查看>>
Codis--分布式redis架构设计
查看>>
2012 2013 2014 Lync MVP 的坚持
查看>>
部署Jboss5拒绝直接使用ip方式访问
查看>>
二级缓存相关属性
查看>>
Ironfan在大数据集群部署、配置管理中的应用
查看>>
【原创】MySQL 实现Oracle或者PostgreSQL的row_number over 这样的排名语法
查看>>
rhel6 上udev的绑定
查看>>
编写校验规则文件
查看>>
演示:配置安全的shell属性
查看>>
python psutil
查看>>
httpd系统自带启动脚本详解
查看>>
架构纵横谈之二 ---- 架构的模式与要点
查看>>
selenium webdirver之ruby-开发ide安装
查看>>
IE9 UpdatePanel出错问题
查看>>
Open***配置密码方式验证
查看>>
实战网络监控Zabbix(1)—— 远程监控主机服务
查看>>