首页
社区
课程
招聘
[原创]初次编写加壳器
发表于: 2021-6-4 16:16 21932

[原创]初次编写加壳器

2021-6-4 16:16
21932

简介

本人学壳两月有余,看了众多大佬的代码,终于独自完成了一个初级加壳器,故做分享讨论。本加壳器用C语言完成,没有加密,反调试等任何功能,仅仅加了个壳而已。并且此加壳器只支持32位的exe程序。
(另外,本人目前做安全运维工作,想找一份逆向方面的工作,病毒分析软件破解之类的,有意者联系,能有1W我就满意了。吐槽一下,搞逆向太难找工作了。)
着重声明:本程序的代码有一小部分是借鉴了论坛中其它大佬的代码。
此加壳器分为两部分,主程序和Shell部分。

加壳器

目录
一 主程序
     1.1 主程序入口函数
     1.2 主程序对话框的回调函数
     1.3 加壳主函数mainPack
          1.3.1 读取要加密的PE32文件
          1.3.2 导入Shell
          1.3.3 找到shell中导出的结构体并设置该结构体
          1.3.4 将导入到内存中的Shell复制到一块新的缓冲区中
          1.3.5 重定位缓冲区中的Shell
          1.3.6 PE文件和Shell进行融合
               1.3.6.1 申请缓冲区用来存放融合后的PE文件
               1.3.6.2 拷贝Shell部分的节区信息到PE缓冲区中
               1.3.6.3 将PE文件缓冲区拷贝到融合后PE的缓冲区中
               1.3.6.4 把Shell的节区部分拷贝到PE融合后的缓冲区中
          1.3.7 为加壳后的PE设置OEP
          1.3.8 将PE融合缓冲区中的内容写入文件中
          1.3.9 释放相关资源
二 Shell部分
     2.1 导出一个结构体
     2.2 Shell部分的入口函数
          2.2.1 得到Windows API在内存中的地址
          2.2.2 得到加壳后PE文件在内存中的模块基地址
          2.2.3 修复原始PE的IAT表
          2.2.4 修复原始PE的重定位表来重定位数据
          2.2.5 跳转到PE的真正代码处
  

一. 主程序

1.1 主程序入口函数

仅创建了一个模态对话框。

1
2
3
4
5
6
int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPreInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd) {
    hInst = hInstance;
    DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC)MainProc, (LPARAM)(NULL));
 
    return TRUE;
}

1.2 主程序对话框的回调函数

此对话框窗口过程函数,完成了三部分:
1) 一个文本框:用来输出一些提示信息。
2) 打开文件按钮:功能是打开一个exe文件
3) 支持文件拖拽
4) 加壳按钮:对exe开始加壳

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
INT_PTR CALLBACK MainProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
 
    // 初始化消息
    case WM_INITDIALOG:
    {
        DragAcceptFiles(hDlg, TRUE);            // 使该对话框支持文件拖拽操作
        pMessageBuffer = (PTCHAR)malloc(sizeof(TCHAR)* MESSAGEBUFFER);
        if (pMessageBuffer) {
            ZeroMemory(pMessageBuffer, MESSAGEBUFFER * sizeof(TCHAR));
        }
        return TRUE;
    }
    break;
 
    // 文件拖拽消息
    case WM_DROPFILES:
    {
        ZeroMemory(szFilePath, MAX_PATH);//清空文件名缓冲
        DragQueryFile((HDROP)wParam, 0, szFilePath, sizeof(szFilePath));
        DragFinish((HDROP)wParam);
        SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, MAX_PATH, (LPARAM)szFilePath);
    }
    break;
 
    // 控件消息
    case WM_COMMAND:
    {
        switch (LOWORD(wParam)) {
        case IDC_CANCEL:
        {
            EndDialog(hDlg, 2);
            return TRUE;
        }
 
        // "打开文件按钮"
        case IDC_BUTTON1:
        {
            if (!OpenFileDlg(szFilePath, hDlg))
                break;
            SendDlgItemMessage(hDlg, IDC_EDIT1, WM_SETTEXT, MAX_PATH, (LPARAM)szFilePath);
        }
        break;
 
        // "开始加壳按钮"
        case IDC_BUTTON2:
        {
            // 清空全局缓冲区和第二个文本框中的内容
            ZeroMemory(pMessageBuffer, MESSAGEBUFFER * sizeof(TCHAR));
            SendDlgItemMessage(hDlg, IDC_EDIT2, WM_SETTEXT, 0, (LPARAM)pMessageBuffer);
 
            // 判断第一个文本框中的文件名是否为空
            if (!GetDlgItemText(hDlg, IDC_EDIT1, szFilePath, MAX_PATH)) {
                AddLine(hDlg, pMessageBuffer, TEXT("文件名不能为空!"));
                return TRUE;
            }
 
            // 判断是否为PE32 exe文件
            if (!IsPEFile(hDlg, szFilePath)) {
                return TRUE;
            }
 
            // 加壳主函数
            mainPack(hDlg);
            return TRUE;
 
        }
        break;
        }
    }
    break;
 
    case WM_CLOSE:
    {
        free(pMessageBuffer);  // 释放内存
        DestroyWindow(hDlg);
    }   
    break;
    }
    return FALSE;
}

1.3 加壳主函数mainPack

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
BOOL mainPack(HWND hDlg) {
 
    // 初始化变量
    PCHAR pFileBuf = NULL;                                    // 该缓冲区存放被加壳的PE文件
    DWORD nFileSize = 0;                                    // PE32文件大小
    PIMAGE_NT_HEADERS32 pFileNtHeader = NULL;                // PE32 NT头
    PIMAGE_SECTION_HEADER pFileSectionHeader = NULL;        // PE32 Section头
    DWORD nFileSection;                                        // 被加壳的可执行文件中的节区数量
    HMODULE hModShell = NULL;                                // Shell.dll的模块句柄
    PSHARE_DATA pShareData = NULL;                            // 共享数据
    DWORD pStartFun = 0;                                    // Shell脱壳入口函数
    PCHAR pShellBuf = NULL;                                    // 将载入到内存中的shell模块的内容,复制一份到这里,方便进行重定位
    DWORD nNewFileSize = 0;                                    // 加壳后生成的文件的大小
    PCHAR pNewFileBuf = NULL;
 
 
    // 1. 读取PE32 文件的相关信息
    pFileBuf = OpenPEFile(hDlg, szFilePath, &nFileSize);
    if (!pFileBuf) {
        AddLine(hDlg, pMessageBuffer, TEXT("打开文件失败"));
        goto end;
    }
    AddLine(hDlg, pMessageBuffer, TEXT("文件打开成功!"));
    pFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pFileBuf + ((PIMAGE_DOS_HEADER)pFileBuf)->e_lfanew);
    pFileSectionHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pFileNtHeader));
    nFileSection = pFileNtHeader->FileHeader.NumberOfSections;
 
 
    // 2. 处理Shell模块
    hModShell = LoadLibrary(TEXT("Shell.dll"));
    if (!hModShell) {
        AddLine(hDlg, pMessageBuffer, TEXT("Shell模块加载失败"));
        goto end;
    }
    AddLine(hDlg, pMessageBuffer, TEXT("Shell模块加载成功"));
    pShareData = (PSHARE_DATA)GetProcAddress(hModShell, "ShareData");            // 获得共享数据在Shell模块中的地址
 
    pShareData->ImageBase = pFileNtHeader->OptionalHeader.ImageBase;
    pShareData->OEP = pFileNtHeader->OptionalHeader.AddressOfEntryPoint;
    pShareData->ImportTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    pShareData->RelocTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    pShareData->IatTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];
 
 
    //3.将Shell附加到PE文件
    //
    //3.1.读取Shell模块到一个新的缓冲区
    MODULEINFO modinfo = { 0 };
    GetModuleInformation(GetCurrentProcess(), hModShell, &modinfo, sizeof(MODULEINFO));
    pShellBuf = (PCHAR)malloc(modinfo.SizeOfImage);
    if (!pShellBuf) {
        goto end;
    }
    RtlZeroMemory(pShellBuf, modinfo.SizeOfImage);
    memcpy_s(pShellBuf, modinfo.SizeOfImage, hModShell, modinfo.SizeOfImage);
 
    //3.2 重定位缓冲区中的Shell
    FixShellReloc(hDlg, pFileBuf, pShellBuf);
    AddLine(hDlg, pMessageBuffer, TEXT("重定位Shell成功!"));
 
    // 3.3 创建一个新的缓冲区,该缓冲区存放加壳后的文件
    nNewFileSize = GetNewFileSize(hDlg, pFileBuf, pShellBuf);
    pNewFileBuf = (PCHAR)malloc(nNewFileSize);
    if (!pNewFileBuf) {
        goto end;
    }
    RtlZeroMemory(pNewFileBuf, nNewFileSize);
    CopyShellSecToFile(hDlg, pFileBuf, pShellBuf);
    CopyFileToNewFile(hDlg, pNewFileBuf, nNewFileSize, pFileBuf, nFileSection);
    CopyShellSecDataToNewFile(hDlg, pNewFileBuf, pShellBuf);
    SetOEP(pShareData, pNewFileBuf, hModShell);
    WriteNewFile(hDlg, szFilePath, pNewFileBuf, nNewFileSize);
 
 
 
    // 程序执行正确,则到此处释放资源
    if (pFileBuf) {
        free(pFileBuf);
        pFileBuf = NULL;
    }
    if (pShellBuf) {
        free(pShellBuf);
        pShellBuf = NULL;
    }
    if (pNewFileBuf) {
        free(pNewFileBuf);
        pShellBuf = NULL;
    }
    return TRUE;
 
    // 程序执行错误,则到此处释放资源
end:
    if (pFileBuf) {
        free(pFileBuf);
        pFileBuf = NULL;
    }
    if (pShellBuf) {
        free(pShellBuf);
        pShellBuf = NULL;
    }
    if (pNewFileBuf) {
        free(pNewFileBuf);
        pShellBuf = NULL;
    }
    return FALSE;
}

1.3.1 读取要加密的PE32文件

OpenPEFile函数的功能为:
申请一块缓冲区,将PE32文件的内容读取到此处。

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
/*--------------------------------------------------------------*/
/* OpenPEFile - 打开PE32 exe文件                               */
/*--------------------------------------------------------------*/
PCHAR OpenPEFile(HWND hDlg,TCHAR* szFilePath, PDWORD dwFileSize) {
    *dwFileSize = 0;
    DWORD NumberOfBytesRW = 0;
 
    HANDLE hFile = CreateFile(
        szFilePath,
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        return  FALSE;
    }
 
    // 获得文件的大小
    DWORD nFileSize = GetFileSize(hFile, NULL);
    *dwFileSize = nFileSize;
 
    // 申请内存,将文件中的内容读到这里
    PCHAR pFileBuf = (PCHAR)malloc(nFileSize);
    if (pFileBuf) {
        RtlZeroMemory(pFileBuf, nFileSize);
        if (!ReadFile(hFile, pFileBuf, nFileSize, &NumberOfBytesRW, NULL)) {
            return FALSE;
        }
    }
    CloseHandle(hFile);
    return pFileBuf;
}

1.3.2 导入Shell

1
2
3
4
5
hModShell = LoadLibrary(TEXT("Shell.dll"));
    if (!hModShell) {
        AddLine(hDlg, pMessageBuffer, TEXT("Shell模块加载失败"));
        goto end;
    }

1.3.3 找到shell中导出的结构体并设置该结构体

1
2
3
4
5
6
7
pShareData = (PSHARE_DATA)GetProcAddress(hModShell, "ShareData");            // 获得共享数据在Shell模块中的地址
 
pShareData->ImageBase = pFileNtHeader->OptionalHeader.ImageBase;
pShareData->OEP = pFileNtHeader->OptionalHeader.AddressOfEntryPoint;
pShareData->ImportTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
pShareData->RelocTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
pShareData->IatTable = pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];

1.3.4 将导入到内存中的Shell复制到一块新的缓冲区中

此步作用:为了方便修改Shell中的内容,比如重定位和导入表。

1
2
3
4
5
6
7
8
MODULEINFO modinfo = { 0 };
    GetModuleInformation(GetCurrentProcess(), hModShell, &modinfo, sizeof(MODULEINFO));
    pShellBuf = (PCHAR)malloc(modinfo.SizeOfImage);
    if (!pShellBuf) {
        goto end;
    }
    RtlZeroMemory(pShellBuf, modinfo.SizeOfImage);
    memcpy_s(pShellBuf, modinfo.SizeOfImage, hModShell, mod

1.3.5 重定位缓冲区中的Shell

1) 因为Shell已经加载到内存中了,所以将Shell中需要基址重定位的项,先进行恢复到之前未加载到内存的状态,然后因为会将此Shell添加到PE文件中,所以需要根据PE文件的ImageBase进行重定位。被加壳后的PE文件,可以直接接管Shell部分的重定位表,由Windoews PEloader直接进行重定位。
2) 对Shell部分的导入表进行修改。因为想让加壳后的PE文件,在执行时,直接由Windows PELoader进行接管。
3) 将PE文件的重定位目录表,导入表和IAT表直接指向Shell部分。用以在启动程序时,直接由PE Loader来接管Shell。

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
void FixShellReloc(HWND hDlg, PCHAR pFileBuf, PCHAR pShellBuf) {
    PIMAGE_DOS_HEADER pFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileLastSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileNewSecHeader = NULL;
    PIMAGE_DOS_HEADER pShellDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pShellNtHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeader = NULL;
    PIMAGE_DATA_DIRECTORY pShellRelocTable = NULL;
    PIMAGE_BASE_RELOCATION pShellRelocData = NULL;
    PIMAGE_BASE_RELOCATION pShellRelocItem = NULL;
    PIMAGE_DATA_DIRECTORY pShellImpTable = NULL;
    PIMAGE_IMPORT_DESCRIPTOR pShellImpData = NULL;
    PIMAGE_IMPORT_DESCRIPTOR pShellImpItem = NULL;
    PIMAGE_THUNK_DATA32 pShellThunkDataItem = NULL;
    PIMAGE_THUNK_DATA32 pShellFirstThunkItem = NULL;
    PTYPE_OFFSET pTypeOffset = NULL;
    UINT iCount = 0;
    PDWORD pRelocValue = 0;                                    // 每个需要重定位的地址
    DWORD dwShellHeaderSizeFileAlign = 0;
    DWORD dwShellHeaderSizeSecAlign = 0;
    DWORD dwFileNewSecBeginRVA = 0;
    DWORD dwFIleNewSecBeginRAW = 0;
 
 
 
    pFileDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
    pFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pFileDosHeader + pFileDosHeader->e_lfanew);
    pFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pFileNtHeader));
    pFileLastSecHeader = pFileSecHeader + pFileNtHeader->FileHeader.NumberOfSections - 1;
    dwFileNewSecBeginRVA = pFileLastSecHeader->VirtualAddress + AlignSize(pFileLastSecHeader->Misc.VirtualSize, pFileNtHeader->OptionalHeader.SectionAlignment);
    dwFIleNewSecBeginRAW = pFileLastSecHeader->PointerToRawData + pFileLastSecHeader->SizeOfRawData;
 
 
    pShellDosHeader = (PIMAGE_DOS_HEADER)pShellBuf;
    pShellNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pShellDosHeader + pShellDosHeader->e_lfanew);
    pShellSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pShellNtHeader));
    dwShellHeaderSizeFileAlign = AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.FileAlignment);
    dwShellHeaderSizeSecAlign = AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.SectionAlignment);
 
    pShellRelocTable = &(pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
    pShellRelocData = (PIMAGE_BASE_RELOCATION)((DWORD)pShellDosHeader + pShellRelocTable->VirtualAddress);
    pShellRelocItem = pShellRelocData;
    pShellImpTable = &(pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
    pShellImpData = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pShellDosHeader + pShellImpTable->VirtualAddress);
    pShellImpItem = pShellImpData;
 
 
 
    while (pShellRelocItem->VirtualAddress) {
        pTypeOffset = (PTYPE_OFFSET)(pShellRelocItem + 1);
        iCount = (pShellRelocItem->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(TYPE_OFFSET);
        for (UINT i = 0; i < iCount;i++) {
            if (pTypeOffset[i].type == 3) {
 
                // 将所有VA地址恢复至LoadLibary前的状态
                pRelocValue = (PDWORD)((DWORD)pShellDosHeader + pShellRelocItem->VirtualAddress + (DWORD)(pTypeOffset[i].offset));
                *pRelocValue = *pRelocValue - pShellNtHeader->OptionalHeader.ImageBase - dwShellHeaderSizeSecAlign + pFileNtHeader->OptionalHeader.ImageBase + dwFileNewSecBeginRVA;
            }
        }
        // 修复重定位表中的RVA
        pShellRelocItem->VirtualAddress = pShellRelocItem->VirtualAddress - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
        pShellRelocItem = (PIMAGE_BASE_RELOCATION)((DWORD)pShellRelocItem + pShellRelocItem->SizeOfBlock);
    }
 
    // 修复导入表中的RVA
    while (pShellImpItem->Name) {
        pShellThunkDataItem = (PIMAGE_THUNK_DATA32)(pShellImpItem->OriginalFirstThunk + (DWORD)pShellDosHeader);
        pShellFirstThunkItem = (PIMAGE_THUNK_DATA32)(pShellImpItem->FirstThunk + (DWORD)pShellDosHeader);
        while (*(PDWORD)pShellThunkDataItem) {
            if (!(*(PDWORD)pShellThunkDataItem & IMAGE_ORDINAL_FLAG32)) {
                pShellThunkDataItem->u1.AddressOfData = pShellThunkDataItem->u1.AddressOfData - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
                pShellFirstThunkItem->u1.AddressOfData = pShellThunkDataItem->u1.AddressOfData;
            }
            pShellThunkDataItem++;
            pShellFirstThunkItem++;
        }
 
        pShellImpItem->OriginalFirstThunk = pShellImpItem->OriginalFirstThunk - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
        pShellImpItem->FirstThunk = pShellImpItem->FirstThunk - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
        pShellImpItem->Name = pShellImpItem->Name - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
        pShellImpItem++;
    }
 
 
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = pShellRelocTable->VirtualAddress - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = pShellRelocTable->Size;
 
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
 
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
    pFileNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = pShellNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
 
    return;
}

1.3.6 PE文件和Shell进行融合

1.3.6.1 申请缓冲区用来存放融合后的PE文件

1) 首先,需计算PE文件和Shell融合后的大小

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
DWORD GetNewFileSize(HWND hDlg, PCHAR pFileBuf, PCHAR pShellBuf) {
    PIMAGE_DOS_HEADER pFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileLastSecHeader = NULL;
    PIMAGE_DOS_HEADER pShellDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pShellNtHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeader = NULL;
    PIMAGE_SECTION_HEADER pShellLastSecHeader = NULL;
    DWORD FileSize = 0;
    DWORD FileShellSize = 0;
 
 
    pFileDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
    pFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pFileDosHeader + pFileDosHeader->e_lfanew);
    pFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pFileNtHeader));
    pFileLastSecHeader = pFileSecHeader + pFileNtHeader->FileHeader.NumberOfSections - 1;
    FileSize = pFileLastSecHeader->PointerToRawData + pFileLastSecHeader->SizeOfRawData;
    ZeroMemory(LineText, MAX_PATH);
    sprintf_s(LineText, MAX_PATH, "---------------File为%d字节-----------", FileSize);
    AddLine(hDlg, pMessageBuffer, LineText);
 
    pShellDosHeader = (PIMAGE_DOS_HEADER)pShellBuf;
    pShellNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pShellDosHeader + pShellDosHeader->e_lfanew);
    pShellSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pShellNtHeader));
    pShellLastSecHeader = pShellSecHeader + pShellNtHeader->FileHeader.NumberOfSections - 1;
    FileShellSize = pShellLastSecHeader->PointerToRawData + pShellLastSecHeader->SizeOfRawData;
    FileShellSize = FileShellSize - AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.FileAlignment);
 
    return FileSize + FileShellSize;
}

2) 得到融合后的大小后,就申请同等大小的一块缓冲区

1
2
3
4
5
6
nNewFileSize = GetNewFileSize(hDlg, pFileBuf, pShellBuf);
    pNewFileBuf = (PCHAR)malloc(nNewFileSize);
    if (!pNewFileBuf) {
        goto end;
    }
    RtlZeroMemory(pNewFileBuf, nNewFileSize);
1.3.6.2 拷贝Shell部分的节区信息到PE缓冲区中

拷贝Shell部分的节区信息到PE缓冲区的PE头末尾,并设置区块的VirtualAddress和PointerToRawData。

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
void CopyShellSecToFile(HWND hDlg, PCHAR pFileBuf, PCHAR pShellBuf) {
    PIMAGE_DOS_HEADER pFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileLastSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileNewSecHeader = NULL;
    PIMAGE_DOS_HEADER pShellDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pShellNtHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeaderItem = NULL;
    DWORD dwShellHeaderSizeFileAlign = 0;
    DWORD dwShellHeaderSizeSecAlign = 0;
    DWORD dwFileNewSecBeginRVA = 0;
    DWORD dwFIleNewSecBeginRAW = 0;
    UINT iNewSec = 0;
 
 
    pFileDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
    pFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pFileDosHeader + pFileDosHeader->e_lfanew);
    pFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pFileNtHeader));
    pFileLastSecHeader = pFileSecHeader + pFileNtHeader->FileHeader.NumberOfSections - 1;
    dwFileNewSecBeginRVA = pFileLastSecHeader->VirtualAddress + AlignSize(pFileLastSecHeader->Misc.VirtualSize, pFileNtHeader->OptionalHeader.SectionAlignment);
    dwFIleNewSecBeginRAW = pFileLastSecHeader->PointerToRawData + pFileLastSecHeader->SizeOfRawData;
    pFileNewSecHeader = pFileSecHeader + pFileNtHeader->FileHeader.NumberOfSections;
 
    pShellDosHeader = (PIMAGE_DOS_HEADER)pShellBuf;
    pShellNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pShellDosHeader + pShellDosHeader->e_lfanew);
    pShellSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pShellNtHeader));
    pShellSecHeaderItem = pShellSecHeader;
    dwShellHeaderSizeFileAlign = AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.FileAlignment);
    dwShellHeaderSizeSecAlign = AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.SectionAlignment);
 
 
    for (iNewSec = 0; iNewSec < pShellNtHeader->FileHeader.NumberOfSections;iNewSec++) {
        memcpy_s(pFileNewSecHeader, sizeof(IMAGE_SECTION_HEADER), pShellSecHeaderItem, sizeof(IMAGE_SECTION_HEADER));
        pFileNewSecHeader->Name[1] = 'v';
        pFileNewSecHeader->VirtualAddress = pFileNewSecHeader->VirtualAddress - dwShellHeaderSizeSecAlign + dwFileNewSecBeginRVA;
        pFileNewSecHeader->PointerToRawData = pFileNewSecHeader->PointerToRawData - dwShellHeaderSizeFileAlign + dwFIleNewSecBeginRAW;       
 
        pFileNewSecHeader++;
        pShellSecHeaderItem++;
    }
    pFileNtHeader->FileHeader.NumberOfSections += iNewSec;
    pFileNtHeader->OptionalHeader.SizeOfImage = AlignSize(pFileNtHeader->OptionalHeader.SizeOfImage, pFileNtHeader->OptionalHeader.SectionAlignment) + AlignSize(pShellNtHeader->OptionalHeader.SizeOfImage, pShellNtHeader->OptionalHeader.SectionAlignment) - AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.SectionAlignment);
    return;
}
1.3.6.3 将PE文件缓冲区拷贝到融合后PE的缓冲区中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOL CopyFileToNewFile(HWND hDlg, PCHAR pNewFileBuf, DWORD nNewFileSize, PCHAR pFileBuf, DWORD nFileSection) {
    PIMAGE_DOS_HEADER pFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pFileLastSecHeader = NULL;
    DWORD FileSize = 0;
 
 
    pFileDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
    pFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pFileDosHeader + pFileDosHeader->e_lfanew);
    pFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pFileNtHeader));
    pFileLastSecHeader = pFileSecHeader + nFileSection - 1;
    FileSize = pFileLastSecHeader->PointerToRawData + pFileLastSecHeader->SizeOfRawData;
    if (!pNewFileBuf) {
        return FALSE;
    }
    memcpy_s(pNewFileBuf, nNewFileSize, pFileBuf, FileSize);
 
    return TRUE;
}
1.3.6.4 把Shell的节区部分拷贝到PE融合后的缓冲区中
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
void CopyShellSecDataToNewFile(HWND hDlg, PCHAR pNewFileBuf, PCHAR pShellBuf) {
    PIMAGE_DOS_HEADER pNewFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pNewFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pNewFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pNewFileNewSecHeader = NULL;
    PIMAGE_DOS_HEADER pShellDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pShellNtHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeader = NULL;
    PIMAGE_SECTION_HEADER pShellSecHeaderItem = NULL;
    PCHAR pNewFileSecData = NULL;
    PCHAR pShellSecData = NULL;
    DWORD SizeOfShellRawData = 0;
    UINT iNewSec = 0;
 
 
    pNewFileDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuf;
    pNewFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pNewFileDosHeader + pNewFileDosHeader->e_lfanew);
    pNewFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pNewFileNtHeader));
 
 
    pShellDosHeader = (PIMAGE_DOS_HEADER)pShellBuf;
    pShellNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pShellDosHeader + pShellDosHeader->e_lfanew);
    pShellSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pShellNtHeader));
    pShellSecHeaderItem = pShellSecHeader;
    pNewFileNewSecHeader = pNewFileSecHeader + (pNewFileNtHeader->FileHeader.NumberOfSections - pShellNtHeader->FileHeader.NumberOfSections);
 
 
    for (iNewSec = 0; iNewSec < pShellNtHeader->FileHeader.NumberOfSections; iNewSec++) {
        pNewFileSecData = (PCHAR)((DWORD)pNewFileDosHeader + pNewFileNewSecHeader->PointerToRawData);
        pShellSecData = (PCHAR)((DWORD)pShellDosHeader + pShellSecHeaderItem->VirtualAddress);
        SizeOfShellRawData = pShellSecHeaderItem->SizeOfRawData;
        memcpy_s(pNewFileSecData, SizeOfShellRawData, pShellSecData, SizeOfShellRawData);
 
        pNewFileNewSecHeader++;
        pShellSecHeaderItem++;
    }
    return;
}

1.3.7 为加壳后的PE设置OEP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void SetOEP(PSHARE_DATA pShareData,PCHAR pNewFileBuf, HMODULE hModShell) {
    PIMAGE_DOS_HEADER pNewFileDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pNewFileNtHeader = NULL;
    PIMAGE_SECTION_HEADER pNewFileSecHeader = NULL;
    PIMAGE_SECTION_HEADER pNewFileNewSecHeader = NULL;
    PIMAGE_DOS_HEADER pShellDosHeader = NULL;
    PIMAGE_NT_HEADERS32 pShellNtHeader = NULL;
 
 
 
    pNewFileDosHeader = (PIMAGE_DOS_HEADER)pNewFileBuf;
    pNewFileNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pNewFileDosHeader + pNewFileDosHeader->e_lfanew);
    pNewFileSecHeader = (PIMAGE_SECTION_HEADER)(IMAGE_FIRST_SECTION(pNewFileNtHeader));
 
 
    pShellDosHeader = (PIMAGE_DOS_HEADER)hModShell;
    pShellNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pShellDosHeader + pShellDosHeader->e_lfanew);
 
    pNewFileNewSecHeader = pNewFileSecHeader + (pNewFileNtHeader->FileHeader.NumberOfSections - pShellNtHeader->FileHeader.NumberOfSections);
    pNewFileNtHeader->OptionalHeader.AddressOfEntryPoint = pShareData->StartFunc - (DWORD)hModShell - AlignSize(pShellNtHeader->OptionalHeader.SizeOfHeaders, pShellNtHeader->OptionalHeader.SectionAlignment) + pNewFileNewSecHeader->VirtualAddress;
    return;
}

1.3.8 将PE融合缓冲区中的内容写入文件中

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
BOOL WriteNewFile(HWND hDlg, TCHAR* szFilePath, PCHAR pNewFileBuf, DWORD nNewFileSize) {
    DWORD dwBtyes;
    size_t charLength = 0;
    TCHAR   szOutPath[MAX_PATH] = { 0 };
    PTCHAR strSuffix = PathFindExtension(szFilePath);         // 获取文件的后缀名
    StringCchLength(szFilePath, STRSAFE_MAX_CCH, &charLength);
    StringCchCopy(szOutPath, MAX_PATH, szFilePath);                // 备份目标文件路径到szOutPath
    PathRemoveExtension(szOutPath);                        // 将szOutPath中保存路径的后缀名去掉
    StringCchCat(szOutPath, MAX_PATH, TEXT("_Pack"));                 // 在路径最后附加“_Pack”
    StringCchCat(szOutPath, MAX_PATH, strSuffix);                // 在路径最后附加刚刚保存的后缀名
    HANDLE hFile = CreateFile(
        szOutPath,
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE) {
        AddLine(hDlg, pMessageBuffer, TEXT("错误!加壳后文件打开失败!"));
        return  FALSE;
    }
    if (!WriteFile(hFile, pNewFileBuf, nNewFileSize, &dwBtyes, NULL)) {
        int error = GetLastError();
        RtlZeroMemory(LineText, MAX_PATH);
        sprintf_s(LineText, MAX_PATH,  "错误!将加壳后的内容写入文件时,出错!,错误代码为%d", error);
        AddLine(hDlg, pMessageBuffer, LineText);
        return FALSE;
    }
    AddLine(hDlg, pMessageBuffer, TEXT("成功!加壳成功"));
    CloseHandle(hFile);
    return TRUE;
}

1.3.9 释放相关资源

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
    // 程序执行正确,则到此处释放资源
    if (pFileBuf) {
        free(pFileBuf);
        pFileBuf = NULL;
    }
    if (pShellBuf) {
        free(pShellBuf);
        pShellBuf = NULL;
    }
    if (pNewFileBuf) {
        free(pNewFileBuf);
        pShellBuf = NULL;
    }
    return TRUE;
 
    // 程序执行错误,则到此处释放资源
end:
    if (pFileBuf) {
        free(pFileBuf);
        pFileBuf = NULL;
    }
    if (pShellBuf) {
        free(pShellBuf);
        pShellBuf = NULL;
    }
    if (pNewFileBuf) {
        free(pNewFileBuf);
        pShellBuf = NULL;
    }
    return FALSE;

二. Shell部分

2.1 导出一个结构体

该结构体为了得到PE原始文件的导入表,基址重定位表,OEP等

1
2
3
4
5
6
7
8
9
10
typedef struct _SHARE_DATA {
    DWORD StartFunc;                    // Shell部分start函数的入口点
    IMAGE_DATA_DIRECTORY ImportTable;   // 原始文件中的导入表
    IMAGE_DATA_DIRECTORY RelocTable;    // 原始文件中的基址重定位表表
    IMAGE_DATA_DIRECTORY IatTable;      // 原始PE文件中的IatTable
    DWORD ImageBase;                    // 原始PE文件中的ImageBase属性
    DWORD OEP;                          // 原始PE文件中的OEP
}SHARE_DATA, * PSHARE_DATA;
 
__declspec(dllexport) SHARE_DATA ShareData = {(DWORD)start};

2.2 Shell部分的入口函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__declspec(naked) void start(void) {
    __asm pushad
 
    //得到函数在内存中的地
    GetKeyFuncAddress();
 
    // 获得当前模块基地址
    dwModuleBase = (DWORD)fn_GetModuleHandleA(NULL);
 
    // 修复导入地址表
    FixIAT(dwModuleBase, ShareData);
 
    // 修复重定位
    FixReloc(dwModuleBase, ShareData);
 
    // 设置真实程序的OEP
    OEP = dwModuleBase + ShareData.OEP;
 
    __asm {
        popad
        jmp OEP
    }       
}

2.2.1 得到Windows API在内存中的地址

1) 首先得得到Kernel32.dll的模块基址

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
HMODULE getKer32Base(void) {
    PLDR_DATA_TABLE_ENTRY pLdrLinkHead = NULL;  // PEB中的模块链表头
    PLDR_DATA_TABLE_ENTRY pLdrLinkTmp = NULL;   // 用来指向模块链表中的各个节点
    PCHAR pModuleStr = NULL;
    __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 pLdrLinkHead, eax
        pop eax
    }
    pLdrLinkTmp = pLdrLinkHead;
    do {
        if (pLdrLinkTmp) {
            pModuleStr = (PCHAR)(pLdrLinkTmp->BaseDllName.Buffer);
            if (pModuleStr) {
                if ((pModuleStr[0] == 'K' || pModuleStr[0] == 'k') &&
                    (pModuleStr[2] == 'E' || pModuleStr[2] == 'e') &&
                    (pModuleStr[4] == 'R' || pModuleStr[4] == 'r') &&
                    (pModuleStr[6] == 'N' || pModuleStr[6] == 'n') &&
                    (pModuleStr[8] == 'E' || pModuleStr[8] == 'e') &&
                    (pModuleStr[10] == 'L' || pModuleStr[10] == 'l') &&
                    pModuleStr[12] == '3' &&
                    pModuleStr[14] == '2' &&
                    pModuleStr[16] == '.' &&
                    (pModuleStr[18] == 'D' || pModuleStr[18] == 'd') &&
                    (pModuleStr[20] == 'L' || pModuleStr[20] == 'l') &&
                    (pModuleStr[22] == 'L' || pModuleStr[22] == 'l')
                    )
                {
                    return (HMODULE)(pLdrLinkTmp->DllBase);
                }
            }
            pLdrLinkTmp = (PLDR_DATA_TABLE_ENTRY)(pLdrLinkTmp->InInitializationOrderLinks.Flink);
            continue;
        }
        break;
    } while (pLdrLinkHead != pLdrLinkTmp);
    return (HMODULE)0;
}

2)根据Kernel32模块基地址,得到GetProcessAddress函数的地址

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
FARPROC pGetProcAddress(HMODULE hModule) {
    FARPROC pRet = NULL;
    PIMAGE_DOS_HEADER lpDosHeader = NULL;
    PIMAGE_NT_HEADERS32 lpPeHeader = NULL;
    PIMAGE_EXPORT_DIRECTORY lpExport = NULL;
    PWORD lpWordOrdinals = NULL;
    PDWORD lpDwFunName = NULL;
    PDWORD lpDwFunAddress = NULL;
    DWORD dwLoop = 0;
    lpDosHeader = (PIMAGE_DOS_HEADER)hModule;
    lpPeHeader = (PIMAGE_NT_HEADERS32)((DWORD)hModule + lpDosHeader->e_lfanew);
    if (!lpPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) {
        return pRet;
    }
    lpExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModule + (DWORD)lpPeHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    if (!lpExport->NumberOfNames) {
        return pRet;
    }
    lpDwFunName = (PDWORD)((DWORD)hModule + (DWORD)lpExport->AddressOfNames);
    lpWordOrdinals = (PWORD)((DWORD)hModule + (DWORD)lpExport->AddressOfNameOrdinals);
    lpDwFunAddress = (PDWORD)((DWORD)hModule + (DWORD)lpExport->AddressOfFunctions);
    for (dwLoop = 0; dwLoop < lpExport->NumberOfNames; dwLoop++) {
        char* pzFun = (char*)((DWORD)hModule + (DWORD)(lpDwFunName[dwLoop]));
        if (
            pzFun[0] == 'G' &&
            pzFun[1] == 'e' &&
            pzFun[2] == 't' &&
            pzFun[3] == 'P' &&
            pzFun[4] == 'r' &&
            pzFun[5] == 'o' &&
            pzFun[6] == 'c' &&
            pzFun[7] == 'A' &&
            pzFun[8] == 'd' &&
            pzFun[9] == 'd' &&
            pzFun[10] == 'r' &&
            pzFun[11] == 'e' &&
            pzFun[12] == 's' &&
            pzFun[13] == 's' &&
            pzFun[14] == 0
            ) {
            pRet = (FARPROC)((DWORD)hModule + lpDwFunAddress[lpWordOrdinals[dwLoop]]);
            break;
        }
    }
    return pRet;
}

3) 最终得到想要的Windows API的地址

1
2
3
4
5
6
7
8
9
VOID GetKeyFuncAddress(VOID) {
 
    // 获得Kernel32.dll模块首地址
    hKer32Base = getKer32Base();
    fn_GetProcAddress = (FN_GetProcAddress)(pGetProcAddress(hKer32Base));
    fn_LoadLibraryA = (FN_LoadLibraryA)fn_GetProcAddress(hKer32Base, "LoadLibraryA");
    fn_VirtualProtect = (FN_VirtualProtect)fn_GetProcAddress(hKer32Base, "VirtualProtect");
    fn_GetModuleHandleA = (FN_GetModuleHandleA)fn_GetProcAddress(hKer32Base, "GetModuleHandleA");
}

2.2.2 得到加壳后PE文件在内存中的模块基地址

1
dwModuleBase = (DWORD)fn_GetModuleHandleA(NULL);

2.2.3 修复原始PE的IAT表

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
VOID FixIAT(DWORD hImageBase, SHARE_DATA shareData){
    // 定义一些临时变量
    DWORD funcAddr = 0;
    PIMAGE_IMPORT_BY_NAME pImportByName = NULL;
    DWORD OldProtect = 0;
    PCHAR pImportModuleName = NULL;
    HMODULE hModuleTmp = 0;
    PDWORD pIAT = NULL;
 
    // 找到导入描述符表
    PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(shareData.ImportTable.VirtualAddress + hImageBase);
 
    // 遍历所有需要导入的模块
    while (pImportDescriptor->Name) {
        pImportModuleName = (char*)(pImportDescriptor->Name + hImageBase);
        hModuleTmp = fn_LoadLibraryA(pImportModuleName);
        pIAT = (PDWORD)(pImportDescriptor->FirstThunk + hImageBase);
 
        // 遍历每个模块的导入函数
        while (*pIAT) {
            fn_VirtualProtect(pIAT, 0x4, PAGE_EXECUTE_READWRITE, &OldProtect);
            if (*pIAT & 0x80000000) {
                funcAddr = (DWORD)fn_GetProcAddress(hModuleTmp, (LPCSTR)((*pIAT) & (~IMAGE_ORDINAL_FLAG32)));
            }
            else {
                pImportByName = (PIMAGE_IMPORT_BY_NAME)(*pIAT + hImageBase);
                funcAddr = (DWORD)fn_GetProcAddress(hModuleTmp, pImportByName->Name);
            }
            *pIAT = funcAddr;
            fn_VirtualProtect(pIAT, 0x4, OldProtect, &OldProtect);
            pIAT++;
        }
        pImportDescriptor++;
    }
    return;
}

2.2.4 修复原始PE的重定位表来重定位数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
VOID FixReloc(DWORD hImageBase, SHARE_DATA shareData) {
    PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)(hImageBase + shareData.RelocTable.VirtualAddress);
    PDWORD RelocAddr = NULL;
    PWORD pTypeOffset = NULL;
    DWORD SizeOfBlock = 0;
    DWORD OldProtect = 0;
    while (pBaseReloc->VirtualAddress) {
        fn_VirtualProtect((LPVOID)(pBaseReloc->VirtualAddress + hImageBase), 0x1000, PAGE_EXECUTE_READWRITE, &OldProtect);
        pTypeOffset = (PWORD)(pBaseReloc + 1);
        SizeOfBlock = 0;
        while (SizeOfBlock < pBaseReloc->SizeOfBlock-8) {
            if (((*pTypeOffset & 0xf000) >> 12) == 3) {
                RelocAddr = (PDWORD)(pBaseReloc->VirtualAddress + (*pTypeOffset & 0x0fff) + hImageBase);
                *RelocAddr = *RelocAddr - shareData.ImageBase + hImageBase;
            }
            pTypeOffset++;
            SizeOfBlock = SizeOfBlock + 2;
        }
        fn_VirtualProtect((LPVOID)(pBaseReloc->VirtualAddress + hImageBase), 0x1000, OldProtect, &OldProtect);
        pBaseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseReloc + pBaseReloc->SizeOfBlock);
    }
    return;
}

2.2.5 跳转到PE的真正代码处

1
2
3
4
5
6
OEP = dwModuleBase + ShareData.OEP;
 
  __asm {
      popad
      jmp OEP
  }

[注意]APP应用上架合规检测服务,协助应用顺利上架!

收藏
免费 6
支持
分享
最新回复 (9)
雪    币: 204
活跃值: (526)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
命名上传了附件,为啥没有?发下度盘吧!
链接:https://pan.baidu.com/s/1U4dfTe1QYXzv6C0BykdeYw 
提取码:cx22 
2021-6-4 16:23
0
雪    币: 50161
活跃值: (20660)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
xshacry 命名上传了附件,为啥没有?发下度盘吧! 链接:https://pan.baidu.com/s/1U4dfTe1QYXzv6C0BykdeYw 提取码:cx22
论坛附件最多8M,你这个100多M,上传不了。
怕你这网盘以后失效,我这里也保存了一份镜像:
链接: https://pan.baidu.com/s/1dhTX6Q48YRBVK0e5-Kw8hA 提取码: g2p6 
2021-6-4 16:30
0
雪    币: 1
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
牛!!!
2021-6-15 22:34
0
雪    币: 228
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
有联系方式?没联系方式别人怎么联系你?
2021-8-9 11:03
0
雪    币: 2394
活跃值: (8795)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
6
xshacry 命名上传了附件,为啥没有?发下度盘吧! 链接:https://pan.baidu.com/s/1U4dfTe1QYXzv6C0BykdeYw 提取码:cx22
游戏币交易视频是分享错了吧
2021-8-9 12:44
0
雪    币: 203
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
上海这边逆向还挺缺人啊。。。楼主是在哪找的工作
2021-8-17 17:43
0
雪    币: 876
活跃值: (594)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
大哥,看你上一个帖子都过去快一年了,还没找到工作
2021-8-20 14:58
0
雪    币: 92
活跃值: (209)
能力值: ( LV6,RANK:95 )
在线值:
发帖
回帖
粉丝
9
字节游戏安全,平时工作就是给游戏写外挂,期待你的来信
huangshiyun.01@bytedance.com
2022-3-8 18:41
0
雪    币: 299
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
支持!!!
2022-5-24 09:06
0
游客
登录 | 注册 方可回帖
返回
//