mirror of
https://github.com/thomasxm/amber
synced 2026-06-08 17:46:04 +00:00
168 lines
9.1 KiB
NASM
168 lines
9.1 KiB
NASM
;#==============================================#
|
||
;# X64 Reflective Loader #
|
||
;# Author: Ege Balcı <egebalci@pm.me> #
|
||
;# Version: 2.0 #
|
||
;#==============================================#
|
||
;
|
||
[BITS 64]
|
||
|
||
|
||
call start ; Get the address of pre-mapped PE image to stack
|
||
incbin "pemap.bin" ; Pre-mapped PE image
|
||
start:
|
||
pop rsi ; Get the address of image to rsi
|
||
call $+5 ; Push the current RIP value to stack
|
||
cld ; Clear direction flags
|
||
sub [rsp],rsi ; Subtract the address of pre mapped PE image and get the image_size+8 to ST[0]
|
||
mov rbp,rsp ; Copy current stack address to rbp
|
||
and rbp,-0x1000 ; Create a new shadow stack address
|
||
mov eax,dword [rsi+0x3C] ; Get the offset of "PE" to eax
|
||
mov rbx,qword [rax+rsi+0x30] ; Get the image base address to rbx
|
||
mov r12d,dword [rax+rsi+0x28] ; Get the address of entry point to r12
|
||
mov r9d,0x40 ; PAGE_EXECUTE_READ_WRITE
|
||
mov r8d,0x00103000 ; MEM_COMMIT | MEM_TOP_DOWN | MEM_RESERVE
|
||
mov rdx,[rsp] ; dwSize
|
||
xor rcx,rcx ; lpAddress
|
||
xchg rsp,rbp ; Swap shadow stack
|
||
mov r10d,0x2C39DFEC ; crc32("KERNEL32.DLL", "VirtualAlloc")
|
||
call api_call ; VirtualAlloc(lpAddress,dwSize,MEM_COMMIT|MEM_TOP_DOWN|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
|
||
xchg rsp,rbp ; Swap shadow stack
|
||
mov rdi,rax ; Save the new base address to rdi
|
||
xor rax,rax ; Zero out the RAX
|
||
xor r8,r8 ; Zero out the R8
|
||
xor r13,r13 ; Zero out the R13
|
||
xor r14,r14 ; Zero out the R14
|
||
mov eax,dword [rsi+0x3C] ; Offset to IMAGE_NT_HEADER ("PE")
|
||
mov ecx,dword [rax+rsi+0xB4] ; Base relocation table size
|
||
mov eax,dword [rax+rsi+0xB0] ; Base relocation table RVA
|
||
add rax,rsi ; Base relocation table memory address
|
||
add rcx,rax ; End of base relocation table
|
||
calc_delta:
|
||
mov rdx,rdi ; Move the new base address to rdx
|
||
sub rdx,rbx ; Delta value
|
||
mov r13d,dword [rax] ; Move the reloc RVA to R13D
|
||
mov r14d,dword [rax+4] ; Move the reloc table size to R14D
|
||
add rax,8 ; Move to the reloc descriptor
|
||
jmp fix ; Start fixing
|
||
get_rva:
|
||
cmp rcx,rax ; Check if the end of the reloc section
|
||
jle reloc_fin ; If yes goto fin
|
||
mov r13d,dword [rax] ; Move the new reloc RVA
|
||
mov r14d,dword [rax+4] ; Move the new reloc table size
|
||
add rax,8 ; Move 8 bytes
|
||
fix:
|
||
cmp r14w,8 ; Check if the end of the reloc block
|
||
jz get_rva ; If yes set the next block RVA
|
||
mov r8w,word [rax] ; Move the reloc desc to r8w
|
||
cmp r8w,0 ; Check if it is a padding word
|
||
je pass ; Pass padding bytes
|
||
and r8w,0x0FFF ; Get the last 12 bits
|
||
add r8d,r13d ; Add block RVA to desc value
|
||
add r8,rsi ; Add the start address of the image
|
||
add [r8],rdx ; Add the delta value to calculated absolute address
|
||
pass:
|
||
sub r14d,2 ; Decrease the index
|
||
add rax,2 ; Move to the next reloc desc.
|
||
xor r8,r8 ; Zero out r8
|
||
jmp fix ; Loop
|
||
reloc_fin: ; All done !
|
||
xor r14,r14 ; Zero out r14
|
||
xor r15,r15 ; Zero out r15
|
||
xor rcx,rcx ; Zero out rcx
|
||
mov eax,dword [rsi+0x3C] ; Offset to IMAGE_NT_HEADER ("PE")
|
||
mov eax,dword [rax+rsi+0x90] ; Import table RVA
|
||
add rax,rsi ; Import table memory address (first image import descriptor)
|
||
push rax ; Save import descriptor to stack
|
||
get_modules:
|
||
cmp dword [rax],0 ; Check if the import names table RVA is NULL
|
||
jz complete ; If yes building process is done
|
||
mov ecx,dword [rax+0x0C] ; Get RVA of dll name to eax
|
||
add rcx,rsi ; Get the dll name address
|
||
call LoadLibraryA ; Load the library
|
||
mov r13,rax ; Move the dll handle to R13
|
||
mov rax,[rsp] ; Move the address of current _IMPORT_DESCRIPTOR to eax
|
||
call get_procs ; Resolve all windows API function addresses
|
||
add dword [rsp],0x14 ; Move to the next import descriptor
|
||
mov rax,[rsp] ; Set the new import descriptor address to eax
|
||
jmp get_modules ; Get other modules
|
||
get_procs:
|
||
mov r14d,dword [rax+0x10] ; Save the current import descriptor IAT RVA
|
||
add r14,rsi ; Get the IAT memory address
|
||
mov rax,[rax] ; Set the import names table RVA to eax
|
||
add rax,rsi ; Get the current import descriptor's import names table address
|
||
mov r15,rax ; Save &INT to R15
|
||
resolve:
|
||
cmp dword [rax],0 ; Check if end of the import names table
|
||
jz all_resolved ; If yes resolving process is done
|
||
mov rax,[rax] ; Get the RVA of function hint to eax
|
||
btr rax,0x3F ; Check if the high order bit is set
|
||
jnc name_resolve ; If high order bit is not set resolve with INT entry
|
||
shl rax,2 ; Discard the high bit by shifting
|
||
shr rax,2 ; Shift back the original value
|
||
call GetProcAddress ; Get the API address with hint
|
||
jmp insert_iat ; Insert the address of API tı IAT
|
||
name_resolve:
|
||
add rax,rsi ; Set the address of function hint
|
||
add rax,2 ; Move to function name
|
||
call GetProcAddress ; Get the function address to eax
|
||
insert_iat:
|
||
mov [r14],rax ; Insert the function address to IAT
|
||
add r14,8 ; Increase the IAT index
|
||
add r15,8 ; Increase the import names table index
|
||
mov rax,r15 ; Set the address of import names table address to eax
|
||
jmp resolve ; Loop
|
||
all_resolved:
|
||
mov qword [r14],0 ; Insert a NULL qword
|
||
ret ; <-
|
||
LoadLibraryA:
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
mov r10d,0xE2E6A091 ; crc32("KERNEL32.DLL", "LoadLibraryA")
|
||
call api_call ; LoadLibraryA(RCX)
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
ret ; <-
|
||
GetProcAddress:
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
mov rcx,r13 ; Move the module handle to RCX as first parameter
|
||
mov rdx,rax ; Move the address of function name string to RDX as second parameter
|
||
mov r10d,0xA18B0B38 ; crc32("KERNEL32.DLL", "GetProcAddress")
|
||
call api_call ; GetProcAddress(ebx,[esp+4])
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
ret ; <-
|
||
complete:
|
||
pop rax ; Clean out the stack
|
||
pop rcx ; Pop the ImageSize into RCX
|
||
push rdi ; Save ImageBase to stack
|
||
mov r13,rdi ; Copy the new base value to r13
|
||
add r13,r12 ; Add the address of entry value to new base address
|
||
memcpy:
|
||
mov al,[rsi] ; Move 1 byte of PE image to AL register
|
||
mov byte [rdi],al ; Move 1 byte of PE image to image base
|
||
mov byte [rsi],0 ; Overwrite copied byte (for less memory footprint)
|
||
inc rsi ; Increase PE image index
|
||
inc rdi ; Increase image base index
|
||
loop memcpy ; Loop until zero
|
||
; Flush instruction cache
|
||
pop r13 ; Pop the image base to r13
|
||
or rcx,-1 ; hProcess
|
||
xor rdx,rdx ; lpBaseAddress
|
||
xor r8,r8 ; hProcess
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
mov r10d,0x975B539E ; crc32("KERNEL32.dll", "FlushInstructionCache")
|
||
call api_call ; FlushInstructionCache(0xffffffff,NULL,NULL);
|
||
xchg rbp,rsp ; Swap shadow stack
|
||
add r13,r12 ; Add the address of entry value to image base
|
||
jmp PE_start ; Wipe artifacts from memory and start PE
|
||
|
||
; ========== API ==========
|
||
%include "CRC32_API/x64_crc32_api.asm"
|
||
|
||
PE_start:
|
||
mov rcx,wipe ; Get the number of bytes until wipe label
|
||
lea rax,[rip] ; Get RIP to RAX
|
||
nop ; Padding
|
||
wipe:
|
||
mov byte [rax],0 ; Wipe 1 byte at a time
|
||
dec rax ; Decraise RAX
|
||
loop wipe ; Loop until RCX = 0
|
||
jmp r13 ; Call the AOE
|
||
|