1.在目标进程中申请内存

2.向目标进程内存中写入shellcode(没有特征,编码比较麻烦)

3.创建远线程执行shellcode

之前可以看到shellcode很难编写还要去依赖库,去字符串区等等很麻烦,为了让被注入代码更容易编写,最好的方法就是通过dll来编写

dll加载:

1.静态调用:通过在我们的程序中添加头文件,以及lib文件来完成调用,前提就是获取dll然后还有头文件

2.动态调用:仅仅只需要一个dll即可完成调用

先写个试一下

  1. #include <Windows.h>
  2. __declspec(dllexport) void Test(){
  3. MessageBox(NULL, NULL, NULL, NULL);
  4. }

可以看到我们的Test,但是这种方式会对Test进行名称粉碎,由c++编译器添加的,需要告诉编译器使用c的方式来命名函数,所以我们这么写,然后还需要指明函数参数的调用约定

1.__stdcall 标准 栈传参,函数内部(被调用者)平栈

2. __cdecl c 栈传参,函数外部(调用者)平栈

3. __fastcall 快速 寄存器传参

4. __thiscall 类的thiscall调用约定,使用ecx寄存器来传递this指针

  1. extern "C"{
  2. __declspec(dllexport) void __stdcall Test(){
  3. MessageBox(NULL, NULL, NULL, NULL);
  4. }
  5. }

上面写到__stdcall是函数内部平参这里来看看

  1. void __stdcall test(int n1, int n2){
  2. return;
  3. }
  4. int main()
  5. {
  6. test(1, 2);
  7. return 0;
  8. }

两个返回8一个返回4

  1. void __stdcall test(int n1, int n2){
  2. 002013C0 push ebp
  3. 002013C1 mov ebp,esp
  4. 002013C3 sub esp,0C0h
  5. 002013C9 push ebx
  6. 002013CA push esi
  7. 002013CB push edi
  8. 002013CC lea edi,[ebp-0C0h]
  9. 002013D2 mov ecx,30h
  10. 002013D7 mov eax,0CCCCCCCCh
  11. 002013DC rep stos dword ptr es:[edi]
  12. return;
  13. }
  14. 002013DE pop edi
  15. 002013DF pop esi
  16. 002013E0 pop ebx
  17. 002013E1 mov esp,ebp
  18. 002013E3 pop ebp
  19. 002013E4 ret 8

很明显改变了参数返回的时候值就会变化而且是函数内部的变化所以是函数内部平栈,绝大部分的windows api都是stdcall,但是也有特例,比如wsprintf

  1. char szBuf[256] = {0};
  2. wsprintfA(szBuf,"%s","1234");

这里很明显因为他是由外界传入的参数来决定多少,所以是函数外部平参

继续回到动态加载dll

1.将目标dll加载到我们进程中

  1. HMODULE hDll = LoadLibraryA("./TestDLL.dll");

返回值是模块句柄

这里就可以看到我们的dll完全被加载到我们的内存里面来了,所以说返回的模块句柄也就是当前dll在当前进程中的首地址,加载过程是由我们的操作系统来完成的(包括各节的扩展分配内存,重定位等等),有时候也可以自己写loadlibraby因为用这个函数太官方了,自己写就叫做内存加载,这是很多病毒的手法

2.计算函数的位置

都有个偏移

可以看到偏移是11122

  1. LPVOID lp = GetProcAddress(hDll, "Test");

可以看到20000+11122 = 31122

也可以写个def

  1. LIBRARY
  2. EXPORTS
  3. Test

这里得到的就是个函数指针

  1. typedef void(*PFN_FPP)();
  2. PFN_FPP lp = (PFN_FPP)GetProcAddress(hDll, "Test");

最后再调用就可以了

  1. lp();

但是如何要求目标进程调用Loadlibrary来加载我们的dll,最后运行我们的dll中的导出函数

可以看到Loadlibrary是存在于Kernel32.dll中的,所以先去找目标进程中的Kernel32.dll的位置,一般程序中都有Kernel32.dll这个,因为他是个很基本的dll

然后再找到该dll导出的loadlibraryA或W函数的位置

因为我们都是用的同一个kernel32.dll所以我们可以通过一个公式堆出来目标进程中的loadlibrary的地址

公式:目标的loadlibrary - 目标的kernel32地址 = 本地的loadlibrary - 本地的kernel32地址

  1. //获取进程句柄
  2. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
  3. HANDLE hDestModule = NULL;
  4. //接下来找到该进程中kernel32.dll的基址
  5. hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
  6. MODULEENTRY32 mo32 = { 0 };
  7. mo32.dwSize = sizeof(MODULEENTRY32);
  8. bRet = Module32First(hSnap, &mo32);
  9. while (bRet)
  10. {
  11. bRet = Module32Next(hSnap, &mo32);
  12. wprintf(mo32.szExePath);
  13. std::wstring wstr = mo32.szExePath;
  14. if (wstr.find(L"KERNEL32.DLL") != std::string::npos){
  15. hDestModule = mo32.modBaseAddr;
  16. break;
  17. }
  18. }
  19. LPVOID lpDestAddr = NULL;
  20. if (hDestModule != NULL){
  21. //获取本进程的kernel32地址
  22. HMODULE hkernel32 = GetModuleHandleA("KERNEL32.DLL");
  23. //计算函数的位置
  24. LPVOID lploadlibrary = GetProcAddress(hkernel32, "LoadLibraryA");
  25. //获取了目标进程中的loadlibrary的地址
  26. lpDestAddr = (char*)lploadlibrary - (char*)hkernel32 + (char*)hDestModule;
  27. }

我们获取到了loadlibrary的地址后就通过CreateRemoteThread加载dll地址和函数地址来调用

  1. //1.在目标进程开辟空间
  2. LPVOID lpAddr = VirtualAllocEx(
  3. hProcess, //在目标进程中开辟空间
  4. NULL, //表示任意地址,随机分配
  5. 1, //内存通常是以分页为单位来给空间 1页=4k 4096字节
  6. MEM_COMMIT, //告诉操作系统给分配一块内存
  7. PAGE_EXECUTE_READWRITE
  8. );
  9. if (lpAddr == NULL){
  10. printf("Alloc error!");
  11. return 0;
  12. }
  13. DWORD dwWritesBytes = 0;
  14. char* pDestDllPath = R"(G:\mytools\TestDll\jiazai\Debug\TestDLL.dll)";
  15. //2.在目标进程中写入目标dll的路径
  16. bRet = WriteProcessMemory(
  17. hProcess, //目标进程
  18. lpAddr, //目标地址 目标进程中
  19. pDestDllPath, //源数据 当前进程中
  20. strlen(pDestDllPath)+1, //写多大
  21. &dwWritesBytes //成功写入的字节数
  22. );
  23. if (!bRet){
  24. VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
  25. return 0;
  26. }
  27. //3.向目标程序调用一个线程 创建远程线程 执行写入代码
  28. HANDLE hRemoteThread = CreateRemoteThread(hProcess, //目标进程
  29. NULL,
  30. 0,
  31. (LPTHREAD_START_ROUTINE)LoadLibraryA, //目标进程的回调函数
  32. lpAddr, //回调参数
  33. 0,
  34. NULL
  35. );

可以看到注入成功了

完整代码

  1. // shellcode.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include <Windows.h>
  5. #include <TlHelp32.h>
  6. #include <string>
  7. typedef void(*PFN_FOO)();
  8. int main()
  9. {
  10. //获取快照
  11. HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  12. PROCESSENTRY32 pe32;
  13. DWORD pid = 0;
  14. pe32.dwSize = sizeof(PROCESSENTRY32);
  15. //查看第一个进程
  16. BOOL bRet = Process32First(hSnap, &pe32);
  17. while (bRet)
  18. {
  19. bRet = Process32Next(hSnap, &pe32);
  20. if (wcscmp(pe32.szExeFile, L"procexp.exe") == 0){
  21. pid = pe32.th32ProcessID;
  22. break;
  23. }
  24. }
  25. //获取进程句柄
  26. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
  27. HANDLE hDestModule = NULL;
  28. //接下来找到该进程中kernel32.dll的基址
  29. hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
  30. MODULEENTRY32 mo32 = { 0 };
  31. mo32.dwSize = sizeof(MODULEENTRY32);
  32. bRet = Module32First(hSnap, &mo32);
  33. while (bRet)
  34. {
  35. bRet = Module32Next(hSnap, &mo32);
  36. wprintf(mo32.szExePath);
  37. std::wstring wstr = mo32.szExePath;
  38. if (wstr.find(L"KERNEL32.DLL") != std::string::npos){
  39. hDestModule = mo32.modBaseAddr;
  40. break;
  41. }
  42. }
  43. LPVOID lpDestAddr = NULL;
  44. if (hDestModule != NULL){
  45. //获取本进程的kernel32地址
  46. HMODULE hkernel32 = GetModuleHandleA("KERNEL32.DLL");
  47. //计算函数的位置
  48. LPVOID lploadlibrary = GetProcAddress(hkernel32, "LoadLibraryA");
  49. //获取了目标进程中的loadlibrary的地址
  50. lpDestAddr = (char*)lploadlibrary - (char*)hkernel32 + (char*)hDestModule;
  51. }
  52. //1.在目标进程开辟空间
  53. LPVOID lpAddr = VirtualAllocEx(
  54. hProcess, //在目标进程中开辟空间
  55. NULL, //表示任意地址,随机分配
  56. 1, //内存通常是以分页为单位来给空间 1页=4k 4096字节
  57. MEM_COMMIT, //告诉操作系统给分配一块内存
  58. PAGE_EXECUTE_READWRITE
  59. );
  60. if (lpAddr == NULL){
  61. printf("Alloc error!");
  62. return 0;
  63. }
  64. DWORD dwWritesBytes = 0;
  65. char* pDestDllPath = R"(G:\mytools\TestDll\TestDLL\Debug\TestDLL.dll)";
  66. //2.在目标进程中写入目标dll的路径
  67. bRet = WriteProcessMemory(
  68. hProcess, //目标进程
  69. lpAddr, //目标地址 目标进程中
  70. pDestDllPath, //源数据 当前进程中
  71. strlen(pDestDllPath)+1, //写多大
  72. &dwWritesBytes //成功写入的字节数
  73. );
  74. if (!bRet){
  75. VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
  76. return 0;
  77. }
  78. //3.向目标程序调用一个线程 创建远程线程 执行写入代码
  79. HANDLE hRemoteThread = CreateRemoteThread(hProcess, //目标进程
  80. NULL,
  81. 0,
  82. (LPTHREAD_START_ROUTINE)lpDestAddr, //目标进程的回调函数
  83. lpAddr, //回调参数
  84. 0,
  85. NULL
  86. );
  87. return 0;
  88. }

所以我们直接把代码写入dll里面就可以为所欲为了,但是不要写同步的代码,不然加载dll会卡死,然后如果我们想要获取到我们注入模块的地址,其实就是需要lpDestAddr的返回值,这里怎么获取,就有一个小技巧,hRemoteThread 这个是线程的模块而lpDestAddr才是我们注入的dll的地址,这里其实只需要获取线程的退出码,就可以获得loadlibrarya的返回基地址

  1. DWORD dwRetCode;
  2. //获取远线程的退出值
  3. GetExitCodeThread(hRemoteThread, &dwRetCode);

同一个文件允许同一个dll加载多次而且地址是不变的只是引用次数会+1

动态加载dll的实现+远线程注入的更多相关文章

  1. 用宏定义封装LoadLibrary,方便的动态加载dll

    同学们动态加载dll的时候是不是感觉挺麻烦的,每次都::LoadLibrary,::GetProcAddress,还要typedef一堆函数.最近闲来无聊,用宏封装了一下,可以少写不少代码,用来也挺方 ...

  2. Delphi静态加载DLL和动态加载DLL示例

    下面以Delphi调用触摸屏动态库xtkutility.dll为例子,说明如何静态加载DLL和动态加载DLL. 直接上代码. 1.静态加载示例 unit Unit1; interface uses W ...

  3. C# 利用反射动态加载dll

    笔者遇到的一个问题,dll文件在客户端可以加载成功,在web端引用程序报错.解决方法:利用反射动态加载dll 头部引用加: using System.Reflection; 主要代码: Assembl ...

  4. c#实现动态加载Dll(转)

    c#实现动态加载Dll 分类: .net2009-12-28 13:54 3652人阅读 评论(1) 收藏 举报 dllc#assemblynullexceptionclass 原理如下: 1.利用反 ...

  5. unity3d动态加载dll的API以及限制

    Unity3D的坑系列:动态加载dll 一.使用限制 现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Androi ...

  6. Unity3D的坑系列:动态加载dll

    我现在参与的项目是做MMO手游,目标平台是Android和iOS,iOS平台不能动态加载dll(什么原因找乔布斯去),可以直接忽略,而在Android平台是可以动态加载dll的,有了这个就可以实现代码 ...

  7. C#,动态加载DLL,通过反射,调用参数,方法,窗体

    .net中常会用到动态加载DLL,而DLL中可能包含各种参数.方法.窗体,如何来调用动态加载这些参数.方法.窗体呢? 在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系: System. ...

  8. 动态加载Dll时,通过Type生成类对象

    原文:动态加载Dll时,通过Type生成类对象 转:http://www.cnblogs.com/zfanlong1314/p/4197383.html "反射"其实就是利用程序集 ...

  9. c#实现动态加载Dll

    原文:c#实现动态加载Dll 原理如下: 1.利用反射进行动态加载和调用. Assembly assembly=Assembly.LoadFrom(DllPath); //利用dll的路径加载,同时将 ...

随机推荐

  1. PC,移动端H5实现实现小球加入购物车效果

    HTML部分: <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" ...

  2. 运行SQL文件报错:Got a packet bigger than 'max_allowed_packet' bytes With statement:

    英文意思:需要使用一个和现在相比较大的空间,可能mysql中的默认空间比文件需要的空间要小 解决方法: 1.修改配置文件中mysql的默认空间大小:在MYSQL的配置文件          my.in ...

  3. DOM 事件机制&事件委托

    一.事件机制 事件是在编程时系统内发生的动作或者发生的事情,系统会在事件出现的时候触发某种信号并且会提供一个自动加载某种动作的机制(来自MDN).每个事件都有事件处理器(有时也叫事件监听器),也就是触 ...

  4. HTML5移动开发之路(1)——jqMobi中Side Menu实现(类似人人网)

    记得以前在做Native App的时候类似于人人网侧边滑动的效果非常的热,很多app仿照该效果进行开发,在jqMobi中也有类似的效果被称为Side Menu.下面我们来一步一步实现该效果. 首先新建 ...

  5. SQL分组排序后取每组最新一条数据的另一种思路

    在hibernate框架和mysql.oracle两种数据库兼容的项目中实现查询每个id最新更新的一条数据. 之前工作中一直用的mybatis+oracle数据库这种,一般写这类分组排序取每组最新一条 ...

  6. C++字符串与指针

    字符串初始化 在C++中基本数据类型并不包括string,string类型其实是一种类类型,通过STL函数库中的模板类basic_string 实例化得到. int main () { // stri ...

  7. 为何要做seo关键词排名

    http://www.wocaoseo.com/thread-229-1-1.html 武汉seo百度指数在150左右,做seo的同仁们都知道这样的一件事情. 真正搜索武汉seo关键词能作为潜在客户的 ...

  8. 泊松分布算法的应用:开一家4S店

    王老板开了一家4S店,卖新车为主,车型也很单一,可是每个月销量都变化很大,他很头疼,该怎么备货,头疼的是: 1)备货少了,可以来了没货可能就不买,去别的店了 2)备货多了,占用库存不说,长久卖不出去就 ...

  9. 09.redis 哨兵主备切换时数据丢失的解决方案

    一.两种数据丢失的情况 1. 异步复制导致的数据丢失   因为master->slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了 2 ...

  10. Unity CommandLine

    CommandLineArguments https://docs.unity3d.com/Manual/CommandLineArguments.html Unity3D游戏开发之“unity3D命 ...