07 驱动内存加载
驱动加载介绍
内存驱动加载不需要签名
当双击一个PE程序的时候发生了什么
1.通过explorer.exe(资源管理器)
定位到双击的文件
2.资源管理器通过CreatProcess
创建进程
3.创建进程的时候开辟一块空间
此时在R3(PEB)和R0(填充基址)需要的地方挂上R3的物理页
4.通过文件路径载入PE
5.接下来是修复和解析
- 解析PE头,将对应的数据目录存放到内存相关的偏移地址
- 修复重定位,有可能出现随机基址,或者指定的地址被占用,这样的话基址就被修改了,所以要修复重定位
- 修复IAT(系统相关函数)导入表,比如在win7、win10都有一个函数,但是函数地址不一样
- 修复TLS(线程)
- 修复延迟导入表,再一次去用才修复的
- 修复SEH异常
- 获取入口点
- call 入口点
上面的这些驱动加载的时候都有,唯一不同的是驱动加载的时候没有修复和解析中的4
和5
也就是修复TLS,和修复异常导入表,不过在驱动中多修复一个cookie
实验(COOKIE不对的情况)
这里为了做演示将Driver Settings -> Target OS Version
改成Win10
然后在win7虚拟机里面执行会蓝屏
接下来分析一下
可以在IP这里看到这里有个security_init_cookie
出问题了
从调用栈这里也可以看到
先是DriverEntry
驱动入口点,然后执行了一个__security_init_cookie+0x1b
的操作,然后就发生错误(RtlFailFast
)
但是很显然这个__security_init_cookie
不是我们自己写的,所以接下来用ida看一下
然后我们进去看一下这个call的cookie
做了什么操作
代码很简单,可以直接看下面的说明
; void __cdecl __security_init_cookie()
___security_init_cookie proc near ; CODE XREF: DriverEntry+5↑p
mov eax, ___security_cookie ; 获取当前的security_cookie值保存到eax中
test eax, eax ; 将eax值与运算,判断eax是否是0
jz short loc_404028 ; 如果是是0,崩溃
cmp eax, 0BB40E64Eh ; 将eax值与0BB40E64Eh比较
jz short loc_404028 ; 相等崩溃
not eax ; 将eax取反
mov dword_403004, eax ; dword_403004 = eax
retn ; 返回
; ---------------------------------------------------------------------------
loc_404028: ; CODE XREF: ___security_init_cookie+7↑j
; ___security_init_cookie+E↑j
push 6
pop ecx
int 29h ; Win8: RtlFailFast(ecx)
___security_init_cookie endp
也可以看一下伪c代码,基本一样
接下来我们把Driver Settings -> Target OS Version
改成Win7,再编译一样,放到ida看一下
; void __cdecl __security_init_cookie()
___security_init_cookie proc near ; CODE XREF: DriverEntry+5↑p
mov edi, edi
push ebp
mov ebp, esp
push ecx
push ecx
mov eax, ___security_cookie ; 将当前的cookie值保存到eax
mov ecx, 0BB40E64Eh ; ecx = 0BB40E64Eh
test eax, eax ; 比较eax是否是0
jz short loc_404029 ; 如果真,跳到loc_404029
cmp eax, ecx ; 比较eax和ecx
jnz short loc_40403E ; 如果真,跳到loc_40403E
loc_404029: ; CODE XREF: ___security_init_cookie+13↑j
rdtsc ; 获取CPU运行至今的时间周期,eax保存低位,edx保存高位
xor eax, offset ___security_cookie ; 将eax值与cookie异或,因为能够跳到这里的cookie都是0,所以eax不变
mov ___security_cookie, eax ; cookie = eax
jnz short loc_40403E ; eax值取反
mov eax, ecx ; eax = ecx = 0BB40E64Eh
mov ___security_cookie, eax ; cookie = eax
loc_40403E: ; CODE XREF: ___security_init_cookie+17↑j
; ___security_init_cookie+25↑j
not eax ; eax值取反
mov dword_403004, eax ; dword_403004 = eax
leave
retn
___security_init_cookie endp
可以很明显的看出来Win7有自我修复的步骤,一旦cookie不存在
或者是特定值
都会进行自我修复
这也是为什么在Win7下可以不修复cookie但是在win10高版本下必须要修复cookie,因为win10的这个蓝屏机制就是通过检查cookie来防止内存加载
修复方式
但是这个SecurityCookie
是在IMAGE_LOAD_CONFIG_DIRECTORY
结构体中的(不知道为什么Windbg找不到这个结构体)
这个结构体分32位和64位,具体可以看微软文档
从中可以很明显的看到这个Cookie
IMAGE_LOAD_CONFIG_DIRECTORY
结构体
typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY32 {
DWORD Size;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD GlobalFlagsClear;
DWORD GlobalFlagsSet;
DWORD CriticalSectionDefaultTimeout;
DWORD DeCommitFreeBlockThreshold;
DWORD DeCommitTotalFreeThreshold;
DWORD LockPrefixTable;
DWORD MaximumAllocationSize;
DWORD VirtualMemoryThreshold;
DWORD ProcessHeapFlags;
DWORD ProcessAffinityMask;
WORD CSDVersion;
WORD DependentLoadFlags;
DWORD EditList;
DWORD SecurityCookie;
DWORD SEHandlerTable;
DWORD SEHandlerCount;
DWORD GuardCFCheckFunctionPointer;
DWORD GuardCFDispatchFunctionPointer;
DWORD GuardCFFunctionTable;
DWORD GuardCFFunctionCount;
DWORD GuardFlags;
IMAGE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity;
DWORD GuardAddressTakenIatEntryTable;
DWORD GuardAddressTakenIatEntryCount;
DWORD GuardLongJumpTargetTable;
DWORD GuardLongJumpTargetCount;
DWORD DynamicValueRelocTable;
DWORD CHPEMetadataPointer;
DWORD GuardRFFailureRoutine;
DWORD GuardRFFailureRoutineFunctionPointer;
DWORD DynamicValueRelocTableOffset;
WORD DynamicValueRelocTableSection;
WORD Reserved2;
DWORD GuardRFVerifyStackPointerFunctionPointer;
DWORD HotPatchTableOffset;
DWORD Reserved3;
DWORD EnclaveConfigurationPointer;
DWORD VolatileMetadataPointer;
DWORD GuardEHContinuationTable;
DWORD GuardEHContinuationCount;
DWORD GuardXFGCheckFunctionPointer;
DWORD GuardXFGDispatchFunctionPointer;
DWORD GuardXFGTableDispatchFunctionPointer;
DWORD CastGuardOsDeterminedFailureMode;
DWORD GuardMemcpyFunctionPointer;
} IMAGE_LOAD_CONFIG_DIRECTORY32, *PIMAGE_LOAD_CONFIG_DIRECTORY32;
很显然,既然结构体里面有,我们就能改
32位程序内存加载
其中签名目录
记录了程序的签名信息,可以通过这个目录得到程序的签名
其中异常目录
在x86下一直是空的,x64下才有值,因为x86不使用资源目录保存异常