• 壳的编写(4)-- 进行加壳操作


    由于我们在编写壳的部分比较简单,那么我们在编写加壳的过程中难免要复杂些。我们要完成加壳的操作必然会要读取被加壳程序的各种信息,并保存到一个结构中,为了便于后面的操作。还有在操作上只能读取源文件,将加壳后的文件要保存到另外的文件中去。

    为此我们在项目Pack_Dll中,我们完成加壳的操作:

             1.读取被加壳程序的PE信息

             2.对相应的区段进行处理(加密)

             3.将壳(Stub)部分添加到加壳程序中

             4.加壳操作

    5.1. 读取被加壳程序的PE信息
             在Pack_Dll.h文件中定义一个结构如下:

    // 用来保存壳(Stub)中用到的PE信息

    typedef struct _GLOBAL_PARAM
    {
    BOOL bShowMessage; // 是否显示解密信息
    DWORD dwOEP; // 程序入口点
    PBYTE lpStartVA; // 起始虚拟地址(被异或加密区)
    PBYTE lpEndVA; // 结束虚拟地址(被异或加密区)
    }GLOBAL_PARAM, *PGLOBAL_PARAM; 
    

      


    5.2. 对相应的区段进行处理(加密)
             给代码区段进行加密,并为其添加可读属性。

    void Pretreatment(PBYTE lpCodeStart, PBYTE lpCodeEnd, PE_INFO stcPeInfo)
    {
    // 1. 加密指定区域
    while (lpCodeStart < lpCodeEnd)
    {
    *lpCodeStart ^= 0x15;
    lpCodeStart++;
    }
    
    // 2. 给第一个区段附加上可写属性
    PDWORD pChara = &(stcPeInfo.pSectionHeader->Characteristics);
    *pChara = *pChara | IMAGE_SCN_MEM_WRITE;
    }
    

      


    5.3. 将壳(Stub)部分添加到加壳程序中
             我们要想将Stub部分添加到被加壳程序中去,就需要获取到Stub的信息。而我们已经在3.2的操作中将Stub工程产生的Stub.dll做为资源成为了项目Pack_Dll的一部分,那么我们就需要读取生成的Pack_Dll.dll并从中以资源的形式获取到Stub.dll句柄。然后提取Stub部分的关键信息,将Stub作为新的区段添加到被加壳程序中。为此我们还需要进行重定位和修复OEP。


    需要添加头文件

    #include <stdlib.h>
    #include "resource.h" //导入资源
    #include "ProcessingPE.h" //PE操作
    #include <winuser.h> //资源转换
    在Pack_Dll.cpp文件中的Implantation方法中完成即可。
    DWORD Implantation(LPVOID &lpFileData, DWORD dwSize, CProcessingPE* pobjPE, 
    PE_INFO stcPeInfo, GLOBAL_PARAM stcParam)
    {
    // 1. 在资源中读取文件内容
    HRSRC hREC = NULL; // 资源对象
    HGLOBAL hREC_Handle = NULL; // 资源句柄
    DWORD dwStubSize = NULL; // 文件大小
    LPVOID lpResData = NULL; // 资源数据指针
    HMODULE hModule = GetModuleHandle(L"Pack_Dll.dll");
    if (!(hREC = FindResource(hModule, MAKEINTRESOURCE(IDR_STUB1), L"STUB"))) return false;
    if (!(hREC_Handle = LoadResource(hModule, hREC))) return false;
    if (!(lpResData = LockResource(hREC_Handle))) return false;
    if (!(dwStubSize = SizeofResource(hModule, hREC))) return false;
    
    // 2. 提取Stub部分的关键信息
    CProcessingPE objProcPE;
    PE_INFO stcStubPeInfo;
    PBYTE lpData = new BYTE[dwStubSize];
    // 2.1 将Stub复制到临时缓冲区,防止重复操作
    CopyMemory(lpData, lpResData, dwStubSize);
    // 2.2 获取Stub的PE信息
    objProcPE.GetPeInfo(lpData, dwStubSize, &stcStubPeInfo);
    // 2.3 算出代码段的相关信息(默认第一个区段为代码段)
    PBYTE lpText = (PBYTE)(stcStubPeInfo.pSectionHeader->PointerToRawData + (DWORD)lpData);
    DWORD dwTextSize = stcStubPeInfo.pSectionHeader->SizeOfRawData;
    
    // 3. 添加区段
    DWORD dwNewSectionSize = 0;
    IMAGE_SECTION_HEADER stcNewSection = { 0 };
    PVOID lpNewSectionData = pobjPE->AddSection(L".LiPass", dwTextSize, 
    IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, &stcNewSection, &dwNewSectionSize);
    
    // 4. 对Stub部分进行的重定位操作
    // 新的加载地址 = (新区段的起始RVA - Stub的".Text"区段的起始RVA) + 映像基址
    DWORD dwLoadImageAddr = (stcNewSection.VirtualAddress - stcStubPeInfo.pSectionHeader->VirtualAddress) + stcPeInfo.dwImageBase;
    objProcPE.FixReloc(dwLoadImageAddr);
    
    // 5. 写入配置参数
    // 5.1 获取Stub的导出变量地址
    PVOID lpPatam = objProcPE.GetExpVarAddr(L"g_stcParam");
    // 5.2 保存配置信息到Stub中
    CopyMemory(lpPatam, &stcParam, sizeof(GLOBAL_PARAM));
    
    // 6. 将Stub复制到新区段中
    CopyMemory(lpNewSectionData, lpText, dwTextSize);
    
    // 7. 计算并设置新OEP
    DWORD dwNewOEP = 0;
    // 7.1 计算新OEP
    DWORD dwStubOEP = stcStubPeInfo.dwOEP;
    DWORD dwStubTextRVA = stcStubPeInfo.pSectionHeader->VirtualAddress;
    DWORD dwNewSectionRVA = stcNewSection.VirtualAddress;
    dwNewOEP = (dwStubOEP - dwStubTextRVA) + dwNewSectionRVA;
    // 7.2 设置新OEP
    pobjPE->SetOEP(dwNewOEP);
    pobjPE->SetDLL();
    
    // 8. 释放资源,函数返回
    delete[] lpData;
    FreeResource(hREC_Handle);
    return dwNewSectionSize;
    }
    

      

    5.4. 加壳操作
             先读取PE信息,并进行加密处理。然后将壳部分追加到程序中,并保存为新的文件。

             在Pack_Dll.cpp文件中的Pack方法中完成即可。

    BOOL Pack(CString strPath)
    {
    CProcessingPE objProcPE; // PE处理对象
    PE_INFO stcPeInfo; // PE信息
    
    HANDLE hFile_In;
    HANDLE hFile_Out;
    DWORD dwFileSize;
    LPVOID lpFileImage;
    WCHAR szOutPath[MAX_PATH] = { 0 };
    
    // 1. 生成输出文件路径
    LPWSTR strSuffix = PathFindExtension(strPath); // 获取文件的后缀名
    wcsncpy_s(szOutPath, MAX_PATH, strPath, wcslen(strPath)); // 备份目标文件路径到szOutPath
    PathRemoveExtension(szOutPath); // 将szOutPath中保存路径的后缀名去掉
    wcscat_s(szOutPath, MAX_PATH, L"_Pack"); // 在路径最后附加“_Pack”
    wcscat_s(szOutPath, MAX_PATH, strSuffix); // 在路径最后附加刚刚保存的后缀名
    
    // 2. 获取文件信息,并映射进内存中
    if (INVALID_HANDLE_VALUE == (hFile_In = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, 0, NULL)))
    {
    return false;
    }
    if (INVALID_FILE_SIZE == (dwFileSize = GetFileSize(hFile_In, NULL)))
    {
    CloseHandle(hFile_In);
    return false;
    }
    if (!(lpFileImage = VirtualAlloc(NULL, dwFileSize * 2, MEM_COMMIT, PAGE_READWRITE)))
    {
    CloseHandle(hFile_In);
    return false;
    }
    DWORD dwRet;
    if (!ReadFile(hFile_In, lpFileImage, dwFileSize, &dwRet, NULL))
    {
    CloseHandle(hFile_In);
    VirtualFree(lpFileImage, 0, MEM_RELEASE);
    return false;
    }
    
    // 3. 获取PE文件信息
    objProcPE.GetPeInfo(lpFileImage, dwFileSize, &stcPeInfo);
    
    // 4. 获取目标文件代码段的起始结束信息
    // 读取第一个区段的相关信息,并将其加密(默认第一个区段为代码段)
    PBYTE lpStart = (PBYTE)(stcPeInfo.pSectionHeader->PointerToRawData + (DWORD)lpFileImage);
    PBYTE lpEnd = (PBYTE)((DWORD)lpStart + stcPeInfo.pSectionHeader->SizeOfRawData);
    PBYTE lpStartVA = (PBYTE)(stcPeInfo.pSectionHeader->VirtualAddress + stcPeInfo.dwImageBase);
    PBYTE lpEndVA = (PBYTE)((DWORD)lpStartVA + stcPeInfo.pSectionHeader->SizeOfRawData);
    
    // 5. 对文件进行预处理
    Pretreatment(lpStart, lpEnd, stcPeInfo);
    
    // 6. 植入Stub
    DWORD dwStubSize = 0;
    GLOBAL_PARAM stcParam = { 0 };
    stcParam.dwOEP = stcPeInfo.dwOEP + stcPeInfo.dwImageBase;
    stcParam.lpStartVA = lpStartVA;
    stcParam.lpEndVA = lpEndVA;
    dwStubSize = Implantation(lpFileImage, dwFileSize, &objProcPE, stcPeInfo, stcParam);
    
    // 7. 将处理完成后的结果写入到新文件中
    if (INVALID_HANDLE_VALUE != (hFile_Out = CreateFile(szOutPath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL)))
    {
    DWORD dwRet = 0;
    WriteFile(hFile_Out, lpFileImage, dwStubSize + dwFileSize, &dwRet, NULL);
    }
    
    // 8. 释放相关资源并返回
    CloseHandle(hFile_In);
    CloseHandle(hFile_Out);
    VirtualFree(lpFileImage, 0, MEM_RELEASE);
    return true;
    }
    

      

    六 编写界面
             在Pack BaseDlg.cpp文件中进行完善即可。

    #include "../Pack_Dll/Pack_Dll.h"
    #ifdef _DEBUG
    #pragma comment(lib, "../Debug/Pack_Dll.lib")
    #else
    #pragma comment(lib, "../Release/Pack_ Dll.lib")
    #endif
    
    void CPackBaseDlg::OnBnClickedButton2()
    {
    UpdateData(true);
    //MessageBox(m_pathStr, m_pathStr, 0);
    
    if (!Pack(m_pathStr))
    {
    MessageBox(L"加密失败-_-!");
    }
    else
    {
    MessageBox(L"加密成功!");
    }
    }
    

      

    七 运行结果如下
    在使用静态编译的时候,要是以Release版本进行输出的时候,一定要将Stub项目的输出目录进行更改回去。

    由于之前一直使用Debug方式进行调试,使用Release版后,需要将Pack_Dll项目中Pack_Dll.rc的资源路径进行修改,将Debug修改成Release即可

    ————————————————
    版权声明:本文为CSDN博主「布衣僧」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/oBuYiSeng/article/details/50528622

  • 相关阅读:
    导出htmlcleaner
    备份
    本地win7搭建SVN
    nutch 导入ecl
    linux 启动nutch
    c++中的构造函数前加上explicit
    string::erase的使用心得
    C++的static关键字(转载)
    Boot Trigger
    strtol()详解
  • 原文地址:https://www.cnblogs.com/luckywolfzyy/p/11384968.html
Copyright © 2020-2023  润新知