能力值:
( 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, 很有可能会超过你分配的内存大小。
请楼主确认下哈。。。
|
能力值:
( 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文件都没有任何问题。
你再仔细考虑考虑?
|
能力值:
( LV7,RANK:110 )
|
-
-
28 楼
哈哈,确实是我搞错啦。
m_pNtHeader->OptionalHeader.SizeOfImage;已经是镜像在内存中对齐后所占用的内存空间了。我一直理解为是文件的在磁盘中的真实大小,这两个值确实不一样。要真是用文件在磁盘中的大小的话,那就真错了哈哈。感谢纠正~
最后,对误报表示歉意~请楼主收下鄙人的膝盖。
|
能力值:
( LV2,RANK:10 )
|
-
-
29 楼
PTYPEOFFSET pTypeOffset = (PTYPEOFFSET)(pPEReloc + 1);//+1是什么意思啊??
DWORD dwNumber = (pPEReloc->SizeOfBlock - 8) / 2;//为什么-8还除2????
可以说明清楚吗?
|
能力值:
( 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数组的首地址了。
|
能力值:
( LV2,RANK:10 )
|
-
-
31 楼
看起来很高端啊 值得学习!感谢分享!
|
能力值:
( LV2,RANK:10 )
|
-
-
32 楼
这年头哪有什么内部视频啊,外部视频都比内部视频有技术含量;
当兴趣或技术变成牟取利益的工具的时候,兴趣就已经不再是兴趣,技术也已经不再是技术了.
|
能力值:
( LV3,RANK:35 )
|
-
-
33 楼
楼主您好,感谢您的分享,对于被加壳程序的导入表修复我有一点点没弄明白
再修复导入表的时候,传入被加壳程序在内存中对齐后的导入表RVA,然后遍历这个结构,全部获取函数地址一次,然后放入IAT中。
但是IAT没有在PE头里面相关联,内存中的PE头部信息应该都没有关联,我不知道程序怎么使用这个函数地址,是通过什么?
以前我认为在使用这个函数的时候,首先通过函数名称,然后链接到地址,在跳转到该地址执行。
现在看您的程序是IAT没有与PE头相关联的,不知道有什么资料可以参考一下的?
|
能力值:
( LV2,RANK:10 )
|
-
-
34 楼
感谢楼主的分享,学习下。
|
能力值:
( LV2,RANK:10 )
|
-
-
35 楼
很不错,感谢楼主,适合学习
|
能力值:
( LV8,RANK:130 )
|
-
-
36 楼
你找个PE文件,用LordPE加载,然后点【目录】,其中有一项就是【IAT】,存着IAT的RVA和大小啊,这些信息通过遍历PE文件的目录表都可以遍历得到。
修复IAT的时候,所需要的信息都保存在PE文件的导入表中,从导入表中可以得到函数名,函数模块,然后从函数模块中得到函数地址,导入表中也保存着函数的填充地址(IAT),这样将你得到的函数地址写到IAT就可以了。
参考书籍的话,PE权威指南 里,导入表那一章应该有说明,希望能帮到你。
|
能力值:
( LV8,RANK:130 )
|
-
-
37 楼
IAT的填充是系统PE加载器完成的,在程序运行之初会一次性全部填充成真是的函数地址,程序用到某个函数的时候,直接去IAT里取函数地址就行,保存函数地址的这个地址是固定不变的。
|
能力值:
( LV2,RANK:10 )
|
-
-
38 楼
感谢楼主无私的奉献,代码可编译,易阅读!
|
能力值:
( LV2,RANK:10 )
|
-
-
39 楼
感谢楼主,想不到一个壳如此复杂
|
能力值:
( LV2,RANK:10 )
|
-
-
40 楼
观摩中。。。
|
能力值:
(RANK:20 )
|
-
-
41 楼
感谢楼猪分享技术,很感谢,特别对于我等菜鸟!!
|
能力值:
( LV2,RANK:10 )
|
-
-
42 楼
很好的文章,分析的很全面
|
能力值:
( LV2,RANK:10 )
|
-
-
43 楼
我会认真学习的 谢谢版主~
|
能力值:
( 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 }
|
能力值:
( LV2,RANK:10 )
|
-
-
45 楼
感谢楼主的分享了。本壳还有地方需要修订就是重定位,因为测试发现电脑重启后,加了壳的程序就不能运行了。
|
能力值:
( LV4,RANK:50 )
|
-
-
46 楼
冰雄
感谢楼主的分享了。本壳还有地方需要修订就是重定位,因为测试发现电脑重启后,加了壳的程序就不能运行了。
我点进来看就是想看作者如何修正系统加载器对重定位操作的
|
能力值:
( 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]); 是这两个变量在初始化的时候出错了吗?
|
能力值:
( LV7,RANK:100 )
|
-
-
48 楼
已经明白,感谢楼主分享
|
能力值:
( LV2,RANK:10 )
|
-
-
49 楼
重定位写的很漂亮。我有个问题,新增加的节的节信息是直接跟在原来的后面的,也就是说占用了原来0作为结尾的IMAGE_SECTION_HEADER。那么问题来了,如果原来的PE文件没有了0作为结尾的多出来的一个IMAGE_SECTION_HEADER,并且下一个字节内容开始就是节表内容的话,怎么办??
最后于 2018-2-26 12:57
被woodchenchen编辑
,原因:
|
能力值:
( 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啊。
|
|
|