windows-CODE注入(远程线程注入)
远程线程注入(先简单说,下面会详细说)
今天整理下代码注入(远程线程注入),所谓代码注入,可以简单的理解为是在指定内进程里申请一块内存,然后把我们自己的执行代码和一些变量拷贝进去(通常是以启线程的方式),然后直接调用对方内存里我们拷贝进去的那部分代码(创建一个线程)。这样就行了,此时我们的这个线程就是目标进程的子线程了。但是要注意一点,代码注入之所以能成功重点是:有些系统常用的dll里的某些函数,在不同的进程里面获取到的地址是一样的(此处注意,一样的概念是指数值一样,但是这个数值存的地方不一样。也就是A里面的b变量的值等于C里面的d变量的值。这个要清楚,不然在学习API劫持的时候可能会迷茫)。
首先先介绍几个需要的API
1.LoadLibrary() 和 GetProcAddress()
这两个函数,一个是加载dll库,一个是从已经加载的库句柄里拿出来某个函数的地址,可以理解成是把一个dll加到内存里,然后获取里面某个函数的地址,得到这个地址后就可以直接调用了,这两个简单的函数经常用到,无论是常规调用还是静态免杀都经常用。
2.OpenProcess()
根据进程id获取进程的句柄,也就是获取进程操控权。
3.VirtualAllocEx()
在指定进程里开辟一块内存,用于存放自己的代码和参数。
4.WriteProcessMemory()
3里面的函数会在一个进程里开辟一块内存,然后在那个内存里直接用本函数4进行数据写入,就是在别人那开一块内存然后写自己的东西。
5.CreateRemoteThread()
最核心的函数,在指定进程的某个内存位置存储的函数为线程函数,启动一个线程,当然被启动的这个线程属于指定的这个进程。
OK上面那5个API就是代码注入需要的几个基本的了,接下来我说一下我对线程注入的理解:
线程注入其实很好理解,就是说我们通过一定的手段在宿主也就是需要被注入的进程那获取权限,得到权限之后我们要在这个进程上开辟一定的内存,然后把自己的线程函数内容以及参数什么的全都拷贝过去,这样目标进程上有我们的函数,我们的参数,我们这个时候只是需要"帮"它启动一下这个线程就OK了,直接用CreateRemoteThread函数在对方进程的某个内存位置的某个线程函数作为线程函数启动。
然后上面的那个解释是宏观的,也就是泛泛而谈,最最核心的思路是这样:对于每个进程,他们调用的API大多都是自己随时调用随时获取的,地址也不一样,但是只有极少数的几个系统核心dll他们不一样,系统为了优化和统一管理,使得每个人load的dll然后从里面获取的函数地址是一样的,也就是大家共用一套已经被载入的dll地址,可以用的也是用的最多的就是User32.dll,kernel32.dll(我目前就知道这两个,但是不重要只要知道一个kernel32.dll然后在里面做一个工厂就行了),既然大家用的地址都一样,那么也就是说我在我本地直接获取里面某个函数的地址,那么你的地址也是这个,那我直接帮你“怂恿”你去执行就行了呗。这样代码注入的思路可以总结成是:我们以所有进程加载的某些DLL地址是相同的,通过这个相同为跳板,“跳过去,然后帮宿主进程做事”,这样想来有点局限,因为只有那么可怜的几个dll大家的地址一样,但是不用担心,我下面会介绍一个我自创的思路,就是在宿主里面建造一个工厂,然后加载任意想加载的东西,如果我们每个产品都要自己生产,然后送货,但是因为有很多货是不合格的,都被扣押了,那么我们为什么不直接生产一个合格的工厂送过去,只要工厂被接受了,那么他所生产的所有产品都被接受了,也就是在宿主进程上加载的dll里面的api肯定和他自己加载的一样啊,这样突破了常规代码注入函数受限制的弊端,下面注意问题的4还会说这个问题。
需要注意的问题:
1.
参数结构体的结构体名字有说道,我之前没注意这个问题,造成一定概率的注入失败。
2.
注意64位32位问题,目前的经验是64位的话就用64位程序注入,32位就用32位程序注入,这个应该可以解决,问题出现在获取进程pid那。
3.
注意一个非常关键的问题,在代码注入的时候,你所需要的任何变量最好自己以内存拷贝的方式传过去,比如我直接在线程函数里开了一个变量 char[512] = "00000",当你把这个函数地址拷贝到指定进程里面去的时候,个"00000"所占的内容并没有被拷贝过去,这样你在调用的时候内存就会出错。
4.
代码注入限制非常多,被注入的线程函数里面内容非常有讲究,大小有限制,存储方式有限制,不能随便调用API函数,比如直接在里面来了一个Sleep(1000),你觉得这个函数是系统的,在那都能调用,然而并不是,直接就内存错误,如果需要,你需要找到对应的API然后加载进去,然后调用,但是又不是所有的AIP函数地址都是统一的,那么怎么办?我之前有一个解决办法是直接导入两个固定的地址LoadLibrary() 和 GetProcAddress(),通常的代码注入者的固定思路是靠这两个函数获取地址然后传过去,然而大家很容易忽略一个思路,就是既然我们知道所有程序的LoadLibrary()
和 GetProcAddress() 地址是一样的,那么我们就直接本地获取这两个函数的地址,然后把这两个地址传到线程函数里,然后在里面在本地生产相应的函数,这样我们加载的API都是目标线程能接受的API地址了,这个思路是当时睡觉时候想的,妈的激动地差点失眠(虽然现在回头整理这些的时候已经不怎么激动了)。
5.
还有很多限制和注意问题,这里就不多说了,需要的可以自己去查一查,我知道的也不多,有的时候也是现用现查。
OK上面是需要知道的思路和基础,下面就直接说实现。
思路大体分这么几个阶段
1.提权 权限不够的话可能到导致敏感函数执行失败。
2.获取pid 获取宿主进程的pid
3.打开宿主进程 可以理解成是获取宿主进程的操控权,当然这一步依赖于1的提权和2获取的pid
4.初始化参数数据 这一步就是得到一些AIP地址了,参数了什么的,然后把它存在参数的结构体里。
5.在宿主进程里分配内存用于存放参数
6.把需要的参数全都拷贝到宿主继承内存里(第4步获取的内存位置)
7.在宿主进程里分配内存用于存放线程函数代码
8.把线程函数直接拷到7获取的内存里
9.启动注入宿主进程的线程
10.善后,释放一下垃圾什么的。
下面我给出一个最基本的代码注入的代码,功能是在qq或者explorer里面注入一个线程,线程就只是弹出来一个对话框(我资源里整理了一个测试项目http://download.csdn.net/detail/u013761036/9603026)。
.H
#pragma once
#include <Windows.h>
#include <stdlib.h>
#include <tlhelp32.h>
#include <Psapi.h>
#include <string>
using std::string;
using std::wstring;
#pragma comment (lib,"Psapi.lib")
#pragma warning(disable:4996)
class CInjection
{
private:
bool AdjustProcessTokenPrivilege();//提权
bool Camp2str(wstring wsStrA ,wstring wsStrB);
DWORD GetProcessIdByName(const wstring &wsProcessName);//获取pid
public:
bool InjectionExeAndShowMessage(const wstring &wsProcessName);
};
.CPP
#include "stdafx.h"
#include "Injection.h"
typedef struct _REMOTE_PARAMETER
{
CHAR cTitle[64];
CHAR cBody[64];
DWORD dwMessAgeBoxShowAddress;
}RemotePara ,* PRemotePara;
bool CInjection::AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return false;
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp))
{
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = luidTmp;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
CloseHandle(hToken);
return FALSE;
}
return true;
}
bool CInjection::Camp2str(wstring wsStrA ,wstring wsStrB)
{
int nSize = wsStrA.length();
for(int i = 0 ;i < nSize ;i ++)
{
if(wsStrA[i] >= 'A' && wsStrA[i] <= 'Z')
wsStrA[i] += 'a'- 'A';
}
nSize = wsStrB.length();
for(int i = 0 ;i < nSize ;i ++)
{
if(wsStrB[i] >= 'A' && wsStrB[i] <= 'Z')
wsStrB[i] += 'a'- 'A';
}
return wsStrA == wsStrB;
}
DWORD CInjection::GetProcessIdByName(const wstring &wsProcessName)
{
HANDLE hProcess = 0;
DWORD dwProcess[2048] ,dwNeeded;
TCHAR tcProcName[MAX_PATH] = {0};
wstring wsNowProcessName = L"";
int nTempSize = 0;
int nPos = 0;
EnumProcesses(dwProcess, sizeof(dwProcess), &dwNeeded);
for(int i = 0 ;i < dwNeeded / sizeof(DWORD) ;i++)
{
if(0 != dwProcess[i])
{
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcess[i]);
GetModuleFileNameEx(hProcess, NULL, tcProcName, MAX_PATH);
nPos = wstring(tcProcName).find_last_of(L'\\');
if(nPos != wstring::npos)
{
wsNowProcessName = wstring(wstring(tcProcName).substr(nPos + 1));
if(Camp2str(wsProcessName ,wsNowProcessName))
{
DWORD aa = dwProcess[i];
return aa;
}
//if(wsProcessName == wsNowProcessName)
// return dwProcess[i];
}
}
}
return 0;
}
//注入的线程函数
static DWORD __stdcall RemoteThread(PRemotePara myData)
{
typedef int (WINAPI *_MessageBoxA)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType
);
_MessageBoxA mbAdd = (_MessageBoxA)myData->dwMessAgeBoxShowAddress;
mbAdd(NULL ,myData->cBody ,myData->cTitle ,MB_OK);
return 0;
}
bool CInjection::InjectionExeAndShowMessage(const wstring &wsProcessName)
{
//1.提权
if(!AdjustProcessTokenPrivilege())
return false;
//2.获取pid
DWORD dwProPID = 0;
if((dwProPID = GetProcessIdByName(wsProcessName)) == 0)
return false;
//3.打开进程
HANDLE hProcess = NULL;
if((hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE ,dwProPID)) == NULL)
return false;
//4.初始化参数数据
RemotePara rpData = {0};
ZeroMemory(&rpData,sizeof(RemotePara));
HINSTANCE hInst=NULL;
hInst=LoadLibrary(L"User32.dll");
if(hInst == NULL)
return false;
rpData.dwMessAgeBoxShowAddress = 0;
rpData.dwMessAgeBoxShowAddress = (DWORD)GetProcAddress(hInst,"MessageBoxA");
if(rpData.dwMessAgeBoxShowAddress == 0)
return false;
FreeLibrary(hInst);
strcat(rpData.cTitle ,"title");
strcat(rpData.cBody ,"body");
//RemoteThread(&rpData);
//5.在宿主进程里分配内存,用于存参数
PRemotePara pPara = NULL;
pPara = (PRemotePara)VirtualAllocEx(hProcess , 0 ,sizeof(RemotePara) ,MEM_COMMIT,PAGE_READWRITE);
if(pPara == NULL) return false;
//6.把参数写入宿主进程里,注意结构体的命名(_REMOTE_PARAMETER)
if(!WriteProcessMemory(hProcess ,pPara ,&rpData ,sizeof(RemotePara) ,0))
return false;
//7.在宿主进程里分配内存,用于写线程函数 1024这个值是我随意填写的,我觉得这么大可以存下上面那个线程函数
void *pRemoteThr = VirtualAllocEx(hProcess , NULL ,1024 ,MEM_COMMIT | MEM_RESERVE ,PAGE_EXECUTE_READWRITE);
if(pRemoteThr == NULL) return false;
//8.把进程函数写入分配的内存里
if(!WriteProcessMemory(hProcess ,pRemoteThr ,&RemoteThread ,1024 ,0))
return false;
//9.启动注入宿主进程的进程
DWORD dwThreadId = 0;
HANDLE hThread = CreateRemoteThread(hProcess ,0 ,0 ,(DWORD (WINAPI *)(LPVOID))pRemoteThr ,pPara ,0 ,&dwThreadId);
if(!hThread) return false;
//10.等待线程结束,然后清理内存
WaitForSingleObject(hThread ,INFINITE);
CloseHandle(hThread);
VirtualFreeEx(hProcess ,pPara ,0 ,MEM_RELEASE);
VirtualFreeEx(hProcess ,pRemoteThr ,0 ,MEM_RELEASE);
CloseHandle(hProcess);
return true;
}
USER
#include "stdafx.h"
#include "ZZZTest.h"
#include "Injection.h"
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
CInjection *pciTest = new CInjection();
pciTest->InjectionExeAndShowMessage(L"qq.exe"); //explorer.exe
delete pciTest;
return 0;
}
windows-CODE注入(远程线程注入)的更多相关文章
- windows:shellcode 远程线程hook/注入(一)
https://www.cnblogs.com/theseventhson/p/13199381.html 上次分享了通过APC注入方式,让目标线程运行shellcode.这么做有个前提条件:目标线程 ...
- 远程线程注入方法CreateRemoteThread
最近在整理学习Windows注入方面的知识,这个远程注入前面早写过,现在看看人家博客的理解整理,整理, 需要源码的可以到我的github上下载. 链接是 https://github.com/Ars ...
- 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程
简介 大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...
- 安全之路 —— 无DLL文件实现远程线程注入
简介 在之前的章节中,笔者曾介绍过有关于远程线程注入的知识,将后门.dll文件注入explorer.exe中实现绕过防火墙反弹后门.但一个.exe文件总要在注入时捎上一个.dll文件着 ...
- 详细解读:远程线程注入DLL到PC版微信
一.远程线程注入的原理 1.其基础是在 Windows 系统中,每个 .exe 文件在双击打开时都会加载 kernel32.dll 这个系统模块,该模块中有一个 LoadLibrary() 函数,可以 ...
- 远程线程注入dll,突破session 0
前言 之前已经提到过,远线程注入和内存写入隐藏模块,今天介绍突破session 0的dll注入 其实今天写这个的主要原因就是看到倾旋大佬有篇文章提到:有些反病毒引擎限制从lsass中dump出缓存,可 ...
- mfc HackerTools远程线程注入
在一个进程中,调用CreateThread或CreateRemoteThreadEx函数,在另一个进程内创建一个线程(因为不在同一个进程中,所以叫做远程线程).创建的线程一般为Windows API函 ...
- 远程线程注入DLL突破session 0 隔离
远程线程注入DLL突破session 0 隔离 0x00 前言 补充上篇的远程线程注入,突破系统SESSION 0 隔离,向系统服务进程中注入DLL. 0x01 介绍 通过CreateRemoteTh ...
- 远程线程注入DLL
远程线程注入 0x00 前言 远程线程注入是一种经典的DLL注入技术.其实就是指一个新进程中另一个进程中创建线程的技术. 0x01 介绍 1.远程线程注入原理 画了一个图大致理解了下远程线程注入dll ...
随机推荐
- 【Arduino学习笔记05】Arduino数字输入、输出和脉冲宽带调制 -- 小项目:彩色小台灯
基本功能: 长按控制按钮开机,长按控制按钮关机(>3s) 通过三个调节按钮调节灯的颜色,每一个按钮分别对应R,G,B值 模式切换:短按控制按钮切换模式(长亮模式/闪烁模式) 元器件清单: Ard ...
- C#的foreach遍历循环和隐式类型变量
C#的foreach遍历循环和隐式类型变量 foreach遍历循环 foreach (<baseType> <name> in <array>>) { //c ...
- phpMyAdmin Transformation 任意文件包含/远程代码执行漏洞
漏洞参考 https://yq.aliyun.com/articles/679633 国外提供了一个在线测试的靶场 默认密码 root toor https://www.vsplate.c ...
- C语言之三字棋的简单实现及扩展
C语言之三字棋的简单实现及扩展 在我们学习完数组之后,我们完全可以利用数组相关知识来写一个微小型的游戏,比如说今天所说的--三子棋. 大纲: 文件组成 实现 完整代码展示 扩展 即: 一.文件 ...
- 对于如何从SM2的pfx证书文件中解析公钥和私钥,并从二次加密的密文中解密
首先呢,由于我的域名之前处理点问题,然后备案第二个网站时候,第一个网站没法访问,所以备案没过,阿里云告诉我要删除一个网站的备案,但是他没告诉我要删除主体,所以我的备案主体成了空壳主体,要传真或者发快递 ...
- FreeBSD——艺术、科学、哲学概论
FreeBSD--艺术.科学.哲学概论→→→→→概论: 信息都有一定的时效性.那么现在是什么时间?现在已经发布了 FreeBSD 12.2,距离下一个版本 13.0 还有约 4 个月. 『约定』 使用 ...
- WS1008网络损伤测试仪
WS1008网络损伤测试仪具备高性能的网络损伤仿真功能.冗余链路测试功能和线速流量生成功能,提供了综合性的网络系统测试方案,可充分测试.验证网络系统的抗损伤能力.链路切换能力及数据转发能力.为高可靠性 ...
- [unknown source] 整数拆分
一.题目 题目描述 定义一个整数拆分序列 \(a\) 的权值为: \[\sum_{i=1}^n\sum_{j=1}^{i-1}\gcd(a_i,a_j) \] 求对于一个整数 \(n\) 所有整数拆分 ...
- Typora的一些快捷键
语法格式 快捷键 标题 # + 空格 = 一级标题, ## + 空格 =二级标题, 以此类推 shift + 数字1 =一级标题 ,shift + 数字2 =二级标题 , 以此类推 有序列表 1 ...
- HDU_3746 Cyclic Nacklace 【KMP的应用】
一.题目 HDU3746 二.分析 KMP比较好解决的一个问题:如果求一个串中的循环节? 仔细回想KMP的用法,重点是next数组,相当于就是后缀和前缀的比较,那么不正是方便了我们确定循环节? 如果以 ...