VC写加密壳

来源:互联网 发布:windows返回快捷键 编辑:程序博客网 时间:2024/05/16 23:36

准备工作

工程文件已经打包。 解决方案内有2个项目:EasyProtect 和 Shell。
EasyProtect 是用来加壳的。
Shell就是壳的核心,编译后是Shell.dll。

EasyProtect 流程


打开被加壳文件 -> 打开Shell.dll -> 提取shell.dll代码段(.MyCode) 数据 ->在被加壳文件中创建新的区段->重定位Shell.dll的代码数据->把重定位好的代码数据写到被加壳程序的新区段中->加密被加壳程序代码段->修正OEP

但是还有一些细节没有提及。
packPE 加壳函数。

bool packPE(char * szFileName){        CPeFile MyPe;        CPeFile MyShell;  // 壳也是一个编译的pe文件        PIMAGE_SECTION_HEADER pNewSec,pShellSec;        PMyDosHeader pDosHead;        char *shellcode;        if(!MyPe.LoadPe(szFileName))                return false;        if(!MyShell.LoadPe(SHELL_FILE))                return false;        pDosHead = (PMyDosHeader)MyPe.GetBuffer();        if (pDosHead->info.flag==0x1447) // 已经加密过了                return false;        /* 获取壳的代码段 */        pShellSec = MyShell.FindSectionByName(".MyCode");        pNewSec = MyPe.AddSection(".fishc",pShellSec->SizeOfRawData);        if(!pNewSec) return false;        // 重定位数据 0.0         FixRelocBase2(MyShell,MyPe.GetImageBase(),pNewSec->VirtualAddress);        // 复制代码数据        shellcode = new char[pShellSec->SizeOfRawData];        bool bRet =        MyShell.ReadDataByRaw(pShellSec->PointerToRawData,shellcode,pShellSec->SizeOfRawData);        if(!bRet)return false;        bRet =         MyPe.WriteDataByRaw(pNewSec->PointerToRawData,shellcode,pShellSec->SizeOfRawData);        if(!bRet) return false;        delete shellcode;        // 加密代码段        char *buf;        char *key = "mooncakeisverylovelygirl";        PIMAGE_SECTION_HEADER cs;        pDosHead->info.CodeSec=MyPe.GetCodeSection();        cs = MyPe.GetSectionById(pDosHead->info.CodeSec);        buf = (char *)MyPe.GetBuffer();        xorPlus(&buf[cs->PointerToRawData],cs->SizeOfRawData,key,strlen(key));        // 修正OEP        DWORD t;         // 获得壳的入口函数在.MyCode中的偏移        t = MyShell.GetEntry() - pShellSec->VirtualAddress;        // 把这个偏移加上新区段的基址就是最终的入口偏移        t+=pNewSec->VirtualAddress;        //保存源程序OEP        DWORD orgOEP;        orgOEP = MyPe.GetEntry()+MyPe.GetImageBase();        pDosHead->info.oep = orgOEP;        pDosHead->info.flag = 0x1447;        MyPe.SetNewEntry(t);        MyPe.FlushBuffer(); //把缓冲区的数据保存到硬盘        return true;}

CPeFile 是我写的PE类,篇幅有限,就不贴这个类的代码了
谈谈重定位:
重定位的基本原理不想多讲,参看小甲鱼老师的视频。 这里需要注意的是Shell.dll 与 被加壳程序的区块基址是不同的。 所以我把这里的重定位称作“区块重定位”因为不具备普遍性,所以就没有收入到CPeFile类中,但是需要CPeFile类的支持~采用了硬编码,shell.dll的代码段是.MyCode

void FixRelocBase2(CPeFile &cPe,DWORD ImageBase,DWORD SecBase){        PIMAGE_BASE_RELOCATION rel;        DWORD relNum;        rel=(PIMAGE_BASE_RELOCATION)(                cPe.rva_to_buffer(cPe.GetDataDirInfo(IMAGE_DIRECTORY_ENTRY_BASERELOC)->VirtualAddress)                );        while (rel->SizeOfBlock)        {                relNum=(rel->SizeOfBlock-8)/2;                for (DWORD i=0;i<relNum;i++)                {                        char Type;                        Type = rel->TypeOffset[i]>>12;                        if (Type==3)                        {                                DWORD *relAdr;                                relAdr   = (DWORD *)rel->VirtualAddress;                                relAdr   = (DWORD *)cPe.rva_to_buffer((DWORD)relAdr + (rel->TypeOffset[i]&0x0fff));                                *relAdr -= cPe.GetImageBase();  //算出RVA                                *relAdr -=cPe.FindSectionByName(".MyCode")->VirtualAddress;                                *relAdr +=SecBase;                                *relAdr +=ImageBase;                        }                }                rel=(PIMAGE_BASE_RELOCATION)((DWORD)rel + rel->SizeOfBlock);        }}

在加壳的过程中,我们可能有些重要信息需要保存,比如OEP。 在EasyProtect中这些信息都保存在DOS头中。DOS只有首尾的字段有效,其他的都可以任意发挥、因此,可以把DOS定义为如下数据结构:

#pragma pack(push)#pragma pack(1)typedef struct {        WORD e_magic; // MZ  必须        struct{                WORD selfdata[23];// 共有29 * 2 字节可以自己定义-.-                DWORD flag; //标志位                DWORD oep;                DWORD CodeSec; //代码段序号        }info;        LONG e_lfanew; // 必须字段 PE头的偏移地址}MyDosHeader,*PMyDosHeader;#pragma pack(pop)

Shell的流程

Shell寄居在被加壳程序中,拥有优先执行权。
Shell流程如下:
保存寄存器状态 ->动态获取API ->加载配置信息->修改代码段内存属性->解密代码段->跳回OEP

#pragma code_seg(".MyCode")  // 设置代码段为.MyCode#pragma comment(linker, "/MERGE:.data=.MyCode") // 设置数据段为 .MyCode#pragma comment(linker, "/MERGE:.rdata=.MyCode")#pragma comment(linker,"/ENTRY:ShellEntry") // 设置入口函数为ShellEntry#include <windows.h>#pragma pack(push)#pragma pack(1)typedef struct {  WORD e_magic; // MZ  必须  struct{    WORD selfdata[23];// 共有29 * 2 字节可以自己定义-.-    DWORD flag; //标志位    DWORD oep;    DWORD CodeSec; //代码段序号  }info;  LONG e_lfanew; // 必须字段 PE头的偏移地址}MyDosHeader,*PMyDosHeader;#pragma pack(pop)#define RvaToVa(base_,rva_) ((ULONG)base_+(ULONG)rva_) //任意指针与整数相减#define p_sub(s1,s2) ((ULONG)s1-(ULONG)s2)//任意指针与整数相加#define  p_add(s1,s2) ((ULONG)s1+(ULONG)s2)// jmp 指令操作码#define jmp_opcode 0xe9/// 一些函数定义typedef DWORD(__stdcall *_GetModuleHandleA)(char *ModuleName);typedef DWORD(__stdcall *_LoadLibraryA)(char *ModuleName);typedef bool(__stdcall *_IsDebuggerPresent)();typedef DWORD(__stdcall *_VirtualProtect)( LPVOID lpAddress,                       SIZE_T dwSize,                       DWORD flNewProtect,                       PDWORD lpflOldProtect);char * shellinfo = "Hello world~.~";int inline __stdcall _strlen(char *str){  int count = 0;  while (*str!='\0')  {    str++;    count++;  }  return count;}int inline __declspec(naked) __stdcall GetKernelBase(){  _asm  {      mov eax,fs:[30h] //;PEB的地址       mov eax, [eax + 0ch] //;Ldr的地址       mov esi, [eax + 01ch] //;Flink地址       lodsd        mov eax, [eax + 08h] //;eax就是kernel32.dll的地址      ret  }}PVOID  _getProcAddress(HMODULE imageBase,char *ExportName){  PIMAGE_DOS_HEADER pDosHead;  PIMAGE_NT_HEADERS pNtHead;  PIMAGE_EXPORT_DIRECTORY pExport;  WORD *Ord;  DWORD *FunAddr;  DWORD *name_list;  DWORD baseorder;  if (!imageBase)  {    return NULL;  }  pDosHead = (PIMAGE_DOS_HEADER)imageBase;  pNtHead = (PIMAGE_NT_HEADERS)p_add(imageBase,pDosHead->e_lfanew);  pExport=(PIMAGE_EXPORT_DIRECTORY)RvaToVa(imageBase,pNtHead->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);  baseorder = pExport->Base;  Ord=(WORD *)RvaToVa(imageBase,pExport->AddressOfNameOrdinals);  FunAddr=(DWORD *)RvaToVa(imageBase,pExport->AddressOfFunctions);  name_list = (DWORD *)RvaToVa(imageBase,pExport->AddressOfNames);  for (DWORD i=0;i<pExport->NumberOfFunctions;i++)  {    DWORD Index;    DWORD FunRva;    char *FunName;    FunName = (char *)RvaToVa(imageBase,name_list[i]);    if (!strcmp(FunName,ExportName))    {      Index=Ord[i];      FunRva=FunAddr[Index];      FunRva+=(ULONG)imageBase;      return (PVOID *)FunRva;    }  }  return NULL;}void xorPlus(char *soure,int dLen,char *Key,int Klen){  for (int i=0;i<dLen;)  {    for (int j=0;j<Klen;j++,i++)    {      soure[i]=soure[i] ^ Key[j];      soure[i]=~soure[i];    }  }}PIMAGE_SECTION_HEADER GetSectionById(PIMAGE_DOS_HEADER pDos,WORD id){  PIMAGE_SECTION_HEADER fisrt;  char *buf;  buf = (char *)pDos;  fisrt = (PIMAGE_SECTION_HEADER)&buf[pDos->e_lfanew+sizeof(IMAGE_NT_HEADERS)];  return &fisrt[id];}PMyDosHeader info; // 这个变量很重要·char *key = "mooncakeisverylovelygirl";void _main(){  DWORD kernelBa;  DWORD kernel32;  _GetModuleHandleA getmodulehandlea;  _VirtualProtect  virtualprotect;  DWORD ImageBase;  PIMAGE_SECTION_HEADER cs;  DWORD old,o2;  kernelBa = GetKernelBase();  getmodulehandlea=(_GetModuleHandleA)_getProcAddress((HMODULE)kernelBa,"GetModuleHandleA");  kernel32 = getmodulehandlea("kernel32");  virtualprotect=(_VirtualProtect)_getProcAddress((HMODULE)kernel32,"VirtualProtect");  ImageBase = getmodulehandlea(NULL);  info = (PMyDosHeader)ImageBase;  cs = GetSectionById((PIMAGE_DOS_HEADER)ImageBase,info->info.CodeSec);  // 修改区块属性  char * soure = (char *)RvaToVa(ImageBase,cs->VirtualAddress);  virtualprotect(soure,cs->SizeOfRawData,PAGE_EXECUTE_READWRITE,&old);  xorPlus(soure,cs->SizeOfRawData,key,24);  virtualprotect(soure,cs->SizeOfRawData,old,&o2);}int __declspec(naked) __stdcall ShellEntry()  // 这里是壳的入口点{  _asm  {    pushad  // 保存寄存器信息    call _main    popad    mov eax,info    push [eax].info.oep    nop    nop    ret  }}

一个比较有意思的现象,下面是一段网上流传的Kernel32.dll基址获取代码:

int inline __declspec(naked) __stdcall GetKernelBase(){        _asm        {                        mov eax,fs:[30h] //;PEB的地址                         mov eax, [eax + 0ch] //;Ldr的地址                         mov esi, [eax + 01ch] //;Flink地址                         lodsd                          mov eax, [eax + 08h] //;eax就是kernel32.dll的地址                        ret        }}

我实际测试的时候得到的是KernelBa.dll的基址(我的系统是win7) 但是KernelBa导出有GetModuleHandleA 所以问题就迎刃而解了。通过GetModuleHandleA 获取Kernel32的基址就OK啦~
附件请到看雪下载:http://bbs.pediy.com/showthread.php?t=202453

0 0
原创粉丝点击