Skip to content

Commit 57803fa

Browse files
committed
Ready to go
1 parent 7a2b035 commit 57803fa

File tree

6 files changed

+93
-153
lines changed

6 files changed

+93
-153
lines changed

README.md

+50-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,60 @@
1-
# Shellcoder.py
1+
# Shellcoder.py 🐚⌨️
22

3-
Write your shellcode in Assembly and execute it with one command!
3+
Write your shellcode in Assembly (NASM) and compile it on Windows x64 with one command!
44

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!
5+
This script helps automate the shellcode development and testing process. It takes your Assembly file with the payload (`shellcode.asm`) and generates a bunch of useful executable files (read below).
66

7-
## Usage
7+
You don't have to repeat all these tedious activities anymore to make your shellcode executable! Keep your focus on shellcoding 🔥🐚🔥
8+
9+
## Installation
10+
11+
The following software must be installed on your system:
812

9-
Shellcoder script most probably should be used on Windows because of the MSVC requirement.
13+
- [Python 3](https://www.python.org/downloads/)
14+
- [NASM (Netwide Assembler)](https://www.nasm.us/)
15+
- [Visual Studio 2022](https://visualstudio.microsoft.com/)
16+
17+
No Python dependencies are necessary! You are ready to go.
18+
19+
## Usage
1020

1121
1. Write your shellcode in `shellcode.asm`
1222
2. Run `python shellcoder.py`
13-
3. Execute output `.exe` file in `out/` directory!
23+
3. Execute `out/malware.exe` file!
24+
25+
![shellcoder.py command line output](/_img/shellcoder-cli.png)
26+
27+
## Output files
28+
29+
The output files of this script are stored in `out/` directory:
30+
31+
- `malware.c` - loader code with the injected payload as C string.
32+
- `malware.exe` - compiled loader with the injected payload.
33+
- `shellcode.exe` - executable file with the payload only. Great for debugging!
34+
- `shellcode.bin` - raw machine code of the assembly payload.
35+
36+
![shellcoder.py output files](/_img/shellcoder-output.png)
37+
38+
## Caveats
39+
40+
- Indicate that you are using 64-bit mode at the beginning of the assembly file. Add `[bits 64]` to the `shellcode.asm`.
41+
- Define entry point in assembly file (required for debugging):
42+
43+
```nasm
44+
[bits 64]
45+
46+
section .text:
47+
global _start
48+
49+
_start:
50+
[...YOUR CODE HERE...]
51+
```
52+
53+
- You cannot use sections other than `.text`. It's a shellcode!
54+
- Remember about [Microsoft x64 Calling Convention](https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170) (stack alignment + shadow space!)
1455

15-
> **IMPORTANT**: Indicate that you are using 64-bit mode at the beginning of the assembly file. Add `[bits 64]` to the `shellcode.asm`.
56+
## How to debug the payload?
1657

17-
## External dependencies
58+
The best way to debug your assembly code is to take `out/shellcode.exe` file and load it into your favorite debugger.
1859

19-
- Python 3
20-
- NASM (Netwide Assembler)
21-
- Visual Studio 2022
60+
Finally you should run `out/malware.exe` to be sure that your payload works as intended after memory injection.

_img/shellcoder-cli.png

39.4 KB
Loading

_img/shellcoder-output.png

12.7 KB
Loading

shellcode.asm

+42-43
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,59 @@
11
[bits 64]
22

3-
;; TODO: DEBUG this shiiiit (x64dbg)
43
section .text:
5-
global _start
4+
global _start
65

76
_start:
7+
88
; Access PEB structure
99
xor rbx, rbx
10-
mov rbx, gs:[0x60] ; RBX = address of PEB struct
11-
mov rbx, [rbx+0x18] ; RBX = address of PEB_LDR_DATA
12-
add rbx, 0x20 ; RBX = address of InMemoryOrderModuleList
10+
mov rbx, gs:[0x60] ; RBX = address of PEB struct
11+
mov rbx, [rbx+0x18] ; RBX = address of PEB_LDR_DATA
12+
add rbx, 0x20 ; RBX = address of InMemoryOrderModuleList
1313

1414
; Go down the double-link list of PEB_LDR_DATA
15-
mov rbx, [rbx] ; RBX = 1st entry in InMemoryOrderModuleList (ntdll.dll)
16-
mov rbx, [rbx] ; RBX = 2st entry in InMemoryOrderModuleList (kernelbase.dll)
17-
mov rbx, [rbx] ; RBX = 3st entry in InMemoryOrderModuleList (kernel32.dll)
15+
mov rbx, [rbx] ; RBX = 1st entry in InMemoryOrderModuleList (ntdll.dll)
16+
mov rbx, [rbx] ; RBX = 2st entry in InMemoryOrderModuleList (kernelbase.dll)
17+
mov rbx, [rbx] ; RBX = 3st entry in InMemoryOrderModuleList (kernel32.dll)
1818

1919
; Get VA address of kernel32.dll
20-
mov rbx, [rbx+0x20] ; RBX = PEB_LDR_DATA.DllBase (address of kernel32.dll)
21-
mov r8, rbx ; R8 = RBX (address of kernel32.dll)
20+
mov rbx, [rbx+0x20] ; RBX = PEB_LDR_DATA.DllBase (address of kernel32.dll)
21+
mov r8, rbx ; R8 = RBX (address of kernel32.dll)
2222

2323
; Get VA address of ExportTable (kernel32.dll)
24-
mov ebx, [r8+0x3c] ; RBX = kernel32.IMAGE_DOS_HEADER.e_lfanew (PE hdrs offset)
25-
add rbx, r8 ; RBX = PeHeaders offset + &kernel32.dll = &PeHeaders
24+
mov ebx, [r8+0x3c] ; RBX = kernel32.IMAGE_DOS_HEADER.e_lfanew (PE hdrs offset)
25+
add rbx, r8 ; RBX = PeHeaders offset + &kernel32.dll = &PeHeaders
2626

2727
xor rcx, rcx
28-
add cl, 0x0088 ; RCX = 0x88 (offset ExportTable RVA)
29-
mov ebx, [rbx+rcx] ; RBX = &PeHeaders + offset ExportTable RVA = ExportTable RVA
30-
add rbx, r8 ; RBX = ExportTable RVA + &kernel32.dll = &ExportTable
31-
mov r9, rbx ; R9 = &ExportTable
28+
add cl, 0x0088 ; RCX = 0x88 (offset ExportTable RVA)
29+
mov ebx, [rbx+rcx] ; RBX = &PeHeaders + offset ExportTable RVA = ExportTable RVA
30+
add rbx, r8 ; RBX = ExportTable RVA + &kernel32.dll = &ExportTable
31+
mov r9, rbx ; R9 = &ExportTable
3232

3333
; Get VA address of ExportTable.AddressOfFunctions
3434
xor r10, r10
35-
mov r10d, [r9+0x1c] ; R10 = ExportTable.AddressOfFunctions RVA
36-
add r10, r8 ; R10 = &kernel32.dll + RVA = &AddressOfFunctions
35+
mov r10d, [r9+0x1c] ; R10 = ExportTable.AddressOfFunctions RVA
36+
add r10, r8 ; R10 = &kernel32.dll + RVA = &AddressOfFunctions
3737

3838
; Get VA address of ExportTable.AddressOfNames
3939
xor r11, r11
40-
mov r11d, [r9+0x20] ; R11 = ExportTable.AddressOfNames RVA
41-
add r11, r8 ; R11 = &kernel32.dll + RVA = &AddressOfNames
40+
mov r11d, [r9+0x20] ; R11 = ExportTable.AddressOfNames RVA
41+
add r11, r8 ; R11 = &kernel32.dll + RVA = &AddressOfNames
4242

4343
; Get VA address of ExportTable.AddressOfNameOrdinals
4444
xor r12, r12
45-
mov r12d, [r9+0x24] ; R12 = ExportTable.AddressOfNameOrdinals RVA
46-
add r12, r8 ; R12 = &kernel32.dll + RVA = &AddressOfNameOrdinals
45+
mov r12d, [r9+0x24] ; R12 = ExportTable.AddressOfNameOrdinals RVA
46+
add r12, r8 ; R12 = &kernel32.dll + RVA = &AddressOfNameOrdinals
4747

4848
; Get address of WinExec function exported from kernel32.dll
4949
xor rcx, rcx
50-
add cl, 0x7 ; RCX = function name length ("WinExec" == 7)
50+
add cl, 7 ; RCX = function name length ("WinExec" == 7)
5151

5252
xor rax, rax
53-
push ax ; STACK + null terminator (2)
54-
mov rax, 0x00636578456E6957 ; RAX = function name = "cexEniW" (WinExec) + 0x00
53+
push rax ; STACK + null terminator (8)
54+
mov rax, 0x00636578456E6957 ; RAX = function name = \0 + "cexEniW" (WinExec)
5555
push rax ; STACK + function name address (8)
56-
mov rsi, rsp ; RSI = &function_name
56+
mov rbx, rsp ; RSI = &function_name
5757

5858
call get_winapi_func
5959
mov r13, rax ; R13 = &WinExec
@@ -64,18 +64,20 @@ mov r13, rax ; R13 = &WinExec
6464
; LPCSTR lpCmdLine, => RCX = "calc.exe",0x0
6565
; UINT uCmdShow => RDX = 0x1 = SW_SHOWNORMAL
6666
; );
67-
xor rax, rax
6867
xor rcx, rcx
6968
xor rdx, rdx
7069

71-
push ax ; STACK + null terminator (2)
72-
mov rax, 0x6578652e636c6163 ; RAX = "exe.clac" (command string: calc.exe)
73-
push rax ; STACK + command string (8)
70+
push rcx ; STACK + null terminator (8)
71+
mov rcx, 0x6578652e636c6163 ; RCX = "exe.clac" (command string: calc.exe)
72+
push rcx ; STACK + command string (8)
73+
7474
mov rcx, rsp ; RCX = LPCSTR lpCmdLine
75+
mov rdx, 0x1 ; RDX = UINT uCmdShow = 0x1 (SW_SHOWNORMAL)
7576

76-
mov dl, 0x1 ; RDX = UINT uCmdShow = 0x1 (SW_SHOWNORMAL)
77-
; Why is here "sub rsp, 0x20" originally ???
78-
call r13 ; Call WinExec(rax, rdx)
77+
and rsp, -16 ; 16-byte Stack Alignment
78+
sub rsp, 32 ; STACK + 32 bytes (shadow space)
79+
80+
call r13 ; WinExec("calc.exe", SW_SHOWNORMAL)
7981

8082
get_winapi_func:
8183
; Requirements (preserved):
@@ -84,7 +86,7 @@ get_winapi_func:
8486
; R11 = &AddressOfNames (ExportTable)
8587
; R12 = &AddressOfNameOrdinals (ExportTable)
8688
; Parameters (preserved):
87-
; RSI = (char*) function_name
89+
; RBX = (char*) function_name
8890
; RCX = (int) length of function_name string
8991
; Returns:
9092
; RAX = &function
@@ -98,16 +100,13 @@ get_winapi_func:
98100
; Loop through AddressOfNames array:
99101
; array item = function name RVA (4 bytes)
100102
loop:
101-
mov rcx, [rsp] ; RCX = length of function_name string
102103
xor rdi, rdi ; RDI = 0
104+
mov rcx, [rsp] ; RCX = length of function_name string
105+
mov rsi, rbx ; RSI = (char*) function_name
103106

104107
mov edi, [r11+rax*4] ; RDI = function name RVA
105108
add rdi, r8 ; RDI = &FunctionName = function name RVA + &kernel32.dll
106-
repe cmpsb ; Compare byte at *RDI (array item str) and *RSI (param function name)
107-
; FIXME: Item NOT FOUND: RDI, RSI
108-
; Something's wrong with stack placing of the function name string.
109-
; Why is there "shr rax, 0x8" originally? WTF?
110-
; R11 = correct
109+
repe cmpsb ; Compare byte *RDI (array item str) and *RSI (param function name)
111110

112111
je resolve_func_addr ; Jump if exported function name == param function name
113112

@@ -116,7 +115,7 @@ get_winapi_func:
116115

117116
resolve_func_addr:
118117
pop rcx ; STACK - RCX (8) = remove length of function_name string
119-
mov ax, [r12+rax*2] ; RAX = ordinal number of function = &AddressOfNameOrdinals + (counter * 2)
120-
mov eax, [r10+rax*4] ; RAX = function RVA = &AddressOfFunctions + (ordinal number * 4)
118+
mov ax, [r12+rax*2] ; RAX = OrdinalNumber = &AddressOfNameOrdinals + (counter * 2)
119+
mov eax, [r10+rax*4] ; RAX = function RVA = &AddressOfFunctions + (OrdinalNumber * 4)
121120
add rax, r8 ; RAX = &function = function RVA + &kernel32.dll
122-
ret
121+
ret

shellcode.asm.bak

-98
This file was deleted.

shellcoder.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def get_msvc_console_environs() -> dict[str, str]:
5050
print(f"[!] MSVC Developer Console error: {process.stderr}")
5151
sys.exit(-1)
5252

53-
envs = {}
53+
envs: dict[str, str] = {}
5454
for line in process.stdout.splitlines():
5555
if '=' in line:
5656
key, value = line.split('=', 1)

0 commit comments

Comments
 (0)