Skip to content

Commit b2e84d8

Browse files
committed
Merge branch 'issue_28' into dev
2 parents e58a788 + 087e315 commit b2e84d8

File tree

6 files changed

+283
-228
lines changed

6 files changed

+283
-228
lines changed

commands/exec_windows.go

Lines changed: 110 additions & 225 deletions
Large diffs are not rendered by default.

commands/shellcode.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func ExecuteShellcode(cmd jobs.Shellcode) jobs.Results {
4343
return results
4444
}
4545

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

4949
switch cmd.Method {
5050
case "self":

core/core.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var Verbose = false
2828
var Debug = false
2929

3030
// Version is the Merlin Agent's version number
31-
var Version = "1.6.3"
31+
var Version = "1.6.5"
3232

3333
// Mutex is used to ensure exclusive access to STDOUT & STDERR
3434
var Mutex = &sync.Mutex{}

docs/CHANGELOG.MD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## 1.6.5 - 2023-06-10
8+
9+
### Changed
10+
11+
- Replaced manual Windows DLL and procedure loads for Golang's Windows package and moved remaining to `os/windows/api` directory
12+
- Replaced `PAGE_EXECUTE_READWRITE` with `PAGE_READWRITE` for shellcode memory allocation
13+
- Replaced `PAGE_EXECUTE` with `PAGE_EXECUTE_READ` after shellcode memory allocation
14+
15+
### Fixed
16+
17+
- [Issue 28](https://github.com/Ne0nd0g/merlin-agent/issues/28) - Use Golang's Windows package for API calls where possible
18+
719
## 1.6.4 - 2023-06-08
820

921
### Changed

os/windows/api/kernel32/kernel32.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,82 @@
1919
// along with Merlin. If not, see <http://www.gnu.org/licenses/>.
2020

2121
package kernel32
22+
23+
import (
24+
// Standard
25+
"fmt"
26+
27+
// X Packages
28+
"golang.org/x/sys/windows"
29+
)
30+
31+
var kernel32 = windows.NewLazySystemDLL("kernel32.dll")
32+
33+
// CreateRemoteThreadEx Creates a thread that runs in the virtual address space of another process and optionally
34+
// specifies extended attributes such as processor group affinity.
35+
// HANDLE CreateRemoteThreadEx(
36+
//
37+
// [in] HANDLE hProcess,
38+
// [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
39+
// [in] SIZE_T dwStackSize,
40+
// [in] LPTHREAD_START_ROUTINE lpStartAddress,
41+
// [in, optional] LPVOID lpParameter,
42+
// [in] DWORD dwCreationFlags,
43+
// [in, optional] LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
44+
// [out, optional] LPDWORD lpThreadId
45+
//
46+
// );
47+
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethreadex
48+
func CreateRemoteThreadEx(hProcess uintptr, lpThreadAttributes uintptr, dwStackSize uintptr, lpStartAddress uintptr, lpParameter uintptr, dwCreationFlags int, lpAttributeList uintptr, lpThreadId uintptr) (addr uintptr, err error) {
49+
createRemoteThreadEx := kernel32.NewProc("CreateRemoteThreadEx")
50+
addr, _, err = createRemoteThreadEx.Call(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uintptr(dwCreationFlags), lpAttributeList, lpThreadId)
51+
if err != windows.Errno(0) {
52+
err = fmt.Errorf("there was an error calling Windows API CreateRemoteThread: %s", err)
53+
} else {
54+
err = nil
55+
}
56+
return
57+
}
58+
59+
// QueueUserAPC Adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.
60+
// DWORD QueueUserAPC(
61+
//
62+
// [in] PAPCFUNC pfnAPC,
63+
// [in] HANDLE hThread,
64+
// [in] ULONG_PTR dwData
65+
//
66+
// );
67+
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-queueuserapc
68+
func QueueUserAPC(pfnAPC uintptr, hThread uintptr, dwData uintptr) (err error) {
69+
queueUserAPC := kernel32.NewProc("QueueUserAPC")
70+
_, _, err = queueUserAPC.Call(pfnAPC, hThread, dwData)
71+
if err != windows.Errno(0) {
72+
err = fmt.Errorf("there was an error calling Windows API QueueUserAPC: %s", err)
73+
} else {
74+
err = nil
75+
}
76+
return
77+
}
78+
79+
// VirtualAllocEx Reserves, commits, or changes the state of a region of memory within the virtual address space of a
80+
// specified process. The function initializes the memory it allocates to zero.
81+
//
82+
// LPVOID VirtualAllocEx(
83+
// [in] HANDLE hProcess,
84+
// [in, optional] LPVOID lpAddress,
85+
// [in] SIZE_T dwSize,
86+
// [in] DWORD flAllocationType,
87+
// [in] DWORD flProtect
88+
// );
89+
//
90+
// https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
91+
func VirtualAllocEx(hProcess uintptr, lpAddress uintptr, dwSize int, flAllocationType int, flProtect int) (addr uintptr, err error) {
92+
virtualAllocEx := kernel32.NewProc("VirtualAllocEx")
93+
addr, _, err = virtualAllocEx.Call(hProcess, lpAddress, uintptr(dwSize), uintptr(flAllocationType), uintptr(flProtect))
94+
if err != windows.Errno(0) {
95+
err = fmt.Errorf("there was an error calling Windows API VirtualAllocEx: %s", err)
96+
} else {
97+
err = nil
98+
}
99+
return
100+
}

os/windows/api/ntdll/ntdll.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//go:build windows
2+
// +build windows
3+
4+
// Merlin is a post-exploitation command and control framework.
5+
// This file is part of Merlin.
6+
// Copyright (C) 2022 Russel Van Tuyl
7+
8+
// Merlin is free software: you can redistribute it and/or modify
9+
// it under the terms of the GNU General Public License as published by
10+
// the Free Software Foundation, either version 3 of the License, or
11+
// any later version.
12+
13+
// Merlin is distributed in the hope that it will be useful,
14+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
// GNU General Public License for more details.
17+
18+
// You should have received a copy of the GNU General Public License
19+
// along with Merlin. If not, see <http://www.gnu.org/licenses/>.
20+
21+
package ntdll
22+
23+
import (
24+
// Standard
25+
"fmt"
26+
27+
// X Packages
28+
"golang.org/x/sys/windows"
29+
)
30+
31+
var ntdll = windows.NewLazySystemDLL("ntdll.dll")
32+
33+
// RtlCopyMemory routine copies the contents of a source memory block to a destination memory block
34+
// void RtlCopyMemory(
35+
//
36+
// void* Destination,
37+
// const void* Source,
38+
// size_t Length
39+
//
40+
// );
41+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlcopymemory
42+
func RtlCopyMemory(dest uintptr, src uintptr, len uint32) (err error) {
43+
rtlCopyMemory := ntdll.NewProc("RtlCopyMemory")
44+
_, _, err = rtlCopyMemory.Call(dest, src, uintptr(len))
45+
if err != windows.Errno(0) {
46+
err = fmt.Errorf("there was an error calling Windows RtlCopyMemory function: %s", err)
47+
} else {
48+
err = nil
49+
}
50+
return
51+
}
52+
53+
// RtlCreateUserThread
54+
//
55+
// NTSTATUS
56+
// RtlCreateUserThread(
57+
// IN HANDLE Process,
58+
// IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL,
59+
// IN BOOLEAN CreateSuspended,
60+
// IN ULONG ZeroBits OPTIONAL,
61+
// IN SIZE_T MaximumStackSize OPTIONAL,
62+
// IN SIZE_T CommittedStackSize OPTIONAL,
63+
// IN PUSER_THREAD_START_ROUTINE StartAddress,
64+
// IN PVOID Parameter OPTIONAL,
65+
// OUT PHANDLE Thread OPTIONAL,
66+
// OUT PCLIENT_ID ClientId OPTIONAL
67+
// );
68+
//
69+
// https://doxygen.reactos.org/da/d0c/sdk_2lib_2rtl_2thread_8c.html#ae5f514e4fcb7d47880171175e88aa205
70+
func RtlCreateUserThread(hProcess uintptr, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient uintptr) (addr uintptr, err error) {
71+
rtlCreateUserThread := ntdll.NewProc("RtlCreateUserThread")
72+
addr, _, err = rtlCreateUserThread.Call(hProcess, lpSecurityDescriptor, bSuspended, zeroBits, maxStack, commitSize, lpStartAddress, pParam, hThread, pClient)
73+
if err != windows.Errno(0) {
74+
err = fmt.Errorf("there was an error calling Windows RtlCreateUserThread function: %s", err)
75+
} else {
76+
err = nil
77+
}
78+
return
79+
}

0 commit comments

Comments
 (0)