2)验证权限: 默认情况下,普通进程没有SeDebugPrivilege 的权限,因此是不能使用函数OpenProcess打开系统进程CSRSS. EXE,如果能够成功打开CSRSS.EXE,则证明进程被调试了 。 3)标志异常。标志寄存器FLAGS的第8位是TF标志, 使用pushfd使所有标志位进栈,然后将esp所指向的数据与 100h做异或, 使用指令popfd出栈, TF标志位就被设置成了1, 同时会引发标志异常。如果存在调试器, 调试器就会处理异常, 而程序不会得到任何关于异常的信息。 3.1.2 反Dump Dump就是把内存中正在运行的进程拷贝出来,重新存储 为EXE文件的技术。主要是使用函数CreateToolhelp32Snapshot 来获得指定进程的句柄,再通过Module32First和Module32Nex 来得到关于进程信息的结构tagMODULE32ENTRY, 其定义如下: typedef struct tagMODULEENTRY32 { DWORD dwSize; //此结构大小 DWORD th32ModuleID; DWORD th32ProcessID; //进程标识符 DWORD GlblcntUsage; DWORD ProccntUsage; BYTE* modBaseAddr; //映像基址 DWORD modBaseSize; //映像大小 HMODULE hModule; TCHAR szModule[MAX_MODULE_NAME32 + 1]; //进程的完整路径 TCHAR szExePath[MAX_PATH]; } MODULEENTRY32, *PMODULEENTRY32; 通过读取内存地址为modBaseAddr,大小为modBaseSize 的数据可得到进程内的所有数据。 根据dump的原理,只需要将进程映像的相关区域设置 为不可读,就可以防止内存映像被dump。具体实现方法是 用函数GetModuleHandle得到当前进程的句柄,再使用函数 VirtualProtect将内存中映像基址的一部分区域(小于整个PE 文件大小大于0)的访问属性设置为PAGE_NOACCESS,函数 定义如下: BOOL WINAPI VirtualProtect( __in LPVOID lpAddress, __in SIZE_T dwSize, __in DWORD flNewProtect, __out PDWORD lpflOldProtect ); 另外,在修改内存属性的方法之外,还可以通过修改 SizeOfImage来干扰dump,实践证明这并不影响进程的正常执 行, 因为进程被正确加载后, SizeOfImage这个字段就不再使用。 下面的代码是通过PEB结构来找到进程影响的大小进行修改: mov eax, fs:[30h] ;获得PEB的首地址 test eax, eax js operation_9x ;9x系统和NT系统不同 operation_NT: mov eax, [eax+0Ch] ;+00c 结构_PEB_LDR_DATA *Ldr mov eax, [eax+0Ch] ;LDR_MODULE的首地址 mov dword ptr [eax+20h], 1000h ;改变[EAX+20h]中保存的进程映像的大小 jmp operation_finished operation_9x: invoke GetModuleHandleA, 0 test edx, edx jns operation_finished cmp dword ptr [edx+8], -1 jne operation_finished mov edx, [edx+4] ;EDX指向系统保存的另一份PE头数据 mov dword ptr [edx+50h], 1000h ;修改此份PE头的SizeOfImage字段 operation_finished: 3.1.3 自校验 自校验是为了保证程序的完整性,保证程序不被修改。使用CRC32(循环冗余校验码)算法,来获得被校验数据的 校验值,存储到USB Key中,确保校验数据不被修改。 本文对磁盘文件的校验数据是EXE文件除去头部分的 校验,大小即GetFileSize的值与PIMAGE_DOS_HEADER中 e_lfanew字段的差值。将这部分数据读出,并计算出CRC32 的值,存储在USB Key中,文件校验过程如图4所示。 由于加壳对文件的影响,磁盘文件的校验值的计算应该 在加壳完成之后,校验值的比对应该在文件脱壳运行之前。 ![]() 另外一种方式是对代码段进行单独的校验。代码段的数据 对于程序来说是至关重要的,也是软件攻击者必定会修改的区 域。因此对代码段完整性的检查是必须的。由于对代码段的加 密和解密的映像, 代码段会发生变化, 同时当程序运行起来以后, 由于全局变量的变化也会使得代码段发生变化,因此代码段的 校验应在加壳之前代码段没有被加密的时候,以及和脱壳之后 代码段恢复并且全局变量没有被重新赋值的时候[1,3] 。 代码段的校验通过文件映像得到PE结构,通过PE结构 |