首页
社区
课程
招聘
[原创][开源]用C++实现的壳(基础版)
发表于: 2015-12-29 19:41 80790

[原创][开源]用C++实现的壳(基础版)

2015-12-29 19:41
80790
收藏
免费 16
支持
分享
最新回复 (67)
雪    币: 321
活跃值: (167)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
26
BOOL CPE::InitPE(CString strFilePath)
{
        //打开文件
        if (OpenPEFile(strFilePath) == FALSE)
                return FALSE;

        //将PE以文件分布格式读取到内存
        m_dwFileSize = GetFileSize(m_hFile, NULL);
        m_pFileBuf = new BYTE[m_dwFileSize];
        DWORD ReadSize = 0;
        ReadFile(m_hFile, m_pFileBuf, m_dwFileSize, &ReadSize, NULL);       
        CloseHandle(m_hFile);
        m_hFile = NULL;

        //判断是否为PE文件
        if (IsPE() == FALSE)
                return FALSE;

        //将PE以内存分布格式读取到内存
        //修正没镜像大小没有对齐的情况
        m_dwImageSize = m_pNtHeader->OptionalHeader.SizeOfImage;
        m_dwMemAlign = m_pNtHeader->OptionalHeader.SectionAlignment;
        m_dwSizeOfHeader = m_pNtHeader->OptionalHeader.SizeOfHeaders;
        m_dwSectionNum = m_pNtHeader->FileHeader.NumberOfSections;

        if (m_dwImageSize % m_dwMemAlign)
                m_dwImageSize = (m_dwImageSize / m_dwMemAlign + 1) * m_dwMemAlign;
        LPBYTE pFileBuf_New = new BYTE[m_dwImageSize];
        memset(pFileBuf_New, 0, m_dwImageSize);
        //拷贝文件头
        memcpy_s(pFileBuf_New, m_dwSizeOfHeader, m_pFileBuf, m_dwSizeOfHeader);
        //拷贝区段
        PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNtHeader);
        for (DWORD i = 0; i < m_dwSectionNum; i++, pSectionHeader++)
        {
                memcpy_s(pFileBuf_New + pSectionHeader->VirtualAddress,
                        pSectionHeader->SizeOfRawData,
                        m_pFileBuf+pSectionHeader->PointerToRawData,
                        pSectionHeader->SizeOfRawData);
        }
        delete[] m_pFileBuf;
        m_pFileBuf = pFileBuf_New;
        pFileBuf_New = NULL;

        //获取PE信息
        GetPEInfo();
       
        return TRUE;
}

仔细阅读了一下源码,对如上函数有疑问。具体代码如下:
if (m_dwImageSize % m_dwMemAlign)
                m_dwImageSize = (m_dwImageSize / m_dwMemAlign + 1) * m_dwMemAlign;
楼主注释为://修正没镜像大小没有对齐的情况

详细情况我来举例。
假设:
文件对齐:0x200
内存对齐:0x1000
PE头大小:0x400(多半如此)
某PE中节的数量,假设为8,且每个节的大小刚好都是0x200(这样比较容易分析问题)
也就是说,PE文件原始大小为0x200 * 9
加载到内存的时候,需要的内存应该为0x1000 * 9(因为每个节要按照SectionAlignment对齐)
但楼主的算法实际情况是:
(0x200 * 9 / 0x1000 + 1)* 0x1000 = 0x1000 * 3(比真实需要的内存小)
显然只是考虑了 文件整体在内存中的对齐,而没有按照每个节来计算对齐需要的空间。

所以,我觉得,楼主这个空间分配小了。
memcpy_s(pFileBuf_New + pSectionHeader->VirtualAddress,
                        pSectionHeader->SizeOfRawData,
                        m_pFileBuf+pSectionHeader->PointerToRawData,
                        pSectionHeader->SizeOfRawData);
拷贝的目标位置 pFileBuf_New + pSectionHeader->VirtualAddress, 很有可能会超过你分配的内存大小。

请楼主确认下哈。。。
2016-3-1 01:22
0
雪    币: 76
活跃值: (1050)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
27
先回答你第一个问题:
if (m_dwImageSize % m_dwMemAlign)
    m_dwImageSize = (m_dwImageSize / m_dwMemAlign + 1) * m_dwMemAlign;
其中m_dwImageSize这个变量是PE文件的镜像【总】大小,是从PE文件中获取到的,不是某一个区段的

大小,这个值用LoadPE查看一下,就是在【基本PE头信息】中的第三项【镜像大小】这个值,这个镜像

大小一般都是0x1000对齐的,但由于我在平时测试时发现有些PE文件的镜像大小并不是0x1000对齐,所

以我就在这里校验一下,如果没对齐就对齐一下,方便在后面添加区段。

再回答第二个问题
    目标位置的buf是每个区段0x1000对齐以后的,原位置的buf是每个区段默认的0x200对齐的。将后

置往前者拷贝,怎么可能会空间不够呢?

加过无数次一般的简单exe文件都没有任何问题。

你再仔细考虑考虑?
2016-3-1 21:25
0
雪    币: 321
活跃值: (167)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
28
哈哈,确实是我搞错啦。
m_pNtHeader->OptionalHeader.SizeOfImage;已经是镜像在内存中对齐后所占用的内存空间了。我一直理解为是文件的在磁盘中的真实大小,这两个值确实不一样。要真是用文件在磁盘中的大小的话,那就真错了哈哈。感谢纠正~

最后,对误报表示歉意~请楼主收下鄙人的膝盖。
2016-3-2 00:48
0
雪    币: 229
活跃值: (36)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
29
PTYPEOFFSET pTypeOffset = (PTYPEOFFSET)(pPEReloc + 1);//+1是什么意思啊??
    DWORD dwNumber = (pPEReloc->SizeOfBlock - 8) / 2;//为什么-8还除2????
可以说明清楚吗?
2016-3-25 15:00
0
雪    币: 76
活跃值: (1050)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
30
首先回答第二个问题:
DWORD dwNumber = (pPEReloc->SizeOfBlock - 8) / 2;//为什么-8还除2????

其中 dwNumber 是这个Reloc区块需要重定位的地址的个数(也就是自己定义的TYPEOFFSET结构体的个数),这个数如何确定,就得分析 IMAGE_BASE_RELOCATION 这个结构体,其结构如图所示:

可见IMAGE_BASE_RELOCATION的前八个字节保存的是 VirtualAddress和SizeOfBlock,所以首先要-8,剩下的都是TypeOffset了,由上面我们自己定义的TYPEOFFSET结构体可知,TypeOffset 是一个数组,数组每项大小为两个字节(16位),它由高 4位和低 12位组成,高 4位代表重定位类型,低 12位是重定位地址,它与VirtualAddress 相加即是指向PE 映像中需要修改的那个代码的地址。

即每个TYPEOFFSET的结构体大小是2个字节。知道总大小,知道单个大小,求数量的话就是总大小除以每个大小,所以是(pPEReloc->SizeOfBlock - 8) / 2

再回答第一个问题:
PTYPEOFFSET pTypeOffset = (PTYPEOFFSET)(pPEReloc + 1);//+1是什么意思啊??

理解了第二问以后,这点也容易理解了,pTypeOffset指向的是TypeOffset数组的首地址,这个首地址相对于这个Reloc区块的偏移是8个字节(前八个字节保存的是VirtualAddress和SizeOfBlock)。(pPEReloc + 1)中的+1其实就是加了8个字节(pPEReloc指向的结构体IMAGE_BASE_RELOCATION的大小就是8个字节)。

这样经过(PTYPEOFFSET)(pPEReloc + 1),+1再强转为PTYPEOFFSET以后就确定TypeOffset数组的首地址了。
2016-3-30 16:46
0
雪    币: 57
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
31
看起来很高端啊  值得学习!感谢分享!
2016-5-26 12:18
0
雪    币: 415
活跃值: (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
32
这年头哪有什么内部视频啊,外部视频都比内部视频有技术含量;
当兴趣或技术变成牟取利益的工具的时候,兴趣就已经不再是兴趣,技术也已经不再是技术了.
2016-5-26 12:51
0
雪    币: 58
活跃值: (11)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
33
楼主您好,感谢您的分享,对于被加壳程序的导入表修复我有一点点没弄明白

再修复导入表的时候,传入被加壳程序在内存中对齐后的导入表RVA,然后遍历这个结构,全部获取函数地址一次,然后放入IAT中。

但是IAT没有在PE头里面相关联,内存中的PE头部信息应该都没有关联,我不知道程序怎么使用这个函数地址,是通过什么?
以前我认为在使用这个函数的时候,首先通过函数名称,然后链接到地址,在跳转到该地址执行。

现在看您的程序是IAT没有与PE头相关联的,不知道有什么资料可以参考一下的?
2016-5-29 14:43
0
雪    币: 465
活跃值: (398)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
34
感谢楼主的分享,学习下。
2016-5-29 16:01
0
雪    币: 34
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
35
很不错,感谢楼主,适合学习
2016-5-30 16:52
0
雪    币: 76
活跃值: (1050)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
36
你找个PE文件,用LordPE加载,然后点【目录】,其中有一项就是【IAT】,存着IAT的RVA和大小啊,这些信息通过遍历PE文件的目录表都可以遍历得到。
修复IAT的时候,所需要的信息都保存在PE文件的导入表中,从导入表中可以得到函数名,函数模块,然后从函数模块中得到函数地址,导入表中也保存着函数的填充地址(IAT),这样将你得到的函数地址写到IAT就可以了。
参考书籍的话,PE权威指南 里,导入表那一章应该有说明,希望能帮到你。
2016-6-1 01:07
0
雪    币: 76
活跃值: (1050)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
37
IAT的填充是系统PE加载器完成的,在程序运行之初会一次性全部填充成真是的函数地址,程序用到某个函数的时候,直接去IAT里取函数地址就行,保存函数地址的这个地址是固定不变的。
2016-6-1 01:09
0
雪    币: 58
活跃值: (56)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
38
感谢楼主无私的奉献,代码可编译,易阅读!
2016-7-5 18:06
0
雪    币: 294
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
39
感谢楼主,想不到一个壳如此复杂
2016-7-22 16:23
0
雪    币: 35
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
40
观摩中。。。
2016-9-21 19:48
0
雪    币: 115
活跃值: (23)
能力值: (RANK:20 )
在线值:
发帖
回帖
粉丝
41
感谢楼猪分享技术,很感谢,特别对于我等菜鸟!!
2016-10-2 08:20
0
雪    币: 1042
活跃值: (560)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
42
很好的文章,分析的很全面
2016-12-7 04:51
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
43
我会认真学习的 谢谢版主~
2016-12-7 22:39
0
雪    币: 1437
活跃值: (4528)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
44
匹配kenrel32名字不就通用了吗?
       _asm
       {
                       push        eax
                       push        ecx
                       push        edi
                       push        esi
                       xor                ecx,  ecx
                       mov                esi,  fs:[30h]                              //  Ptr32  _PEB
                       mov                esi,  [esi  +  0ch]                        //  Ptr32  _PEB_LDR_DATA
                       mov                esi,  [esi  +  1ch]                        //  Get  InInitializationOrderModuleList.Flink
               next_module  :
                       mov                eax,  [esi  +  8h]                          //  eax  =  kernel32.DLL地址
                       mov                edi,  [esi  +  20h]                        //  BaseDllName
                       mov                esi,  [esi]                                    //  下一个模块
                       cmp[edi  +  12  *  2],  cx                              //  模块结尾是0
                       jne                next_module                                  //继续循环
                       mov          dwKerenl32Addr,eax                    //找到的地址
                       pop                esi
                       pop                edi
                       pop                ecx
                       pop          eax
       }
2017-4-24 18:26
0
雪    币: 1485
活跃值: (1135)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
45
感谢楼主的分享了。本壳还有地方需要修订就是重定位,因为测试发现电脑重启后,加了壳的程序就不能运行了。
2017-9-5 08:17
0
雪    币: 293
活跃值: (287)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
46






冰雄



感谢楼主的分享了。本壳还有地方需要修订就是重定位,因为测试发现电脑重启后,加了壳的程序就不能运行了。

我点进来看就是想看作者如何修正系统加载器对重定位操作的
2017-9-5 09:14
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
47
版主你好,在看你的代码的时候发现一个bug,希望指点迷津
//************************************************************
//  函数名称:        InitValue
//  函数说明:        初始化变量
//  作        者:        cyxvc
//  时        间:        2015/12/25
//  返  回        值:        void
//************************************************************
void  CPE::InitValue()
{
       m_hFile                                =  NULL;
       m_pFileBuf                        =  NULL;
       m_pDosHeader                =  NULL;
       m_pNtHeader                        =  NULL;
       m_pSecHeader                =  NULL;
       m_dwFileSize                =  0;
       m_dwImageSize                =  0;
       m_dwImageBase                =  0;
       m_dwCodeBase                =  0;
       m_dwCodeSize                =  0;
       m_dwPEOEP                        =  0;
       m_dwShellOEP                =  0;
       m_dwSizeOfHeader        =  0;
       m_dwSectionNum                =  0;
       m_dwFileAlign                =  0;
       m_dwMemAlign                =  0;
       m_PERelocDir                =  {  0  };
        m_PEImportDir                =  {  0  };
       m_IATSectionBase        =  0;
       m_IATSectionSize        =  0;
}
报错为:
PE.cpp(40):  error  C2059:  语法错误:“{”
1>PE.cpp(40):  error  C2143:  语法错误  :  缺少“;”(在“{”的前面)
1>PE.cpp(40):  error  C2143:  语法错误  :  缺少“;”(在“}”的前面)
1>PE.cpp(41):  error  C2059:  语法错误:“{”
1>PE.cpp(41):  error  C2143:  语法错误  :  缺少“;”(在“{”的前面)
1>PE.cpp(41):  error  C2143:  语法错误  :  缺少“;”(在“}”的前面)


//保存重定位目录信息
       m_PERelocDir  = 
               IMAGE_DATA_DIRECTORY(m_pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);

       //保存IAT信息目录信息
       m_PEImportDir  =
               IMAGE_DATA_DIRECTORY(m_pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
是这两个变量在初始化的时候出错了吗?
2017-10-3 16:03
0
雪    币: 416
活跃值: (162)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
48
已经明白,感谢楼主分享
2017-12-6 14:48
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
49
重定位写的很漂亮。我有个问题,新增加的节的节信息是直接跟在原来的后面的,也就是说占用了原来0作为结尾的IMAGE_SECTION_HEADER。那么问题来了,如果原来的PE文件没有了0作为结尾的多出来的一个IMAGE_SECTION_HEADER,并且下一个字节内容开始就是节表内容的话,怎么办??
最后于 2018-2-26 12:57 被woodchenchen编辑 ,原因:
2018-2-24 04:51
0
雪    币: 221
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
50
楼主你好,我有两个问题:
1.在加壳程序中已经loadlibrary加载shell到了内存中,为什么后面又
       MODULEINFO  modinfo  =  {  0  };
       GetModuleInformation(GetCurrentProcess(),  hShell,  &modinfo,  sizeof(MODULEINFO));
       PBYTE    pShellBuf  =  new  BYTE[modinfo.SizeOfImage];
       memcpy_s(pShellBuf,  modinfo.SizeOfImage,  hShell,  modinfo.SizeOfImage);
在内存中继续申请一块内存保存Shell模块呢?而且在memcpy_s函数中,将hShell传参到了源地址这一参数上,这是为什么?
2.在加壳程序中使用的函数,比如XorCode(0x15)加密函数,SetShellReloc(pShellBuf,  (DWORD)hShell);还原Shell的重定位信息;
这两个函数中的参数有什么用?在函数体中没有用到XorCode的0x15参数,SetShellReloc得hShell参数啊。

顺便说一下,楼主你在上面贴出来的代码中加密函数XorCode异或的都是0x15,不是项目代码中的i啊。
2018-3-27 20:20
0
游客
登录 | 注册 方可回帖
返回
//