我在之前的文章里说过,要使你的驱动在WIN64系统上加载,有两种方法:1.向微软缴纳高额的“保护费”(即购买驱动签名,虽然说把微软比喻为黑社会不太好,不过这种行径真的很像黑社会);2.破解微软的驱动签名验证机制。如果要在WIN64上进行内核HOOK,那么交保护费都不行,只能破解微软内核保护(PatchGuard)机制。有了这两个门神,不得不说使得WIN64系统十分安全。举个例子,我有个同学他对电脑一窍不通,之前用WINXP时经常中毒要我帮忙重装系统,后来给他安装了WIN7X64后,半年也没有中毒,而且还是在没有安装杀毒软件的情况下。但是对于普通的编程爱好者或者小公司而言,这两个门神会导致开发投入极高,甚至无法开发出安全类的软件,简而言之,就是不能在WIN64平台上吃底层安全这碗饭了。广东地区有句俗语,叫“断人财路犹如杀人父母”,但很明显,这句话的道理是全世界通用的。于是自从VISTAX64出来后,各种对付这两个门神的方法层出不穷,个人认为效果最好的就是fyyre放出的破解工具(disable_pg_ds)了,可惜这个工具不开源。不久之前,我在FASM官方论坛找到了类似这种工具的源代码,今天就把源码的分析心得写成文章分享给大家。顺便再次感叹下,外国人实在太大方了!在此特地感谢这位乐于分享的外国人Feryno!
要实现突破数字签名验证机制和PatchGuard,必须修改两个文件,winload.exe以及ntoskrnl.exe。修改ntoskrnl.exe这个好理解,那么为什么要修改winload.exe呢?因为winload.exe会校验ntoskrnl.exe有没有被修改过,如果发现它被修改过就拒绝加载修改过的ntoskrnl.exe。
首先把WINLOAD.EXE扔到IDA里,看看要修改哪些地方:
.text:00000000004057E8OslInitializeCodeIntegrityprocnear OslpMain+61Cp ;CODEXREF: ;DATA .text:00000000004057E8 XREF:.pdata:00000000004B2168o .text:00000000004057E8 .text:00000000004057E8var_58 .text:00000000004057E8var_50 .text:00000000004057E8var_48 .text:00000000004057E8var_38 .text:00000000004057E8arg_8 .text:00000000004057E8arg_18 .text:00000000004057E8 =qwordptr-58h =dwordptr-50h =dwordptr-48h =qwordptr-38h =qwordptr10h =qwordptr20h .text:00000000004057E8 mov rax,rsp rbx .text:00000000004057EB push .text:00000000004057EC .text:00000000004057ED .text:00000000004057EE .text:00000000004057F0 .text:00000000004057F2 .text:00000000004057F6 .text:00000000004057F9 .text:00000000004057FC .text:0000000000405800 .text:0000000000405807 .text:000000000040580B .text:000000000040580F .text:0000000000405812 .text:0000000000405816 BlImgQueryCodeIntegrityBootOptions push push push push sub rbp rdi r12 r13 rsp,50h xor r13d,r13d r12d,ecx r8,[rax+18h] rcx,BlpApplicationEntry rdx,[rax+10h] [rax+20h],r13 rdi,r13 mov lea lea lea mov mov mov [rax-38h],r13 call
需要修改的是加粗的那行(00000000004057E8):
movrax,rsp
修改为:
moval,1
ret
48h,8Bh,C4h
B0h,01h
C3h
这么做的用途是跳过对BlImgQueryCodeIntegrityBootOptions的调用,据我了解,此函数随后会调用ImgArchPcatLoadBootApplication,接下来还会调用BlImgLoadPEImageEx等函数来判断文件有没有签名以及checksum对不对。所以这是一个难缠的家伙,直接跳过。
接下来是修改ntoskrnl.exe的两个地方,下面是第一个地方:
PAGE:00000001403EAA60SepInitializeCodeIntegrityprocnear ;CODEXREF:SepInitializationPhase1+231p PAGE:00000001403EAA60 PAGE:00000001403EAA60arg_0 PAGE:00000001403EAA60 PAGE:00000001403EAA60 PAGE:00000001403EAA65 PAGE:00000001403EAA66 PAGE:00000001403EAA6A PAGE:00000001403EAA6C PAGE:00000001403EAA72 PAGE:00000001403EAA78 PAGE:00000001403EAA7A PAGE:00000001403EAA81 PAGE:00000001403EAA84 PAGE:00000001403EAA8B PAGE:00000001403EAA92 PAGE:00000001403EAA99 =qwordptr8 mov push sub xor cmp [rsp+arg_0],rbx rdi rsp,20h ebx,ebx cs:InitIsWinPEMode,bl loc_1403EAB0C jnz xor mov lea mov mov mov mov eax,eax cs:g_CiEnabled,1 edi,[rbx+6] cs:g_CiCallbacks,rax cs:qword_14021EE48,rax cs:qword_14021EE50,rax rax,cs:qword_1402A8120 PAGE:00000001403EAAA0 PAGE:00000001403EAAA3 PAGE:00000001403EAAA5 PAGE:00000001403EAAAC PAGE:00000001403EAAAE PAGE:00000001403EAAB5 cmp jz rax,rbx shortloc_1403EAAF7 [rax+98h],rbx shortloc_1403EAAEE rcx,[rax+98h] cmp jz mov lea lea rdx,??_C@_0BJ@KFBEEMJI@DISABLE_INTEGRITY_CHECKS?$AA@NNGAKEGL@ PAGE:00000001403EAABC PAGE:00000001403EAAC1 PAGE:00000001403EAAC8 call mov SepIsOptionPresent rcx,cs:qword_1402A8120 rdx,??_C@_0M@LNFBLGLD@TESTSIGNING?$AA@NNGAKEGL@ PAGE:00000001403EAACF PAGE:00000001403EAAD6 PAGE:00000001403EAAD8 PAGE:00000001403EAADB PAGE:00000001403EAAE0 PAGE:00000001403EAAE2 PAGE:00000001403EAAE9 PAGE:00000001403EAAEB PAGE:00000001403EAAEE PAGE:00000001403EAAEEloc_1403EAAEE: mov cmp rcx,[rcx+98h] eax,ebx cmovnzedi,ebx call cmp mov jz SepIsOptionPresent eax,ebx rax,cs:qword_1402A8120 shortloc_1403EAAEE edi,8 or ;CODEXREF:SepInitializeCodeIntegrity+4Cj PAGE:00000001403EAAEE ; SepInitializeCodeIntegrity+89j PAGE:00000001403EAAEE cmp jz rax,rbx PAGE:00000001403EAAF1 shortloc_1403EAAF7 rbx,[rax+30h] PAGE:00000001403EAAF3 lea PAGE:00000001403EAAF7 PAGE:00000001403EAAF7loc_1403EAAF7: ;CODEXREF:SepInitializeCodeIntegrity+43j PAGE:00000001403EAAF7 ; SepInitializeCodeIntegrity+91j PAGE:00000001403EAAF7 PAGE:00000001403EAAFE PAGE:00000001403EAB01 PAGE:00000001403EAB03 PAGE:00000001403EAB08 PAGE:00000001403EAB0A PAGE:00000001403EAB0C ; lea mov mov call mov jmp r8,g_CiCallbacks rdx,rbx ecx,edi CiInitialize ebx,eax shortloc_1403EAB12 --------------------------------------------------------------------------- PAGE:00000001403EAB0C PAGE:00000001403EAB0Cloc_1403EAB0C: PAGE:00000001403EAB0C mov cs:g_CiEnabled,bl PAGE:00000001403EAB12 PAGE:00000001403EAB12loc_1403EAB12: ;CODEXREF:SepInitializeCodeIntegrity+AAj PAGE:00000001403EAB12 PAGE:00000001403EAB14 PAGE:00000001403EAB19 PAGE:00000001403EAB1D PAGE:00000001403EAB1E mov mov add pop retn eax,ebx rbx,[rsp+28h+arg_0] rsp,20h rdi PAGE:00000001403EAB1ESepInitializeCodeIntegrityendp
需要修改的是加粗的那行(00000001403EAA72):
jnzloc_1403EAB0C 修改为: 0Fh,85h,94h,00h,00h,00h nop 90h jmploc_1403EAB0C E9h,94h,00h,00h,00h
这样做的目的是跳过对是否为WINPE模式的判断,因为如果是WINPE模式的话就不会启动PatchGuard(以下有TDL4的简介作证:
TDL4thereforedisablesthisfeaturebymakingthesystemthinkthatitisbootingintotheWinPEsystemrestoremode–inwhichPatchguardisdisabled–earlyinthebootprocess.
)。
第二个地方:
INIT:0000000140561340sub_140561340procnear ;CODEXREF:KiFilterFiberContext+FFp INIT:0000000140561340 INIT:0000000140561340 INIT:0000000140561340var_F78 INIT:0000000140561340var_F70 INIT:0000000140561340var_F68 INIT:0000000140561340var_F60 INIT:0000000140561340var_F58 ... ;KiFilterFiberContext+187p =qwordptr-0F78h =qwordptr-0F70h =qwordptr-0F68h =qwordptr-0F60h =dwordptr-0F58h ... ... INIT:0000000140561340var_48 INIT:0000000140561340arg_0 INIT:0000000140561340arg_8 INIT:0000000140561340arg_10 INIT:0000000140561340arg_18 INIT:0000000140561340 INIT:0000000140561340 INIT:0000000140561345 INIT:0000000140561349 INIT:000000014056134D INIT:000000014056134E =byteptr-48h =dwordptr8 =dwordptr10h =dwordptr18h =qwordptr20h mov [rsp+arg_10],r8d [rsp+arg_8],edx [rsp+arg_0],ecx rbx mov mov push push rbp INIT:000000014056134F INIT:0000000140561350 INIT:0000000140561351 INIT:0000000140561353 INIT:0000000140561355 INIT:0000000140561357 INIT:0000000140561359 INIT:0000000140561360 INIT:0000000140561362 INIT:0000000140561368 INIT:000000014056136A INIT:000000014056136C INIT:0000000140561371 INIT:0000000140561371loc_140561371: ;CODEXREF:sub_140561340+28j INIT:0000000140561371 INIT:0000000140561378 INIT:0000000140561380 INIT:0000000140561383 INIT:0000000140561388 INIT:000000014056138B INIT:0000000140561391 INIT:0000000140561399 INIT:000000014056139E INIT:00000001405613A1 ... push push push push push push sub rsi rdi r12 r13 r14 r15 rsp,0F58h xor edi,edi cmp cs:InitSafeBootMode,edi shortloc_140561371 al,1 jz mov jmp loc_1405640D9 lea lea mov call cmp jz rbx,FsRtlUninitializeSmallMcb rdx,[rsp+0F98h+var_E40] rcx,rbx RtlPcToFileHeader rax,rdi loc_1405640D7 mov call cmp jz rcx,[rsp+0F98h+var_E40] RtlImageNtHeader rax,rdi loc_1405640D7 ... ... INIT:00000001405640D9loc_1405640D9: sub_140561340+2Cj ;CODEXREF: ; INIT:00000001405640D9 sub_140561340+9C36j INIT:00000001405640D9 INIT:00000001405640E0 INIT:00000001405640E2 INIT:00000001405640E4 INIT:00000001405640E6 INIT:00000001405640E8 INIT:00000001405640E9 INIT:00000001405640EA INIT:00000001405640EB INIT:00000001405640EC INIT:00000001405640EC add pop pop pop pop pop pop pop pop retn endp rsp,0F58h r15 r14 r13 r12 rdi rsi rbp rbx
需要修改的是加粗的那行(0000000140561368):
jzshortloc_140561371 74h,07h
修改为:
nop nop 90h 90h
这么修改的目的很明显,去掉这个条件跳转,让关于是否安全模式的判断失效。因为如果是安全模式,也不会启动PatchGuard机制(fyyre在博客里的原话:PatchGuarddoesnotinitializeifwebootintosafemode.)。
经过这三处修改,数字签名校验机制和PatchGuard机制被彻底玩趴下了。但还没有完,修改过的两个文件还要重新计算checksum才行。接下来说说具体编码。由于Feryno是用汇编语言实现这一过程的,所以代码很长(560行),但是内容不多,手法也很普通(读取PE文件到buffer-搜索特征码查找位置-修改刚才提到的位置的字节内容-重新计算checksum-把修改过的buffer输出为文件),所以我估计用高级语言,一两百行就能搞定。核心代码:
;//字节替换部分 patch_bytes: push push rbxrsirdi rcx poprsi cld lodsb movzxebx,al leardi,[file_buf] lear8,[rdi+rdx] ;loadsizeofbytestofind ;endoffile align10h patch_bytes_L0: push rsirdi movecx,ebx repzcmpsb poprdirsi jzpatch_bytes_L4 scasb ;rdi+1in1-byteinstruction learax,[rdi+rbx*1] cmprax,r8 jbepatch_bytes_L0 ;endoffilereached jmppatch_bytes_L9 patch_bytes_L4: ;matchingbytesfound,patchthem addrsi,rbx lodsb ;loadsizeofbytestobewritten movzxecx,al repzmovsb patch_bytes_L9: poprdirsirbx ret ;//重新计算checksum reconstruct_crc: ;in:ecx=sizeoffile structIMAGE_DOS_HEADER e_magicrw1;Magicnumber e_cblp e_cp rw1;Bytesonlastpageoffile rw1;Pagesinfile e_crlc e_cparhdr e_minalloc e_maxalloc e_ss rw1;Relocations rw1;Sizeofheaderinparagraphs rw1;Minimumextraparagraphsneeded rw1;Maximumextraparagraphsneeded rw1;Initial(relative)SSvalue rw1;InitialSPvalue e_sp e_csum e_ip rw1;Checksum rw1;InitialIPvalue e_cs rw1;Initial(relative)CSvalue rw1;Fileaddressofrelocationtable rw1;Overlaynumber e_lfarlc e_ovno e_res rw4;Reservedwords e_oemid e_oeminfo e_res2 e_lfanew rw1;OEMidentifier(fore_oeminfo) rw1;OEMinformation;e_oemidspecific rw10;Reservedwords rd1;Fileaddressofnewexeheader ends IMAGE_DOS_SIGNATURE structIMAGE_FILE_HEADER Machine =MZ rw1 rw1 rd1 NumberOfSections TimeDateStamp PointerToSymbolTable NumberOfSymbols rd rd rw rw 1 1 1 1 SizeOfOptionalHeader Characteristics ends IMAGE_SIZEOF_FILE_HEADER IMAGE_FILE_MACHINE_AMD64 structIMAGE_DATA_DIRECTORY VirtualAddress = = sizeof.IMAGE_FILE_HEADER;=20 8664h ;AMD64(K8) rd rd 1 1 Size ends IMAGE_NUMBEROF_DIRECTORY_ENTRIES= structIMAGE_OPTIONAL_HEADER64 ;Standardfields. Magic 16 rw rb rb rd rd rd rd rd 1 1 1 1 1 1 1 1 MajorLinkerVersion MinorLinkerVersion SizeOfCode SizeOfInitializedData SizeOfUninitializedData AddressOfEntryPoint BaseOfCode ;NTadditionalfields. ImageBase SectionAlignment rq1 rd 1 FileAlignment rd1 MajorOperatingSystemVersionrw1 MinorOperatingSystemVersionrw1 MajorImageVersion MinorImageVersion MajorSubsystemVersion MinorSubsystemVersion Win32VersionValue rw1 rw1 rw1 rw1 rd1 rd1 rd1 rd1 rw1 rw1 SizeOfImage SizeOfHeaders CheckSum Subsystem DllCharacteristics SizeOfStackReserve SizeOfStackCommit SizeOfHeapReserve SizeOfHeapCommit LoaderFlags rq1 rq1 rq1 rq1 rd1 rd1 NumberOfRvaAndSizes ;IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; DataDirectory IMAGE_DATA_DIRECTORY rb (IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1)*(sizeof.IMAGE_DATA_DIRECTORY) ends IMAGE_SIZEOF_NT_OPTIONAL64_HEADER=240 IMAGE_NT_OPTIONAL_HDR64_MAGIC structIMAGE_NT_HEADERS64 =020Bh Signature rd1 FileHeader OptionalHeader IMAGE_FILE_HEADER IMAGE_OPTIONAL_HEADER64 ends IMAGE_NT_SIGNATURE leardx,[file_buf] =PE cmp[rdx+IMAGE_DOS_HEADER.e_magic],IMAGE_DOS_SIGNATURE jnzreconstruct_crc_L9 moveax,[rdx+IMAGE_DOS_HEADER.e_lfanew] addrax,rdx cmp[rax+IMAGE_NT_HEADERS64.Signature],IMAGE_NT_SIGNATURE jnzreconstruct_crc_L9 cmp [rax+IMAGE_NT_HEADERS64.FileHeader.Machine],IMAGE_FILE_MACHINE_AMD64 jnzreconstruct_crc_L9 cmp [rax+IMAGE_NT_HEADERS64.OptionalHeader.Magic],IMAGE_NT_OPTIONAL_HDR64_M AGIC jnzreconstruct_crc_L9 ;CRC ;eraseoriginalfilecrc and[rax+IMAGE_NT_HEADERS64.OptionalHeader.CheckSum],0 ;ecx=size ;rdx=file_buf movr10d,ecx shrecx,1 xorr9d,r9d xorr8d,r8d calculate_checksum: movr9w,[rdx] addr8d,r9d movr9w,r8w shrr8d,16 addr8d,r9d addrdx,2 loop calculate_checksum addr8d,r10d mov[rax+IMAGE_NT_HEADERS64.OptionalHeader.CheckSum],r8d ;Checksumdone reconstruct_crc_L9: ret ;//总体实现:相当于main函数 ;//基本流程:read_system_file-patch_bytes-reconstruct_crc- write_system_file start: push rbx subrsp,8*(4+2) learcx,[file0] call read_system_file oreax,eax movebx,eax jzexit_failed movedx,ebx learcx,[file0ed] ;sizeoffile ;sizeofthewholefile ;pointertopatchingdata call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movecx,ebx ;sizeofthewholefile reconstruct_crc ;sizeofthewholefile call movedx,ebx learcx,[file0n] call write_system_file cmpeax,ebx jnzexit_delete_file0n learcx,[file1] call read_system_file oreax,eax movebx,eax jnzL0 learcx,[file2] call read_system_file 85 oreax,eax movebx,eax jzexit_failed L0:movedx,ebx learcx,[file1ed] call patch_bytes movedx,ebx learcx,[file2ed] call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movedx,ebx learcx,[file3ed] call patch_bytes movecx,ebx call reconstruct_crc movedx,ebx learcx,[file1n] call write_system_file cmpeax,ebx jzexit_success ;jmpexit_delete_file1n_file0n exit_delete_file1n_file0n: learcx,[file1n] call [DeleteFileA] exit_delete_file0n: learcx,[file0n] call [DeleteFileA] exit_failed: learbx,[msg_failed] jmpexit_msg exit_success: learbx,[msg_success] exit_msg: ;STD_OUTPUT_HANDLE ;INVALID_HANDLE_VALUE =-11 =-1 ;movrcx,STD_OUTPUT_HANDLE push STD_OUTPUT_HANDLE poprcx call push [GetStdHandle] rax poprcx ifINVALID_HANDLE_VALUE=-1 incrax else cmprax,INVALID_HANDLE_VALUE endif jzexit andqword[rsp+8*(4+0)],0 lear9,[rsp+8*(4+1)] movr8d,msg_size ;leardx,[rbx] push rbx poprdx lea rcx,[rcx] call xor add pop ;alreadyset [WriteFile] eax,eax rsp,8*(4+2) rbx exit: ret
接下来要用到批处理了,光有打过补丁的winload.exe和ntoskrnl.exe还不行,还需要建立新的BCD入口,把PEAUTH服务设为手动,防止在登陆时蓝屏:
@ECHOOFF ECHO. ECHOCreatingpatchedcopiesofwinload,ntkrnlmp/ntoskrnl... ECHO. patch.exe ECHO. ECHOCreatingBCDEntry... ECHO. setENTRY_GUID={46595952-454E-4F50-4747-554944FFFFFF} bcdedit-create%ENTRY_GUID%-dDriverSigning&PatchGuardDisabled -applicationOSLOADER bcdedit-set%ENTRY_GUID%devicepartition=%SYSTEMDRIVE% bcdedit-set%ENTRY_GUID%osdevicepartition=%SYSTEMDRIVE% bcdedit-set%ENTRY_GUID%systemroot\Windows bcdedit-set%ENTRY_GUID%path\Windows\system32\freeload.exe bcdedit-set%ENTRY_GUID%kernelgoodkrnl.exe bcdedit-set%ENTRY_GUID%recoveryenabled0 bcdedit-set%ENTRY_GUID%nxOptOut bcdedit-set%ENTRY_GUID%nointegritychecks1 bcdedit-set%ENTRY_GUID%testsigning1 bcdedit-displayorder%ENTRY_GUID%-addlast bcdedit-timeout5 bcdedit-default%ENTRY_GUID% ECHO. ECHOSettingPEAUTHservicetomanual...(avoidBSODatloginscreen) ECHO. scconfigpeauthstart=demand ECHO. ECHOComplete! ECHO. PAUSE
不得不提的是,在代码里似乎有一段跟破解PatchGuard和数字签名无关的内容,作者在帖子里说sowhenyoureboot,Patchguardisnotintialized,driverdigitalsignaturesarenotnecessary,canonicaladdressformoflongmodevirtualmemoryischeckedcorrectly(长模式虚拟内存的规范地址形式被正确检查?就是加粗的这句,我尚未明白它的意义,他还举了“不规范”的地址形式,如:0000800000000000h、FFFF7FFFFFFFFFFFh)。如果有哪位读者明白它的意义,还请不吝赐教。本文代码在WINDOWS7X64和WINDOWS2008R2下测试成功,效果就是选择了相应的内核启动项后可以加载没有任何签名的内核驱动,也可以进行KernelInlineHook而不蓝屏。
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息。作者不鼓励或支持任何形式的非法软件破解行为。