软件加壳的目的是为了防止外部程序对软件进行静态反汇编分析或者动态分析达到对软件保护的目的。壳子的种类很多,大体种类分2种二进制壳和指令壳,二进制壳不改变硬编码的含义,指令壳则改变了原有硬编码的含义。 本篇文章介绍二进制壳的加壳的一种思路及实现。
1,加壳过程
加壳流程如下图所示:
(1)将待加壳程序全部进行加密,加密可以采用任何方式;(2)在壳子程序中新增一个节,大小为待加壳程序的大小+解壳程序的大小;(3)将解壳程序拷贝到1位置,加密后的待加壳程序拷贝到2位置;(4)修改壳子程序的OEP到新增节的位置。(5)存盘壳子程序。
这里的壳子程序可以是任意程序,只提供了外壳。壳子程序是任意的,新增节后的位置也是变化的,所以解壳程序一定是shell程序。
2,解壳程序
打开壳子程序的时候,程序会从OEP开始启动,执行的是解壳程序:(1)读取加密后的待加壳程序2;(2)解密得到原来的程序;(3)以挂起的方式创建进程,创建的进程是壳子程序进程,这里要注意的是以挂起的方式创建的进程,没有加载任何模块。(4)获取新创建的进程的Contex环境,(5)卸载外壳程序,卸载掉原来壳子程序imageBase开始到程序内存镜像这部分空间的数据,此时只有空间,而内部的内存处于没有使用状态。(6)在创建的进程内部在待加壳程序的imageBase处申请内存空间,大小是待加壳程序的imageBase。(7)将待加壳程序拉伸,复制到刚刚申请到的imageBase处,此时,在壳子的空间内部,已经偷天换日编程了待加壳的程序了。(8)修改外壳Contex的imagebase=待加壳的imagebase,OEP=待加壳程序的OEP了。(9)恢复外壳程序的主线程。(10)主线程启动后,解壳结束,壳子内部运行的是待加壳程序。好一个偷天换日。
整个解壳程序都需要使用shellCode的方式来写,编译后,抠出硬编码存储成字节数组,所以拷贝的解壳程序就是这个字节数组。
3,加壳代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int _tmain( int argc, _TCHAR* argv[]) { char * szShell = "../Debug/shell.exe" ; char * szObj = "../Debug/xxx.exe" ; HPE shell = loadPE(szShell); HPE object = loadPE(szObj); int length = getFileLength(object); BYTE * src = getFileBuffer(object); HPE newObj = addSection(shell, ".src" , length + 0x2000, 0x60000020); IMAGE_OPTIONAL_HEADER32* pOptionalHeader = getOptionalHeader(newObj); IMAGE_SECTION_HEADER* pShellSection = getLastSection(newObj); pOptionalHeader->AddressOfEntryPoint = pShellSection->VirtualAddress; memcpy (( void *)(pShellSection->PointerToRawData + ( int )getFileBuffer(newObj)), myshellcode, sizeof (myshellcode)); memcpy (( void *)(pShellSection->PointerToRawData + ( int )getFileBuffer(newObj) + 0x2000), src, length); writeFile( "../Debug/test.exe" , newObj); releasePE(newObj); return 0; } |
4,解壳代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | void Decrypt() { BOOL bResult = TRUE; HANDLE hProcess = NULL; TERMINATEPROCESS TerminateProcess = NULL; try { HINSTANCE hKernel32 = NULL; __asm { push eax mov eax, fs:[0x30] // PEB mov eax, [eax + 0x0c] // LDR mov eax, [eax + 0x0c] // 本进程模块, LDA_DATA_TABLE_ENTRY 结构 mov eax, [eax] // ntdll 模块 mov eax, [eax] // kernel32 模块 mov eax, [eax + 0x18] // dllBase mov hKernel32, eax pop eax } char szGetProcAddress[] = { 'G' , 'e' , 't' , 'P' , 'r' , 'o' , 'c' , 'A' , 'd' , 'd' , 'r' , 'e' , 's' , 's' , '\0' }; GETPROCADDRESS GetProcAddress = NULL; // 在 kernel32中寻找 GetProcAddress 函数 IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hKernel32; IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)(( DWORD )hKernel32 + pDosHeader->e_lfanew); IMAGE_EXPORT_DIRECTORY* pExportDirectory = (IMAGE_EXPORT_DIRECTORY*)(( DWORD )hKernel32 + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); short * functionOrdinals = ( short *)(( DWORD )hKernel32 + pExportDirectory->AddressOfNameOrdinals); int * adddressOfNames = ( int *)(( DWORD )hKernel32 + pExportDirectory->AddressOfNames); int * addressOfFunctions = ( int *)(( DWORD )hKernel32 + pExportDirectory->AddressOfFunctions); for ( int i = 0; i < pExportDirectory->NumberOfNames; ++i) { // 第 i 个函数名地址 char * name = ( char *)(( DWORD )hKernel32 + adddressOfNames[i]); char * left = name; char * right = szGetProcAddress; bool res = true ; while (*left || *right) { if (*left != *right) { res = false ; break ; } ++left; ++right; } if (res) { GetProcAddress = (GETPROCADDRESS)(( DWORD )hKernel32 + addressOfFunctions[functionOrdinals[i]]); break ; } } pDosHeader = NULL; pNtHeader = NULL; char szLoadLibrary[] = { 'L' , 'o' , 'a' , 'd' , 'L' , 'i' , 'b' , 'r' , 'a' , 'r' , 'y' , 'A' , '\0' }; char szGetModuleHandle[] = { 'G' , 'e' , 't' , 'M' , 'o' , 'd' , 'u' , 'l' , 'e' , 'H' , 'a' , 'n' , 'd' , 'l' , 'e' , 'A' , '\0' }; char szCreateProcess[] = { 'C' , 'r' , 'e' , 'a' , 't' , 'e' , 'P' , 'r' , 'o' , 'c' , 'e' , 's' , 's' , 'A' , '\0' }; char szGetModuleFileName[] = { 'G' , 'e' , 't' , 'M' , 'o' , 'd' , 'u' , 'l' , 'e' , 'F' , 'i' , 'l' , 'e' , 'N' , 'a' , 'm' , 'e' , 'A' , '\0' }; char szGetThreadContext[] = { 'G' , 'e' , 't' , 'T' , 'h' , 'r' , 'e' , 'a' , 'd' , 'C' , 'o' , 'n' , 't' , 'e' , 'x' , 't' , '\0' }; char szReadProcessMemory[] = { 'R' , 'e' , 'a' , 'd' , 'P' , 'r' , 'o' , 'c' , 'e' , 's' , 's' , 'M' , 'e' , 'm' , 'o' , 'r' , 'y' , '\0' }; char szZwUnmapViewOfSection[] = { 'Z' , 'w' , 'U' , 'n' , 'm' , 'a' , 'p' , 'V' , 'i' , 'e' , 'w' , 'O' , 'f' , 'S' , 'e' , 'c' , 't' , 'i' , 'o' , 'n' , '\0' }; char szNtDll[] = { 'n' , 't' , 'd' , 'l' , 'l' , '.' , 'd' , 'l' , 'l' , '\0' }; char szMemcpy[] = { 'm' , 'e' , 'm' , 'c' , 'p' , 'y' , '\0' }; char szMemset[] = { 'm' , 'e' , 'm' , 's' , 'e' , 't' , '\0' }; char szMSVCRT[] = { 'm' , 's' , 'v' , 'c' , 'r' , 't' , '.' , 'd' , 'l' , 'l' , '\0' }; char szMalloc[] = { 'm' , 'a' , 'l' , 'l' , 'o' , 'c' , '\0' }; char szFree[] = { 'f' , 'r' , 'e' , 'e' , '\0' }; char szVirtualAllocEx[] = { 'V' , 'i' , 'r' , 't' , 'u' , 'a' , 'l' , 'A' , 'l' , 'l' , 'o' , 'c' , 'E' , 'x' , '\0' }; char szWriteProcessMemory[] = { 'W' , 'r' , 'i' , 't' , 'e' , 'P' , 'r' , 'o' , 'c' , 'e' , 's' , 's' , 'M' , 'e' , 'm' , 'o' , 'r' , 'y' , '\0' }; char szSetThreadContext[] = { 'S' , 'e' , 't' , 'T' , 'h' , 'r' , 'e' , 'a' , 'd' , 'C' , 'o' , 'n' , 't' , 'e' , 'x' , 't' , '\0' }; char szResumeThread[] = { 'R' , 'e' , 's' , 'u' , 'm' , 'e' , 'T' , 'h' , 'r' , 'e' , 'a' , 'd' , '\0' }; char szTerminateProcess[] = { 'T' , 'e' , 'r' , 'm' , 'i' , 'n' , 'a' , 't' , 'e' , 'P' , 'r' , 'o' , 'c' , 'e' , 's' , 's' , '\0' }; char szExitProcess[] = { 'E' , 'x' , 'i' , 't' , 'P' , 'r' , 'o' , 'c' , 'e' , 's' , 's' , '\0' }; LOADLIBRARY LoadLibraryA = (LOADLIBRARY)GetProcAddress(hKernel32, szLoadLibrary); GETMODULEHANDLE GetModuleHandleA = (GETMODULEHANDLE)GetProcAddress(hKernel32, szGetModuleHandle); CREATEPROCESS CreateProcessA = (CREATEPROCESS)GetProcAddress(hKernel32, szCreateProcess); GETMODULEFILENAME GetModuleFileNameA = (GETMODULEFILENAME)GetProcAddress(hKernel32, szGetModuleFileName); GETTHREADCONTEXT GetThreadContext = (GETTHREADCONTEXT)GetProcAddress(hKernel32, szGetThreadContext); READPROCESSMEMORY ReadProcessMemory = (READPROCESSMEMORY)GetProcAddress(hKernel32, szReadProcessMemory); WRITEPROCESSMEMORY WriteProcessMemory = (WRITEPROCESSMEMORY)GetProcAddress(hKernel32, szWriteProcessMemory); VIRTUALALLOCEX VirtualAllocEx = (VIRTUALALLOCEX)GetProcAddress(hKernel32, szVirtualAllocEx); SETTHREADCONTEXT SetThreadContext = (SETTHREADCONTEXT)GetProcAddress(hKernel32, szSetThreadContext); RESUMETHREAD ResumeThread = (RESUMETHREAD)GetProcAddress(hKernel32, szResumeThread); TerminateProcess = (TERMINATEPROCESS)GetProcAddress(hKernel32, szTerminateProcess); EXITPROCESS ExitProcess = (EXITPROCESS)GetProcAddress(hKernel32, szExitProcess); HMODULE hNtDll = GetModuleHandleA(szNtDll); ZWUNMAPVIEWOFSECTION ZwUnmapViewOfSection = (ZWUNMAPVIEWOFSECTION)GetProcAddress(hNtDll, szZwUnmapViewOfSection); MEMCPY memcpy = (MEMCPY)GetProcAddress(hNtDll, szMemcpy); MEMSET memset = (MEMSET)GetProcAddress(hNtDll, szMemset); HMODULE hMSVCRT = LoadLibraryA(szMSVCRT); MALLOC malloc = (MALLOC)GetProcAddress(hMSVCRT, szMalloc); FREE free = (FREE)GetProcAddress(hMSVCRT, szFree); HANDLE hModule = GetModuleHandleA(NULL); pDosHeader = (IMAGE_DOS_HEADER*)hModule; pNtHeader = (IMAGE_NT_HEADERS*)(( DWORD )hModule + pDosHeader->e_lfanew); IMAGE_SECTION_HEADER* pSectionHeader = (IMAGE_SECTION_HEADER*)(( DWORD )&pNtHeader->OptionalHeader + pNtHeader->FileHeader.SizeOfOptionalHeader); pSectionHeader = pSectionHeader + pNtHeader->FileHeader.NumberOfSections - 1; // 获取程序数据 char * src = ( char *)(( DWORD )hModule + pSectionHeader->VirtualAddress + 0x2000); int srcSize = pSectionHeader->SizeOfRawData - 0x2000; // 拉伸 pDosHeader = (IMAGE_DOS_HEADER*)src; pNtHeader = (IMAGE_NT_HEADERS*)(( DWORD )src + pDosHeader->e_lfanew); IMAGE_OPTIONAL_HEADER32* pOptionalHeader = &pNtHeader->OptionalHeader; pSectionHeader = (IMAGE_SECTION_HEADER*)(( DWORD )pOptionalHeader + pNtHeader->FileHeader.SizeOfOptionalHeader); int length = pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].VirtualAddress + pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1].SizeOfRawData; //有时候会出现length比Image还要大 length = length > pOptionalHeader->SizeOfImage ? length : pOptionalHeader->SizeOfImage; char * imageBuffer = ( char *) malloc (length); memset (imageBuffer, 0, length); memcpy (imageBuffer, src, pOptionalHeader->SizeOfHeaders); for ( int i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i) { memcpy (imageBuffer + pSectionHeader->VirtualAddress, src + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData); ++pSectionHeader; } char szModuleName[MAX_PATH]; GetModuleFileNameA(NULL, szModuleName, MAX_PATH); STARTUPINFO si; memset (&si, 0, sizeof (si)); PROCESS_INFORMATION pi; memset (&pi, 0, sizeof (pi)); si.cb = sizeof (si); CreateProcessA(NULL, szModuleName, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); hProcess = pi.hProcess; CONTEXT context; context.ContextFlags = CONTEXT_FULL; GetThreadContext(pi.hThread, &context); DWORD dwEntryPoint = context.Eax; DWORD baseAddress = context.Ebx + 8; DWORD shellImageBase = 0; ReadProcessMemory(pi.hProcess, ( LPVOID )baseAddress, &shellImageBase, 4, NULL); ZwUnmapViewOfSection(pi.hProcess, ( PVOID )shellImageBase); LPVOID buffer = VirtualAllocEx(pi.hProcess, ( LPVOID )pOptionalHeader->ImageBase, pOptionalHeader->SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!buffer) { VirtualAllocEx(pi.hProcess, NULL, pOptionalHeader->SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); pDosHeader = (IMAGE_DOS_HEADER*)imageBuffer; pNtHeader = (IMAGE_NT_HEADERS*)(( DWORD )imageBuffer + pDosHeader->e_lfanew); IMAGE_OPTIONAL_HEADER32* pOptionalHeader = &pNtHeader->OptionalHeader; IMAGE_BASE_RELOCATION* pBaseRelocation = (IMAGE_BASE_RELOCATION*)(( DWORD )imageBuffer + pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); while (pBaseRelocation->SizeOfBlock || pBaseRelocation->VirtualAddress) { short * pTable = ( short *)(( int )pBaseRelocation + 8); int len = (pBaseRelocation->SizeOfBlock - 8) / 2; for ( int i = 0; i < len; ++i) { DWORD RVA = pBaseRelocation->VirtualAddress + (pTable[i] & 0x0FFF); if (((pTable[i] & 0xF000) >> 12) == 3) { *( DWORD *)(RVA + ( DWORD )imageBuffer) += (( DWORD )buffer - pOptionalHeader->ImageBase); } } pBaseRelocation = (IMAGE_BASE_RELOCATION*)(( DWORD )pBaseRelocation + pBaseRelocation->SizeOfBlock); } pOptionalHeader->ImageBase = ( DWORD )buffer; } WriteProcessMemory(pi.hProcess, buffer, imageBuffer, pOptionalHeader->SizeOfImage, NULL); WriteProcessMemory(pi.hProcess, ( LPVOID )(context.Ebx + 8), &buffer, 4, NULL); context.Eax = pOptionalHeader->ImageBase + pOptionalHeader->AddressOfEntryPoint; SetThreadContext(pi.hThread, &context); ResumeThread(pi.hThread); free (imageBuffer); ExitProcess(0); } catch (...) { if (hProcess) TerminateProcess(hProcess, 0); return ; } } |
5,如何脱壳
这种壳子解密完后,内存中的程序就是原来拉伸后的文件,只需要一步一步F8跟进,就会跟到OEP,然后把内存dump出来就可以了。
转自看雪论坛
- 文章2302
- 用户1336
- 访客10980850
成功不是从不跌倒,而是每次跌倒后都能重新站起来。
排名前5的开源在线机器学习
Android自定义蜂窝view
Android应用性能优化系列视图篇——隐藏在资源图片中的内存杀手
Java中的(耦合)控制反转
OpenGL读取帧缓存数据
首发:Thinkpad T550黑苹果10.13.4安装教程
反编译修改class文件变量
IntelliJ IDEA2018~2019.1激活码-注册码
p2p通信,打洞技术,穿越NAT的实现(附NAT环境检测工具)
【黑苹果安装】——如何在windows下操作EFI分区
Could not resolve io.flutter 解决方法
MPAndroidChart标记控件MarkerView的使用方法
imencode和imdecode使用