安全矩阵

 找回密码
 立即注册
搜索
查看: 3175|回复: 0

一份专供初学者食用的AES加密壳

[复制链接]

14

主题

16

帖子

318

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
318
发表于 2020-6-4 10:20:36 | 显示全部楼层 |阅读模式
此文发布于FreeBuff平台原文链接 :https://www.freebuf.com/articles/system/235167.html
前言
功能:一个源程序加壳与解壳的过程。
原理:
- 加壳过程,即在壳文件上加一个新节(加密后的源程序)。
- 解壳过程,即在加密壳的进程空间中,为解密后的源程序分配空间,再卸载壳程序将控制权交给源程序。

备注:
这里首先假设读者已经对PE文件有所了解,所以对PE文件的操作不再赘述。

壳和源程序其实都是独立的程序,加载到内存执行都会有自己的4GB空间。

符号说明:

src.exe 是没有加壳的源程序

shell.exe 是壳程序

shell.exe.encrypt.exe 是加密壳程序

OEP  : 程序入口点

实现环境

win10,VC++6.0

加壳过程

1、获取 shell.exe 的路径

2、获取 src.exe 的路径

3、将 src.exe 程序读取到内存中,加密

4、在 shell.exe 程序中新增一个节,并在新节中加入 加密后的src.exe 。

解壳过程

1、获取 shell.exe.encrypt.exe 的路径

2、在shell.exe.encrypt.exe 中获取 src.exe 的数据        

(0)定位到shell.exe.encrypt.exe的最后一个节   

(1)取出最后一个节的数据,解密

3、拉伸解密后的PE文件,存储到缓冲区

PE文件有两种状态:磁盘中的文件映像、内存中拉伸的状态

为什么会有两种状态呢? 首先得知道PE文件的执行过程:

当一个PE文件被执行时,PE装载器首先根据PE header的偏移量,跳转到PE header的位置。

当PE装载器跳转到PE header后,检查PE header是否有效。如果该PE header有效,就跳转到PE header的尾部。

接着是PE header尾部的节表,PE 装载器开始读取节表中的信息,并采用**文件映射方法将这些节段映射到内存**。

PE文件映射入内存后,PE装载器将继续处理PE文件中其他的目录表。

如上可知,PE文件执行的过程中,有一个从文件映射到内存的过程,所以就有PE的两种状态。

我们要做的是模仿PE加载器,让PE文件执行起来,所以就需要在内存中做拉伸。

4、以挂起方式运行shell.exe.encrypt.exe 进程

挂起主线程,是为了创建一个子线程来修改主线程的运行环境(CONTEXT),修改的运行环境为 shell.exe的运行环境。

(0)以挂起的方式创建shell.exe.encrypt.exe 进程,并得到主线程的 CONTEXT             

(1)卸载外壳程序的文件镜像                                                                         

如果shell.exe和 shell.exe.encrypt.exe 进程有相同基址,并且shell.exe的内存镜像小于进程shell.exe.encrypt.exe 的内存镜像,

那么只需要调用WriteProcessMemory来把可执行程序shell.exe的镜像写到进程shell.exe.encrypt.exe的内存空间中,并从 基址 开始执行即可。

否则,需要调用ZwUnmapViewOfSection来取消shell.exe.encrypt.exe的映像映射,然后通过 VirtualAllocEx 在 shell.exe.encrypt.exe 进程中为

可执行程序 shell.exe 分配足够的空间。调用VirtualAllocEx的时候,必须提供可执行程序 shell.exe 的 基址,用来分配从指定位置开始的的空间。

然后把可执行程序 shell.exe 的镜像复制到进程 shell.exe.encrypt.exe 的内存空间,并从分配的空间的起始地址开始执行。

[(CreateProcessA)创建一个新的进程,新进程在调用进程的安全上下文中运行。](https://docs.microsoft.com/en-us ... sapi-createprocessa)

  1. BOOL CreateProcessA(
  2.   LPCSTR                lpApplicationName, //执行的模块名,可为null
  3.   LPSTR                 lpCommandLine, //要执行的命令行,可为null
  4.   LPSECURITY_ATTRIBUTES lpProcessAttributes, //该指针决定子进程是否可以继承新进程对象的返回句柄,如果为空,则不能继承句柄。
  5.   LPSECURITY_ATTRIBUTES lpThreadAttributes,//该指针决定子线程是否可以继承新进程对象的返回句柄,如果为空,则不能继承句柄。
  6.   BOOL                  bInheritHandles, //处理每个可继承句柄是否被新进程继承的选项,true表示可继承,否表示不可继承
  7.   DWORD                 dwCreationFlags, //控制优先级类和流程创建的标志。
  8.   LPVOID                lpEnvironment,   //指向新进程的环境块的指针。
  9.   LPCSTR                lpCurrentDirectory, //进程当前目录的完整路径。
  10.   LPSTARTUPINFOA        lpStartupInfo, //启动信息
  11.   LPPROCESS_INFORMATION lpProcessInformation //进程信息
  12. );
复制代码


  [(ZwUnmapViewOfSection)卸载内存中的文件映射](https://docs.microsoft.com/en-us ... wunmapviewofsection)


  1. NTSYSAPI NTSTATUS ZwUnmapViewOfSection(
  2.   HANDLE ProcessHandle, //要取消映射的进程句柄
  3.   PVOID BaseAddress //从虚拟地址空间此处开始取消映射
  4. );
复制代码


[(VirtualAllocEx分配最佳内存)](https://docs.microsoft.com/en-us ... yapi-virtualallocex)
  1. LPVOID VirtualAllocEx(
  2.   HANDLE hProcess, //在此进程空间内分配内存
  3.   LPVOID lpAddress, //为要分配的页区域指定所需的起始地址的指针
  4.   SIZE_T dwSize,   //分配的内存大小
  5.   DWORD  flAllocationType, //分配的内存页的大小
  6.   DWORD  flProtect //要分配的页区域的内存保护
  7. );
复制代码


(2)在指定的位置申请指定大小的内存

指定的位置:src.exe 的 基址

指定大小:src.exe 的 SizeOfImage

内存分配:VirtualAllocEx        

CONTEXT对象的ebx寄存器指向进程的PEB,eax寄存器的值为进程的OEP,ebx+8 为进程的基址。

(3)如果申请内存失败,查看 src.exe 是否有重定位表的数据,如果有,就在任意位置申请指定大小的内存,然后修复重定位表。    指定大小:src.exe 的 SizeOfImage

(4)如果申请内存失败,并且没有重定位表的数据,返回失败     


如果指定位置的内存申请失败,说明该位置已经被占用,如果这时候要让程序在别的位置也能够正常运行,就依赖于重定位表,如果没有重定位表,那么程序就无法正常执行了。

(5)如果内存申请成功,复制PE数据到shell的进程空间中

(6)修正运行环境的ImageBase和OEP


5、恢复执行主进程

备注:这里的难点在于理解 挂起创建进程的过程,在挂起的过程中 涉及到的两个重点函数ZwUnmapViewOfSection  VirtualAllocEx 也比较难理解,需要了解操作系统的内存机制。



几点思考

1、以前只会用工具,死记步骤,现在除了用工具,还可以写简单的工具(hai shi yong gong ju)(~~o(>_<)o ~~)。

2、更重要的收获是,从哪些大佬的利器当中汲取一点智慧。

3、以前老师逼着学都学不下去的操作系统,现在居然饶有兴趣地看了Over And Over Again (but, yi ran shi xiao cai)。

4、程序加壳是为了保护程序,那么这种"保护"是不是也可以成为恶意软件的”护身符“,绕过杀软的扫描?后续会带着这些思考继续学习逆向工程。

主要代码实现

代码中都有详细的备注,由于篇幅有限,没有贴上全部的代码。欢迎大佬指正,新人共勉!
  1. 1、AES加壳
  2. #include "stdafx.h"
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<time.h>

  6. #include "AES.h"
  7. #include "PEOperate.h"

  8. /* AES 加密*/
  9. BOOL Packer()
  10. {

  11.         TCHAR* shellPath = "shell.exe";
  12.         TCHAR* srcPath = "src.exe";

  13.         DWORD SrcFileSize=0;
  14.         LPVOID pSrcFileBuffer = LoadPEFile(srcPath,SrcFileSize);
  15.         CHAR* pOld = (CHAR*)pSrcFileBuffer;

  16.         //循环加密
  17.         //DWORD fileSize = 0;        
  18.         LPVOID pSrcFileBufferEncode = malloc(SrcFileSize);
  19.         memset(pSrcFileBufferEncode,SrcFileSize,0);
  20.         CHAR* pNew = (CHAR*)pSrcFileBufferEncode;

  21.         //void TestAddSecToFile(LPSTR lpszFile)
  22.         //数据加密

  23.         unsigned char key[] =
  24.         {
  25.                 0x2b, 0x7e, 0x15, 0x16,
  26.                 0x28, 0xae, 0xd2, 0xa6,
  27.                 0xab, 0xf7, 0x15, 0x88,
  28.                 0x09, 0xcf, 0x4f, 0x3c
  29.         };
  30.         AES aes(key);
  31.         aes.Cipher(pSrcFileBuffer, SrcFileSize);
  32.         pSrcFileBufferEncode = pSrcFileBuffer;
  33.         /*
  34.         将加密代码加入到文件内部
  35.         shellPath 源壳文件
  36.         pSrcFileBufferEncode  源程序加密后的文件
  37.         SrcFileSize 加密文件的长度,由于这里使用的加密算法(AES)只是异或和移位循环,所以源文件和加密文件的长度都是一样的
  38.         */
  39.         AddSecToFile(shellPath,pSrcFileBufferEncode,SrcFileSize);

  40.         return TRUE;
  41. }

  42. /* 异或加密
  43. BOOL Packer()
  44. {

  45.         TCHAR* shellPath = "shell.exe";
  46.         TCHAR* srcPath = "src.exe";

  47.         DWORD SrcFileSize=0;
  48.         LPVOID pSrcFileBuffer = LoadPEFile(srcPath,SrcFileSize);
  49.         CHAR* pOld = (CHAR*)pSrcFileBuffer;

  50.         //循环加密
  51.         //DWORD fileSize = 0;        
  52.         LPVOID pSrcFileBufferEncode = malloc(SrcFileSize);
  53.         memset(pSrcFileBufferEncode,SrcFileSize,0);
  54.         CHAR* pNew = (CHAR*)pSrcFileBufferEncode;

  55.         //void TestAddSecToFile(LPSTR lpszFile)
  56.         //数据加密
  57.         for(int i=0;i<(int)SrcFileSize;i++)
  58.         {
  59.                 pNew[i] = pOld[i]^KEY;        
  60.         }
  61.         

  62.         //将加密代码加入到文件内部
  63.         //shellPath 源壳文件
  64.         //pSrcFileBufferEncode  源程序加密后的文件
  65.         //SrcFileSize 加密文件的长度,由于这里使用的加密算法(AES)只是异或和移位循环,所以源文件和加密文件的长度都是一样的
  66.         
  67.         AddSecToFile(shellPath,pSrcFileBufferEncode,SrcFileSize);

  68.         return TRUE;
  69. }
  70. */

  71. #ifndef DEBUG //测试
  72. void main()
  73. {
  74.         Packer();
  75. }
  76. #endif
  77. 2、AES解壳
  78. #include "stdafx.h"
  79. #include<stdio.h>
  80. #include<stdlib.h>
  81. #include<time.h>

  82. #include "AES.h"
  83. #include "PEOperate.h"

  84.                         
  85. // 重定向PE用到的地址  
  86. void DoRelocation(LPVOID pFileBuffer, void *OldBase, void *NewBase)
  87. {
  88.         PIMAGE_DOS_HEADER pDosHeader = NULL;
  89.         pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;        
  90.         PIMAGE_NT_HEADERS peH = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
  91.         
  92.     unsigned long Delta = (unsigned long)NewBase - peH->OptionalHeader.ImageBase;  
  93.     PImageBaseRelocation p = (PImageBaseRelocation)((unsigned long)OldBase   
  94.         + peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);  
  95.     while(p->VirtualAddress + p->SizeOfBlock)  
  96.     {  
  97.         unsigned short *pw = (unsigned short *)((int)p + sizeof(*p));  
  98.         for(unsigned int i=1; i <= (p->SizeOfBlock - sizeof(*p)) / 2; ++i)  
  99.         {  
  100.             if((*pw) & 0xF000 == 0x3000){  
  101.                 unsigned long *t = (unsigned long *)((unsigned long)(OldBase) + p->VirtualAddress + ((*pw) & 0x0FFF));  
  102.                 *t += Delta;  
  103.             }  
  104.             ++pw;  
  105.         }  
  106.         p = (PImageBaseRelocation)pw;  
  107.     }  
  108. }  

  109. VOID GetEncryptFileContext(LPVOID pFileBuffer,DWORD &OEP,DWORD &ImageBase)
  110. {
  111.         PIMAGE_DOS_HEADER pDosHeader = NULL;
  112.         PIMAGE_NT_HEADERS pNTHeader = NULL;
  113.         PIMAGE_FILE_HEADER pPEHeader = NULL;
  114.         PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
  115.         PIMAGE_SECTION_HEADER pSectionHeader = NULL;
  116.         //pFileBuffer= ReadPEFile(lpszFile);
  117.         
  118.         if(!pFileBuffer)
  119.         {
  120.                 printf("文件读取失败\n");
  121.                 return;
  122.         }
  123.         
  124.         //MZ标志
  125.         if(*((PWORD)pFileBuffer)!=IMAGE_DOS_SIGNATURE)
  126.         {
  127.                 printf("不是有效的MZ标志\n");
  128.                 free(pFileBuffer);
  129.                 return;
  130.         }
  131.         //DOS头
  132.         pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;

  133.         //判断是否是有效的PE
  134.         if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew))!=IMAGE_NT_SIGNATURE)
  135.         {
  136.                 printf("不是有效的PE标志\n");
  137.                 free(pFileBuffer);
  138.                 return;
  139.         }
  140.         
  141.         pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
  142.         
  143.         //PE头
  144.         pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
  145.         
  146.         //可选择PE头
  147.         pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);

  148.         //获取信息
  149.         OEP = pOptionHeader->AddressOfEntryPoint;
  150.         ImageBase = pOptionHeader->ImageBase;

  151. }

  152. VOID GetNtHeaderInfo(LPVOID pFileBuffer,DWORD &ImageBase,DWORD &ImageSize)
  153. {
  154.         PIMAGE_DOS_HEADER pDosHeader = NULL;
  155.         PIMAGE_NT_HEADERS pNTHeader = NULL;
  156.         PIMAGE_FILE_HEADER pPEHeader = NULL;
  157.         PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
  158.         PIMAGE_SECTION_HEADER pSectionHeader = NULL;

  159.         if(!pFileBuffer)
  160.         {
  161.                 printf("文件读取失败\n");
  162.                 return;
  163.         }
  164.         
  165.         //MZ标志
  166.         if(*((PWORD)pFileBuffer)!=IMAGE_DOS_SIGNATURE)
  167.         {
  168.                 printf("不是有效的MZ标志\n");
  169.                 free(pFileBuffer);
  170.                 return;
  171.         }
  172.                
  173.         //DOS头
  174.         pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;

  175.         //判断是否是有效的PE
  176.         if(*((PDWORD)((DWORD)pFileBuffer+pDosHeader->e_lfanew))!=IMAGE_NT_SIGNATURE)
  177.         {
  178.                 printf("不是有效的PE标志\n");
  179.                 free(pFileBuffer);
  180.                 return;
  181.         }

  182.         pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);

  183.         //NT头
  184.         pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);

  185.         //可选择PE头
  186.         pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);


  187.         //获取信息
  188.         ImageBase = pOptionHeader->ImageBase;
  189.         ImageSize = pOptionHeader->SizeOfImage;
  190.         
  191. }


  192. //是否有重定位表
  193. BOOL HasRelocationTable(LPVOID pFileBuffer)  
  194. {  

  195.         PIMAGE_DOS_HEADER pDosHeader = NULL;
  196.         PIMAGE_NT_HEADERS pNTHeader = NULL;
  197.         PIMAGE_FILE_HEADER pPEHeader = NULL;
  198.         PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
  199.         PIMAGE_SECTION_HEADER pSectionHeader = NULL;
  200.         PIMAGE_DATA_DIRECTORY DataDirectory=NULL;
  201.         
  202.         //Header信息
  203.         pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
  204.         pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
  205.         pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
  206.         pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
  207.         pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);

  208.         //定位Directory_Data;
  209.         DataDirectory = pOptionHeader->DataDirectory;
  210.         
  211.     return (DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)  
  212.         && (DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);  
  213. }  

  214. /*
  215. 在指定位置分配空间

  216. shellDirectory : 原始壳的地址
  217. shellProcess : 加密壳的进程句柄
  218. encryptFileBuffer :加密壳程序的FileBuffer
  219. 位置: Src的ImageBase
  220. 大小:  Src的SizeOfImage
  221. */
  222. LPVOID AllocShellSize(LPSTR shellDirectory,HANDLE shellProcess,LPVOID encryptFileBuffer)
  223. {
  224.         typedef void *(__stdcall *pfVirtualAllocEx)(unsigned long, void *, unsigned long, unsigned long, unsigned long);  
  225.         pfVirtualAllocEx MyVirtualAllocEx = NULL;  
  226.         MyVirtualAllocEx = (pfVirtualAllocEx)GetProcAddress(GetModuleHandle("Kernel32.dll"), "VirtualAllocEx"); //获取VirtualAllocEx 函数地址
  227.         
  228.         LPVOID pShellBuffer = ReadPEFile(shellDirectory);

  229.         DWORD shellImageBase=0;
  230.         DWORD shellImageSize=0;
  231.         DWORD encryptImageBase=0;
  232.         DWORD encryptImageSize=0;

  233.         //获得ImageBase ImageSize, 进行信息比较
  234.         GetNtHeaderInfo(pShellBuffer,shellImageBase,shellImageSize);
  235.         GetNtHeaderInfo(encryptFileBuffer,encryptImageBase,encryptImageSize);

  236.         if(shellImageBase == 0 || shellImageSize==0 || encryptImageBase == 0 || encryptImageSize==0)
  237.         {
  238.                 MessageBox(0,"申请空间失败","失败",0);
  239.                 return NULL;
  240.         }

  241.         void *p = NULL;  

  242.         //在指定进程的指定位置分配内存
  243.         /*
  244.                 VirtualAllocEx:在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0。
  245.                 LPVOID VirtualAllocEx(
  246.                 HANDLE hProcess, // 申请内存所在的进程句柄
  247.                 LPVOID lpAddress, // 保留页面的内存地址;一般用NULL自动分配
  248.                 SIZE_T dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
  249.                 DWORD flAllocationType,
  250.                 DWORD flProtect
  251.     */
  252.         //如果指定位置内存没有被占用,则取 MAX(shellImageSize,encryptImageSize)
  253.         if(shellImageBase == encryptImageBase )
  254.         {
  255.                 shellImageSize = (shellImageSize >= encryptImageSize) ? shellImageSize: encryptImageSize;
  256.                 // 最小的分配方式,具体用法查MSDN,分配失败会返回NULL
  257.                 p = VirtualAllocEx(shellProcess,(void*)shellImageBase,shellImageSize,MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  258.         }
  259.         
  260.         // 指定位置被占用 & 进程中有重定位表
  261.         if((p == NULL) && HasRelocationTable(encryptFileBuffer)){  
  262.                 //任意位置分配空间
  263.                 p = VirtualAllocEx(shellProcess, NULL, encryptImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  264.             
  265.                 //重定位处理
  266.                 if(p) {
  267.                         DoRelocation(encryptFileBuffer, (void*)encryptImageBase, p);
  268.                 }else{
  269.                         
  270.                         MessageBox(0,"申请空间失败3","失败3",0);
  271.                         return NULL;
  272.                 }
  273.     }

  274.         return p;
  275. }

  276. /*
  277. 卸载(释放)原外壳占用内存
  278. ProcHnd: 卸载的进程句柄
  279. BaseAddr: 卸载的基址
  280. 返回:TRUE 卸载成功
  281.        FALSE 卸载失败
  282. */
  283. BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr)  
  284. {  
  285.     typedef unsigned long (__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long);  
  286.     pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;  
  287.     BOOL res = FALSE;  
  288.     HMODULE m = LoadLibrary("ntdll.dll");  
  289.     if(m){  
  290.                 ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(m, "ZwUnmapViewOfSection");

  291.                 if(ZwUnmapViewOfSection)  
  292.                         res = (ZwUnmapViewOfSection((unsigned long)ProcHnd, BaseAddr) == 0);  //取消映射目标进程的内存
  293.                 FreeLibrary(m);  
  294.     }  
  295.     return res;  
  296. }  


  297. LPVOID GetLastSecData(LPSTR lpszFile,DWORD &fileSize)
  298. {

  299.         LPVOID pFileBuffer = NULL;
  300.         pFileBuffer= ReadPEFile(lpszFile);
  301.         if(!pFileBuffer)
  302.         {
  303.                 printf("文件读取失败\n");
  304.                 return NULL;
  305.         }


  306.         PIMAGE_DOS_HEADER pDosHeader = NULL;
  307.         PIMAGE_NT_HEADERS pNTHeader = NULL;
  308.         PIMAGE_FILE_HEADER pPEHeader = NULL;
  309.         PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
  310.         PIMAGE_SECTION_HEADER pSectionHeader = NULL;
  311.         PIMAGE_SECTION_HEADER pSectionHeader_LAST = NULL;

  312.         //Header信息
  313.         pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
  314.         pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
  315.         pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader)+4);
  316.         pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
  317.         pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader+pPEHeader->SizeOfOptionalHeader);
  318.         pSectionHeader_LAST = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader+(pPEHeader->NumberOfSections-1)*40);

  319.         int fileLength = pSectionHeader_LAST->PointerToRawData+pSectionHeader_LAST->SizeOfRawData;
  320.         
  321.         //判断是否已经加壳
  322.         if(strcmp((char*)pSectionHeader_LAST->Name,".NewSec")!=0)
  323.         {
  324.                 MessageBox(0,"没有加壳","错误",0);
  325.                 return NULL;
  326.         }
  327.         
  328.         fileSize = pSectionHeader_LAST->SizeOfRawData;
  329.         LPVOID pEncryptBuffer = malloc(fileSize);
  330.         memset(pEncryptBuffer,0,fileSize);
  331.         CHAR* pNew = (CHAR*)pEncryptBuffer;

  332.         CHAR* pOld = (CHAR*)((DWORD)pFileBuffer+pSectionHeader_LAST->PointerToRawData);

  333.         //将最后一个段的数据拷贝到pEncryptBuffer中,并解密
  334.         unsigned char key[] =
  335.         {
  336.                 0x2b, 0x7e, 0x15, 0x16,
  337.                 0x28, 0xae, 0xd2, 0xa6,
  338.                 0xab, 0xf7, 0x15, 0x88,
  339.                 0x09, 0xcf, 0x4f, 0x3c
  340.         };
  341.         AES aes(key);
  342.         aes.InvCipher(pOld, fileSize);
  343.         pEncryptBuffer = pOld;
  344.         
  345.         return pEncryptBuffer;
  346. }

  347. #ifdef DEBUG
  348. int main()
  349. {

  350.         char* shellDirectory = "shell.exe.encrypt1.exe"; //这是加壳后的程序
  351.         DWORD encryptSize = 0;

  352.         LPVOID encryptFileBuffer = NULL;
  353.         encryptFileBuffer = GetLastSecData(shellDirectory,encryptSize);
  354.         
  355.         //失败则结束
  356.         if(encryptFileBuffer == NULL)
  357.         {
  358.                 MessageBox(0,"解密失败","失败",0);
  359.                 return 0;
  360.         }
  361.         
  362.         /*
  363.         以挂起的形式创建进程
  364.         创建子进程,并且需要在子进程初始化之前修改运行环境(修改壳程序的运行环境-->子进程的运行环境)
  365.         */
  366.         STARTUPINFO si={0};
  367.         si.cb = sizeof(STARTUPINFO);
  368.         PROCESS_INFORMATION pi;
  369.         CreateProcessA(shellDirectory,
  370.                 NULL,
  371.                 NULL,
  372.                 NULL,
  373.                 FALSE,
  374.                 CREATE_SUSPENDED,
  375.                 NULL,
  376.                 NULL,
  377.                 &si,&pi);
  378.         
  379.         TCHAR szTempStr[256]={0};
  380.         
  381.         sprintf(szTempStr,"进程消息: %x , %x \n",pi.hProcess,pi.hThread);
  382.         CONTEXT contx;  //外壳的上下文环境
  383.         contx.ContextFlags = CONTEXT_FULL;
  384.         GetThreadContext(pi.hThread,&contx);
  385.         DWORD shellOEP = contx.Eax; //OEP的值存在EAX寄存器中
  386.         
  387.         char* baseAddress = (CHAR*)contx.Ebx+8;//获取IMAGE_BASE的信息
  388.         TCHAR szBuffer[4]={0};
  389.         ReadProcessMemory(pi.hProcess,baseAddress,szBuffer,4,NULL);
  390.         int* fileImageBase;
  391.         fileImageBase = (int*)szBuffer;
  392.         DWORD shellImageBase  = *fileImageBase;

  393.         BOOL isUnload = UnloadShell(pi.hProcess,shellImageBase);//卸载外壳程序内存

  394.         LPVOID p = AllocShellSize(shellDirectory,pi.hProcess,encryptFileBuffer);//在外壳进程空间的指定位置分配内存
  395.         if(p == NULL)
  396.         {
  397.                 MessageBox(0,"内存分配失败","错误",0);
  398.                 return 0;
  399.         }
  400.         

  401.         DWORD pEncryptImageSize=0;
  402.         LPVOID pEncryptImageBuffer = FileBufferToImageBuffer(encryptFileBuffer,pEncryptImageSize);//将加密的源程序数据拷贝到新分配的内存空间
  403.         
  404.         unsigned long old;
  405.         WriteProcessMemory(pi.hProcess, (void *)(contx.Ebx+8), &p, sizeof(DWORD), &old);

  406.         if(WriteProcessMemory(pi.hProcess, p, pEncryptImageBuffer, pEncryptImageSize, &old))
  407.         {// 复制PE数据到shell的进程空间中  

  408.                 DWORD encryptFileOEP = 0;
  409.                 DWORD encryptFileImageBase = 0;
  410.                                 
  411.                 GetEncryptFileContext(encryptFileBuffer,encryptFileOEP,encryptFileImageBase);

  412.         contx.ContextFlags = CONTEXT_FULL;
  413.                                 
  414.                 //修复入口地址为源程序的入口
  415.                 contx.Eax = encryptFileOEP + (DWORD)p;
  416.         SetThreadContext(pi.hThread, &contx);// 更新主进程的运行环境为源程序的运行环境
  417.         
  418.                 LPVOID szBufferTemp = malloc(pEncryptImageSize);
  419.                 memset(szBufferTemp,0,pEncryptImageSize);
  420.                 ReadProcessMemory(pi.hProcess,p,szBufferTemp,pEncryptImageSize,NULL);
  421.                
  422.                 //////这个是测试用的 实际壳程序将加密文件解密后直接跳转到源程序的入口执行:脱壳后程序保存到文件////////
  423.                 MemeryTOFile(szBufferTemp,"111111.exe");
  424.                 ///////////////////////////////////////////////////////////////////////////////////////////////////////

  425.         ResumeThread(pi.hThread);// 恢复执行主线程  
  426.         CloseHandle(pi.hThread);  
  427.      }
  428.         return 0;
  429. }
  430. #endif
复制代码



参考资料

《加密与解密》

《应用密码学:协议、算法与C源程序》


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-3-29 05:01 , Processed in 0.018997 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表