KMSRussian 发表于 2012-8-31 03:05:40

Shellcode In X64-2Search Function using hash

1 关于WinExec-run-calc X64

关于X64的可编译完整工程,我用谷歌没有找到,检索到的网址里面感觉这两个不错
/////http://mcdermottcybersecurity.com/articles/windows-x64-shellcode
/////http://code.google.com/p/win-exec-calc-shellcode/downloads/list
推荐下
xfish帖子地址附带了hash计算器
http://bbs.pediy.com/showthread.php?t=86216
我在这里放一个fasm的完整可编译工程,把xfish的那份32位的WinExec run cmd
改成了64位的WinExec run calc 代码写的有点罗嗦,加入nop,然后用IDA或者其他工具
提取机器码的那个纯体力活我就不做了,只放工程和讲解,这份东西花了我大概四天的时间。
一天半学习fasm,剩下几天复习PE结构和做其他的。好了,我们开始。
   
                         2 fasm的一些基础知识

网上有一份1.67的fasm中文手册,可以供你参考。下面是一个fasm hello world 的例子 你可以从中
感受下fasm的语法和关键字,以及64位ASM编程。


//////////////////////////////////////////
format PE64 GUI

    include 'win64a.inc'

entry start

section '.text' code readable executable

   start:
   subrsp,8*5   ; reserve stack for API use and make stack dqword aligned

   movr9d,0
   lear8,
   leardx,
   movrcx,0
   call

   movecx,eax
   call

section '.data' data readable writeable

   _caption db 'Win64 assembly program',0
   _message db 'Hello World!',0

      section '.import' import data readable writeable
      library kernel32, 'kernel32.dll',\
    user32, 'user32.dll'
      include 'api\kernel32.inc'
      include 'api\user32.inc'
//////////////////////////////////////////////

直接复制粘贴到fasm里面就可以编译通过生成一个hello world,通过这个
例子你可以亲身感受下fasm,利于我们接下来的学习。

                     3shellcode工程注解

xfish的那份代码,基本没怎么讲解,也没怎么注释,看起来的确有点难度,
下面的代码,注释比较详细。代码既不精简,也不苗条,仅作为测试Demo进行
讲解
完整的工程代码如下,直接可以复制粘贴,编译运行。

///////////////////////////////////////////////////



format PE64 CONSOLE


macro .text {section '.text' code readable executable writeable}
macro .code {section '.code' code readable executable }
macro .data {section '.data' data readable writeable }

entry__Entry
include 'win64axp.inc'

.text

__Entry:
nop
nop
nop
nop
      call    GetKrnlBase3   
      push 016EF74Bh; Hash WinExec    32
      push rsi   ;kernel32 module   64
      call    GetApi

    mov rdx,5
    lea rcx,
   callrax
      ;   ret
      
   ;由于GetApi是我们自己实现的函数
   ;我们不一定非得 r9 r8 rdx rcx
   ;对齐是10h+8堆栈对齐
      
    ;      xor r9, r9

   ;       cinvoke getch
    invoke ExitProcess,0
;   moveax,
;   moveax, ;Get _PEB_LDR_DATA
;   moveax, ;Get InInitializationOrderModuleList.Flink,
    ;此时eax指向的是ntdll模块的InInitializationOrderModuleList线性地址。所以我们获得它的下一个则是

kernel32.dll
;   moveax,
;moveax,
;ret   
; add 3
   struct IMAGE_EXPORT_DIRECTORY
    Characteristics       dd      ?;未使用
    TimeDateStamp       dd      ?;文件生成时间
    MajorVersion      dw      ?;主版本号,一般为0
    MinorVersion      dw      ?;次版本号,一般为0
    nName         dd      ?;模块的真实名称
    nBase         dd      ?;基数, 加上序数就是函数地址数组的索引值
    NumberOfFunctions       dd      ?;AddressOfFunctions阵列的元素个数
    NumberOfNames       dd      ?;AddressOfNames阵列的元素个数
    AddressOfFunctions       dd      ?;指向函数地址数组
    AddressOfNames       dd      ?;函数名字的指针地址
    AddressOfNameOrdinals   dd      ?;指向输出序列号数组
ends

;++
;
; int
;GetApi(
;   IN HINSTANCE hModule,
;   IN int      iHashApi,   
;   )
;
; Routine Description:
;
;    获取指定函数的内存地址
;
; Arguments:
;
;    (esp)          - return address
;
;    Data   (esp+4) - hDllHandle
;         (esp+8) - nReason
; Return Value:
;
;    eax -> Function Mem Address。
;
;--

GetApi:
pop rdx; save return addr
pop rax; hModule kernel32.dll基地址
pop rcx; lpApiString 在这里是Hash32位的

push rdx ;return addr 再次入栈 保存返回地址 栈中只有rdx
;各类寄存器依次入站pushad
push rax
push rcx
push rdx
push rbx
push rsp
push rbp
push rsi
push rdi


mov ebx, eax; hModule rbx
mov edi, ecx; hashapi rdi

mov eax, ; 此时rax为e_lfanew的值 保存了PE头文件
;的偏移位置
mov esi, ;数据目录表的第一个成员 保存了_IMAGE_EXPORT_D的RVA 32位下是78h
lea esi, ;esi==numberofnames的内存地址
cld   
lodsd; mov eax,esi=esi+4
xchgeax,edx ;edx= NumberOfNames==有名字的函数的数量
lodsd ;mov eax, esi=esi+4eax此时是eat的rva 增加之后的对应AddressOfNames
pushrax;=AddressOfFunctionsEAT的RVA 此时栈中有返回地址 pushadEAT的RVA
lodsd ;mov eax, esi=esi+4增加之后的对应AddressOfNameOrdinals
xchgeax,ebp;此时ebp 是ENT 的rva
lodsd;mov eax, esi=esi+4   没有增加之前的对应AddressOfNameOrdinals
xchg eax,ebp;
;ebp = eot的rva, eax = ent的rva
;ebx此时是hModule
addeax,ebx; 此时eax是ENT的内存地址
;ebx此时是hModule
xchg eax,esi; 此时rsi是指向ent的内存地址VA=IB+RVA
;其中大家最 为关注的输入表、导出表、
;重定位表、资源的结构体跟 PE32 一样,没有发生任何变化。

.LoopScas:
dec edx ;edx= 有名字的函数的数量
jz.Ret;.Ret先没写
lodsd ;mov eax,esi=esi+4 此时eax是ent的内容
;内容也就是各个ASCII字符串的RVA
add eax,ebx ;IB+字符串的rva得到ASCII字符串的内存地址
;rbx此时是hModule
push rdx; rdx= NumberOfNames-1 做递减器 保存好 以免寄存器改变
;殃及递减器
;此时栈中有返回地址EAT的RVA 有名字的函数数量
;;全是64位的寄存器
push rax; rax==函数名字的内存地址 也就是ASCII字符串的内存地址
   ;此时栈中有返回地址EAT的RVA 有名字的函数数量函数名字的内存地址
   ;全是64位的寄存器
call GetRolHash
;此时edi是hash
;eax存贮了计算之后所得到的hash
;此时栈中有getapi的返回地址EAT的RVA 有名字的函数数量
pop rdx; rdx= NumberOfNames-1 做递减器 保存好 以免寄存器改变
; ;此时栈中有getapi的返回地址EAT的RVA
;rsi 下个函数名字的rva
cmp eax,edi
jz .GetAddr
;ebp = AddressOfNameOrdinals,
addebp, 2
;ebp = eot的rva
;ebp = AddressOfNameOrdinals 的rva
jmp.LoopScas


;从 AddressOfNames 字段指向得到的函数名称地址表的第一项开始,
;在循环中将每一项定义的函数名与要查找的函数名相比较,
;如果没有任何一个函数名是符合的,表示文件中没有指定名称的函数
;如果某一项定义的函数名与要查找的函数名符合,
;那么记下这个函数名在字符串地址表中的索引
;值,然后在 AddressOfNamesOrdinals 指向的
;数组中以同样的索引值取出数组项的值,我们这里假设这个值是x
;最后,以 x 值作为索引值,在 AddressOfFunctions
;字段指向的函数入口地址表中获取的
;RVA 就是函数的入口地址。

;简单说是:查找AddressOfNames ,对应到a项,取AddressOfNamesOrdinals
;的第a项的值得到b,取AddressOfFunctions 的第b项


   ;rbx此时是hModule
    ;ebp = AddressOfNameOrdinals 的rva
    ;指向另一个word 类型的数组(注意不是双字数组)
   ; ===AddressOfFunctions也就是EAT的rva
    ; ;此时栈中有getapi的返回地址EAT的RVA
.GetAddr:
   xorrax,rax
   movzx eax,word ;   00004000h
   shleax,2
   addeax,
   moveax,;得到了函数的RVA地址
   addeax,ebx
      ; ;此时栈中有getapi的返回地址 pushad EAT的RVA
.Ret:

   pop rcx ;
   mov ,rax
;       popad
pop rdi
pop rsi
pop rbp
pop rsp
pop rbx
pop rdx
pop rcx
pop rax
   ret
   ;EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX按照这些指令出栈可能会覆盖寄存器的值
;必须mov ,eax
   
   
GetKrnlBase3:
   mov rsi,    ;peb from teb
   mov rsi,     ;_peb_ldr_data from peb
   mov rsi,    ;InInitializationOrderModuleList.Flink,
   ;rsi==00232c90 00000000
   mov rsi, ;kernelbase.dll
   ;rsi=00232b20 00000000
   mov rsi,       ;kernel32.dll

       mov rsi, ; pay attention to danwei
   ret




GetRolHash:
pop rcx;返回地址
pop rax;函数名字内存地址
push rcx;压入返回地址
push rsi;下个函数名字的rva下个ent的内容 也就是下个函数名字的rva
;此时栈中有getapi的返回地址EAT的RVA 有名字的函数数量
;GetRolHash返回地址 下个函数名字的rva
xor rdx,rdx
xchg eax,esi ;rsi=第一个函数的名字的内存地址
;eax==下个函数名的RVA
cld

.Next:
lodsb ; mov al, si=si+1
test al,al ;按位与测试直到函数最后一个0字符
jz .Ret
rol edx,3
xor dl,al ;
jmp .Next

.Ret:
xchg eax,edx ;此时eax存储了hash
pop rsi ;下个函数名字的rva
ret ;pop rcx 返回地址 正好堆栈平衡


.data
;type                   db "%I64x",0
;hello_msg      db 0Dh,0Ah
calcdb "calc.exe " ,0
show db 'SW_SHOW'
section '.idata' import data readable writable

   library kernel,'KERNEL32.DLL'

   import kernel,\
    ExitProcess,'ExitProcess'

;szCaptiondb 'test',0
;
;   section '.import' import data readable writeable
;   library kernel32, 'kernel32.dll',user32, 'user32.dll'
;   include 'api\kernel32.inc'
;   include 'api\user32.inc'

///////////////////////////////////////////////

注释的很详细了 需要说明的有这么几点
1堆栈平衡是重重之重,如果你记不住,就在每一行代码后面加好注释
搞清楚这个时候堆栈里面都还有些什么
2PE32+改变比较大的也就是NT头变成了IMAGE_NT_HEADERS64,使一些
偏移发生了变化,可以自行dt查看
3导出表没有发生变化
4导出表的后三个成员一定要好好看看,这是看懂代码的关键所在。


                            4对call WinExec的说明

最后的代码我是这样写的
    mov rdx,5
    lea rcx,
    callrax
实际上googlecode上面的那个作者也是用的rdx rcx传递的参数的,他的代码是这样的:
   PUSH    B2DW('c', 'a', 'l', 'c')      ; Stack = "calc", 0
   PUSH    RSP
   POP   RCX                           ; RCX = &("calc")
   PUSH    RCX                           ; WinExec messes with stack -
   CDQ                                 ; RDX = 0
   CALL    RDI                           ; WinExec(&("calc"), 0);
你可能问,用r9\r8传递参数不可以吗,我用r9 r8传递参数结果程序crash。我们写个简单的小程序,使用
WinExec调用calc,IDA中显示这样的结果
; int __cdecl main(int argc, const char **argv, const char **envp)
main proc near
sub   rsp, 28h
lea   rcx, CmdLine    ; "calc.exe"
mov   edx, 5          ; uCmdShow
call    cs:__imp_WinExec
xor   eax, eax
add   rsp, 28h
retn
调用WinExec的时候 windows本身就用的是rcx\rdx传递的参数,我们也还是老老实实的用rdx\rcx传递参数吧

Fasm 完整可编译工程

watchsky 发表于 2012-8-31 09:49:49

WinExec与PE有什么联系?表示没看懂

KMSRussian 发表于 2012-8-31 09:58:47

1get kernel32 addr
2find winexec addr in kernel32 export table (pay attension to pe32+ struct)
3call winexec using rdx rcx

kk1025 发表于 2013-4-11 18:13:34

精華就一定要頂
页: [1]
查看完整版本: Shellcode In X64-2Search Function using hash