_ _ (_) | | __ ____ __ _ _ _ _ __ ___ _ __ _ __ ___ | |_ \ \ / /\ \/ /| || | | || '_ ` _ \ | '_ \ | '_ \ / _ \| __| \ V / > < | || |_| || | | | | || |_) |_ | | | || __/| |_ \_/ /_/\_\| | \__,_||_| |_| |_|| .__/(_)|_| |_| \___| \__| _/ | | | |__/ |_| /---------------------------------------------------------------------------------------\ |>...................[ X64平台病毒感染技术初探 ]..................<| |>......................[ by chx4[x64asm]/vxjump.net ].....................<| |>......................[ 2010-3-28 ].....................<| |>......................[ http://chx4.tk ].....................<| \>...................... [ chx4@x64asm.com ] ...................... ENDM ;-------------------------------------------------------------------------------- ;等值定义 FALSE EQU 0H TRUE EQU 1H NULL EQU 0H MB_OK EQU 0H MB_ICONASTERISK EQU 40H MB_ICONINFORMATION EQU MB_ICONASTERISK PAGE_NOACCESS EQU 1 PAGE_READONLY EQU 2 PAGE_READWRITE EQU 4 PAGE_WRITECOPY EQU 8 PAGE_EXECUTE EQU 10H PAGE_EXECUTE_READ EQU 20H PAGE_EXECUTE_READWRITE EQU 40H PAGE_EXECUTE_WRITECOPY EQU 80H PAGE_GUARD EQU 100H PAGE_NOCACHE EQU 200H MEM_COMMIT EQU 1000H MEM_RESERVE EQU 2000H MEM_DECOMMIT EQU 4000H MEM_RELEASE EQU 8000H MEM_FREE EQU 10000H MEM_PRIVATE EQU 20000H MEM_MAPPED EQU 40000H MEM_RESET EQU 80000H MEM_TOP_DOWN EQU 100000H ;初始化数据定义 .DATA .DATA? lpRemoteCode QWORD ? ;分配的代码搬移地址 cbRemoteCode QWORD ? ;代码长度 ;代码段 .CODE ;-------------------------------------------------------------------------------- ;远程代码 ;-------------------------------------------------------------------------------- REMOTE_CODE_START: JMP RemoteCode ALIGN 16 hInstance QWORD ? hNtDll QWORD ? hKernel32 QWORD ? hUser32 QWORD ? lpLdrLoadDll QWORD ? lpLdrUnloadDll QWORD ? lpLdrGetDllHandle QWORD ? lpLdrGetProcedureAddress QWORD ? lpRtlInitAnsiString QWORD ? lpRtlAnsiStringToUnicodeString QWORD ? lpRtlFreeUnicodeString QWORD ? lpMessageBoxA QWORD ? stLdrLoadDll UNICODE_STRING stLdrUnLoadDll UNICODE_STRING stLdrGetDllHandle UNICODE_STRING stLdrGetProcedureAddress UNICODE_STRING stAnKernel32 ANSI_STRING stAnUser32 ANSI_STRING stUnKernel32 UNICODE_STRING stUnUser32 UNICODE_STRING szLdrLoadDll BYTE "LdrLoadDll",0 szLdrUnLoadDll BYTE "LdrUnloadDll",0 szLdrGetDllHandle BYTE "LdrGetDllHandle",0 szLdrGetProcedureAddress BYTE "LdrGetProcedureAddress",0 szRtlInitAnsiString BYTE "RtlInitAnsiString",0 szRtlAnsiStringToUnicodeString BYTE "RtlAnsiStringToUnicodeString",0 szRtlFreeUnicodeString BYTE "RtlFreeUnicodeString",0 szMessageBoxA BYTE "MessageBoxA",0 szKernel32 BYTE "\??\C:\Windows\System32\Kernel32.Dll",0 szUser32 BYTE "\??\C:\Windows\System32\User32.Dll",0 szInfo BYTE "提示",0 szInformation BYTE "软件提示:程序在没有引入系统函数的情况下自行加载DLL并调用函数成功!",0 ;该函数目前用到两个参数,RCX是模块基地址,RDX是函数索引或名称 ;引用到四个寄存器,RBX是重定位寄存器,R8是临时中转寄存器,RSI是PE结构指针,RDI是导出表指针 GetProcAddressEx PROC ;保存传递进来的参数寄存器 mov [rsp+8H],rcx mov [rsp+10H],rdx mov [rsp+18H],r8 mov [rsp+20H],r9 ;设置堆栈栈顶指针, push rbp mov rbp,rsp ;保存所有非易失寄存器 push rbx push rdx push rdi push rsi push r10 push r11 push r12 push r13 push r14 push r15 ;为函数调用分配足够堆栈 ;注意必须确保堆栈按16字节对齐 sub rsp, 40H @Entry: ;进行重定位 call @F @@: ;取得@@编译地址,因为OFFSET伪指令无法使用 mov r8,$ ;弹出@ENTRY实际地址到RBX寄存器 pop rbx ;运行地址与编译地址相减,得到实际地址偏移 sub rbx,r8 ;初始化寄存器 xor rax,rax mov rsi,[rbp+8H+8H] mov rdi,[rbp+8H+8H] ;检查DOS签名 cmp (IMAGE_DOS_HEADER PTR [rsi]).e_magic,IMAGE_DOS_SIGNATURE jne @Exit ;取得NT结构指针 mov r8d,(IMAGE_DOS_HEADER PTR [rsi]).e_lfanew add rsi,r8 ;检查PE签名 cmp (IMAGE_NT_HEADERS64 PTR [rsi]).Signature,IMAGE_NT_SIGNATURE jne @Exit ;检查运行平台 cmp (IMAGE_NT_HEADERS64 PTR [rsi]).FileHeader.Machine,IMAGE_FILE_MACHINE_AMD64 jne @Exit ;获得导出表VA mov r8d,(IMAGE_NT_HEADERS64 PTR [rsi]).OptionalHeader.DataDirectory[0].VirtualAddress test r8d,r8d jz @Exit ;保存导出表指针 add rdi,r8 ;取得模块名称 ;mov eax,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).nName ;add rax,[rbp+8H+8H] ;检查导出函数总数 cmp (IMAGE_EXPORT_DIRECTORY PTR [rdi]).NumberOfFunctions,0 jz @Exit ;判断RDX参数为序号还是函数名 cmp QWORD PTR [rbp+8H+10H],10000H ja @String @Index: ;得到索引值 mov r8d,[rbp+8H+10H] sub r8d,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).nBase ;检查索引值有效性 cmp r8d,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).NumberOfFunctions ja @Exit ;获取地址表RVA xor rax,rax mov eax,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).AddressOfFunctions add rax,[rbp+8H+8H] ;获取函数RVA mov eax,DWORD PTR [rax+r8*4] ;与基地址相加得到函数地址 add rax,[rbp+8H+8H] ;返回 jmp @Exit @String: ;检查以名称导出的函数个数 cmp (IMAGE_EXPORT_DIRECTORY PTR [rdi]).NumberOfNames,0 jz @Exit ;得到导出函数个数 mov r8d,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).NumberOfNames xor rcx,rcx @@Loop: ;搜索导出函数名称 mov eax,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).AddressOfNames add rax,[rbp+8H+8H] ;获取函数名RVA mov eax,DWORD PTR [rax+rcx*4] ;与基地址相加得到函数名指针 add rax,[rbp+8H+8H] mov r9,rax ;保存用到的寄存器 push rcx push rdi push rsi @StrLen: ;设置目标串指针 mov rdi,[rbp+8H+10H] ;设置查找字符为0 xor rax,rax ;设置查找次数为-1(FFFFFFFF) or rcx,0FFFFFFFFFFFFFFFFH ;正向查找 cld ;重复查找 repnz scasb ;RCX寄存器取反 not rcx ;得到长度 lea rcx,[rcx-1] @StrCmp: ;比较字符串是否相同 mov rsi,[rbp+8H+10H] mov rdi,r9 cld repz cmpsb ;恢复用到的寄存器 pop rsi pop rdi pop rcx ;相同则跳转(PUSH/POP操作不影响ZF标志) jz @GetAddr ;检查是否遍历导出名称表 cmp r8,rcx jb @@Exit inc rcx jmp @@Loop @@Exit: jmp @Exit @GetAddr: ;获取地址名称表RVA xor rax,rax mov eax,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).AddressOfNameOrdinals add rax,[rbp+8H+8H] ;获取函数名索引 xor r8,r8 mov r8w,WORD PTR [rax+rcx*2] ;获取地址表RVA xor rax,rax mov eax,(IMAGE_EXPORT_DIRECTORY PTR [rdi]).AddressOfFunctions add rax,[rbp+8H+8H] ;获取函数名RVA mov eax,DWORD PTR [rax+r8*4] ;与基地址相加得到函数地址 add rax,[rbp+8H+8H] jmp @Exit @Exit: ;释放为函数调用分配的堆栈 add rsp, 40H ;恢复所有非易失寄存器 pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop rsi pop rdi pop rdx pop rbx ;恢复堆栈栈顶指针 pop rbp ;返回 ret GetProcAddressEx ENDP ;-------------------------------------------------------------------------------- ALIGN 16 ;重新对齐代码 RemoteCode PROC ;设置堆栈栈顶指针 push rbp mov rbp,rsp ;分配其他局部变量 ;保存所有非易失寄存器 push rbx push rdx push rdi push rsi push r10 push r11 push r12 push r13 push r14 push r15 ;为函数调用分配足够堆栈 ;注意必须确保堆栈按16字节对齐 sub rsp, 40H @ENTRY: call @RELOC @RELOC: ;取得@RELOC编译地址,因为OFFSET伪指令无法使用 mov rax,$ ;弹出@ENTRY实际地址到RBX寄存器 pop rbx ;计算@ENTRY地址实际地址与编译地址的差,用来矫正重定位数据 sub rbx,rax ;获得NTDLL模块句柄 mov rax,gs:[60H] ;指向 PEB 结构的指针 mov rax,[rax+18H] ;指向 PEB_LDR_DATA 结构的指针 mov rax,[rax+30H] ;指向 InInitializationOrderModuleList 结构的指针 mov rax,[rax+10H] ;指向NTDLL模块基地址 ;_LDR_DATA_TABLE_ENTRY.InInitializationOrderLinks与 ;_LDR_DATA_TABLE_ENTRY.DllBase的偏移差是10H mov [rbx+hNtDll],rax ;获取LdrLoadDll函数地址 mov rcx,[rbx+hNtDll] lea rdx,[rbx+szLdrLoadDll] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpLdrLoadDll],rax ;获取lpLdrUnloadDll函数地址 mov rcx,[rbx+hNtDll] lea rdx,[rbx+szLdrUnLoadDll] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpLdrUnloadDll],rax ;获取RtlInitAnsiString函数地址 mov rcx,[rbx+hNtDll] lea rdx,[rbx+szRtlInitAnsiString] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpRtlInitAnsiString],rax ;获取RtlAnsiStringToUnicodeString函数地址 mov rcx,[rbx+hNtDll] lea rdx,[rbx+szRtlAnsiStringToUnicodeString] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpRtlAnsiStringToUnicodeString],rax ;获取RtlFreeUnicodeString函数地址 mov rcx,[rbx+hNtDll] lea rdx,[rbx+szRtlFreeUnicodeString] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpRtlFreeUnicodeString],rax ;初始化USER32路径字符串 lea rcx,[rbx+stAnUser32] lea rdx,[rbx+szUser32] call [rbx+lpRtlInitAnsiString] lea rcx,[rbx+stUnUser32] lea rdx,[rbx+stAnUser32] mov r8,TRUE call [rbx+lpRtlAnsiStringToUnicodeString] ;加载USER32.DLL xor rcx,rcx xor rdx,rdx lea r8,[rbx+stUnUser32] lea r9,[rbx+hUser32] call [rbx+lpLdrLoadDll] test rax,rax jnz @ERROR ;获取MessageBoxA函数地址 mov rcx,[rbx+hUser32] lea rdx,[rbx+szMessageBoxA] call GetProcAddressEx test rax,rax jz @ERROR mov [rbx+lpMessageBoxA],rax xor rcx,rcx lea rdx,[rbx+szInformation] lea r8,[rbx+szInfo] mov r9,MB_OK or MB_ICONINFORMATION call [rbx+lpMessageBoxA] ;释放USER32.DLL mov rcx,[rbx+hUser32] call [rbx+lpLdrUnloadDll] ;释放UNICODE字符串 lea rcx,[rbx+stUnUser32] call [rbx+lpRtlFreeUnicodeString] @ERROR: ;释放为调用函数分配的堆栈 add rsp, 40H ;恢复所有非易失寄存器 pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop rsi pop rdi pop rdx pop rbx ;恢复堆栈栈顶指针 pop rbp ;返回 xor rax,rax ret RemoteCode ENDP REMOTE_CODE_END: ;-------------------------------------------------------------------------------- ;程序入口点 Entry PROC ;分配堆栈 sub rsp,28H ;申请内存 xor rcx,rcx ;内存起始地址由系统分配 mov rdx,4094H ;1024字节 mov r8,MEM_COMMIT ;内存已提交 mov r9,PAGE_EXECUTE_READWRITE ;内存可读可写可执行 call VirtualAlloc jz @EXIT mov lpRemoteCode,rax ;取得代码长度 mov rax,offset REMOTE_CODE_END mov rcx,OFFSET REMOTE_CODE_START sub rax,rcx mov cbRemoteCode,rax ;搬迁代码 cld lea rsi,REMOTE_CODE_START ;发送缓冲区(源) mov rdi,lpRemoteCode ;接收缓冲区(目标) mov rcx,cbRemoteCode ;传送数量 rep movsb ;写入数据 call lpRemoteCode ;释放内存 mov rcx,lpRemoteCode xor rdx,rdx mov r8,MEM_RELEASE call VirtualFree @EXIT: ;释放堆栈 add rsp,28H ;退出 ret Entry ENDP ;-------------------------------------------------------------------------------- ;程序结束 END 代码注释很详细,我不再赘述! [-]反汇编引擎 原先本来准备写个针对x64的反汇编引擎,后来发现国外的一个组织已经对此做了研究,并且提供了一个相当完善的引擎,因此我们就直接拿来用吧!在附件里我会 给大家上传这个完善的引擎!希望方便大家使用!这个引擎的名字叫做:BeaEngine 3.1 。目前没有开源,只是提供了一个叫做Bean64.dll的核心支持库文件和一个叫 做bean.inc的定义文件! 如果对指令编码或者引擎有兴趣的朋友建议研究下开源的AMD x64调试器fdbg里面的发汇编引擎(http://www.x64asm.com/Download.html这里可以下载到). 下面我们直接用bean组织提供的反汇编引擎来写个demo,来一起看看它的使用方法。 ;; ;;author:chx4[Www.X64asm.Com] ;;date:2010.4.8 ;;module:x64 Length Disasmly Enginer ;;test! ;; ;;BUILD IT:ml64 bean.ASM /link /subsystem:console /LARGEADDRESSAWARE:NO /defaultlib:user32.lib /defaultlib:kernel32.lib /entry:_start /section:.text,RWE include bean.inc extrn GetStdHandle:proc extrn WriteFile:proc extrn LoadLibraryA:proc extrn GetProcAddress:proc .data mydis _Disasm <> errmsg db 'read unreadable mem!so fuck!',0 count dd 50 _crlf db ' ',0Dh,0Ah,0 .code _strlen: ;;一个参数 ;;in- ;;r12d:POINT to string ;;out- ;;eax:0 or string len ;; xor rax,rax mov edx,r12d test edx,edx jz _out dec eax dec edx _char: inc edx inc eax cmp BYTE ptr [edx],0 jne _char _out: ret _strlen_end: _stdout: ;;一个参数,指向字符串的指针 ;;in- ;;r13d:POINTto string ;; sub rsp,28h mov rcx,-11 call GetStdHandle mov ebx,eax mov esi,r13d mov edi,esi cld _strlen_: lodsb or al,al jnz _strlen_ sub esi,r13d dec esi sub rsp,08h xor rax,rax mov [rsp+20h],rax mov r9d,esp mov r8d,esi mov edx,edi mov ecx,ebx call WriteFile add rsp,08h add rsp,28h ret _start proc sub rsp,28h jmp _begin __disasm dq ? _begin: call _dll db 'bean64.dll',0 _dll: pop rcx call LoadLibraryA mov rcx,rax call _func db 'Disasm',0 _func: pop rdx call GetProcAddress mov __disasm,rax ;;初始化eip mov rax,_strlen mov mydis.Eip,rax ;;初始化架构平台 mov mydis.Archi,64 ;;循环开始反汇编 _dis: lea rcx,mydis call __disasm or eax,eax jne _next lea rcx,errmsg call _stdout add rsp,28h ret _next: inc eax jne _dis_ inc mydis.EIP jmp _display _dis_: dec rax add mydis.EIP,rax _display: lea r13d,mydis.CompleteInstr call _stdout lea r13d,_crlf call _stdout dec count jne _dis _ret: add rsp,28h ret _start endp end 那么基本上到这里大家应该知道如何的使用这个反汇编引擎了! [+]x64平台特殊环境介绍 说起x64,实际上从系统的角度来看,只是提供的权限和应用的区别,总体而言是优良的,从cpu的角度来看的话,处理能力更加强了! 从病毒的角度来看,感染文件依然是可行的,但是新增的安全机制对我们提出了先的挑战,我们需要做更深入的研究才能得以突破! 下面我就我所研究的领域简单的做个总结! 1.64位默认的地址尺寸是64位,默认的操作数尺寸是32位 【保护模式下,Default Operand-Size 依赖于当前 code segment-descriptor 的 D 位,也就是 CS.D(code segment register's D)。 当 CS.D = 1 时,Default Operand-Size 是 32 位的,CS.D = 0 时,Default Operand-Size 是 16 位的。】 2.x64下面的操作方式成为长方式,长方8式分为64位方式和兼容方式。长方式不支持传统的实方式和虚拟86方式,也不支持硬件任务切换 3.x64下面的标志寄存器为RFLAGS,高32位保留,低32位与EFLAGS相同 4.x64下面的堆栈尺寸不由在SS描述符中的一位控制,也不能由指令前缀超越 5.GDTR IDTR TR LDTR 扩展至10字节,所以可以保持全64位基地址 6.64位方式下面,位移量和立即数不扩展至64位,在计算期间的话,符号扩展至64位,但是对mov指令,特别的提供了64位的偏移量和立即数 7.在64方式,一条指令不能同时访问传统高字节(AH,BH,CH,DH)和新字节寄存器之一(比如RAX的低字节)。但是可以同时引用传统的低字节 (AL,BL,CL,DL)和新的字节寄存器(比如R8和RBP的低字节)。当有REX前缀的应用时,会改变高字节引用为低字节引用 8.对于CS DS ES SS FS GS这些段寄存器,在64位方式下也是要做一些传统的检查的,只是一些,而不是全部,一位有的检查在64方式下是行不通 的(比如界限检查时禁止的)因为做了这些检查的话可以供兼容模式下面使用。 9.64下面多了一种寻址方式,就是RIP相对寻址技术。就是RIP+位移量指到下一条指令的RIP 10.REX.W优先于66h前缀 【REX prefix字节的组成部分如下: 0 1 0 0 0 0 0 0 - - - - W R X B ● bit0:REX.B 大家可以看到是一些基础的总结,但是不要小看这些基础的知识,实际上了解了这些的话,基本可以着手来研究x64平台的病毒! ● bit1:REX.X ● bit2:REX.R ● bit3:REX.W ● bit4 ~ bit7:此域固定为 0100,也就是高半字节为 4。 -------------------------------------------------------------------------------- REX.W 用来打开 64 位访问能力,REX.W = 1 时,操作数是 64 位, REX.W = 0 时,操作数是 Default Operand-Size 】 [+]其他 [-]总结 由于本人对于x64也是在研究之中,所以文章中难免有纰漏的地方,希望各位不吝指出。 总之我想说的是,对于x64的病毒技术我们需要做的大概有四方面: 1.相关基础知识的夯实 2.新增安全机制的突破 3.代码优化的突破 4.传统技术的完善 希望各位同仁能与我一起在此深入的探讨和研究! [-]参考文献 《指令编码内幕》 作者:mik Intel官方文档 rgb关于x64病毒的研究 旖旎先生所作的研究(www.x64asm.com) [-]感谢 感谢旖旎,感谢rgb 相关代码及fdbg的下载见--代码&&工具中 http://www.vxjump.net/files/a_page/4/code1.htm 开源的x64反汇编引擎在代码&&工具提供,希望大家能把这个开源的引擎不断的完善,并分享给vxjump的朋友们。