组成:
1.引导程序:由vc6 win32 dll工程
2.主程序:MFC工程
引导程序与主程序一起开发,使用时引导程序作为资源放到主程序中。
主程序执行流程:
1. 载入待加壳程序与引导程序,保存待加壳程序入口点与基址。
LPEFile m_lpeFile,m_lpeStup; if (!LoadPE(&m_lpeFile,&m_lpeStup)) { MessageBox("载入文件错误!"); } //(保存原来程序信息) g_TargetMsg.dwEntryRVA=m_lpeFile.GetNtOptionalHeader()->AddressOfEntryPoint; g_TargetMsg.dwImageBase=m_lpeFile.GetNtOptionalHeader()->ImageBase;
m_lpeFile.AddSection(_T(".Evil0r"),dwStupSectionSize1,IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_MEM_EXECUTE);//加区段
3.加入导入表并修复导入表
1.添加区段,用来保存引导段所需要的导入表信息
2.重定位引导段导入表。(其实也是重定位rva,根据所在区段的rva不同来重定位,此时OriginalFirstThunk跟FirstThunk指向的区域是相同的,还没有填充IAT)
3.写入重定位后的导入表数据
4.修改数据目录,也是跟重定位RVA差不多,先得到原先的与区段rva的差值,然后加上现在区段rva。不过此时要保存下以前导入表rva。
4.将保存的信息写入新加的引导段的一个位置,在写引导程序时预留一个位置,用来还原原程序。
5.做一些其他处理,这里对从OEP开始的0x100个字节进行简单的异或加密
引导段的设计:
预先处理:
首先将data段合并到text段中,并指定入口点。
现在我们可以在入口点函数里大刀阔斧的写代码了,不用像普通的加壳程序那样用蛋疼的内联汇编去写引导程序,因为我们在主程序中会将这一部分的代码重定位,并添加了引导段使用的导入表段。
引导程序的固定位置保存着待加壳程序一些信息的结构体,在主程序中会帮引导程序填充好这一块的内容。借助这个,引导程序主要做的就是模拟PE加载器填充加壳程序的IAT,填充的时候注意,要在之前修改下目标文件的rdata区段的属性使其可写,并且因为我们在主程序中对目标程序OEP开始处0x100大小的代码进行异或加密了,这里还要给异或回来,要异或回来的话就要使目标程序代码段可写。最后跳到原程序OEP去执行。
有了框架就可以自己添加任何处理代码了。
http://download.csdn.net/detail/evi10r/4109644
http://download.csdn.net/detail/evi10r/4109643
我上传的这个程序代码加壳后OD中单步走几步就脱掉了。
只是MARK点东西
-----------------------------------------华丽的分割线---------------------------------------------------------
遇到的问题记录:
c移位函数 右移是否一定补0?
答:正数无论左移右移都是补0,负数都是补1
ZeroMemory和memset的区别:
1、ZeroMemory是微软的SDK提供的,memset是属于C Run-time Library提供的。因此ZeroMemory只能用于Windows系统,而memset还可用于其他系统。
2、ZeroMemory是一个宏,只是用于把一段内存的内容置零,内部其实是用 memset实现的,而memset除了对内存进行清零操作,还可以将内存置成别的字符。
3、如果程序是Win32程序而且不想连接c运行时库,就用ZeroMemory;如果需要跨平台,就用memset。 所以,如果ZeroMemory和memset用于清零操作,其本质是一样的。
ZeroMemory和 “={0}”的区别:
1、ZeroMemory会将结构中所有字节置0,而“={0}”只会将成员置0,其中填充字节不变。
2、一个struct有构造函数或虚函数时,ZeroMemory可以编译通过,而“={0}”会产生编译错误。其中,“={0}”的编译错误起到了一定的保护作用,因为对一个有虚函数的对象使用ZeroMemory时,会将其虚函数的指针置0,这是非常危险的(调用虚函数时,空指针很可能引起程序崩溃)。
区段控制
#pragma code_seg("data")
#pragma data_seg ("shareddata")
#pragma comment(linker,"/MERGE:.datac=.data")
FARPROC WINAPI GetProcAddress(
__in HMODULE hModule,
__in LPCSTR lpProcName
);
如果此参数是一个序数值,它必须在一个字的底字节,高字节必须为0。
加密与解密P280说IMAGE_THUNK_DATA的低32位是序号,这是不是弄错了?
实际代码中是这样调用的
*(PDWORD)dwpFirstThunk=(DWORD)GetProcAddress(hMod,(const char*)\ (pOriginalFirstThunk->u1.Ordinal&0x0000FFFF));
在vs08中会将未使用到的全局变量给优化掉,本来我用vs08写引导段的时候,却发现他不生成.data段,原来是那个全局结构体变量没使用他就直接给优化没了,这样的话我们为了方便预留一个地方让主程序填充这个与目标程序信息有关的结构体,就使用VC6来写引导段程序。