简单的EXE外壳,只有 初级的功能,壳子程序是一段ShellCode,会弹出来一个消息框,可以处理一下做一个exe的加密工具;也可以该吧改吧,做一个完善的壳子。
《加密与解密(第三版)》中的壳子是用汇编写的,没写过几行汇编,所以就用C实现了,开始加完壳子不能运行,用X64dbg调了半个小时才找到。志同道合的朋友如果需要可以拿去使用。不说废话,上代码。
加壳程序:
* Copyright (c) 2018 initm.com All rights reserved. * 作者: 梦回起点 * 功能: 软件加壳 * 邮件: i@initm.com * 描述: 软件加壳,并为加壳后的软件增加一个消息框 * 限制: 1.目前只有保护导入表的功能 * 2.只能加密32位exe文件 * 3.只能处理节表有空余位置的文件 * 声明:1.水能载舟,亦能覆舟。这段代码改一下就是一个EXE加密工具、外壳;同样加点功能也会变成木马、病毒。我劝你善良 * 2.代码可以在注明出处的情况下无条件复制,传播。 * 完成时间: 2018-10-10 10:18 */ /* // 存放壳子运行需要数据及保存的导入表和重定位表的信息等 存放ShellCode尾部,相当于从end函数的地方开始写 // 保存当前镜像的基地址 */ ///////////////////////////ShellCode 在这里//////////////////////// // DWORD pImageBase ;这个镜像基址因为只加密exe所以没啥用。dll重定位需要使用,但是没做这个功能 // DWORD nOEP ;原来程序的入口点 // DWORD nImportNowRVA ;现在导入表的RVA 就是原区段最后的rva+shellcode的大小+sizeof(DWORD)*4 // DWORD nImportRVA ;这个是原来的导入表的RVA // 壳子导入表的存储结构复制的《加密与解密(第三版)》中的那个结构 // ImportTable DD AddressFirst - ImportTable; OriginalFirstThunk // DD 0, 0; TimeDataStamp, ForwardChain // AppImpRVA1 DD DllName - ImportTable; Name // AppImpRVA2 DD AddressFirst - ImportTable; FirstThunk // DD 0, 0, 0, 0, 0 // AddressFirst DD FirstFunc - ImportTable; 指向IMAGE_tHUNK_DATA // AddressSecond DD SecondFunc - ImportTable; 指向IMAGE_tHUNK_DATA // AddressThird DD ThirdFunc - ImportTable; 指向IMAGE_tHUNK_DATA // DD 0 // DllName DB 'KERNEL32.dll' // DW 0 // FirstFunc DW 0 // DB 'GetProcAddress', 0 // SecondFunc DW 0 // DB 'GetModuleHandleA', 0 // ThirdFunc DW 0 // DB 'LoadLibraryA', 0 // ImportTableEnd LABEL DWORD // 原来的导入表结构如下 // |(PDWORD)FirstThunk|(PDWORD)functionCount|(PBYTE)dllName or (PDWORD) number|(PBYTE)functionName(...X functionCount)|(PDWORD)FirstThunk|... #include "stdafx.h" #include <windows.h> #include <stdio.h> //这里就是ShellCode的代码编译出来的硬编码,这个只是加壳 PCHAR ShellCode = "\x55\x8B\xEC\xE8\x08\x00\x00\x00\x5D\xC3\xCC\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x0C\xC7\x45\xFC\x00\x00\x00\x00\x8D\x45\xFC\x89\x45\xFC\x8B\x4D\xFC\x83\xC1\x08\x89\x4D\xFC\x8B\x55\xFC\x8B\x02\x83\xE8\x08\x89\x45\xFC\xB9\x10\x10\x1F\x00\x81\xE9\x00\x10\x1F\x00\x03\x4D\xFC\x89\x4D\xFC\xBA\xA0\x12\x1F\x00\x81\xEA\x10\x10\x1F\x00\x03\x55\xFC\x89\x55\xF8\x8B\x45\xF8\x50\xB9\xB0\x10\x1F\x00\x81\xE9\x10\x10\x1F\x00\x03\x4D\xFC\x51\xE8\x3F\x00\x00\x00\x83\xC4\x08\x89\x45\xF4\xFF\x55\xF4\x8B\xE5\x5D\xC3\xCC\xCC\x55\x8B\xEC\x51\xC7\x45\xFC\x00\x00\x00\x00\x8B\x45\x08\x03\x45\xFC\x0F\xBE\x08\x85\xC9\x74\x0B\x8B\x55\xFC\x83\xC2\x01\x89\x55\xFC\xEB\xE8\x8B\x45\xFC\x8B\xE5\x5D\xC3\xCC\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x3C\x8B\x45\x0C\x83\xC0\x10\x89\x45\xD4\x8B\x4D\xD4\x83\xC1\x28\x89\x4D\xE8\xBA\x04\x00\x00\x00\x6B\xC2\x00\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xD8\xB8\x04\x00\x00\x00\xC1\xE0\x00\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xD0\xB8\x04\x00\x00\x00\xD1\xE0\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xC8\x6A\x00\xFF\x55\xD0\x89\x45\xEC\x8B\x45\x0C\x8B\x48\x04\x89\x4D\xC4\x8B\x55\x0C\x8B\x45\xEC\x03\x42\x08\x89\x45\xFC\x8B\x4D\x0C\x8B\x55\xEC\x03\x51\x0C\x89\x55\xF0\xC7\x45\xF8\x00\x00\x00\x00\x8B\x45\xFC\x83\x38\x00\x0F\x84\x5D\x01\x00\x00\x6B\x4D\xF8\x14\x8B\x55\xF0\x8B\x45\xFC\x8B\x00\x89\x44\x0A\x10\x6B\x4D\xF8\x14\x8B\x55\xF0\x8B\x45\xEC\x03\x44\x0A\x10\x89\x45\xDC\x8B\x4D\xFC\x8B\x51\x04\x89\x55\xCC\x6B\x45\xF8\x14\x8B\x4D\xF0\xC7\x44\x01\x04\x00\x00\x00\x00\x6B\x55\xF8\x14\x8B\x45\xF0\xC7\x44\x10\x08\x00\x00\x00\x00\x8B\x4D\xFC\x83\xC1\x08\x89\x4D\xE4\x8B\x55\xE4\x2B\x55\xEC\x6B\x45\xF8\x14\x8B\x4D\xF0\x89\x54\x01\x0C\x8B\x55\xE4\x52\xE8\xDF\xFE\xFF\xFF\x83\xC4\x04\x8B\x4D\xFC\x8D\x54\x01\x09\x89\x55\xFC\xC7\x45\xF4\x00\x00\x00\x00\xEB\x09\x8B\x45\xF4\x83\xC0\x01\x89\x45\xF4\x8B\x4D\xF4\x3B\x4D\xCC\x0F\x8D\xB9\x00\x00\x00\x8B\x55\xE4\x52\xFF\x55\xC8\x89\x45\xE0\x83\x7D\xE0\x00\x74\x65\x8B\x45\xFC\x8B\x08\x81\xE1\x00\x00\x00\xF0\x81\xF9\x00\x00\x00\xF0\x75\x26\x8B\x55\xFC\x8B\x02\x25\xFF\xFF\xFF\x0F\x50\x8B\x4D\xE0\x51\xFF\x55\xD8\x8B\x55\xF4\x8B\x4D\xDC\x89\x04\x91\x8B\x55\xFC\x83\xC2\x04\x89\x55\xFC\xEB\x2A\x8B\x45\xFC\x50\x8B\x4D\xE0\x51\xFF\x55\xD8\x8B\x55\xF4\x8B\x4D\xDC\x89\x04\x91\x8B\x55\xFC\x52\xE8\x4E\xFE\xFF\xFF\x83\xC4\x04\x8B\x4D\xFC\x8D\x54\x01\x01\x89\x55\xFC\xEB\x3F\x8B\x45\xF4\x8B\x4D\xDC\xC7\x04\x81\x00\x00\x00\x00\x8B\x55\xFC\x8B\x02\x25\x00\x00\x00\xF0\x3D\x00\x00\x00\xF0\x75\x0B\x8B\x4D\xFC\x83\xC1\x04\x89\x4D\xFC\xEB\x16\x8B\x55\xFC\x52\xE8\x0D\xFE\xFF\xFF\x83\xC4\x04\x8B\x4D\xFC\x8D\x54\x01\x01\x89\x55\xFC\xE9\x32\xFF\xFF\xFF\x8B\x45\xF8\x83\xC0\x01\x89\x45\xF8\xE9\x97\xFE\xFF\xFF\x8B\x45\xEC\x03\x45\xC4\x8B\xE5\x5D\xC3\xCC\xCC\xCC"; //这个可以弹出一个MessageBox,ShellCode中的代码对应的就是这个 PCHAR ShellCodeMessage = "\x55\x8B\xEC\xE8\x08\x00\x00\x00\x5D\xC3\xCC\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x0C\xC7\x45\xFC\x00\x00\x00\x00\x8D\x45\xFC\x89\x45\xFC\x8B\x4D\xFC\x83\xC1\x08\x89\x4D\xFC\x8B\x55\xFC\x8B\x02\x83\xE8\x08\x89\x45\xFC\xB9\x10\x10\x38\x01\x81\xE9\x00\x10\x38\x01\x03\x4D\xFC\x89\x4D\xFC\xBA\x40\x13\x38\x01\x81\xEA\x10\x10\x38\x01\x03\x55\xFC\x89\x55\xF8\x8B\x45\xF8\x50\xB9\xB0\x10\x38\x01\x81\xE9\x10\x10\x38\x01\x03\x4D\xFC\x51\xE8\x3F\x00\x00\x00\x83\xC4\x08\x89\x45\xF4\xFF\x55\xF4\x8B\xE5\x5D\xC3\xCC\xCC\x55\x8B\xEC\x51\xC7\x45\xFC\x00\x00\x00\x00\x8B\x45\x08\x03\x45\xFC\x0F\xBE\x08\x85\xC9\x74\x0B\x8B\x55\xFC\x83\xC2\x01\x89\x55\xFC\xEB\xE8\x8B\x45\xFC\x8B\xE5\x5D\xC3\xCC\xCC\xCC\xCC\xCC\xCC\x55\x8B\xEC\x83\xEC\x64\x8B\x45\x0C\x83\xC0\x10\x89\x45\xD0\x8B\x4D\xD0\x83\xC1\x28\x89\x4D\xE8\xBA\x04\x00\x00\x00\x6B\xC2\x00\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xDC\xB8\x04\x00\x00\x00\xC1\xE0\x00\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xCC\xB8\x04\x00\x00\x00\xD1\xE0\x8B\x4D\xE8\x8B\x14\x01\x89\x55\xD4\x6A\x00\xFF\x55\xCC\x89\x45\xEC\xC6\x45\xA8\x55\xC6\x45\xA9\x73\xC6\x45\xAA\x65\xC6\x45\xAB\x72\xC6\x45\xAC\x33\xC6\x45\xAD\x32\xC6\x45\xAE\x2E\xC6\x45\xAF\x64\xC6\x45\xB0\x6C\xC6\x45\xB1\x6C\xC6\x45\xB2\x00\xC6\x45\x9C\x4D\xC6\x45\x9D\x65\xC6\x45\x9E\x73\xC6\x45\x9F\x73\xC6\x45\xA0\x61\xC6\x45\xA1\x67\xC6\x45\xA2\x65\xC6\x45\xA3\x42\xC6\x45\xA4\x6F\xC6\x45\xA5\x78\xC6\x45\xA6\x41\xC6\x45\xA7\x00\x8D\x45\xA8\x50\xFF\x55\xD4\x89\x45\xC8\x8D\x4D\x9C\x51\x8B\x55\xC8\x52\xFF\x55\xDC\x89\x45\xC4\xC6\x45\xB4\x48\xC6\x45\xB5\x65\xC6\x45\xB6\x6C\xC6\x45\xB7\x6C\xC6\x45\xB8\x6F\xC6\x45\xB9\x00\x6A\x00\x8D\x45\xB4\x50\x8D\x4D\xB4\x51\x6A\x00\xFF\x55\xC4\x8B\x55\x0C\x8B\x42\x04\x89\x45\xBC\x8B\x4D\x0C\x8B\x55\xEC\x03\x51\x08\x89\x55\xFC\x8B\x45\x0C\x8B\x4D\xEC\x03\x48\x0C\x89\x4D\xF0\xC7\x45\xF8\x00\x00\x00\x00\x8B\x55\xFC\x83\x3A\x00\x0F\x84\x5E\x01\x00\x00\x6B\x45\xF8\x14\x8B\x4D\xF0\x8B\x55\xFC\x8B\x12\x89\x54\x01\x10\x6B\x45\xF8\x14\x8B\x4D\xF0\x8B\x55\xEC\x03\x54\x01\x10\x89\x55\xD8\x8B\x45\xFC\x8B\x48\x04\x89\x4D\xC0\x6B\x55\xF8\x14\x8B\x45\xF0\xC7\x44\x10\x04\x00\x00\x00\x00\x6B\x4D\xF8\x14\x8B\x55\xF0\xC7\x44\x0A\x08\x00\x00\x00\x00\x8B\x45\xFC\x83\xC0\x08\x89\x45\xE4\x8B\x4D\xE4\x2B\x4D\xEC\x6B\x55\xF8\x14\x8B\x45\xF0\x89\x4C\x10\x0C\x8B\x4D\xE4\x51\xE8\x44\xFE\xFF\xFF\x83\xC4\x04\x8B\x55\xFC\x8D\x44\x02\x09\x89\x45\xFC\xC7\x45\xF4\x00\x00\x00\x00\xEB\x09\x8B\x4D\xF4\x83\xC1\x01\x89\x4D\xF4\x8B\x55\xF4\x3B\x55\xC0\x0F\x8D\xBA\x00\x00\x00\x8B\x45\xE4\x50\xFF\x55\xD4\x89\x45\xE0\x83\x7D\xE0\x00\x74\x66\x8B\x4D\xFC\x8B\x11\x81\xE2\x00\x00\x00\xF0\x81\xFA\x00\x00\x00\xF0\x75\x27\x8B\x45\xFC\x8B\x08\x81\xE1\xFF\xFF\xFF\x0F\x51\x8B\x55\xE0\x52\xFF\x55\xDC\x8B\x4D\xF4\x8B\x55\xD8\x89\x04\x8A\x8B\x45\xFC\x83\xC0\x04\x89\x45\xFC\xEB\x2A\x8B\x4D\xFC\x51\x8B\x55\xE0\x52\xFF\x55\xDC\x8B\x4D\xF4\x8B\x55\xD8\x89\x04\x8A\x8B\x45\xFC\x50\xE8\xB2\xFD\xFF\xFF\x83\xC4\x04\x8B\x4D\xFC\x8D\x54\x01\x01\x89\x55\xFC\xEB\x3F\x8B\x45\xF4\x8B\x4D\xD8\xC7\x04\x81\x00\x00\x00\x00\x8B\x55\xFC\x8B\x02\x25\x00\x00\x00\xF0\x3D\x00\x00\x00\xF0\x75\x0B\x8B\x4D\xFC\x83\xC1\x04\x89\x4D\xFC\xEB\x16\x8B\x55\xFC\x52\xE8\x71\xFD\xFF\xFF\x83\xC4\x04\x8B\x4D\xFC\x8D\x54\x01\x01\x89\x55\xFC\xE9\x31\xFF\xFF\xFF\x8B\x45\xF8\x83\xC0\x01\x89\x45\xF8\xE9\x96\xFE\xFF\xFF\x8B\x45\xEC\x03\x45\xBC\x8B\xE5\x5D\xC3\xCC\xCC\xCC\xCC\xCC\xCC\xCC"; //两段ShellCode各自的字节数 #define SHELLCODESIZE 672 #define SHELLCODEMESSAGESIZE 832 //对齐 size_t AlignSize(const size_t& size, const DWORD& align) { return (size + align - 1) / align * align; } //转换一个rva到指针 PCHAR RVA2Ptr(PCHAR pHeader, const DWORD& rva) { return pHeader + rva; } /*******************************************************************************/ /* ImportSaveAndClear用来保存并清空原来的导入表 */ /* |(PDWORD)FirstThunk|(PDWORD)functionCount|(PBYTE)dllName|(PBYTE)functionName*/ /* char* pBuffer 存放这个结构的地址 */ /* PIMAGE_IMPORT_DESCRIPTOR pImport 程序导入表的地址 */ /* char* lpHeader 文件内存中展开的首地址 */ /*******************************************************************************/ size_t ImportSaveAndClear(PCHAR pBuffer, PIMAGE_IMPORT_DESCRIPTOR pImport, PCHAR lpHeader) { IMAGE_IMPORT_DESCRIPTOR empty; memset(&empty, 0, sizeof(empty)); PCHAR pBackBuffer = pBuffer; //如果是最后一个了,那么返回 while (0 != memcmp(pImport, &empty, sizeof(empty))){ //保存FristThunk *(PDWORD)pBuffer = pImport->FirstThunk; pBuffer += sizeof(DWORD); //保存函数的个数 PDWORD pCount = (PDWORD)pBuffer; pBuffer += sizeof(DWORD); //保存dll名字 PCHAR name = (PCHAR)RVA2Ptr(lpHeader, pImport->Name); memcpy(pBuffer, name, strlen(name)); pBuffer += strlen(name) + 1; PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)RVA2Ptr(lpHeader, pImport->FirstThunk); int nCount(0); for (nCount; pIAT->u1.AddressOfData; nCount++, pIAT++) { if (pIAT->u1.AddressOfData & (0x1 << 31)) {//如果是按照编号导出的 *(PDWORD)(pBuffer) = pIAT->u1.AddressOfData | 0xF0000000; pBuffer += sizeof(DWORD); } else { PIMAGE_IMPORT_BY_NAME name = (PIMAGE_IMPORT_BY_NAME)RVA2Ptr(lpHeader, pIAT->u1.AddressOfData); memset(pIAT, 0, sizeof(IMAGE_THUNK_DATA)); memcpy(pBuffer, name->Name, strlen((LPCSTR)name->Name)); pBuffer += strlen((LPCSTR)name->Name) + 1; memset(name, 0, sizeof(IMAGE_IMPORT_BY_NAME) + strlen((LPCSTR)name->Name)); } } *pCount = nCount;//写入这个dll一共有多少个函数 memset(pImport, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR)); pImport++; } (*(PDWORD)pBuffer) = 0;//写入一个结尾的标记 pBuffer += sizeof(DWORD); return pBuffer - pBackBuffer; } /*******************************************************************************/ /* AddSection用来增加一个新节 */ /* 没有考虑节表位置不够的情况,这种情况需要特殊处理的 */ /* PIMAGE_SECTION_HEADER pImport 需要插入位置的地址 */ /* DWORD size 需要插入的节的大小 */ /* DWORD rva内存中的偏移位置 */ /* DWORD rvaAlign内存中的对齐大小 */ /* DWORD foa文件中的偏移位置 */ /* DWORD fileAlign文件炸的对齐大小 */ /*******************************************************************************/ DWORD AddSection(PIMAGE_SECTION_HEADER pImport, DWORD size, DWORD rva, DWORD rvaAlign, DWORD foa, DWORD fileAlign) {//增加一个空余的节,如果没有地方,那么返回失败,这个可以修改的,可以合并现有节的,但是时间有限,不实现了 IMAGE_SECTION_HEADER empty; memset(&empty, 0, sizeof(IMAGE_SECTION_HEADER)); //如果找到空余的段 if (0 != memcmp(&pImport[1], &empty, sizeof(IMAGE_SECTION_HEADER))) { return 0; } pImport->Name[0] = 0; pImport->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE; pImport->SizeOfRawData = AlignSize(size, fileAlign); pImport->PointerToRawData = foa; pImport->Misc.VirtualSize = AlignSize(size, rvaAlign); pImport->VirtualAddress = rva; return pImport->Misc.VirtualSize; } /*******************************************************************************/ /* AddShell主功能函数 */ /*PCHAR szName 需要加壳的文件 */ /*******************************************************************************/ void AddShell(PCHAR szName) { char tail[409600]; size_t nShellImportSize(0); PCHAR lpSaveData(nullptr); size_t nSaveDataSize(0); memset(tail, 0, sizeof(tail));//用来存放构造的尾部数据 //读取文件信息,EntryPoint,ImageBase DWORD nShellCodeSize(SHELLCODEMESSAGESIZE); //把ShellCode先读出来 memcpy(tail, ShellCodeMessage, nShellCodeSize); PDWORD pImageBase = (PDWORD)&tail[nShellCodeSize]; PDWORD pEntryPoint = (PDWORD)&tail[nShellCodeSize + sizeof(DWORD)]; //现在变形的导入表的RVA PDWORD pImportNowRVA = (PDWORD)&tail[nShellCodeSize + sizeof(DWORD) * 2]; //原来导入表的RVA PDWORD pImportRVA = (PDWORD)&tail[nShellCodeSize + sizeof(DWORD) * 3]; HANDLE hFile = CreateFileA(szName, GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (INVALID_HANDLE_VALUE == hFile) {//打开文件失败 return; } size_t nFileSize = GetFileSize(hFile, NULL); IMAGE_DOS_HEADER dosHeader; DWORD dwSize(0); if (!ReadFile(hFile, &dosHeader, sizeof(dosHeader), &dwSize, NULL)) { CloseHandle(hFile); return; } if (IMAGE_DOS_SIGNATURE != dosHeader.e_magic) { CloseHandle(hFile); return; } SetFilePointer(hFile, dosHeader.e_lfanew, 0, FILE_BEGIN); IMAGE_NT_HEADERS ntHeader; if (!ReadFile(hFile, &ntHeader, sizeof(ntHeader), &dwSize, NULL)) { CloseHandle(hFile); return; } if (IMAGE_NT_SIGNATURE != ntHeader.Signature) { CloseHandle(hFile); return; } *pImageBase = ntHeader.OptionalHeader.ImageBase; *pEntryPoint = ntHeader.OptionalHeader.AddressOfEntryPoint; SetFilePointer(hFile, 0, 0, FILE_BEGIN); //读出PE头 PCHAR lpBuffer = new CHAR[ntHeader.OptionalHeader.SizeOfImage]; ReadFile(hFile, lpBuffer, ntHeader.OptionalHeader.SizeOfHeaders, &dwSize, NULL); PIMAGE_NT_HEADERS pNtHeader((PIMAGE_NT_HEADERS)(lpBuffer + dosHeader.e_lfanew)); PIMAGE_SECTION_HEADER pSectionTable((PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader + + sizeof(ntHeader.Signature) + sizeof(ntHeader.FileHeader) + ntHeader.FileHeader.SizeOfOptionalHeader)); //处理节表 size_t nSectionCount(pNtHeader->FileHeader.NumberOfSections); for (size_t i = 0; i < nSectionCount; i++) { pSectionTable[i].SizeOfRawData = AlignSize(pSectionTable[i].SizeOfRawData, pNtHeader->OptionalHeader.FileAlignment); pSectionTable[i].Misc.VirtualSize = AlignSize(pSectionTable[i].Misc.VirtualSize, pNtHeader->OptionalHeader.SectionAlignment); pSectionTable[i].Characteristics |= IMAGE_SCN_MEM_WRITE; pSectionTable[i].Characteristics &= ~IMAGE_SCN_MEM_SHARED; if (pNtHeader->FileHeader.NumberOfSections - 1 == i && pSectionTable[i].VirtualAddress + pSectionTable[i].SizeOfRawData > pNtHeader->OptionalHeader.SizeOfImage) { pSectionTable[i].SizeOfRawData = pNtHeader->OptionalHeader.SizeOfImage - pSectionTable[i].VirtualAddress; } SetFilePointer(hFile, pSectionTable[i].PointerToRawData, 0, FILE_BEGIN); //读取所有的节数据 ReadFile(hFile, &lpBuffer[pSectionTable[i].VirtualAddress], pSectionTable[i].SizeOfRawData, &dwSize, 0); } //如果最后有数据 size_t nSecitonAllSizeFile = pSectionTable[nSectionCount - 1].SizeOfRawData + pSectionTable[nSectionCount - 1].PointerToRawData; size_t nSecitonAllSize = pSectionTable[nSectionCount - 1].VirtualAddress + pSectionTable[nSectionCount - 1].Misc.VirtualSize; if (nSecitonAllSizeFile < nFileSize) { nSaveDataSize = nFileSize - nSecitonAllSizeFile; lpSaveData = new char[nSaveDataSize]; SetFilePointer(hFile, nSecitonAllSizeFile, 0, FILE_BEGIN); //读取文件数据 ReadFile(hFile, lpSaveData, nSaveDataSize, &dwSize, 0); } //获取导入表数据,构造自己的结构,清除现在的导入表,写入导入表 PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RVA2Ptr(lpBuffer, pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); *pImportRVA = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; //把原来导入表的数据放进来,写这段的时候太痛苦了,纯体力劳动 PBYTE pShellImportTable = (PBYTE)&tail[nShellCodeSize + sizeof(DWORD) * 4]; {//构造壳子的导入表 PBYTE pPointer = pShellImportTable; //把导入表的RVA存起来 *pImportNowRVA = nSecitonAllSize + nShellCodeSize + sizeof(DWORD) * 4; //AddressFirst DWORD AddressFirstRVA = *(PDWORD)pPointer = *pImportNowRVA + sizeof(IMAGE_IMPORT_DESCRIPTOR) * 2; pPointer += sizeof(DWORD); //TimeDataStamp *(PDWORD)pPointer = 0; pPointer += sizeof(DWORD); //ForwardChain *(PDWORD)pPointer = 0; pPointer += sizeof(DWORD); //DllName *(PDWORD)pPointer = AddressFirstRVA + sizeof(IMAGE_THUNK_DATA)*4; pPointer += sizeof(DWORD); //FirstThunk *(PDWORD)pPointer = AddressFirstRVA; pPointer += sizeof(DWORD); memset(pPointer, 0, sizeof(IMAGE_IMPORT_DESCRIPTOR)); pPointer += sizeof(IMAGE_IMPORT_DESCRIPTOR); //IMAGE_tHUNK_DATA-FirstFunc ((PIMAGE_THUNK_DATA)pPointer)[0].u1.AddressOfData = AddressFirstRVA + sizeof(IMAGE_THUNK_DATA) * 4 + strlen("KERNEL32.dll") + 3; ((PIMAGE_THUNK_DATA)pPointer)[1].u1.AddressOfData = ((PDWORD)pPointer)[0] + strlen("GetProcAddress") + 1 + sizeof(WORD); ((PIMAGE_THUNK_DATA)pPointer)[2].u1.AddressOfData = ((PDWORD)pPointer)[1] + strlen("GetModuleHandleA") + 1 + sizeof(WORD); ((PIMAGE_THUNK_DATA)pPointer)[3].u1.AddressOfData = 0; pPointer += sizeof(IMAGE_THUNK_DATA) * 4; memcpy(pPointer, "KERNEL32.dll", strlen("KERNEL32.dll") + 1); pPointer += strlen("KERNEL32.dll") + 3; //IMAGE_IMPORT_BY_NAME ((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint = 0; memcpy(&((PIMAGE_IMPORT_BY_NAME)pPointer)->Name, "GetProcAddress", strlen("GetProcAddress")+1); pPointer += sizeof(((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint) + strlen("GetProcAddress") + 1; ((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint = 0; memcpy(&((PIMAGE_IMPORT_BY_NAME)pPointer)->Name, "GetModuleHandleA", strlen("GetModuleHandleA") + 1); pPointer += sizeof(((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint) + strlen("GetModuleHandleA") + 1; ((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint = 0; memcpy(&((PIMAGE_IMPORT_BY_NAME)pPointer)->Name, "LoadLibraryA", strlen("LoadLibraryA") + 1); pPointer += sizeof(((PIMAGE_IMPORT_BY_NAME)pPointer)->Hint) + strlen("LoadLibraryA") + 1; nShellImportSize = pPointer - pShellImportTable; *pImportNowRVA += nShellImportSize; //printf("%d", nShellImportSize); } size_t ImportSize = ImportSaveAndClear(&tail[nShellCodeSize + sizeof(DWORD) * 4 + nShellImportSize], pImportTable, lpBuffer); //增加一个节,将ShellCode和尾部数据填入 size_t nAddSectionSize = nShellCodeSize + sizeof(DWORD) * 4 + nShellImportSize + ImportSize; size_t sectionSize = AddSection(&pSectionTable[nSectionCount], nAddSectionSize, nSecitonAllSize, pNtHeader->OptionalHeader.SectionAlignment, nSecitonAllSizeFile, pNtHeader->OptionalHeader.FileAlignment); if (0 == sectionSize) {//如果添加节失败了,那么返回 CloseHandle(hFile); delete lpBuffer; delete lpSaveData; return; } //修改入口点,镜像大小 pNtHeader->OptionalHeader.AddressOfEntryPoint = nSecitonAllSize; pNtHeader->OptionalHeader.SizeOfImage = pNtHeader->OptionalHeader.SizeOfImage + sectionSize; pNtHeader->OptionalHeader.SizeOfHeaders = AlignSize((char*)&pSectionTable[nSectionCount+1] - lpBuffer, ntHeader.OptionalHeader.FileAlignment); pNtHeader->FileHeader.NumberOfSections += 1; pNtHeader->OptionalHeader.BaseOfCode = nSecitonAllSize; pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = nShellImportSize; pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = nSecitonAllSize + nShellCodeSize + sizeof(DWORD) * 4; //把这个文件写入 //把文件原来的文件清空 SetFilePointer(hFile, 0, 0, FILE_BEGIN); SetEndOfFile(hFile); //写入头部 WriteFile(hFile, lpBuffer, pNtHeader->OptionalHeader.SizeOfHeaders, &dwSize, 0); //写入各个区段信息 for (int i = 0; i < pNtHeader->FileHeader.NumberOfSections - 1; i++) { SetFilePointer(hFile, pSectionTable[i].PointerToRawData, 0, FILE_BEGIN); WriteFile(hFile, RVA2Ptr(lpBuffer, pSectionTable[i].VirtualAddress), pSectionTable[i].SizeOfRawData, &dwSize, 0); } SetFilePointer(hFile, pSectionTable[pNtHeader->FileHeader.NumberOfSections - 1].PointerToRawData, 0, FILE_BEGIN); WriteFile(hFile, tail, pSectionTable[pNtHeader->FileHeader.NumberOfSections - 1].SizeOfRawData, &dwSize, 0); //写入尾部信息 if (nSaveDataSize) { SetFilePointer(hFile, 0, 0, FILE_END); WriteFile(hFile, lpSaveData, nSaveDataSize, &dwSize, 0); } CloseHandle(hFile); delete lpBuffer; delete lpSaveData; } int main(int argc, char* argv[]) { if (argc < 2) { printf("请拖动需要加密的文件到本文件上"); getchar(); return 0; } try{ AddShell(argv[1]); } catch (std::bad_alloc) { printf("内存不足"); } return 0; }
壳子程序:
* Copyright (c) 2018 initm.com All rights reserved. * 作者: 梦回起点 * 功能: 软件加壳 * 邮件: i@initm.com * 描述: 软件加壳,并为加壳后的软件增加一个消息框 * 限制: 1.目前只有保护导入表的功能 * 2.只能加密32位exe文件 * 3.只能处理节表有空余位置的文件 * 声明:1.水能载舟,亦能覆舟。这段代码改一下就是一个EXE加密工具、外壳;同样加点功能也会变成木马、病毒。我劝你善良 * 2.代码可以在注明出处的情况下无条件复制,传播。 * 完成时间: 2018-10-10 10:18 */ #include "stdafx.h" #include <windows.h> void Work(); PBYTE Restore(PBYTE pThisFunction, PBYTE pData); void End(); typedef int (WINAPI *TMessageBoxA)( _In_opt_ HWND hWnd, _In_opt_ LPCSTR lpText, _In_opt_ LPCSTR lpCaption, _In_ UINT uType); typedef HMODULE(WINAPI* TGetModuleHandleA)(_In_opt_ LPCSTR lpModuleName); typedef FARPROC(WINAPI* TGetProcAddress)(__in HMODULE hModule, __in LPCSTR lpProcName); typedef HMODULE (WINAPI* TLoadLibraryA)(__in LPCSTR lpLibFileName); /////////////////////////////////ShellCode Start///////////////////////////////// void Start() { //push ebp //mov ebp, esp Work();//这里得先call一下,否则算不出来函数的地址 //ret addr } /* // 一般情况下,函数调用后栈中的情况是这个样子的,利用这个结构可以获取到返回地址,进而获取函数首地址 // ebp-4 第一个变量 // ebp-> old ebp // ret addr // args */ void Work() { /* * 这里这么复杂主要是为了获取本函数的地址和数据的地址 * 第一个DWORD长度的空间存放ImageBase,就可以根据ImageBase来获取函数了 */ /*pThisFunction 在ebp-4的位置,这里根据编译器不同可能会不同*/ PBYTE pThisFunction = nullptr; pThisFunction = (PBYTE)&pThisFunction; pThisFunction += 8;/*现在指向的是返回地址*/ /*8是 push ebp mov ebp, esp call ?Work@@YAXXZ 三条指令的长度*/ pThisFunction = (PBYTE)(*(PDWORD)pThisFunction) - 8;/*start的地址*/ pThisFunction = pThisFunction + (DWORD)((PBYTE)Work - (PBYTE)Start);/*这里就是本函数的地址了*/ PBYTE pData = pThisFunction + ((DWORD)End - (DWORD)Work);/*这里指向数据了,数据的格式需要定义*/ //解密恢复原来的数据,返回入口点 void(__stdcall * pEntryPoint)() = (void(__stdcall*)())Restore(pThisFunction + ((DWORD)Restore - (DWORD)Work), pData); //调用入口点 pEntryPoint(); } size_t MyStrlen(PCHAR str) { int i = 0; //找0 while (str[i]) { i++; } return i; } PBYTE Restore(PBYTE pThisFunction, PBYTE pData) { //现在的导入表 获取当前程序运行时候的导入表 PIMAGE_IMPORT_DESCRIPTOR pImageTable = (PIMAGE_IMPORT_DESCRIPTOR)((PDWORD)pData + 4); PDWORD pImportFunctionAddr = (PDWORD)(pImageTable + 2);//这里就是导入函数的地址,因为是3个函数,所以一共占有4个DWORD TGetProcAddress pGetProcAddress = (TGetProcAddress)pImportFunctionAddr[0]; TGetModuleHandleA pGetModuleHandleA= (TGetModuleHandleA)pImportFunctionAddr[1]; TLoadLibraryA pLoadLibraryA = (TLoadLibraryA)pImportFunctionAddr[2]; //获取镜像的基地址 PBYTE pImageBase = (PBYTE)pGetModuleHandleA(NULL); CHAR szUser32[] = {'U', 's', 'e', 'r', '3', '2', '.', 'd', 'l', 'l', '\0' }; CHAR szMessageBox[] = {'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', '\0'}; HMODULE hUser32 = pLoadLibraryA(szUser32); TMessageBoxA pMessageBox = (TMessageBoxA)pGetProcAddress(hUser32, szMessageBox); CHAR szMessage[] = {'H', 'e', 'l', 'l', 'o', '\0'}; pMessageBox(NULL, szMessage, szMessage, MB_OK); //入口点的rva 存储在偏移为1的地址 DWORD nEntryPoint = *((PDWORD)pData + 1); //现在自定义结构的导入表的首地址 PBYTE pImportTableNow = (PBYTE)(pImageBase + *((PDWORD)pData + 2)); PIMAGE_IMPORT_DESCRIPTOR pImportTableRestore = (PIMAGE_IMPORT_DESCRIPTOR)(pImageBase + *((PDWORD)pData + 3)); //char virtualAlloc[] = { 'V','i','r','t','u','a','l','A','l','l','o','c' }; //根据函数的地址填充导入表 //(PDWORD)FirstThunk | (PDWORD)functionCount | (PBYTE)dllName | (PBYTE)functionName(...X functionCount) INT j = 0; while (*(PDWORD)pImportTableNow)//如果没有到结尾 { pImportTableRestore[j].FirstThunk = *(PDWORD)pImportTableNow; PDWORD pIAT = (PDWORD)(pImageBase + pImportTableRestore[j].FirstThunk);//IAT表 INT count = *((PDWORD)pImportTableNow + 1); //获取这个dll一共导入了多少个函数 pImportTableRestore[j].TimeDateStamp = 0; pImportTableRestore[j].ForwarderChain = 0; //赋值名字的RVA PBYTE name = (PBYTE)((PDWORD)pImportTableNow + 2); pImportTableRestore[j].Name = name - pImageBase; pImportTableNow = (PBYTE)((PDWORD)pImportTableNow + 2) + MyStrlen((PCHAR)name) + 1; //填充导入函数的地址 for (INT i = 0; i < count; i++){ HMODULE hModule = pLoadLibraryA((PCHAR)name); if (hModule) { //如果是编号 if (0xF0000000 == (*(PDWORD)pImportTableNow & (0xF0000000))) { pIAT[i] = (DWORD)pGetProcAddress(hModule, (LPCSTR)(*(PDWORD)pImportTableNow & 0xFFFFFFF)); pImportTableNow += sizeof(DWORD); }else{ pIAT[i] = (DWORD)pGetProcAddress(hModule, (PCHAR)pImportTableNow); pImportTableNow += MyStrlen((PCHAR)pImportTableNow) + 1; } //指向下一个函数 } else { pIAT[i] = 0; if (0xF0000000 == (*(PDWORD)pImportTableNow & (0xF0000000))) { pImportTableNow += sizeof(DWORD); } else { pImportTableNow += MyStrlen((PCHAR)pImportTableNow) + 1; } } } j++; } /*解密代码段*/ //不考虑加密算法,先不写了吧 //调用原函数入口点,这里有一个问题就是会多压入一个返回地址,但是无伤大雅,因为只考虑exe,所以多次进入该函数的情况不存在 return (pImageBase + nEntryPoint); } //////////////////////////////ShellCode End//////////////////////////////////////// void __declspec(naked) End() { } int main() { //将ShellCode写出来 HANDLE hFile = CreateFileA("E:\\ShellCode.bin", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); DWORD dwSize; WriteFile(hFile, (LPVOID)Start, (DWORD)((PBYTE)End - (PBYTE)Start), &dwSize, NULL); CloseHandle(hFile); return 0; }
文章评论