动态加载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的路径加载,同时将 ...
随机推荐
- xfs文件系统修复方法
1. 前言首先尝试mount和umount文件系统,以便重放日志,修复文件系统,如果不行,再进行如下操作.fdisk -l 查看硬盘分区情况mount -l 查看文件系统挂载情况df -h 查看文件系 ...
- Java 8新特性(三):Optional类
在上一篇介绍Stream流式数据处理的文章中提到了Optional类,这是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题.本篇文章将详细介绍Option ...
- 开始System.out.println();
第一篇博客 我在逛别人的博客的时候,发现他们有些内容是写他们自己的人生,有些是关于技术方面的个人观点分享探讨,每当看到好的文章的时候,我已经习惯的去点击收藏.有的时候我也在想,我为什么就不能做一个输出 ...
- 如何利用微博客进行seo赚钱营销
http://www.wocaoseo.com/thread-130-1-1.html 我们知道做SEO就是用人的思维来模仿搜索引擎的习惯,尽量适应seo的规则,并按照搜索引擎的规则和习惯 ...
- QT_QGIS_基本使用
QT_QGIS_基本使用 1.新建画布 2.添加矢量图层 1.打开矢量图层 2.新建矢量图层 1.添加几何要素--点 2.添加几何要素--线 3.添加栅格图层 1.打开栅格图层 小 ...
- type类型为number的input标签可以输入字母e
主要原因是:e在数学上代表的是无理数,是一个无限不循环的小数,其值约为2.7182818284,所以在输入e的时候,输入框会把e当成一个数字看待. 可以采用下面的方式来避免这个BUG,在input标签 ...
- JS深浅拷贝及其实现
基本数据类型和引用数据类型 JS数据分为基本数据类型和引用数据类型.基本数据类型的变量存储在栈中,引用数据类型则存储在堆中,引用数据类型的存储地址则保存在栈中. 下面来看一个小例子 // 基本数据类型 ...
- Micro-CMS v1 (Hacker101 CTF)
这道题一共有四个flag,初步观察可以创建或者修改发布的内容. Flag1: 首先随便创建了一个页面,创建完成后页面会直接跳转到我们所创建的页面. 初步判断网页应该是根据地址栏后的数字来查询并且显示页 ...
- Vue.use() 不一样的发现
1.Vue.use()首先是可以接受一个函数 var a = function () { console.log(a)}Vue.use(a) // 没有问题会输出a 2.当这个a上面有install属 ...
- 重要bug记录
导唱功能:需求点分析:本地已下载歌曲播放,判断是否有音频原唱伴奏版权,无版权按钮显示“导唱”,有版权显示“播原唱”.程序实现逻辑: 1.下载歌曲时调用一个歌曲信息接口,返回歌曲的一些属性信息,其中包括 ...