简单的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;
}
