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
- VC写加密壳
- 用jni方式传递DES加密字符串 - 补充"java调用vc写的dll"
- vc简单加密字符串
- 加密解密字符串vc
- vc base64加密、解密
- 自写加密方法
- 写MD5加密
- js写md5加密
- 写个vc工具
- vc 写windows 服务
- vc 写windows 服务
- 用vc写DLL - -
- VC++读、写注册表
- 用VC写线程
- vc++ 写xml
- 用VC写DLL
- VC打造文件加密工具
- vc++实现文件加密解密
- Servlet学习总结
- pull方式解析xml文件以及用Serializer序列化器生成xml文件
- 计算机进制理解
- samba服务器配置文件示例
- FiniteTimeAction,Follow,Speed源码分析
- VC写加密壳
- B2B移动商务社交平台贸人E家APP在深首发
- poj2388Who's in the Middle【堆排序】
- 第三方微博登陆,通过OAuth 2.0 进行授权
- 24. 排序
- HDOJ 5400 Arithmetic Sequence 暴力枚举
- JMX监控Zookeeper状态Java API
- HashMap HashTable CourrentHashMap 区别
- Git Gui for Windows的建库、克隆(clone)、上传(push)、下载(pull)、合并