网上对于X64下Shellcode的讨论比较少,本文我们将着重讨论X64下的Shellcode的实现。
shellcode的第一步。第一步都是找到某模块基址,如kernel32.dll。
由于kernel32.dll内含一些很重要的函数,如LoadLibrary和GetProcAddress,获取这些函数后,病毒或者shellcode就可以为所欲为的加载其他模块。
X64下shellcode的编写和32位下的区别由于X64下_asm关键字的取消,我们没有办法在VC中内联汇编了,所以需要找一款64位的汇编工具,可以选择微软的masm,但本文推荐fasm,基本不需要进行什么设置,只需把本文给出的.asm文件直接打开或者复制粘贴后编译就可以看到效果了。
虚拟机X64的硬件环境
硬件:拥有两块硬盘的台式机或者笔记本,内存最好大于4GB。笔记本可以加装光驱位硬盘,或者内存条,两块硬盘的好处就是真实的系统在主盘上启动,虚拟机放在从盘。即使同时启动3、4个虚拟机(我是说同时点虚拟机的power按钮),真实的操作系统也不是很卡。我的笔记本硬件是I3380M2.53Ghz的主频,两块硬盘,主盘5400rpm,从盘7200rpm。
其实本文很简单,只要弄懂下面几行代码就完成任务了。
GetKrnlBase3: movrsi,[gs:60h] movrsi,[rsi+18h] movrsi,[rsi+30h] ; pebfromteb ;_peb_ldr_datafrompeb movrsi,[rsi] movrsi,[rsi] ;kernelbase.dll ;kernel32.dll movrsi,[rsi+10h];payattentiontodanwei ret
X64下的一些基础知识
1)X64下增加了r8~r15这8个64位通用寄存器。16位进化成32位,32位又进化成64位,即ax-eax-rax,兼容以前的寄存器。
2)pushad、popad等命令在X64下无法使用。
3)记住这个顺序:r9r8rdxrcx。X64下都是用的__fastcall方式,4个寄存器用完之后才用堆栈传递参数。
4)fs,x64下换成了gs,[fs:0]的指向自然由[gs:0]继承。
WinDBG查看结构和代码解释findkernel32.dll,本文只说一种方法,通用于win8x64。customer验证的方法就是把本文给出的程序直接拖进X64Win8虚拟机,即会看到kernel32.dll的地址被打印出来。
movrsi,[gs:60h] movrsi,[rsi+18h] ; pebfromteb ;_peb_ldr_datafrompeb
这两行代码不用解释,我们看“movrsi,[rsi+30h];InInitializationOrderModuleList.Flink”。
本文采用的是从InInitializationOrderModuleList这个链表来找寻kernel32.dll的。我所查阅到的国外一些X64shellcode都是从InLoadOrderModuleList来找的,当然方法是一样的。
我们来看一下InInitializationOrderModuleList的结构。在Windbg中连续输入以下命令:
/* PROCESSfffffa800b429b30 SessionId:1Cid:04b0 Peb:7fffffd9000ParentCid:048c Image:explorer.exe */ !process00 //.processexplorer.exe的PROCESS值 .processfffffa800b429b30 .reload
看到如下的提示后:
ConnectedtoWindows77601x64targetat(FriAug3100:34:01.0412012(UTC+8:00)), ptr64TRUE LoadingKernelSymbols ............................................................... ................................................................ ............................. LoadingUserSymbols//千万要注意UserSymbols是否加载成功 ................................................................ ................................................................ ............................................................. Loadingunloadedmodulelist ..........................................
继续输入“!peb”,可以得到如下结果。
1:kd!peb PEBat000007fffffd9000 InheritedAddressSpace: No ReadImageFileExecOptions:No Beingdebugged: ImageBaseAddress: Ldr No 00000000ffa20000 0000000077742640 Ldr.Initialized: Yes Ldr.InInitializationOrderModuleList:00000000000627a0.00000000084dda10 Ldr.InLoadOrderModuleList: Ldr.InMemoryOrderModuleList: 0000000000062690.00000000084dd9f0 00000000000626a0.00000000084dda00 Module ffa200004ce7a144Nov2018:21:562010C:\Windows\Explorer.EXE 776100004ce7c8f9Nov2021:11:212010C:\Windows\SYSTEM32\ntdll.dll 773f00004ce7c78bNov2021:05:152010C:\Windows\system32\kernel32.dll 7fefd730000 4ce7c78c Nov 20 21:05:16 2010 C:\Windows\system32\KERNELBASE.dll 7fefeb700004a5bde6bJul1409:24:592009C:\Windows\system32\ADVAPI32.dll
我们主要关心InInitializationOrderModuleList,下面分步进行说明。
1)查看Ldr.InInitializationOrderModuleList。
1:kddd00000000000627a0 00000000`000627a0 00000000`000627b0 77610000 00062c90000000007774267000000000 77610000000000000000000000000000ntdll.dll的基地址 00000000`000627c0 00000000`000627d0 00000000`000627e0 00000000`000627f0 00000000`00062800 00000000`00062810 001a900000000000003c003a00000000 00062600000000000014001200000000 777253f800000000000040040000ffff 000cb510000000007774aac000000000 4ce7c8f9000000000000000000000000 00000000000000000006281800000000
2)查看模块入口dd00062c90。
1:kddd00000000000627a0 00000000`000627a0 00000000`000627b0 77610000 00062c90000000007774267000000000 77610000000000000000000000000000ntdll.dll的基地址 00000000`000627c0 00000000`000627d0 00000000`000627e0 00000000`000627f0 00000000`00062800 00000000`00062810 001a900000000000003c003a00000000 00062600000000000014001200000000 777253f800000000000040040000ffff 000cb510000000007774aac000000000 4ce7c8f9000000000000000000000000 00000000000000000006281800000000
3)接下来就是查找kernel32.dll了。
1:kddd00062b20 00000000`00062b20 00000000`00062b30 773f0000 00063ab00000000000062c9000000000 773f00000000000077405ea000000000kernel32.dll的基地址 00000000`00062b40 00000000`00062b50 00000000`00062b60 00000000`00062b70 00000000`00062b80 00000000`00062b90 0011f000000000000042004000000000 00062ab000000000001a001800000000 00062ad800000000000840040000ffff 041401d0000000007774aa4000000000 4ce7c78b000000000000000000000000 000000000000000000063dc000000000
参考上面的几条命令,下面的两条命令也自然可以理解了。
movrsi,[rsi] movrsi,[rsi] ;kernelbase.dll ;kernel32.dll
之后只剩下下面这条指令了。
movrsi,[rsi+10h];payattentiontodanwei
其中的10h也就是10进制的16。
1:kddt_LIST_ENTRY ntdll!_LIST_ENTRY +0x000Flink :Ptr64_LIST_ENTRY :Ptr64_LIST_ENTRY +0x008Blink
由上面的结果可知,在X64下,Flink和Blink的两个指针长度由4变成了8。那么,为什么加上指针的长度就得到了kernel32.dll的基地址呢?我们可以看下面这条命令的执行结果,此处用的是InInitializationOrderLinks,所以加2个指针长度就可以了;如果用的是InLoadOrderLinks,得加几个指针长度呢?大家可以自行计算下。
1:kddt_LDR_DATA_TABLE_ENTRY ntdll!_LDR_DATA_TABLE_ENTRY +0x000InLoadOrderLinks:_LIST_ENTRY +0x010InMemoryOrderLinks:_LIST_ENTRY +0x020InInitializationOrderLinks:_LIST_ENTRY +0x030DllBase :Ptr64Void +0x038EntryPoint +0x040SizeOfImage +0x048FullDllName +0x058BaseDllName +0x068Flags :Ptr64Void :Uint4B :_UNICODE_STRING :_UNICODE_STRING :Uint4B
下面给出一个64位的FASM程序,通用于WIN7和WIN8,运行之后可以打印出kernel32.dll的基地址。
formatPE64CONSOLE macro.text{section.texcodereadableexecutablewriteable} macro.code{section.codecodereadableexecutable} macro.data{section.datadatareadablewriteable} entry __Entry includewin64axp.inc .text __Entry: ; ; ; push call dword[esp] GetKrnlBase GetKrnlBase2 GetKrnlBase3 call call xorrcx,rcx cinvokeprintf,type,rsi ccall [printf],hello_msg ; cinvokeprintf,type,0 cinvokegetch invokeExitProcess,rcx ; moveax,[fs:30h] ; ; mov mov eax,[eax+0ch];Get_PEB_LDR_DATA eax,[eax+1ch];GetInInitializationOrderModuleList.Flink,
此时eax指向的是ntdll模块的InInitializationOrderModuleList线性地址,所以我们获得它的下一个地址则是kernel32.dll
mov mov ret eax,[eax] eax,[eax+8h] GetKrnlBase3: movrsi,[gs:60h];pebfromteb movrsi,[rsi+18h] movrsi,[rsi+30h] ;_peb_ldr_datafrompeb ;InInitializationOrderModuleList.Flink, ;rsi==00232c9000000000 movrsi,[rsi] ;rsi=00232b2000000000 ;kernelbase.dll movrsi,[esi] ;kernel32.dll movrsi,[rsi+10h];payattentiontodanwei ret .data type db"%I64x",0 hello_msg db0Dh,0Ah section.IDAtaimportdatareadablewritable librarykernel,KERNEL32.DLL,\ msvcrt,msvcrt.dll importkernel,\ ExitProcess,ExitProcess importmsvcrt,\ printf,printf,\ getch,_getch
其中[gs:0]指向teb,使用Windbg即可查看teb。
1:kddt_teb ntdll!_TEB +0x000NtTib +0x038EnvironmentPointer:Ptr64Void +0x040ClientId:_CLIENT_ID :_NT_TIB +0x050ActiveRpcHandle:Ptr64Void +0x058ThreadLocalStoragePointer:Ptr64Void +0x060ProcessEnvironmentBlock:Ptr64_PEB 1:kddt_peb ntdll!_PEB +0x000InheritedAddressSpace:UChar +0x001ReadImageFileExecOptions:UChar +0x002BeingDebugged +0x003BitField :UChar :UChar +0x003ImageUsesLargePages:Pos0,1Bit +0x003IsProtectedProcess:Pos1,1Bit +0x003IsLegacyProcess:Pos2,1Bit +0x003IsImageDynamicallyRelocated:Pos3,1Bit +0x003SkipPatchingUser32Forwarders:Pos4,1Bit +0x003SpareBits +0x008Mutant :Pos5,3Bits :Ptr64Void +0x010ImageBaseAddress:Ptr64Void +0x018Ldr :Ptr64_PEB_LDR_DATA 1:kddt_PEB_LDR_DATA ntdll!_PEB_LDR_DATA +0x000Length :Uint4B :UChar :Ptr64Void +0x004Initialized +0x008SsHandle +0x010InLoadOrderModuleList:_LIST_ENTRY +0x020InMemoryOrderModuleList:_LIST_ENTRY +0x030InInitializationOrderModuleList:_LIST_ENTRY 1:kddt_LIST_ENTRY ntdll!_LIST_ENTRY +0x000Flink +0x008Blink :Ptr64_LIST_ENTRY :Ptr64_LIST_ENTRY 1:kddt_LDR_DATA_TABLE_ENTRY ntdll!_LDR_DATA_TABLE_ENTRY +0x000InLoadOrderLinks:_LIST_ENTRY +0x010InMemoryOrderLinks:_LIST_ENTRY +0x020InInitializationOrderLinks:_LIST_ENTRY +0x030DllBase :Ptr64Void +0x038EntryPoint +0x040SizeOfImage +0x048FullDllName +0x058BaseDllName +0x068Flags :Ptr64Void :Uint4B :_UNICODE_STRING :_UNICODE_STRING :Uint4B
(完)
本文内容所提及均为本地测试或经过目标授权同意,旨在提供教育和研究信息,内容已去除关键敏感信息和代码,以防止被恶意利用。文章内提及的漏洞均已修复,作者不鼓励或支持任何形式的非法行为。