压缩壳的实现相关细节(强奸PE)

加壳 原理:
改变原始OEP的指向 指向壳程序 壳程序可以添加其它程序 如弹密码框 只有密码正确就运行(只是所有函数动态加载)

typedef int (WINAPI *LPMESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, UINT); //MessageBoxW
typedef DWORD(WINAPI *LPGETPROCADDRESS)(HMODULE, LPCSTR);         // GetProcAddress
typedef HMODULE(WINAPI *LPLOADLIBRARYEX)(LPCTSTR, HANDLE, DWORD); // LoadLibaryEx
typedef HMODULE(WINAPI *GETModuleHandle)(
    _In_opt_ LPCTSTR lpModuleName
    );
typedef BOOL(WINAPI* SHOWWINDOW)(
    _In_ HWND hWnd,
    _In_ int  nCmdShow
    );
typedef BOOL(WINAPI* GteMessage)(
    _Out_    LPMSG lpMsg,
    _In_opt_ HWND  hWnd,
    _In_     UINT  wMsgFilterMin,
    _In_     UINT  wMsgFilterMax
    );
typedef LRESULT(WINAPI* DISpatchMessage)(
    _In_ const MSG *lpmsg
    );
typedef ATOM(WINAPI* REGisterClass)(
    _In_ const WNDCLASS *lpWndClass
    );
typedef HWND(WINAPI *CREateWindowEx)(
    _In_     DWORD     dwExStyle,
    _In_opt_ LPCTSTR   lpClassName,
    _In_opt_ LPCTSTR   lpWindowName,
    _In_     DWORD     dwStyle,
    _In_     int       x,
    _In_     int       y,
    _In_     int       nWidth,
    _In_     int       nHeight,
    _In_opt_ HWND      hWndParent,
    _In_opt_ HMENU     hMenu,
    _In_opt_ HINSTANCE hInstance,
    _In_opt_ LPVOID    lpParam
    );
typedef VOID(WINAPI* POSTQuitMessage)(
    _In_ int nExitCode
    );
typedef LRESULT(WINAPI* DEFWindowProc)(
    _In_ HWND   hWnd,
    _In_ UINT   Msg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    );
typedef BOOL(*UPDateWindow)(
    _In_ HWND hWnd
    );
typedef int (WINAPI* GETWindowText)(
    _In_  HWND   hWnd,
    _Out_ LPTSTR lpString,
    _In_  int    nMaxCount
    );
typedef int (WINAPI* GETWindowTextLength)(
    _In_ HWND hWnd
    );
typedef HWND(WINAPI* GETDlgItem)(
    _In_opt_ HWND hDlg,
    _In_     int  nIDDlgItem
    );
typedef BOOL(WINAPI* SETWindowText)(
    _In_     HWND    hWnd,
    _In_opt_ LPCTSTR lpString
    );
typedef BOOL(WINAPI* TRanslateMessage)(
    _In_ const MSG *lpMsg
    );
typedef LPVOID(WINAPI *MYVIRTUALALLOC)(
    _In_opt_ LPVOID lpAddress,
    _In_     SIZE_T dwSize,
    _In_     DWORD  flAllocationType,
    _In_     DWORD  flProtect
    );
typedef BOOL(WINAPI *MYVIRTUALFREE)(
    _In_ LPVOID lpAddress,
    _In_ SIZE_T dwSize,
    _In_ DWORD  dwFreeType
    );
typedef HMODULE(WINAPI *MYLOADLIBRARY)(
    _In_ LPCSTR lpLibFileName
    );
wchar_t g_wcbuf100[100] = { 0 };
wchar_t g_MIMA100[100] = L"haidragon";
wchar_t wStrtext[100] = L"请输入密码";
_declspec(thread) int g_num;
/////////////////////////////////////////////////////////////
//初始化
LPGETPROCADDRESS    g_funGetProcAddress         = nullptr;
LPLOADLIBRARYEX     g_funLoadLibraryEx          = nullptr;
HMODULE             hModuleKernel32             = nullptr;
HMODULE             hModuleUser32               = nullptr;
GETModuleHandle     g_funGetModuleHandle        = nullptr;
LPMESSAGEBOX        g_funMessageBox             = nullptr;
CREateWindowEx      g_funCreateWindowEx         = nullptr;
POSTQuitMessage     g_funPostQuitMessage        = nullptr;
DEFWindowProc       g_funDefWindowProc          = nullptr;
GteMessage          g_funGetMessage             = nullptr;
REGisterClass       g_funRegisterClass          = nullptr;
SHOWWINDOW          g_funShowWindow             = nullptr;
UPDateWindow        g_funUpdateWindow           = nullptr;
DISpatchMessage     g_funDispatchMessage        = nullptr;
GETWindowText       g_funGetWindowText          = nullptr;
GETWindowTextLength g_funGetWindowTextLength    = nullptr;
GETDlgItem          g_funGetDlgItem             = nullptr;
SETWindowText       g_funSetWindowText          = nullptr;
TRanslateMessage    g_funTranslateMessage       = nullptr;
MYVIRTUALALLOC      g_VirtualAlloc              = nullptr;
MYVIRTUALFREE       g_VirtualFree               = nullptr;
MYLOADLIBRARY    g_LoadLibraryA                 = nullptr;

DWORD g_dwImageBase;
DWORD g_oep;
void start();
PACKINFO g_PackInfo = { (DWORD)start };
//获取kernel32模块加载基址
DWORD GetKernel32Base()
{
    DWORD dwKernel32Addr = 0;
    __asm
    {
        push eax
        mov eax, dword ptr fs : [0x30] // eax = PEB的地址
        mov eax, [eax + 0x0C]          // eax = 指向PEB_LDR_DATA结构的指针
        mov eax, [eax + 0x1C]          // eax = 模块初始化链表的头指针InInitializationOrderModuleList
        mov eax, [eax]                 // eax = 列表中的第二个条目
        mov eax, [eax + 0x08]          // eax = 获取到的Kernel32.dll基址(Win7下获取的是KernelBase.dll的基址)
        mov dwKernel32Addr, eax
        pop eax
    }

    return dwKernel32Addr;
}

//获取GetProcAddress的基址
DWORD GetGPAFunAddr()
{
    DWORD dwAddrBase = GetKernel32Base();

    // 1. 获取DOS头、NT头
    PIMAGE_DOS_HEADER pDos_Header;
    PIMAGE_NT_HEADERS pNt_Header;
    pDos_Header = (PIMAGE_DOS_HEADER)dwAddrBase;
    pNt_Header = (PIMAGE_NT_HEADERS)(dwAddrBase + pDos_Header->e_lfanew);

    // 2. 获取导出表项
    PIMAGE_DATA_DIRECTORY   pDataDir;
    PIMAGE_EXPORT_DIRECTORY pExport;
    pDataDir = pNt_Header->OptionalHeader.DataDirectory;
    pDataDir = &pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT];
    pExport = (PIMAGE_EXPORT_DIRECTORY)(dwAddrBase + pDataDir->VirtualAddress);

    // 3、获取导出表的必要信息
    DWORD dwModOffset = pExport->Name;                                  // 模块的名称
    DWORD dwFunCount = pExport->NumberOfFunctions;                      // 导出函数的数量
    DWORD dwNameCount = pExport->NumberOfNames;                         // 导出名称的数量

    PDWORD pEAT = (PDWORD)(dwAddrBase + pExport->AddressOfFunctions);   // 获取地址表的RVA
    PDWORD pENT = (PDWORD)(dwAddrBase + pExport->AddressOfNames);       // 获取名称表的RVA
    PWORD pEIT = (PWORD)(dwAddrBase + pExport->AddressOfNameOrdinals);  //获取索引表的RVA
    // 4、获取GetProAddress函数的地址
    for (DWORD i = 0; i < dwFunCount; i++)
    {
        if (!pEAT[i])
        {
            continue;
        }

        // 4.1 获取序号
        DWORD dwID = pExport->Base + i;

        // 4.2 变量EIT 从中获取到 GetProcAddress的地址
        for (DWORD dwIdx = 0; dwIdx < dwNameCount; dwIdx++)
        {
            // 序号表中的元素的值 对应着函数地址表的位置
            if (pEIT[dwIdx] == i)
            {
                //根据序号获取到名称表中的名字
                DWORD dwNameOffset = pENT[dwIdx];
                char * pFunName = (char*)(dwAddrBase + dwNameOffset);

                //判断是否是GetProcAddress函数
                if (!strcmp(pFunName, "GetProcAddress"))
                {
                    // 获取EAT的地址 并将GetProcAddress地址返回
                    DWORD dwFunAddrOffset = pEAT[i];
                    return dwAddrBase + dwFunAddrOffset;
                }
            }
        }
    }
    return -1;
}
//初始化API
bool InitializationAPI()
{
    g_num;//使用tls变量,产生tls节表
    //初始化
    g_funGetProcAddress              = (LPGETPROCADDRESS)   GetGPAFunAddr();
    g_funLoadLibraryEx               = (LPLOADLIBRARYEX)    g_funGetProcAddress((HMODULE)GetKernel32Base(), "LoadLibraryExW");
    hModuleKernel32                  =                      g_funLoadLibraryEx(L"Kernel32.dll", NULL, NULL);
    hModuleUser32                    =                      g_funLoadLibraryEx(L"user32.dll", NULL, NULL);
    g_LoadLibraryA                   = (MYLOADLIBRARY)      g_funGetProcAddress(hModuleKernel32, "LoadLibraryA");
    g_funGetModuleHandle             = (GETModuleHandle)    g_funGetProcAddress(hModuleKernel32, "GetModuleHandleW");
    g_VirtualAlloc                   = (MYVIRTUALALLOC)     g_funGetProcAddress(hModuleKernel32, "VirtualAlloc");
    g_VirtualFree                    = (MYVIRTUALFREE)      g_funGetProcAddress(hModuleKernel32, "VirtualFree");
    g_funMessageBox                  = (LPMESSAGEBOX)       g_funGetProcAddress(hModuleUser32, "MessageBoxW");
    g_funCreateWindowEx              = (CREateWindowEx)     g_funGetProcAddress(hModuleUser32, "CreateWindowExW");
    g_funPostQuitMessage             = (POSTQuitMessage)    g_funGetProcAddress(hModuleUser32, "PostQuitMessage");
    g_funDefWindowProc               = (DEFWindowProc)      g_funGetProcAddress(hModuleUser32, "DefWindowProcW");
    g_funGetMessage                  = (GteMessage)         g_funGetProcAddress(hModuleUser32, "GetMessageW");
    g_funRegisterClass               = (REGisterClass)      g_funGetProcAddress(hModuleUser32, "RegisterClassW");
    g_funShowWindow                  = (SHOWWINDOW)         g_funGetProcAddress(hModuleUser32, "ShowWindow");
    g_funUpdateWindow                = (UPDateWindow)       g_funGetProcAddress(hModuleUser32, "UpdateWindow");
    g_funDispatchMessage             = (DISpatchMessage)    g_funGetProcAddress(hModuleUser32, "DispatchMessageW");
    g_funGetWindowText               = (GETWindowText)      g_funGetProcAddress(hModuleUser32, "GetWindowTextW");
    g_funGetWindowTextLength         = (GETWindowTextLength)g_funGetProcAddress(hModuleUser32, "GetWindowTextLengthW");
    g_funGetDlgItem                  = (GETDlgItem)         g_funGetProcAddress(hModuleUser32, "GetDlgItem");
    g_funSetWindowText               = (SETWindowText)      g_funGetProcAddress(hModuleUser32, "SetWindowTextW");
    g_funTranslateMessage            = (TRanslateMessage)   g_funGetProcAddress(hModuleUser32, "TranslateMessage");

    g_dwImageBase                    = (DWORD)g_funGetModuleHandle(NULL);
    g_oep                            = g_PackInfo.TargetOepRva + g_dwImageBase;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
) {

    switch (uMsg)
    {
    case WM_CREATE: {
        wchar_t wStr[20] = L"窗口回调函数触发";
        wchar_t wStr2[20] = L"haha";
        g_funMessageBox(NULL, wStr, wStr2, NULL);
        /////////////////////////////////////////////////////////////////////////////////////
        DWORD dwStyle = ES_LEFT | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE;
        DWORD dwExStyle = WS_EX_CLIENTEDGE | WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR;
        HWND hWnd = g_funCreateWindowEx(
            dwExStyle, //dwExStyle 扩展样式
            L"Edit", //lpClassName 窗口类名
            wStrtext, //lpWindowName 窗口标题
            dwStyle, //dwStyle 窗口样式
            150, //x 左边位置
            100, //y 顶边位置
            200, //nWidth 宽度
            20, //nHeight 高度
            hwnd, //hWndParent 父窗口句柄
            (HMENU)0x1002, //ID
            g_funGetModuleHandle(0), //hInstance 应用程序句柄
            NULL //lpParam 附加参数
        );
        return 0;
        /////////////////////////////////////////////////////////////////////////////////
    }
    case WM_COMMAND: {
        WORD wId = LOWORD(wParam);
        WORD wCode = HIWORD(wParam);
        HANDLE hChild = (HANDLE)lParam;
        if (wId == 0x1001 && wCode == BN_CLICKED)
        {

            HWND hwndCombo = g_funGetDlgItem(hwnd, 0x1002);
            int cTxtLen = g_funGetWindowTextLength(hwndCombo);
            g_funGetWindowText(hwndCombo, g_wcbuf100, 100);

            wchar_t wStr[20] = L"按钮触发";
            wchar_t wStr2[20] = L"haha";
            g_funMessageBox(NULL, wStr, wStr2, NULL);
            wchar_t wStr3[20] = L"";
            if (decide() == 1) {
                //g_funPostQuitMessage(0);
                g_funShowWindow(hwnd, SW_HIDE);
                //运行壳代码
                FusedFunc((DWORD)AllFunc);
                //_asm jmp g_PackInfo.TargetOep;
                wchar_t wStr[20] = L"密码正确!!!";
                wchar_t wStr2[20] = L"haha";
                g_funMessageBox(NULL, wStr, wStr2, NULL);
                _asm jmp g_oep;
            }
            else {
                wchar_t wStr[20] = L"密码错误请重新输入!!!";
                wchar_t wStr2[20] = L"haha";
                g_funMessageBox(NULL, wStr, wStr2, NULL);
            }
            g_funSetWindowText(hwndCombo, wStr3);
            return 1;
        }
        break;
    }
    case WM_CLOSE:
    {
        g_funPostQuitMessage(0);
        //return 0;
        break;
    }

    }
    // 返回默认的窗口处理过程
    return g_funDefWindowProc(hwnd, uMsg, wParam, lParam);
}
void CtrateWin() {

    MSG msg = { 0 };
    wchar_t wStr[20] = L"allenboy";
    wchar_t wStr2[20] = L"haha";
    g_funMessageBox(NULL, wStr, wStr2, NULL);
    // 先注册窗口类
    WNDCLASS wcs = {};
    wcs.lpszClassName = L"dragon";
    wcs.lpfnWndProc = WindowProc;
    wcs.hbrBackground = (HBRUSH)(COLOR_CAPTIONTEXT + 1);
    /////////////////////////////////////////////////////////////////////////////////////////
    //RegisterClass
    //RegisterClass(&wcs);
    g_funRegisterClass(&wcs);
    //#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,\
        //nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)
//注册窗口
//CreateWindowEx

//窗口类名一定要与上面的一致
    HWND hWnd = g_funCreateWindowEx(0L, L"dragon", L"haidragon", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        500, 200, 500, 500,
        NULL, NULL, NULL, NULL);
    // 三种风格  WS_OVERLAPPEDWINDOW  WS_POPUPWINDOW  WS_CHILDWINDOW

    g_funCreateWindowEx(0L, L"BUTTON", L"ok", WS_CHILD | WS_VISIBLE,
        200, 150,// 在父窗口的客户区的位置,
        100, 50,// 宽 高
        hWnd,// 父窗口
        (HMENU)0x1001,// 如果是顶层窗口 就是菜单句柄 子窗口就是本身的ID
                      //GetModuleHandle
        g_funGetModuleHandle(0), NULL);

    //ShowWindow(hWnd, SW_SHOW);
    g_funShowWindow(hWnd, SW_SHOW);
    g_funUpdateWindow(hWnd);
    /*while (GetMessage(&msg, 0, 0, 0))
    {
    DispatchMessage(&msg);
    }*/
    while (g_funGetMessage(&msg, 0, 0, 0))
    {

        //DispatchMessage(&msg);
        g_funTranslateMessage(&msg);
        g_funDispatchMessage(&msg);
    }
}

效果图

加密代码段 简单异或

//对代码段进行加密
void PEpack::Encode()
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
    // 定位到代码段,并将每个段加密
    pSection = pSection + m_codeIndex;
    PCHAR pStart = pSection->PointerToRawData + m_pNewBuf;
    for (int i = 0; i < (pSection->Misc.VirtualSize); i++)
    {
        pStart[i] ^= 0x20;
    }

}
////////////壳中解密//////////////////////
//解密
void Decode()
{

    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)g_dwImageBase;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + g_dwImageBase);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);

    // 找到.text段,并解密
    while (TRUE)
    {
        if (!strcmp((char*)pSection->Name, ".text"))
        {
            PCHAR pStart = pSection->VirtualAddress + (PCHAR)g_dwImageBase;
            for (int i = 0; i < pSection->Misc.VirtualSize; i++)
            {
                pStart[i] ^= 0x20;
            }
            break;
        }
        pSection = pSection + 1;
    }
}

压缩(除tls 表 资源表 与 头除外 这里先于拷贝壳的重定位表 不然也会被压缩 就运行不起来了 )
压缩时的问题有 资源段问题 tls段问题
首先 运行时很会用到 资源 同时当运行壳代码之前会启动线程,因些就会用到tls数据段
所以这俩个都不压 其它全部压然后 放到最后,同时在压缩的时候重建PE(也就是后面的往上移)
最重的一点是在所有操作前 保存相关节与节的前后顺序与在原来大小和压缩后的大小等等


效果图



压缩 (同时重建PE)

//压缩PE文件

void PEpack::CompressPE(PPACKINFO &pPackInfo)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);

    // 用于记录压缩区段的个数
    pPackInfo->PackSectionNumber = 0;

    // 1.1 获取文件头的大小,并获取除资源段,tls之外段的文件中的总大小
    DWORD SecSizeWithOutResAndTls = 0;
    PIMAGE_SECTION_HEADER pSectionTmp1 = pSection;
    BOOL isFirstNoEmptySec = TRUE;
    DWORD dwHeaderSize = 0;
    //先计算要拷贝的总大小 
    for (SIZE_T i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        // 用于获得第一个非空区段的文件偏移,也就是文件头大小
        if (isFirstNoEmptySec && pSectionTmp1->SizeOfRawData != 0)
        {
            dwHeaderSize = pSectionTmp1->PointerToRawData;
            isFirstNoEmptySec = FALSE;
        }
        // 用于获取 非 rsrc/tls段的总大小
        if (pSectionTmp1->VirtualAddress != m_pResSectionRva &&
            pSectionTmp1->VirtualAddress != m_pTlsSectionRva)
        {
            SecSizeWithOutResAndTls += pSectionTmp1->SizeOfRawData;
        }
        pSectionTmp1 = pSectionTmp1 + 1;
    }
    // 1.2 读取要压缩的段到内存
    // 申请内存
    PCHAR memWorked = new CHAR[SecSizeWithOutResAndTls];
    // 已经拷贝的内存大小
    DWORD dwCopySize = 0;
    // 保存这些区段到内存
    PIMAGE_SECTION_HEADER pSectionTmp2 = pSection;
    //再次循环
    for (SIZE_T i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        //判断是否为tls与资源表
        if (pSectionTmp2->VirtualAddress != m_pResSectionRva &&
            pSectionTmp2->VirtualAddress != m_pTlsSectionRva)
        {
         //开始拷贝
            memcpy_s(memWorked + dwCopySize, pSectionTmp2->SizeOfRawData,
                m_pNewBuf + pSectionTmp2->PointerToRawData, pSectionTmp2->SizeOfRawData);
            dwCopySize += pSectionTmp2->SizeOfRawData;
        }
        pSectionTmp2 = pSectionTmp2 + 1;
    }
    // 1.3 压缩,并获取压缩后的大小(文件对齐在添加区段中进行)
    LONG blen;
    PCHAR packBuf = Compress(memWorked, SecSizeWithOutResAndTls, blen);

    // 1.4 保存资源段 到内存空间
    PCHAR resBuffer = new CHAR[m_ResSizeOfRawData];
    PCHAR tlsBuffer = new CHAR[m_TlsSizeOfRawData];
    //原样
    memcpy_s(resBuffer, m_ResSizeOfRawData, m_ResPointerToRawData + m_pNewBuf, m_ResSizeOfRawData);
    memcpy_s(tlsBuffer, m_TlsSizeOfRawData, m_TlsPointerToRawData + m_pNewBuf, m_TlsSizeOfRawData);

    // 1.6 设置压缩信息到信息结构体中
    //     并且将m_pBuf中的非资源段和非tls段的区段文件偏移和大小置为0
    PIMAGE_DOS_HEADER pOriDos = (PIMAGE_DOS_HEADER)m_pBuf;
    PIMAGE_NT_HEADERS pOriNt = (PIMAGE_NT_HEADERS)(pOriDos->e_lfanew + m_pBuf);
    PIMAGE_SECTION_HEADER pOriSection = IMAGE_FIRST_SECTION(pOriNt);

    for (int i = 0; i < pOriNt->FileHeader.NumberOfSections; i++)
    {
        if (pOriSection->VirtualAddress != m_pResSectionRva&&
            pOriSection->VirtualAddress != m_pTlsSectionRva)
        {
            // 用于获取压缩节区的数量
            pPackInfo->PackSectionNumber++;
            // 设置第i个节的压缩节区index
            pPackInfo->PackInfomation[pPackInfo->PackSectionNumber][0] = i;
            // 设置压缩节区的文件大小
            pPackInfo->PackInfomation[pPackInfo->PackSectionNumber][1] = pOriSection->SizeOfRawData;

            // 设置原来的节区的文件中偏移和大小为0
            pOriSection->SizeOfRawData = 0;
            pOriSection->PointerToRawData = 0;
        }

        pOriSection = pOriSection + 1;
    }

    // 1.6 申请新空间,使m_pNewBuf指向之,将m_pBuf文件头拷贝
    m_FileSize = dwHeaderSize + m_ResSizeOfRawData + m_TlsSizeOfRawData;
    //开始上移
    m_pNewBuf = nullptr;
    m_pNewBuf = new CHAR[m_FileSize];

    // 1.6 修改res段的区段头,并拷贝
    pOriSection = IMAGE_FIRST_SECTION(pOriNt);
    //开始修改资源与tls段的头  先判断它俩个位置
    if (m_ResSectionIndex < m_TlsSectionIndex)
    {
        pOriSection[m_ResSectionIndex].PointerToRawData = dwHeaderSize;
        pOriSection[m_TlsSectionIndex].PointerToRawData = dwHeaderSize + m_ResSizeOfRawData;
        memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
        memcpy_s(m_pNewBuf + dwHeaderSize, m_ResSizeOfRawData, resBuffer, m_ResSizeOfRawData);
        memcpy_s(m_pNewBuf + dwHeaderSize + m_ResSizeOfRawData
            , m_TlsSizeOfRawData, tlsBuffer, m_TlsSizeOfRawData);
    }
    else if (m_ResSectionIndex > m_TlsSectionIndex)
    {
        pOriSection[m_TlsSectionIndex].PointerToRawData = dwHeaderSize;
        pOriSection[m_ResSectionIndex].PointerToRawData = dwHeaderSize + m_TlsSizeOfRawData;
        memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
        memcpy_s(m_pNewBuf + dwHeaderSize, m_TlsSizeOfRawData, tlsBuffer, m_TlsSizeOfRawData);
        memcpy_s(m_pNewBuf + dwHeaderSize + m_TlsSizeOfRawData
            , m_ResSizeOfRawData, resBuffer, m_ResSizeOfRawData);
    }
    else
    {
        memcpy_s(m_pNewBuf, dwHeaderSize, m_pBuf, dwHeaderSize);
    }
    /*else if(m_ResSectionIndex == -1 && m_TlsSectionIndex == -1)
    {
    }*/

    delete[] m_pBuf;
    m_pBuf = m_pNewBuf;

    // 添加区段(压缩后的所有数据)
    pPackInfo->packSectionRva = AddSection(".pack", packBuf, blen, 0xC0000040);
    pPackInfo->packSectionSize = CalcAlignment(blen, 0x200);
    // 1.7 添加.pack段
    delete[] memWorked;
    free(packBuf);
    delete[] resBuffer;
}

解压

void Decompress()
{
    // 1.获取节区头首地址

    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)g_dwImageBase;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + g_dwImageBase);
    PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);

    // 2.解压压缩区段
    PCHAR lpPacked = ((PCHAR)g_dwImageBase + g_PackInfo.packSectionRva);// 内存地址
    DWORD dwPackedSize = aPsafe_get_orig_size(lpPacked);// 获取解压后的大小
    PCHAR lpBuffer = (PCHAR)g_VirtualAlloc(NULL, dwPackedSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//申请内存
    aPsafe_depack(lpPacked, g_PackInfo.packSectionSize, lpBuffer, dwPackedSize);// 解压
    // 3.将各区段还原回去
    DWORD offset = 0;
    for (int i = 0; i < g_PackInfo.PackSectionNumber; i++)
    {
        // 区段的标号
        int index = g_PackInfo.PackInfomation[i][0];
        // 这个区段的SizeOfRawData
        int size = g_PackInfo.PackInfomation[i][1];
        int * pint = &size;
        PCHAR destionVA = (PCHAR)g_dwImageBase + pSecHeader[index].VirtualAddress;
        PCHAR srcVA = lpBuffer + offset;
        _asm {
            mov eax, eax
            mov eax, eax
            mov eax, eax
            mov eax, eax
            mov eax, eax
            mov eax, eax
        }
        //memcpy(destionVA, srcVA, size);
        _asm {
            mov esi, srcVA
            mov edi, destionVA
            mov ebx, pint
            mov ecx, [ebx]
            cld;地址增量传送
            rep movsb;rep执行一次串指令后ecx减一
        }
        offset += size;
    }
    g_VirtualFree(lpBuffer, dwPackedSize, MEM_DECOMMIT);

}

增加随机基址
回顾 重定位表
要想实现随机基址 必须解决俩个问题
第一 壳代码的重定位 问题
第二 目标程序(被加壳程序 )的重定位
解决第一个问题 由于壳程序 区段都在text中 把text区段拷贝到目标程序时,同时也要拷贝重定位 把原来目标程序的重定位目录表的指向改为拷贝的重定位的地址
解决第二个问题 原来的重定位表 被压缩了 壳代码来修复

拷贝重定位 同时修改指向

//对于动态加载基址,需要将stub的重定位区段(.reloc)修改后保存,将PE重定位信息指针指向该地址

void PEpack::ChangeReloc(PCHAR pBuf)
{
    // 定位到第一个重定位块
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pBuf);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
    PIMAGE_DATA_DIRECTORY pRelocDir = (pNt->OptionalHeader.DataDirectory + 5);
    PIMAGE_BASE_RELOCATION pReloc =
        (PIMAGE_BASE_RELOCATION)(pRelocDir->VirtualAddress + pBuf);

    // 开始更改重定位
    while (pReloc->SizeOfBlock != 0)
    {

        // 重定位项开始的项,将其定位到在此之前添加allen段(要他原来的RVA 变为壳代码中的RVA)
        //原理:重位表的每一块中 pReloc->VirtualAddress 为每一页的起始地址(也就是0x1000 0x2000...),根据页分块
        //原来的代码段在相对于加载基址(假如0x400000)的第2页也就是0x1000(第二页的起始地址)表示PE中的头与数据目录表等等
        // 而这个重定位是dll 的也就是他没有头了 要减去0x1000(所有的)  同时加了节的RVA,也就是最后一节壳代码的开始(RVA)  相当于代码段从这里开始
        //例如要找到需要重定位的那个地址的va  va=加载基址 +页基址(pReloc->VirtualAddress)+页内地址(重定位块中的一个数组保存就是)
        pReloc->VirtualAddress = (DWORD)(pReloc->VirtualAddress - 0x1000 + GetLastSectionRva());
        // 定位到下一个重定位块
        pReloc = (PIMAGE_BASE_RELOCATION)((PCHAR)pReloc + pReloc->SizeOfBlock);
    }
    DWORD dwRelocRva = 0;
    DWORD dwRelocSize = 0;
    DWORD dwSectionAttribute = 0;
    while (TRUE)
    {
        if (!strcmp((char*)pSection->Name, ".reloc"))
        {
            dwRelocRva = pSection->VirtualAddress;
            dwRelocSize = pSection->SizeOfRawData;
            dwSectionAttribute = pSection->Characteristics;
            break;
        }
        pSection = pSection + 1;
    }

    // 将stubdll的.reloc添加到PE文件的最后,命名为.nreloc,返回该区段的Rva
    DWORD RelocRva = AddSection(".nreloc", dwRelocRva + pBuf, dwRelocSize, dwSectionAttribute);

    // 将重定位信息指向新添加的区段
    PIMAGE_DOS_HEADER pExeDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    PIMAGE_NT_HEADERS pExeNt = (PIMAGE_NT_HEADERS)(pExeDos->e_lfanew + m_pNewBuf);
    pExeNt->OptionalHeader.DataDirectory[5].VirtualAddress = RelocRva;
    pExeNt->OptionalHeader.DataDirectory[5].Size = dwRelocSize;

}

修复重定位

void FixReloc()
{

    //以下是重定位
    DWORD *tmp;
    if (g_PackInfo.RelocRva)  //如果没有重定位表表示不用重定位,跳过重定位代码
    {
        DWORD relocation = (DWORD)g_dwImageBase - g_PackInfo.ImageBase;
        IMAGE_BASE_RELOCATION  *relocationAddress = (IMAGE_BASE_RELOCATION*)(g_PackInfo.RelocRva + (DWORD)g_dwImageBase);

        while (relocationAddress->VirtualAddress != 0)
        {
            LPVOID rva = (LPVOID)((DWORD)g_dwImageBase + relocationAddress->VirtualAddress);
            DWORD BlockNum = (relocationAddress->SizeOfBlock - 8) / 2;
            if (BlockNum == 0) break;
            WORD *Offset = (WORD *)((DWORD)relocationAddress + 8);
            for (int i = 0; i < (int)BlockNum; i++)
            {
                if ((Offset[i] & 0xF000) != 0x3000) continue;
                tmp = (DWORD*)((Offset[i] & 0xFFF) + (DWORD)rva);
                *tmp = (*tmp) + relocation;
            }
            relocationAddress = (IMAGE_BASE_RELOCATION*)((DWORD)relocationAddress + relocationAddress->SizeOfBlock);
        }
    }
}

tls处理
tls回顾
https://www.cnblogs.com/iBinary/p/7697355.html
先简单说下
1。PE目录表第10项为tls目录它指向tls表,tls表保存在rdata段 表的一些地址又指向另外的地址就是 tls数据段
2。有个这个消息之后,如果压缩所有,有几个问题:
第一 rdata很定要压缩,但压缩完后 表也就没了
第二 有的程序本就没有线程 也就对应着没有索引数组
第三 tls被我处理了 加载器不知道了,怎么处理
一个个来
先解决第一个 rdata段压缩了 没了tls表 那就用stub的(壳对应的tls 由于它的段都被合成text了 但是要做一些地址转换问题)
解决第二个 有的程序没有 那我就在壳程序里创建一个线程局部变量 对应着 它就给我生成一个tls表
解决第三个 tls表不就是保存一些回调函数嘛 加载器也就是判断你有没有回调 有调用 没有不调用 那处理了tls表 加载器不会调用(处理tls也就是把回调地址变为0) 那就壳代码来调用
总结:
在加壳之前我先做相关处理 处理tls表
然后加壳之后 设置tls表 为stub中的表也就是设置 目录表第10项tls表的指向
还有一个最重要的就是 不管你有没有我先创建一个线程(索引数组)也就是创建一个线程局部变量 这个在壳代码里创建的 所以放在共享结构体中 tls表中保存的都是VA (逆向工程核心原理第454页有讲)


效果图

代码
1。第一步先读文件 设置相应的地址

//读取要加密文件到内存  同时获取相关的区段信息
void PEpack::ReadTargetFile(char* pPath, PPACKINFO& pPackInfo)
{
    DWORD dwRealSize = 0;
    //1 打开文件
    HANDLE hFile = CreateFileA(
        pPath, 0x0001, FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL, NULL
    );
    //2 获取文件大小
    m_FileSize = GetFileSize(hFile, NULL);
    m_dwNewFileSize = m_FileSize;
    //3 申请这么大的空间
    m_pBuf = new CHAR[m_FileSize];
    m_pNewBuf = m_pBuf;
    memset(m_pBuf, 0, m_FileSize);

    //4 把文件内容读取到申请出的空间中
    ReadFile(hFile, m_pBuf, m_FileSize, &dwRealSize, NULL);

    m_pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    m_pNt = (PIMAGE_NT_HEADERS)(m_pDos->e_lfanew + m_pNewBuf);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(m_pNt);
    // 保存原始节区数
    m_OriSectionNumber = m_pNt->FileHeader.NumberOfSections;

    // 获取OEP
    DWORD dwOEP = m_pNt->OptionalHeader.AddressOfEntryPoint;
    // 获得资源段的信息
    m_pResRva = m_pNt->OptionalHeader.DataDirectory[2].VirtualAddress;
    m_pResSectionRva = 0;
    m_ResSectionIndex = -1;
    m_ResPointerToRawData = 0;
    m_ResSizeOfRawData = 0;

    // 获取tls的信息
    m_pTlsSectionRva = 0;
    m_TlsSectionIndex = -1;
    m_TlsPointerToRawData = 0;
    m_TlsSizeOfRawData = 0;
    if (m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress)
    {
        // 获得tls表指针
        PIMAGE_TLS_DIRECTORY32 g_lpTlsDir =
            (PIMAGE_TLS_DIRECTORY32)(RvaToOffset(m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
        // 获得tls数据起始rva
        m_pTlsDataRva = g_lpTlsDir->StartAddressOfRawData - m_pNt->OptionalHeader.ImageBase;
    }

    for (int i = 0; i < m_pNt->FileHeader.NumberOfSections; i++)
    {
        // 如果oep在这个区段,就判断这个区段是代码段
        if (dwOEP >= pSection->VirtualAddress &&
            dwOEP <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
        {
            // 获取代码段所在区段序号[通过oep判断]
            m_codeIndex = i;
        }
        if (m_pResRva >= pSection->VirtualAddress &&
            m_pResRva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
        {
            // 获取rsrc段的信息
            m_pResSectionRva = pSection->VirtualAddress;
            m_ResPointerToRawData = pSection->PointerToRawData;
            m_ResSizeOfRawData = pSection->SizeOfRawData;
            m_ResSectionIndex = i;
        }
        // 获取tls信息
        if (m_pNt->OptionalHeader.DataDirectory[9].VirtualAddress)
        {
            if (m_pTlsDataRva >= pSection->VirtualAddress &&
                m_pTlsDataRva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
            {
                m_pTlsSectionRva = pSection->VirtualAddress;
                m_TlsSectionIndex = i;
                m_TlsPointerToRawData = pSection->PointerToRawData;
                m_TlsSizeOfRawData = pSection->SizeOfRawData;
            }
        }

        pSection = pSection + 1;
    }

    //5 关闭文件
    CloseHandle(hFile);
}

2.第二步
处理tls

BOOL PEpack::DealwithTLS(PPACKINFO & pPackInfo)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + m_pNewBuf);
    DWORD dwImageBase = pNt->OptionalHeader.ImageBase;
    //先判断
    if (pNt->OptionalHeader.DataDirectory[9].VirtualAddress == 0)
    {
        pPackInfo->bIsTlsUseful = FALSE;
        return FALSE;
    }
    else
    {
        //设置为处理了tls
        pPackInfo->bIsTlsUseful = TRUE;

        PIMAGE_TLS_DIRECTORY32 g_lpTlsDir =
            (PIMAGE_TLS_DIRECTORY32)(RvaToOffset(pNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
        // 获取tlsIndex(索引地址)的Offset 
        DWORD indexOffset = RvaToOffset(g_lpTlsDir->AddressOfIndex - dwImageBase);
        // 读取设置tlsIndex的值
        pPackInfo->TlsIndex = 0;//index一般默认值为0
        //这里是文件中的
        if (indexOffset != -1)
        {
            //取内容
            pPackInfo->TlsIndex = *(DWORD*)(indexOffset + m_pNewBuf);
        }
        // 设置tls表中的信息 
        m_StartOfDataAddress = g_lpTlsDir->StartAddressOfRawData;
        m_EndOfDataAddress = g_lpTlsDir->EndAddressOfRawData;
        m_CallBackFuncAddress = g_lpTlsDir->AddressOfCallBacks;

        // 将tls回调函数rva设置到共享信息结构体
        pPackInfo->TlsCallbackFuncRva = m_CallBackFuncAddress;
        return TRUE;
    }

}

3.第三步 压缩后 设置tls

void PEpack::SetTls(DWORD NewSectionRva, PCHAR pStubBuf, PPACKINFO pPackInfo)
{
    PIMAGE_DOS_HEADER pStubDos = (PIMAGE_DOS_HEADER)pStubBuf;
    PIMAGE_NT_HEADERS pStubNt = (PIMAGE_NT_HEADERS)(pStubDos->e_lfanew + pStubBuf);

    PIMAGE_DOS_HEADER pPeDos = (PIMAGE_DOS_HEADER)m_pNewBuf;
    PIMAGE_NT_HEADERS pPeNt = (PIMAGE_NT_HEADERS)(pPeDos->e_lfanew + m_pNewBuf);

    //0 将pe目录表9指向stub的tls表
    pPeNt->OptionalHeader.DataDirectory[9].VirtualAddress =
        (pStubNt->OptionalHeader.DataDirectory[9].VirtualAddress - 0x1000) + NewSectionRva;
    pPeNt->OptionalHeader.DataDirectory[9].Size =
        pStubNt->OptionalHeader.DataDirectory[9].Size;

    PIMAGE_TLS_DIRECTORY32  pITD =
        (PIMAGE_TLS_DIRECTORY32)(RvaToOffset(pPeNt->OptionalHeader.DataDirectory[9].VirtualAddress) + m_pNewBuf);
    // 获取公共结构体中tlsIndex的va
    //共享信息结构体的首地址-stubdll在内存的指针+4(在结构体的中偏移)-stub头部+NewSectionRva+加载基址   
    DWORD indexva = ((DWORD)pPackInfo - (DWORD)pStubBuf + 4) - 0x1000 + NewSectionRva + pPeNt->OptionalHeader.ImageBase;
    pITD->AddressOfIndex = indexva;
    pITD->StartAddressOfRawData = m_StartOfDataAddress;
    pITD->EndAddressOfRawData = m_EndOfDataAddress;

    // 这里先取消tls的回调函数,向共享信息结构体中传入tls回调函数指针,在stub解壳的过程中手动调用tls,并将tls回调函数指针设置回去
    pITD->AddressOfCallBacks = 0;

    m_pBuf = m_pNewBuf;
}

IAT修复
压缩后加载器不会修复了(因为设置为0了)壳里修复 简单的处理
主要是有一个地址,就是自己申请一块内存空间,构造一段硬编码,将原函数地址填到这个硬编码的指定位置,然后将内存空间首地址写到IAT表

void IATReloc()
{
    //读取IAT的dll , 获得dll加载基址; 读取IAT中的函数名 , 获得函数地址; 申请指定大小的空间

    // 1.获取第一项iat项
    PIMAGE_IMPORT_DESCRIPTOR pImportTable =
        (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)g_PackInfo.ImportTableRva + g_dwImageBase);
    if (g_PackInfo.ImportTableRva) //如果没用导入表则跳过
    {
        HMODULE lib;
        IMAGE_THUNK_DATA *IAT, *INTable;
        IMAGE_IMPORT_BY_NAME *IatByName;

        while (pImportTable->Name)//(pImportTable->FirstThunk)
        {
            lib = g_LoadLibraryA((char *)(pImportTable->Name + (DWORD)g_dwImageBase));

            IAT = (IMAGE_THUNK_DATA *)(pImportTable->FirstThunk + (DWORD)g_dwImageBase);
            INTable = (IMAGE_THUNK_DATA *)((pImportTable->OriginalFirstThunk ? pImportTable->OriginalFirstThunk : pImportTable->FirstThunk) + (DWORD)g_dwImageBase);
            while (INTable->u1.AddressOfData)
            {
                DWORD dwAddress;
                if ((((DWORD)INTable->u1.Function) & 0x80000000) == 0)
                {
                    IatByName = (IMAGE_IMPORT_BY_NAME *)((DWORD)INTable->u1.AddressOfData + (DWORD)g_dwImageBase);
                    dwAddress = (DWORD)g_funGetProcAddress(lib, (char *)(IatByName->Name));
                }
                else
                {
                    dwAddress = (DWORD)g_funGetProcAddress(lib, (LPCSTR)(INTable->u1.Ordinal & 0xFFFF));
                }
                char *dllName = (char *)(pImportTable->Name + (DWORD)g_dwImageBase);

                // 只重定向这几个dll,如果所有的都重定向会出错
                if ((!strcmp(dllName, "kernel32.dll"))
                    || (!strcmp(dllName, "user32.dll"))
                    || (!strcmp(dllName, "advapi32.dll"))
                    || (!strcmp(dllName, "gdi32.dll")))
                {
                    // 申请虚拟内存
                    PCHAR virBuf = (PCHAR)g_VirtualAlloc(NULL, 7, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

                    // 赋值机器码     
                    // mov ebx,address ;jmp address   0xbb 00 00 00 00 ff e3
                    virBuf[0] = 0xBB;
                    *(DWORD*)(virBuf + 1) = dwAddress;
                    virBuf[5] = 0xFF;
                    virBuf[6] = 0xE3;

                    // 将iat表填充为这个
                    IAT->u1.Function = (DWORD)virBuf;
                }
                else
                {
                    IAT->u1.Function = dwAddress;
                }

                INTable++;
                IAT++;
            }
            pImportTable++;
        }
    }
}

转载于:https://blog.51cto.com/haidragon/2129607

你可能感兴趣的:(压缩壳的实现相关细节(强奸PE))