动态加载dll的实现+远线程注入
1.在目标进程中申请内存
2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦)
3.创建远线程执行shellcode
之前可以看到shellcode很难编写还要去依赖库,去字符串区等等很麻烦,为了让被注入代码更容易编写,最好的方法就是通过dll来编写
dll加载:
1.静态调用:通过在我们的程序中添加头文件,以及lib文件来完成调用,前提就是获取dll然后还有头文件
2.动态调用:仅仅只需要一个dll即可完成调用
先写个试一下
#include <Windows.h>
__declspec(dllexport) void Test(){
MessageBox(NULL, NULL, NULL, NULL);
}
可以看到我们的Test,但是这种方式会对Test进行名称粉碎,由c++编译器添加的,需要告诉编译器使用c的方式来命名函数,所以我们这么写,然后还需要指明函数参数的调用约定
1.__stdcall 标准 栈传参,函数内部(被调用者)平栈
2. __cdecl c 栈传参,函数外部(调用者)平栈
3. __fastcall 快速 寄存器传参
4. __thiscall 类的thiscall调用约定,使用ecx寄存器来传递this指针
extern "C"{
__declspec(dllexport) void __stdcall Test(){
MessageBox(NULL, NULL, NULL, NULL);
}
}
上面写到__stdcall是函数内部平参这里来看看
void __stdcall test(int n1, int n2){
return;
}
int main()
{
test(1, 2);
return 0;
}
两个返回8一个返回4
void __stdcall test(int n1, int n2){
002013C0 push ebp
002013C1 mov ebp,esp
002013C3 sub esp,0C0h
002013C9 push ebx
002013CA push esi
002013CB push edi
002013CC lea edi,[ebp-0C0h]
002013D2 mov ecx,30h
002013D7 mov eax,0CCCCCCCCh
002013DC rep stos dword ptr es:[edi]
return;
}
002013DE pop edi
002013DF pop esi
002013E0 pop ebx
002013E1 mov esp,ebp
002013E3 pop ebp
002013E4 ret 8
很明显改变了参数返回的时候值就会变化而且是函数内部的变化所以是函数内部平栈,绝大部分的windows api都是stdcall,但是也有特例,比如wsprintf
char szBuf[256] = {0};
wsprintfA(szBuf,"%s","1234");
这里很明显因为他是由外界传入的参数来决定多少,所以是函数外部平参
继续回到动态加载dll
1.将目标dll加载到我们进程中
HMODULE hDll = LoadLibraryA("./TestDLL.dll");
返回值是模块句柄
这里就可以看到我们的dll完全被加载到我们的内存里面来了,所以说返回的模块句柄也就是当前dll在当前进程中的首地址,加载过程是由我们的操作系统来完成的(包括各节的扩展分配内存,重定位等等),有时候也可以自己写loadlibraby因为用这个函数太官方了,自己写就叫做内存加载,这是很多病毒的手法
2.计算函数的位置
都有个偏移
可以看到偏移是11122
LPVOID lp = GetProcAddress(hDll, "Test");
可以看到20000+11122 = 31122
也可以写个def
LIBRARY
EXPORTS
Test
这里得到的就是个函数指针
typedef void(*PFN_FPP)();
PFN_FPP lp = (PFN_FPP)GetProcAddress(hDll, "Test");
最后再调用就可以了
lp();
但是如何要求目标进程调用Loadlibrary来加载我们的dll,最后运行我们的dll中的导出函数
可以看到Loadlibrary是存在于Kernel32.dll中的,所以先去找目标进程中的Kernel32.dll的位置,一般程序中都有Kernel32.dll这个,因为他是个很基本的dll
然后再找到该dll导出的loadlibraryA或W函数的位置
因为我们都是用的同一个kernel32.dll所以我们可以通过一个公式堆出来目标进程中的loadlibrary的地址
公式:目标的loadlibrary - 目标的kernel32地址 = 本地的loadlibrary - 本地的kernel32地址
//获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
HANDLE hDestModule = NULL;
//接下来找到该进程中kernel32.dll的基址
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32 mo32 = { 0 };
mo32.dwSize = sizeof(MODULEENTRY32);
bRet = Module32First(hSnap, &mo32);
while (bRet)
{
bRet = Module32Next(hSnap, &mo32);
wprintf(mo32.szExePath);
std::wstring wstr = mo32.szExePath;
if (wstr.find(L"KERNEL32.DLL") != std::string::npos){
hDestModule = mo32.modBaseAddr;
break;
}
}
LPVOID lpDestAddr = NULL;
if (hDestModule != NULL){
//获取本进程的kernel32地址
HMODULE hkernel32 = GetModuleHandleA("KERNEL32.DLL");
//计算函数的位置
LPVOID lploadlibrary = GetProcAddress(hkernel32, "LoadLibraryA");
//获取了目标进程中的loadlibrary的地址
lpDestAddr = (char*)lploadlibrary - (char*)hkernel32 + (char*)hDestModule;
}
我们获取到了loadlibrary的地址后就通过CreateRemoteThread加载dll地址和函数地址来调用
//1.在目标进程开辟空间
LPVOID lpAddr = VirtualAllocEx(
hProcess, //在目标进程中开辟空间
NULL, //表示任意地址,随机分配
1, //内存通常是以分页为单位来给空间 1页=4k 4096字节
MEM_COMMIT, //告诉操作系统给分配一块内存
PAGE_EXECUTE_READWRITE
);
if (lpAddr == NULL){
printf("Alloc error!");
return 0;
}
DWORD dwWritesBytes = 0;
char* pDestDllPath = R"(G:\mytools\TestDll\jiazai\Debug\TestDLL.dll)";
//2.在目标进程中写入目标dll的路径
bRet = WriteProcessMemory(
hProcess, //目标进程
lpAddr, //目标地址 目标进程中
pDestDllPath, //源数据 当前进程中
strlen(pDestDllPath)+1, //写多大
&dwWritesBytes //成功写入的字节数
);
if (!bRet){
VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
return 0;
}
//3.向目标程序调用一个线程 创建远程线程 执行写入代码
HANDLE hRemoteThread = CreateRemoteThread(hProcess, //目标进程
NULL,
0,
(LPTHREAD_START_ROUTINE)LoadLibraryA, //目标进程的回调函数
lpAddr, //回调参数
0,
NULL
);
可以看到注入成功了
完整代码
// shellcode.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include <TlHelp32.h>
#include <string>
typedef void(*PFN_FOO)();
int main()
{
//获取快照
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32;
DWORD pid = 0;
pe32.dwSize = sizeof(PROCESSENTRY32);
//查看第一个进程
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
bRet = Process32Next(hSnap, &pe32);
if (wcscmp(pe32.szExeFile, L"procexp.exe") == 0){
pid = pe32.th32ProcessID;
break;
}
}
//获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
HANDLE hDestModule = NULL;
//接下来找到该进程中kernel32.dll的基址
hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32 mo32 = { 0 };
mo32.dwSize = sizeof(MODULEENTRY32);
bRet = Module32First(hSnap, &mo32);
while (bRet)
{
bRet = Module32Next(hSnap, &mo32);
wprintf(mo32.szExePath);
std::wstring wstr = mo32.szExePath;
if (wstr.find(L"KERNEL32.DLL") != std::string::npos){
hDestModule = mo32.modBaseAddr;
break;
}
}
LPVOID lpDestAddr = NULL;
if (hDestModule != NULL){
//获取本进程的kernel32地址
HMODULE hkernel32 = GetModuleHandleA("KERNEL32.DLL");
//计算函数的位置
LPVOID lploadlibrary = GetProcAddress(hkernel32, "LoadLibraryA");
//获取了目标进程中的loadlibrary的地址
lpDestAddr = (char*)lploadlibrary - (char*)hkernel32 + (char*)hDestModule;
}
//1.在目标进程开辟空间
LPVOID lpAddr = VirtualAllocEx(
hProcess, //在目标进程中开辟空间
NULL, //表示任意地址,随机分配
1, //内存通常是以分页为单位来给空间 1页=4k 4096字节
MEM_COMMIT, //告诉操作系统给分配一块内存
PAGE_EXECUTE_READWRITE
);
if (lpAddr == NULL){
printf("Alloc error!");
return 0;
}
DWORD dwWritesBytes = 0;
char* pDestDllPath = R"(G:\mytools\TestDll\TestDLL\Debug\TestDLL.dll)";
//2.在目标进程中写入目标dll的路径
bRet = WriteProcessMemory(
hProcess, //目标进程
lpAddr, //目标地址 目标进程中
pDestDllPath, //源数据 当前进程中
strlen(pDestDllPath)+1, //写多大
&dwWritesBytes //成功写入的字节数
);
if (!bRet){
VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
return 0;
}
//3.向目标程序调用一个线程 创建远程线程 执行写入代码
HANDLE hRemoteThread = CreateRemoteThread(hProcess, //目标进程
NULL,
0,
(LPTHREAD_START_ROUTINE)lpDestAddr, //目标进程的回调函数
lpAddr, //回调参数
0,
NULL
);
return 0;
}
所以我们直接把代码写入dll里面就可以为所欲为了,但是不要写同步的代码,不然加载dll会卡死,然后如果我们想要获取到我们注入模块的地址,其实就是需要lpDestAddr的返回值,这里怎么获取,就有一个小技巧,hRemoteThread 这个是线程的模块而lpDestAddr才是我们注入的dll的地址,这里其实只需要获取线程的退出码,就可以获得loadlibrarya的返回基地址
DWORD dwRetCode;
//获取远线程的退出值
GetExitCodeThread(hRemoteThread, &dwRetCode);
同一个文件允许同一个dll加载多次而且地址是不变的只是引用次数会+1
动态加载dll的实现+远线程注入的更多相关文章
- 用宏定义封装LoadLibrary,方便的动态加载dll
同学们动态加载dll的时候是不是感觉挺麻烦的,每次都::LoadLibrary,::GetProcAddress,还要typedef一堆函数.最近闲来无聊,用宏封装了一下,可以少写不少代码,用来也挺方 ...
- Delphi静态加载DLL和动态加载DLL示例
下面以Delphi调用触摸屏动态库xtkutility.dll为例子,说明如何静态加载DLL和动态加载DLL. 直接上代码. 1.静态加载示例 unit Unit1; interface uses W ...
- C# 利用反射动态加载dll
笔者遇到的一个问题,dll文件在客户端可以加载成功,在web端引用程序报错.解决方法:利用反射动态加载dll 头部引用加: using System.Reflection; 主要代码: Assembl ...
- c#实现动态加载Dll(转)
c#实现动态加载Dll 分类: .net2009-12-28 13:54 3652人阅读 评论(1) 收藏 举报 dllc#assemblynullexceptionclass 原理如下: 1.利用反 ...
- unity3d动态加载dll的API以及限制
Unity3D的坑系列:动态加载dll 一.使用限制 现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Androi ...
- Unity3D的坑系列:动态加载dll
我现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的,有了这个就可以实现代码 ...
- C#,动态加载DLL,通过反射,调用参数,方法,窗体
.net中常会用到动态加载DLL,而DLL中可能包含各种参数.方法.窗体,如何来调用动态加载这些参数.方法.窗体呢? 在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系: System. ...
- 动态加载Dll时,通过Type生成类对象
原文:动态加载Dll时,通过Type生成类对象 转:http://www.cnblogs.com/zfanlong1314/p/4197383.html "反射"其实就是利用程序集 ...
- c#实现动态加载Dll
原文:c#实现动态加载Dll 原理如下: 1.利用反射进行动态加载和调用. Assembly assembly=Assembly.LoadFrom(DllPath); //利用dll的路径加载,同时将 ...
随机推荐
- Vue DevTools 安装应用
1.https: //github.com/vuejs/vue-devtools 从这上面下载Vue DevTools: 2.npm install(cnpm install) && ...
- Invalid credentials for 'https://repo.magento.com/packages.json'
Use your public key as username and private key as password from your magento connect account You ca ...
- Java中实现对集合中对象按中文首字母排序
有一个person对象如下: public class Person { private String id;private String nam; } 一个list集合如下: List<Emp ...
- 浅谈AQS原理
一.AQS介绍 AQS,即AbstractQueuedSynchronizer, 抽象队列同步器,它是Java多线程模块用来构建锁和其他同步组件的基础框架.来看下同步组件对AQS的使用: AQS是一个 ...
- Spring Cloud Config整合Spring Cloud Kubernetes,在k8s上管理配置
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Kubernetes有专门的ConfigMap和Secret来管理配置,但它也有一些局限性,所以还是希望通过Spring C ...
- JDK、JRE与JVM之间的关系
初学JAVA很容易被其中的很多概念弄的傻傻分不清楚,首先从概念上理解一下吧: JDK(Java Development Kit)简单理解就是Java开发工具包,JRE(Java Runtime Env ...
- 树链剖分详解&题解 P6098 【[USACO19FEB]Cow Land G】
看到各位大佬们已经把其他的东西讲的很明白了,我这个 juruo 就讲一讲最基本的树链剖分吧. 0.树剖是什么?能吃吗? 不能吃 树剖是树链剖分的简称,我们一般说的树剖其实指重链剖分.当然,还有一种长链 ...
- 分享一个登录页面(前端框架layui)-20200318
效果图 对该页面的总结: 1.前端框架layui layui官网:https://www.layui.com/,下载之后,简单配置就可使用 2.layui模块引用与使用的方式 <script&g ...
- 【Go语言探险】线上奇怪日志问题的排查
最近在日志中发现一些奇怪的日志,大致长这样: Error 2020-08-28 06:59:38.813+00:00 ... _msg=get immersion context, fetch tra ...
- 华为云服务器基于hadoop2.7.5安装hive-2.3.7
1. Hive简介 The Apache Hive data warehouse software facilitates reading, writing, and managing large d ...