前段时间的Exploit Me挑战赛,使我深刻的感到ShellCode编写完全不同与病毒,对于栈空间得按字节来省着用,ShellCode中hash式函数调用势在必行。另外,在unicode环境下的shell处理,也得过关才行。参照《The Shellcoder's Handbook》 中关于“百叶窗”法,及相关网文资料,自己也写了拆分ShellCode字符串模块,在此一并放出 
   CreateWinAPINameHash功能是获取指定模块中函数名称串的HASH值,并返回Hash的Key值,要计算实际中函数名称串的HASH值只需要调用次过程就可以;ShellCodeToStdByteChar功能是对于已经写好的ShellCode,将其中的非纯数字和字母字节进行拆分,使之转换为字母字节。另外,GetBaseAddressOfKernel32ByTEB与GetKernel32BaseAddressBySearch是两种不同方法获取Kernel32.dll 的基址的子过程,以及GetStringSize用于获取字符串大小的子程序,是顺便送给大家公用子程序,省的自己写 

代码:
.586
.model  flat, stdcall
option  casemap:none

include windows.inc
include kernel32.inc
include user32.inc                                                  
includelib kernel32.lib
includelib user32.lib

    .const
szLoadLibraryA    db  'LoadLibraryA',0
szGetProcAddressA  db  'GetProcAddressA',0
szGetProcAddresAs  db  'GetProcAddresAs',0
szFileName    db  ".\ShellCode.log",0
szkernel32Dll    db  "kernel32.Dll",0

    .data
ppKernel32Win32ApiName  dd  szLoadLibraryA,szGetProcAddressA,szGetProcAddresAs  

    .data?
pKernel32Win32ApiHash  dd  lengthof ppKernel32Win32ApiName dup  (?)

    .code
start:
  call  GetBaseAddressOfKernel32ByTEB

  push  -1
  push  -1
  push  lengthof ppKernel32Win32ApiName
  push  offset pKernel32Win32ApiHash
  push  offset ppKernel32Win32ApiName
  push  eax
  call  CreateWinAPINameHash
  xor  eax, eax
  ret
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;如何编写Unicode形式的ShellCode及过WideCharToMultiByte
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;--------------------------------------CreateWinAPINameHash---------------------------------------------
;函数功能:  获取指定模块中函数名称串的HASH值,并返回Hash的Key值
;入口参数:  hModule    :  被Hash函数所在模块的句柄或基址
;       lppWinApiName  :  函数名称串地址表首地址
;       lpHashCode    :  接收函数名称串被Hash后的缓冲区地址
;       dwNumberString  :  函数名称串的个数
;            dwHashKey          :  可以预设Hash的Key值的范围的上限(-1则为系统寻找合适KEY值)[0--31]
;            dwFilterFlags      :  设置Hash值过滤标志:
;                                  0--不含零字节;1--不含连续的零字节;-1--不过滤
;出口参数:  成功则EAX返回正确Hash的Key值,否则为返回值小于零.           
;-------------------------------------------------------------------------------------------------------
CreateWinAPINameHash  proc  hModule:dword,lppWinApiName:dword,lpHashCode:dword,\
                                dwNumberString:dword,dwHashKey:dword,dwFilterFlags:dword
local  ret_value:dword  
  pushad
  mov  ebx, hModule
  mov  eax, ebx
;********************************************************************
; 从 PE 文件头的数据目录获取导出表地址
;********************************************************************  
  add  eax, dword ptr [ebx + IMAGE_DOS_HEADER.e_lfanew]
  assume  eax : ptr IMAGE_NT_HEADERS
  mov  eax, [eax].OptionalHeader.DataDirectory.VirtualAddress
  add  eax, ebx
  assume  eax : ptr IMAGE_EXPORT_DIRECTORY
;********************************************************************
; 计算函数名的hash值
;********************************************************************
  mov  ecx, dword ptr [eax].NumberOfNames  ;检查是否搜索完毕
  mov  eax, dword ptr [eax].AddressOfNames  ;取出函数名字符串地址表指针(RVA)
  add  eax, ebx        ;eax真实指向函数名字符串地址表首地址
  push  ecx
  push  eax
  shl  ecx, 2
  push  ecx
  push  LMEM_ZEROINIT
  call  LocalAlloc        ;申请临时内存
  pop  esi
  pop  ecx
  test  eax, eax
  je  exit_CreateWinAPINameHash
  mov  edx, eax        ;检测执行结果
  
  mov  eax, dwHashKey
  mov  edi, -1
  cmp  eax, edi
  jne  @f
  mov  al, 32
@@:
  push  eax
  jmp  @f
next_CreateWinAPINameHash:
  push  edi
@@:
  push  ecx
  push  edx
  push  esi
  push  ebx
@@:
  call  HashWinAPIString
  cmp  eax, 0
  jl  @f
  cmp  eax, edi
  je  @f
  xchg  eax, edi
  cmp  edi, eax
  jl  next_CreateWinAPINameHash
  push  edi
  push  dwNumberString
  push  lpHashCode
  push  lppWinApiName
  push  dwFilterFlags
  jmp  @b
@@:
  push  eax
  push  edx
  call  LocalFree      ;释放临时内存
  pop  eax
exit_CreateWinAPINameHash:
  mov  ret_value, eax
  popad
  mov  eax, ret_value
  ret
CreateWinAPINameHash  endp
;--------------------------------------HashWinAPIString-------------------------------------------------
;函数功能:  将函数名称串进行HASH计算
;入口参数:  lpBaseIED    :  被Hash函数所在模块的IMAGE_EXPORT_DIRECTORY指针
;       lpStringTable  :  函数名称串地址表首地址
;       lpHashCode    :  接收函数名称串被Hash后的缓冲区地址
;       dwNumberString  :  函数名称串的个数
;            dwHashKey          :  可以预设Hash的Key值(-1则为系统寻找合适KEY值)[0--31]
;出口参数:  成功则EAX返回正确Hash的Key值,否则为-1.             
;-------------------------------------------------------------------------------------------------------
HashWinAPIString  proc  lpBaseIED:dword,lpStringTable:DWORD,lpHashCode:dword,\
                                dwNumberString:dword,dwHashKey:dword
local  hash_key:byte,string_table_base:dword
  pushad
  mov  eax, dwHashKey
  inc  eax
  test  eax, eax
  jne  @f
  mov  al, 32
@@:
  dec  eax
  mov  hash_key, al
  mov  ebx, lpBaseIED
hash_key_HashWinAPIString:
  mov  edi, lpHashCode
  mov  ecx, dwNumberString
  push  ebx
  push  ecx
  push  edi          ; call CheckSameDwordByte
  dec  ebx
  je  @f
  inc  ebx
  jge  @f
  inc  ebx
@@:
  push  lpStringTable
  pop  string_table_base 
hash_next_HashWinAPIString:
  push  ecx
  xor  eax, eax
  cdq
  mov  esi, string_table_base
  mov  esi, dword ptr [esi]      ;取出函数名字符串地址表第一个单元内容(函数名字符串RVA)
  add  esi, ebx        ;esi真实指向函数名字符串
;----------------------hash--------------------------
@@:
  lodsb
  mov  cl, hash_key
  ror  edx, cl
  add  edx, eax
  test  al, al
  jne  @b
;----------------------------------------------------
  mov  eax, edx
  stosd
  add  string_table_base, 4
  pop  ecx
  loop  hash_next_HashWinAPIString

  call  CheckSameDwordByte
  test  eax, eax
  je  @f
  dec  hash_key
  jge  hash_key_HashWinAPIString
@@:
  popad
  movzx  eax, hash_key
  ret
HashWinAPIString  endp
;--------------------------------------CheckSameDwordByte-----------------------------------------------
;函数功能:  检验HASH值的合法性
;入口参数:  lpBuffer    :  需要检验的数据缓冲区首地址
;       dwLengthOfType  :  被检数据的长度(以字位单位计算DWORD)
;       dwFlags    :  设定检验标志: 0,则检验是否非法ASCII码0;
;                                                1,则检验是否非法Unicode码0;
;                                                -1,则不检验是否非法;
;                                                否则检验是否含重复字.
;出口参数:  成功则EAX返回0, 否则为EAX≠0              
;-------------------------------------------------------------------------------------------------------
CheckSameDwordByte  proc  lpBuffer:dword, dwLengthOfType:dword, dwFlags:dword
  push  ecx
  push  edi
  push  esi
  mov  esi, lpBuffer
  mov  ecx, dwLengthOfType
  cld
  xor  eax, eax
  push  eax
  dec  eax
  cmp  dwFlags, eax
  pop  eax
  je  ret_CheckSameDwordByte
  mov  edi, esi
  cmp  dwFlags, eax
  je  ASCII_CheckSameDwordByte
  cmp  dwFlags, 1
  je  Unicode_CheckSameDwordByte
  dec  ecx
@@:
  push  ecx
  lodsd
  mov  edi, esi
  repne  scasd
  test  ecx, ecx
  jne  @f
  pop  ecx
  loop  @b
  push  ecx
@@:
  pop  ecx
  jmp  exit_CheckSameDwordByte
ASCII_CheckSameDwordByte:
  shl  ecx, 2
  repne  scasb
  jmp  exit_CheckSameDwordByte
Unicode_CheckSameDwordByte:
  lodsd
  ror  eax, 8
  test  ax, ax
  je  exit_CheckSameDwordByte
  loop  Unicode_CheckSameDwordByte
exit_CheckSameDwordByte:
  mov  eax, ecx
ret_CheckSameDwordByte:
  pop  esi
  pop  edi
  pop  ecx
  ret
CheckSameDwordByte  endp
;----------------------------------ShellCodeToStdByteChar------------------------------
;函数功能:  拆分ShellCode字符串,使之转换为纯数字和字母字符
;入口参数:  lpStdByteCharStr  :  需要处理的ShellCode字符串地址
;       lpShellCodeCharStr :  转换后的接收缓冲区
;出口参数:  EAX返回转换后的字符串长度(BYTE)                  
;--------------------------------------------------------------------------------------
ShellCodeToStdByteChar  proc  lpStdByteCharStr:dword,lpShellCodeCharStr:dword
  push  ebx
  push  ecx
  push  esi
  push  edi

  mov  esi, lpStdByteCharStr
  mov  edi, lpShellCodeCharStr
  push  edi
  xor  eax, eax
  mov  ecx, eax
  dec  ecx
  cld
  repnz  scasb
  not  ecx
  dec  ecx        ; 获取待处理串长度

  mov  ebx, esp
  cld
next_ShellCodeToStdByteChar:
  lodsb
  cmp  al, 30h
  jl  must_ShellCodeToStdByteChar
  cmp  al, 7ah
  jg  must_ShellCodeToStdByteChar
  cmp  al, 61h
  jge  @f
  cmp  al, 5ah
  jg  must_ShellCodeToStdByteChar
  cmp  al, 41h
  jge  @f
  cmp  al, 3ah
  jge  must_ShellCodeToStdByteChar
@@:
  stosb          ; 对数字,字母不需要拆分
  jmp  continue_ShellCodeToStdByteChar
must_ShellCodeToStdByteChar:
  push  eax
@@:
  and  al, 1111b      ; 拆分: 将一个字节的高低四位分别扩展到
  add  al, 41h        ; 两个字节中,加41h后存储
  stosb
  pop  eax
  shr  al, 4
  cmp  ebx, esp
  je  @b
  push  eax
continue_ShellCodeToStdByteChar:
  loop  next_ShellCodeToStdByteChar
  xor  eax, eax
  lodsb          ; 添加串结束符
  pop  eax
  xchg  eax, edi
  sub  eax, edi      ; 统计转换后串长度

  pop  edi
  pop  esi
  pop  ecx
  pop  ebx
  ret
ShellCodeToStdByteChar  endp
;--------------------------------GetBaseAddressOfKernel32ByTEB-----------------------------------
;函数功能:  根据TEB结构定位Kernel32.dll 的基址
;入口参数:  无
;出口参数:  成功则返回Kernel32.dll 的基址在EAX中             
;------------------------------------------------------------------------------------------------
GetBaseAddressOfKernel32ByTEB  proc
  assume  fs:nothing
  xor  eax, eax
  mov  eax, dword ptr fs:[eax+30h]
  mov  eax, dword ptr [eax+0ch]
  mov  eax, dword ptr [eax+1ch]
  mov  eax, dword ptr [eax]
  mov  eax, dword ptr [eax+8]
  ret
GetBaseAddressOfKernel32ByTEB  endp
;-----------------------------------GetKernel32BaseAddressBySearch--------------------------------------
;函数功能:  在内存中扫描 Kernel32.dll 的基址
;入口参数:  lpKernel32SpaceOfAny  :  Kernel32.dll领空内的任一地址
;出口参数:  成功则返回Kernel32.dll 的基址在EAX中,否则为EAX=0                  
;-------------------------------------------------------------------------------------------------------
GetKernel32BaseAddressBySearch    proc  lpKernel32SpaceOfAny : DWORD    
  mov  eax,lpKernel32SpaceOfAny      
  xor  edx,edx
@@:
  dec  eax               ;暴力查找 Kernel32.dll 的基地址
  mov  dx,word ptr [eax+IMAGE_DOS_HEADER.e_lfanew]
  test  dx,0f000h
  jnz  @b
  cmp  eax,[eax+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
  jnz  @b
  ret  4  
GetKernel32BaseAddressBySearch   endp
;--------------------------------------------GetStringSize---------------------------------------------
;函数功能:  用于获取字符串大小的子程序
;入口参数:  lpOutTexts    :  需要检测字符串的地址  
;出口参数:  eax字符串大小
;------------------------------------------------------------------------------------------------------
GetStringSize  proc  lpStringBuffer:dword
  push  ecx
  push  edi
  mov  edi, lpStringBuffer
  xor  eax, eax
  mov  ecx, eax
  dec  ecx
  cld
  repnz  scasb
  not  ecx
  mov  eax, ecx
  dec  eax
  pop  edi
  pop  ecx
  ret
GetStringSize  endp
end  start
        以上个功能模块的使用注释中写的很明白,如同MSDN,在此就不重复说明了。
上传的附件 HashCall v 2.20.rar