Skip to content

Commit

Permalink
Merge branch 'issue_28' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Ne0nd0g committed Jun 10, 2023
2 parents e58a788 + 087e315 commit b2e84d8
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 228 deletions.
335 changes: 110 additions & 225 deletions commands/exec_windows.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions commands/shellcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func ExecuteShellcode(cmd jobs.Shellcode) jobs.Results {
return results
}

cli.Message(cli.INFO, fmt.Sprintf("Shelcode execution method: %s", cmd.Method))
cli.Message(cli.INFO, fmt.Sprintf("Executing shellcode %x", shellcodeBytes))
cli.Message(cli.INFO, fmt.Sprintf("Shelcode execution method: %s, size: %d", cmd.Method, len(shellcodeBytes)))
cli.Message(cli.DEBUG, fmt.Sprintf("Shellcode %x", shellcodeBytes))

switch cmd.Method {
case "self":
Expand Down
2 changes: 1 addition & 1 deletion core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var Verbose = false
var Debug = false

// Version is the Merlin Agent's version number
var Version = "1.6.3"
var Version = "1.6.5"

// Mutex is used to ensure exclusive access to STDOUT & STDERR
var Mutex = &sync.Mutex{}
12 changes: 12 additions & 0 deletions docs/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## 1.6.5 - 2023-06-10

### Changed

- Replaced manual Windows DLL and procedure loads for Golang's Windows package and moved remaining to `os/windows/api` directory
- Replaced `PAGE_EXECUTE_READWRITE` with `PAGE_READWRITE` for shellcode memory allocation
- Replaced `PAGE_EXECUTE` with `PAGE_EXECUTE_READ` after shellcode memory allocation

### Fixed

- [Issue 28](https://github.com/Ne0nd0g/merlin-agent/issues/28) - Use Golang's Windows package for API calls where possible

## 1.6.4 - 2023-06-08

### Changed
Expand Down
79 changes: 79 additions & 0 deletions os/windows/api/kernel32/kernel32.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,82 @@
// along with Merlin. If not, see <http://www.gnu.org/licenses/>.

package kernel32

import (
// Standard
"fmt"

// X Packages
"golang.org/x/sys/windows"
)

var kernel32 = windows.NewLazySystemDLL("kernel32.dll")

// CreateRemoteThreadEx Creates a thread that runs in the virtual address space of another process and optionally
// specifies extended attributes such as processor group affinity.
// HANDLE CreateRemoteThreadEx(
//
// [in] HANDLE hProcess,
// [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
// [in] SIZE_T dwStackSize,
// [in] LPTHREAD_START_ROUTINE lpStartAddress,
// [in, optional] LPVOID lpParameter,
// [in] DWORD dwCreationFlags,
// [in, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
// [out, optional] LPDWORD lpThreadId
//
// );
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethreadex
func CreateRemoteThreadEx(hProcess uintptr, lpThreadAttributes uintptr, dwStackSize uintptr, lpStartAddress uintptr, lpParameter uintptr, dwCreationFlags int, lpAttributeList uintptr, lpThreadId uintptr) (addr uintptr, err error) {
createRemoteThreadEx := kernel32.NewProc("CreateRemoteThreadEx")
addr, _, err = createRemoteThreadEx.Call(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uintptr(dwCreationFlags), lpAttributeList, lpThreadId)
if err != windows.Errno(0) {
err = fmt.Errorf("there was an error calling Windows API CreateRemoteThread: %s", err)
} else {
err = nil
}
return
}

// QueueUserAPC Adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.
// DWORD QueueUserAPC(
//
// [in] PAPCFUNC pfnAPC,
// [in] HANDLE hThread,
// [in] ULONG_PTR dwData
//
// );
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
func QueueUserAPC(pfnAPC uintptr, hThread uintptr, dwData uintptr) (err error) {
queueUserAPC := kernel32.NewProc("QueueUserAPC")
_, _, err = queueUserAPC.Call(pfnAPC, hThread, dwData)
if err != windows.Errno(0) {
err = fmt.Errorf("there was an error calling Windows API QueueUserAPC: %s", err)
} else {
err = nil
}
return
}

// VirtualAllocEx Reserves, commits, or changes the state of a region of memory within the virtual address space of a
// specified process. The function initializes the memory it allocates to zero.
//
// LPVOID VirtualAllocEx(
// [in] HANDLE hProcess,
// [in, optional] LPVOID lpAddress,
// [in] SIZE_T dwSize,
// [in] DWORD flAllocationType,
// [in] DWORD flProtect
// );
//
// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
func VirtualAllocEx(hProcess uintptr, lpAddress uintptr, dwSize int, flAllocationType int, flProtect int) (addr uintptr, err error) {
virtualAllocEx := kernel32.NewProc("VirtualAllocEx")
addr, _, err = virtualAllocEx.Call(hProcess, lpAddress, uintptr(dwSize), uintptr(flAllocationType), uintptr(flProtect))
if err != windows.Errno(0) {
err = fmt.Errorf("there was an error calling Windows API VirtualAllocEx: %s", err)
} else {
err = nil
}
return
}
79 changes: 79 additions & 0 deletions os/windows/api/ntdll/ntdll.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//go:build windows
// +build windows

// Merlin is a post-exploitation command and control framework.
// This file is part of Merlin.
// Copyright (C) 2022 Russel Van Tuyl

// Merlin is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.

// Merlin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Merlin. If not, see <http://www.gnu.org/licenses/>.

package ntdll

import (
// Standard
"fmt"

// X Packages
"golang.org/x/sys/windows"
)

var ntdll = windows.NewLazySystemDLL("ntdll.dll")

// RtlCopyMemory routine copies the contents of a source memory block to a destination memory block
// void RtlCopyMemory(
//
// void* Destination,
// const void* Source,
// size_t Length
//
// );
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory
func RtlCopyMemory(dest uintptr, src uintptr, len uint32) (err error) {
rtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
_, _, err = rtlCopyMemory.Call(dest, src, uintptr(len))
if err != windows.Errno(0) {
err = fmt.Errorf("there was an error calling Windows RtlCopyMemory function: %s", err)
} else {
err = nil
}
return
}

// RtlCreateUserThread
//
// NTSTATUS
// RtlCreateUserThread(
// IN HANDLE Process,
// IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL,
// IN BOOLEAN CreateSuspended,
// IN ULONG ZeroBits OPTIONAL,
// IN SIZE_T MaximumStackSize OPTIONAL,
// IN SIZE_T CommittedStackSize OPTIONAL,
// IN PUSER_THREAD_START_ROUTINE StartAddress,
// IN PVOID Parameter OPTIONAL,
// OUT PHANDLE Thread OPTIONAL,
// OUT PCLIENT_ID ClientId OPTIONAL
// );
//
// https://doxygen.reactos.org/da/d0c/sdk_2lib_2rtl_2thread_8c.html#ae5f514e4fcb7d47880171175e88aa205
func RtlCreateUserThread(hProcess uintptr, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient uintptr) (addr uintptr, err error) {
rtlCreateUserThread := ntdll.NewProc("RtlCreateUserThread")
addr, _, err = rtlCreateUserThread.Call(hProcess, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient)
if err != windows.Errno(0) {
err = fmt.Errorf("there was an error calling Windows RtlCreateUserThread function: %s", err)
} else {
err = nil
}
return
}

0 comments on commit b2e84d8

Please sign in to comment.