壳作为一种主要的软件保护手段大概可以分为压缩壳和加密壳两类。而现在
流行的加壳程序无论是压缩的还是加密的几乎都是针对应用层程序的,对于驱动
程序的保护壳则几乎是空白。笔者只在一些国外加密狗的驱动中见过类似应用层
的保护壳。本篇文章主要介绍驱动加壳程序与应用层加壳程序在编写上的区别以
及一些注意事项。
1.校验和的计算
驱动程序被加壳后必须重新进行校验和的计算控制工程网版权所有,否则加壳后的驱动加载会
失败
;*****************计算pe文件校验和*********************
CalcPECheckSum PROC lpBaseAddr:DWORD,dwFileSize:DWORD
LOCAL CheckSum:DWORD
pushad
mov ecx,dwFileSize
inc ecx
shr ecx,
xor eaxCONTROL ENGINEERING China版权所有,eax
clc
mov esi,lpBaseAddr
cal_checksum:
adc ax,word ptr [esi]
inc esi
inc esi
loop cal_checksum
mov ebx,dwFileSize
add eaxCONTROL ENGINEERING China版权所有,ebx
mov CheckSum,eax
popad
mov eaxCONTROL ENGINEERING China版权所有,CheckSum
ret
CalcPECheckSum endp
;*******************************************************
2.原始IAT的处理
由于原驱动程序被加上了我们的外壳,所以原驱动程序的IAT表的填写工作
要由我们的外壳程序来完成。应用层壳一般通过GetModuleHandle和GetProcAdd
ress两个API来完成这个工作,或者自己实现这两个API的功能。而驱动壳是要
随驱动程序一起被加载到内核当中去的,但内核里没有这两个函数,需要我们
自己对这两个函数做内核的实现。当然也可以用MmGetSystemRoutineAddress函
数控制工程网版权所有,不过它只能得到ntoskrnl.exe和hal.dll两个模块的函数控制工程网版权所有,对于其它模块则
无能为力了,影响壳的通用性。
壳的GetModuleHandle函数可以通过遍历PsLoadedModuleList链表来实现控制工程网版权所有,
关于遍历这个链表的方法可以参照Futo的代码,通过DRIVEROBJECT的DriverSec
tion成员来完成,而驱动对象可以从堆栈当中找到。
壳的GetProcAddress函数的实现就很简单了,内核模块本身也是PE文件,
直接遍历一下PE的导出表就ok了。
还有一点需要注意的就是UNICODE的转换,PE文件里面的字符串是以ASCII
方式存储的,而内核里的字符串多半是用UNICODE方式存放的,这点需要注意。
3.节表的处理
在给程序加壳的时候一般都要添加新节,用于存放壳的代码,应用层程序
的节表的最后一项和第一个节之间一般是有一个很大的空间可以用来添加新的
节表项的,但一般情况下驱动程序节表的最后一项后面紧接着就是第一个节CONTROL ENGINEERING China版权所有,
根本没有足够的0x28大小的空间存放新的节表项。解决的方法有两种,第一种
将所有的节向后移动,而第二种方法则是将PE头整体向前移动覆盖掉部分无用
的dos头,留出足够的空间存放新的节表项。
另外一点需要注意的是,加壳后的驱动程序的每一个节表项必须满足如下
两个公式,才能