anormal book #1: design your own cpu

      A new kind of protections ?

       Design your own CPU !!
       
      by anormal/kindergarten





.Index.
    1. intro, ideas, etc...
    2. first example: stup8CPU
    3. second example: memoCPU
    4. closing words: greetings, stupid things, etc...





1. intro, ideas, etc... 

    hey you!: What are you doing?, let me guess it... sitting comfortably in 
your chair, looking at the screen of your display, reading the new essays from 
reverser???. Maybe with a glass of your favorite drink?(remember more caffeine 
means more power!!!, so get a jolt cola now!!!), and listening to some deep and 
relaxing ambient music (let me recommend you: "lifeforms" or "isdn" from future 
sound of london, "ghost in the shell" original sound track, or some of the 
goldies CDs: "timeless", "saturnzreturn").

    Reversers is a nice site, isnt it???. He, he... well in this essay ill 
try to show you a new kind of protection scheme. I say "new", because i only 
have seen this one 3 times, ill explain this later. Through this text, we are 
going to learn how design and implement our own cpus, of course in software. 

    A new CPU?. Yes, let me explain the main points of this idea:

    1. Design the architecture of a new cpu: number of registers, kind and usage 
of memory, opcodes and special functions, like ports for accessing external 
devices, etc.

    2. Write a emulation of this CPU for intel x86 machines (or motorola, or 
whatever ...). We could code this emulation in assembler, c, lisp, basic, etc... 
I think a big overbloated compiler (like visual basic) will generate enough tons 
of code to keep our crackers happy for a while, of course, the two examples in 
this essay are coded in hardcore assembler.

    3. Code the protection scheme: keyfiles, passwords checks, hashing 
cryptography schemes, etc..., in the assembler language of the emulated cpu.

    4. Compile the protection, with our own compiler, assembler, etc, and ...

    5. Execute the protection scheme under the emulator.

Nice, eh?. Im sure you got the point.

    I think this is a good protection, because we hide the real protection 
scheme under tons of bastard code. The well learn scheme cmp var,1 - jne 
go_beggar, will be hidden deep inside the kernel of the emulator. The only 
defense we have against a cracker is trying to bore him, if he get bored tracing 
tons of stupid code hell give up.
    Some advantages are: extensibility: we could code different protections in 
seconds. Portability: just coding the emulator for a different system, unix, 
macs, etc, we can execute the "same protection" in a portable fashion.

    Weak points: the cracker could reverse engineer the emulator, guess how the 
cpu works, make a dissasembler, and ... , you could guess the rest. Funny, eh?

    A solution: we must code emulator for the cpu to be very hard to understand. 
Its a lot harder to patch the code in this way, remember that the opcodes of 
the cpu are emulated in a confusing way. The cracker must patch the emulated 
code, not the emulator. He first need to build a disassembler or, at last, know 
very well the inner working of the CPU.

    Lets see the examples. 


2. first example: stup8CPU 

    In this example we are going to design a cpu with this features(?): 


-------------------------
project name: stup8cpu
por: anormal/kindergarten
-------------------------

:description:
-------------

 registers:
            1 accumulator (8 bits)
            1 index       (8 bits)

 memory:
            0..................31....................255 bytes
            [-- idx to inm ---][----- code + data -----]
            [------------ 256 bytes of ram ------------]

:opcodes:
---------
    pnemonic       type     opcode    meaning         
   ------------------------------------------------
    mov idx,@inm   arith     010 (2)  load a number in index register
    load           arith     001 (1)  load accumulator with contents of [idx]
    sub acc,@inm   arith     011 (3)  accumulator-=inm
    inc idx        arith     000 (0)  inc idx
    output         ctrl      101 (5)  print accumulator in screen
    input          ctrl      110 (6)  input bytes from keyboard and put them in [idx] 0 terminated
    jnz @label     ctrl      111 (7)  jmp to label if accumulator is <> 0
    stop           ctrl      100 (4)  stop the cpu
                             
:notes:
--------

	this cpu has 2 8bits registers: 1 accumulator and 1 index.
	
	it has a total memory of 256 bytes.
	
		the first 32 bytes of memory are used to store inmediates. So, the total amount of diferent
	inmediates in a program are 32. Every opcode is coded in just 1 byte, the opcodes that works
	with inmediates, dont have the inmediate in the byte, they only have an index to the first 32 bytes
	Do you understand?.

		We have 8 different opcodes. So we need only 3 bits of opcodes. We use the rest 5 bits to code the
	index to the inmediates zone.

		Putting an index to inmediates zone in an opcode that doesnt use index (like the stop) causes
	not effect.
	
	After the cpu is powered on, the initial program counter is just after the inmediates zone.
	
    
:example protection in the asm code of the emulated cpu:
--------------------------------------------------------

    mov  idx,messini      ;point idx to message
print1:
    load                  ;load a byte from message
    sub  acc,key0         ;decode byte of message
    inc  idx              ;to get next byte
    output                ;print the char in the screen
    jnz  print1           ;loop

    mov idx,pass          
    input                 ;get a string from keyboard

    load                  ;get char
    sub acc,key1          ;sub key1
    jnz bad               ;its <>0 then is bad, 
    inc idx

    load 
    sub acc,key2          ;etc..
    jnz bad
    inc idx

    load
    sub acc,key3
    jnz bad
    inc idx

    load
    sub acc,key4
    jnz bad
    inc idx

    load
    sub acc,key5           ;well put 1 check for every letter in the password
    jnz bad
    inc idx

    mov idx,messok         ;right password
print2:
    load
    inc  idx
    sub  acc,key6          ;print message
    output
    jnz  print2
    stop                   ;stop cpu
bad:
    mov idx,messbad        ;bad password
    jnz print2

:emulation:
-----------

    notes: - bx points to the position 0 of the emulated ram
           - al contains the actual opcode to be emulated
           - ip of the emulated cpu is hold in si

	The inner working of this emulation is very easy.
	
	Main loop of the emulator:
	
				start:
			    mov bx,offset ram        ;points bx to emulated ram 
			    mov si,offset ram+32d    ;initial ip
			run:
			    lodsb     			    ;load opcode
			    mov di,ax
			    and di,0111b 	        ;get instruction
			    shl di,1
			    add di,offset optable
			    call [di]               ;emulate!
			endemul:
			    jmp run					;get next opcode
	

	The procedures use to emulated the different opcodes are:
	
			movidx:
			    shr al,3
			    xlat				;al=bx+al, with this we get an inmediate	
			    mov idx,al
			    ret
			
			load:
			    mov al,idx
			    xlat
			    mov acc,al
			    ret
			
			subacc:
			    shr al,3
			    xlat
			    sub acc,al
			    ret
			
			incidx:
			    inc idx
			    ret
			
			output:
			    mov dl,acc
			    mov ah,02
			    int 21h
			    ret
			
			input:
			    mov dx,offset buffer
			    mov ah,0ah
			    int 21h
			    mov al,idx
			    sub ah,ah
			    mov di,ax
			    add di,bx
			    xchg ax,si
			    mov si,offset buffer+2
			    xor cx,cx
			    mov cl,[buffer+1]
			    rep movsb
			    xchg ax,si
			    ret
			
			jnzlabel:
			   cmp acc,0
			   jne jump
			   ret
			jump:
			   sub ah,ah
			   shr al,3
			   xlat
			   mov si,ax
			   add si,bx
			   ret
			
			stop:
			    pop ax
			    ret


	Ok, i wish you understand all.
	
		The only thing that is missing its how we are going to compile the protection, and how to use
	the language.

	Well, very simple, well use macros in turbo assembler to build the opcodes in memory.
	
	Look at this:
	
	; opcode macronization :)

			_inc_idx macro
			      db 0							;opcode=0
			endm
			
			_load macro
			      db 1							;opcode=0
			endm
			
			_mov_idx macro idx_inm
			      db 2 or (idx_inm shl 3)		;opcode=2 + index
			endm
			
			_sub_acc macro idx_inm
			      db 3 or (idx_inm shl 3)		;opcode=3 + index
			endm
			
			_stop macro
			      db 4							;opcode=4
			endm
			
			_output macro
			      db 5							;opcode=5
			endm
			
			_input macro
			      db 6							;opcode=6
			endm
			
			_jnz macro idx_inm
			      db 7 or (idx_inm shl 3)		;opcode=7 + index 
			endm

	
	This is complete source code. Just do tasm /m2 stup8cpu.asm and a tlink /t stup8cpu.obj to compile it.
	

; ------------------------------------------------------------------------
;  project: stup8cpu
;  code: anormal/loki/kindergarten/trkitzr
;
;  rev: 0.000 : Sun 02-08-1998 : initial code, not debug
;  rev: 0.001 : Sun 02-15-1998 : some fixes, some debugging
;  rev: 0.002 : Sun 02-22-1998 : +debugging
;                                i was getting a stupid error due to the use of si without initializating
;  time: 0.000 : 1:30
;  time: 0.001 : 1:10
;  time: 0.002 : 2:10



c0de segment
assume cs:c0de,ds:c0de
org 100h
.486


; opcode macronization :)

_inc_idx macro
      db 0
endm

_load macro
      db 1
endm

_mov_idx macro idx_inm
      db 2 or (idx_inm shl 3)
endm

_sub_acc macro idx_inm
      db 3 or (idx_inm shl 3)
endm

_stop macro
      db 4
endm

_output macro
      db 5
endm

_input macro
      db 6
endm

_jnz macro idx_inm
      db 7 or (idx_inm shl 3)
endm

start:
    mov bx,offset ram
    mov si,offset ram+32d    ;initial ip
run:
    lodsb         ;load opcode
    mov di,ax
    and di,0111b      ;get instruction
    shl di,1
    call [di+offset optable]
endemul:
    jmp run

db 0bfh
movidx:
    shr al,3
    xlat
    mov idx,al
    ret

db 0bah
load:
    mov al,idx
    xlat
    mov acc,al
    ret

db 0eah
subacc:
    shr al,3
    xlat
    sub acc,al
    ret

db 0bah
incidx:
    inc idx
    ret

db 0cdh
output:
    mov dl,acc
    mov ah,02
    int 21h
    ret

acc db 0
input:
    mov dx,offset buffer
    mov ah,0ah
    int 21h
    mov al,idx
    sub ah,ah
    mov di,ax
    add di,bx
    xchg ax,si
    mov si,offset buffer+2
    xor cx,cx
    mov cl,[buffer+1]
    rep movsb
    xchg ax,si
    ret

idx db 0

jnzlabel:
   cmp acc,0
   jne jump
   ret
db 0b2h
jump:
   sub ah,ah
   shr al,3
   xlat
   mov si,ax
   add si,bx
   ret

db 0b8h
stop:
    pop ax
    ret

buffer db 20h,0
       db 20h dup (0)

db 0eah
optable dw incidx,load,movidx,subacc,stop,output,input,jnzlabel

;ram!!!
;vector of pointers to real inmediates
ram   db iniMess-ram     ;0
      db imprime-ram     ;1
let_o db 'o'             ;2
      db pass-ram        ;3
      db badtry-ram      ;4
let_u db 'u'             ;5
      db okMess-ram      ;6
      db badMess-ram     ;7
let_l db 'l'             ;8
      db imprime2-ram    ;9
let_r db 'r'             ;10
let_p db 'p'             ;11

      db 32-12 dup(0)

   _mov_idx 0
imprime:
   _load
   _sub_acc 10    ;decode message
   _inc_idx
   _output
   _jnz 1         ;must be 'r' terminated, so 'r'-'r'=0 and the loop ends

   _mov_idx 3
   _input

   _load			;the algorithm for checking the password is very, very, very, very simple :)
   _sub_acc 8		;well, it really is pure crap
   _jnz 4			;we only compare letter to letter, if not is equal then go_beggar! :)
   _inc_idx

   _load
   _sub_acc 5
   _jnz 4
   _inc_idx

   _load
   _sub_acc 11
   _jnz 4
   _inc_idx

   _load
   _sub_acc 5
   _jnz 4
   _inc_idx

   _load
   _sub_acc 8
   _jnz 4
   _inc_idx

   _load
   _sub_acc 2
   _jnz 4
   _inc_idx

   _mov_idx 6
imprime2:
   _load
   _inc_idx
   _sub_acc 8
   _output
   _jnz 9
   _stop

badtry:
   _mov_idx 7
   _jnz 9

pass:
   db 23,93,83,25,73,83,91   ;just some trash

;we could encode this strings with a irp or irpc macro in tasm, but ive prefered to do it so for the sake
;of clarity, just use a good editor with columns to fill all that. One of my favorites is Boxer 7.5.
iniMess:
    db 0ah + 'r'
    db 0dh + 'r'
    db 's' + 'r'
    db 't' + 'r'
    db 'u' + 'r'
    db 'p' + 'r'
    db '8' + 'r'
    db 'C' + 'r'
    db 'P' + 'r'
    db 'U' + 'r'
    db ' ' + 'r'
    db 'b' + 'r'
    db 'y' + 'r'
    db ' ' + 'r'
    db 'a' + 'r'
    db 'n' + 'r'
    db 'o' + 'r'
    db 'r' + 'r'
    db 'm' + 'r'
    db 'a' + 'r'
    db 'l' + 'r'
    db '/' + 'r'
    db 'k' + 'r'
    db 'i' + 'r'
    db 'd' + 'r'
    db 'e' + 'r'
    db 'r' + 'r'
    db 'g' + 'r'
    db 'a' + 'r'
    db 'r' + 'r'
    db 't' + 'r'
    db 'e' + 'r'
    db 'n' + 'r'
    db ' ' + 'r'
    db '-' + 'r'
    db ' ' + 'r'
    db 'f' + 'r'
    db 'e' + 'r'
    db 'b' + 'r'
    db '-' + 'r'
    db '9' + 'r'
    db '8' + 'r'
    db 0ah + 'r'
    db 0dh + 'r'
    db 'E' + 'r'
    db 'n' + 'r'
    db 't' + 'r'
    db 'e' + 'r'
    db 'r' + 'r'
    db ' ' + 'r'
    db 'p' + 'r'
    db 'a' + 'r'
    db 's' + 'r'
    db 's' + 'r'
    db 'w' + 'r'
    db 'o' + 'r'
    db 'r' + 'r'
    db 'd' + 'r'
    db ':' + 'r'
    db ' ' + 'r'
    db 'r'      ;terminator

okMess:
    db 0ah + 'l'
    db 0dh + 'l'
    db 'n' + 'l'
    db 'i' + 'l'
    db 'c' + 'l'
    db 'e' + 'l'
    db '!' + 'l'
    db '!' + 'l'
    db '!' + 'l'
    db '!' + 'l'
    db ',' + 'l'
    db ' ' + 'l'
    db 'e' + 'l'
    db 'm' + 'l'
    db 'a' + 'l'
    db 'i' + 'l'
    db 'l' + 'l'
    db ' ' + 'l'
    db 'm' + 'l'
    db 'e' + 'l'
    db ' ' + 'l'
    db 'n' + 'l'
    db 'o' + 'l'
    db 'w' + 'l'
    db '!' + 'l'
    db '!' + 'l'
    db ' ' + 'l'
    db 't' + 'l'
    db 'o' + 'l'
    db ' ' + 'l'
    db 'a' + 'l'
    db 'n' + 'l'
    db 'o' + 'l'
    db 'r' + 'l'
    db 'm' + 'l'
    db 'a' + 'l'
    db 'l' + 'l'
    db '@' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db 'x' + 'l'
    db '.' + 'l'
    db 'c' + 'l'
    db 'o' + 'l'
    db 'm' + 'l'
    db 0ah + 'l'
    db 0dh + 'l'
    db 'l'      ;terminator

badMess:
    db 0ah + 'l'
    db 0dh + 'l'
    db 'w' + 'l'
    db 'r' + 'l'
    db 'o' + 'l'
    db 'n' + 'l'
    db 'g' + 'l'
    db '!' + 'l'
    db ',' + 'l'
    db ' ' + 'l'
    db 't' + 'l'
    db 'r' + 'l'
    db 'y' + 'l'
    db ' ' + 'l'
    db 'a' + 'l'
    db 'g' + 'l'
    db 'a' + 'l'
    db 'i' + 'l'
    db 'n' + 'l'
    db ' ' + 'l'
    db 'm' + 'l'
    db 'a' + 'l'
    db 'n' + 'l'
    db '!' + 'l'
    db '.' + 'l'
    db '.' + 'l'
    db '.' + 'l'
    db 0ah + 'l'
    db 0dh + 'l'
    db 'l'      ;terminator

c0de ends
end start




3. second example: memoCPU 

    Ok. Are you ready for the second example?, open your eyes because this is a 
bit more complicated than the first one.

    This cpu is a bit special. It only has memory. No registers. Why?, well, i 
dont know, i just designed so. Call it inspiration or whatever you want.


memory
--------
   0ffh bytes
   
   after cpu reset, i.e. at startup, the initial program counter will be 0.
   so well put our code at beginning of ram.
   
registers
----------

   sorry, no registers.
   in fact, the only problem will be how to implement indexes, this is solved through the mov opcode.
   
opcodes
-------

 	   an opcode is 4 bytes long, formed by opcode,param1,param2,next_ip
	
       opcode is the number of the opcode.
	
	   param1, param2 are parameters of the opcode.
	
	   and next_ip??, hehehe, this is feature i added after reading one of the apendixes in the nice
	"The jargon file", a compendium of words related to hacker (sub)culture, read it to get true
	knowledge from the real ancient masters.
		let me explain, next_ip is the position of the next opcode to be executed, eh?, yeah, in the x86 
	architecture, the next opcode is the next after the actual opcode. but my cpu is crazy like rats, so
	it let you to expecify the next opcode. Think a bit on this, we could create a web execution, a
	labyrinth of opcodes, jumping up and down though memory. A total knigtmare for the cracker!!!.
	
		in the examples i havent used this feature, i dont want you to get crazy. Just know that
	feature and use it when you want.
	
opcode  pnemonic	param1	param2	 next_ip	meaning
--------------------------------------------------------------------------------
  0		xor  		orig1  ,dorig2  ,next_ip    xors dorig2 with contents of orig1
  2		add  		orig1  ,dorig2  ,next_ip    adds contents of orig1 to dorig2
  4		inp  		dest,  ,cnt     ,next_ip    input cnt bytes from keyboard and put them in dest, 
  												cnt is updated with the number of bytes read
  6		out  		orig   ,nil     ,next_ip    display char in orig
  8		mov  		@orig  ,dest    ,next_ip    movs uses at double indirection, done so to implement
  												indexes, mov contents of address pointed by orig to dest
  a		stp  		nil    ,nil     ,nil        stop cpu
  c		jnz  		orig1  ,next_ip1,next_ip2   if contents of orig1<>0 jmp to ip1 else to ip2

	i am sure you understood all the opcodes, if not reread them.
	


	this is the initialization and main loop of the emulator:
	
    sub  eax,eax
    sub  cx,cx
    sub  dx,dx
    mov  si,offset memory	;si will be used like program counter, initially points to position 0
    mov  di,si				;di is used to acces the memory, 
    sub  bx,bx

nextCycle:
      mov  eax,[si]         ;get opcode from next_ip
      mov  bl,al
      call [bx+offset opcodeTable]
      shr  eax,8            ;the next_ip is in the 1st byte of eax
      mov  si,ax            ;si=next_ip
      add  si,di            ;+initial offset
    jmp  nextCycle
	

		very simple, just get an opcode in eax, and call the emulation procedure for that opcode.
	after returning from the call, the hi word of eax will be in ax, so shr eax,8 puts the next_ip 
	opcode in al, we put it in si and loop!
	
	

	lets see the emulation procedures of each opcode, and the associated table of offsets to them.
	
;emulation procedures ----------------------------------------------------
opXor:
    mov  bl,ah       ;get orig1
    mov  cl,[bx+di]  ;get value of orig1, remember di is always pointing to begining of memory
    shr  eax,16		 ;to get orig2, and next_ip
    mov  bl,al       ;get orig2
xorit:
    xor  [bx+di],cl  ;xor it
ret

opAdd:
    ;let's do it automodifying the code of opXor
    mov  byte ptr [xorit],bh         ;bh is always 0, 0 is the opcode for add :)
    call opXor
    mov  byte ptr [xorit],30h        ;restore the value of the xor opcode = 30h
ret

opInp:
    mov  cl,ah                ;get dest
    shr  eax,16
    mov  bl,al
    push bx                   ;saves ptr to maxcnt
    mov  al,[bx+di]           ;get max counter
    mov  maxcnt,al
    mov  dx,offset maxcnt	  ;well read bytes with int 21h
    push ax
    mov  ah,0ah
    int  21h
    pop  ax
    mov  bl,cl              ;restore dest
    mov  cl,cnt
    mov  si,offset buffer
copy:
    mov  al,[si]			;copy bytes from the x86 memory to memoCPU 
    mov  [bx+di],al
    inc  si
    inc  bx
    dec  cl
    jnz  copy
    pop  bx
    mov  cl,cnt
    mov  [bx+di],cl        ;updates maxcnt
    ret
ret

opOut:
    mov bl,ah				;nothing interesting here
    mov dl,[bx+di]
    mov ah,02
    int 21h
    shr  eax,16
ret

opMov:
    mov  bl,ah       		;get orig1
    mov  bl,[bx+di]  		;get pointer to value of orig1
    mov  cl,[bx+di]  		;get value of orig1
    shr  eax,16
    mov  bl,al       		;get orig2
    mov  [bx+di],cl  		;xor it
ret


opStp:
    pop  ax                  ;pop the return address to the main loop
ret


opJnz:
    mov  bl,ah
    shr  eax,16
    cmp  [bx+di],bh    ;remember bh is always 0, is orig1=0?
    je   not0
    mov  ah,al         ;change the nextip
not0:
ret

opcodeTable dw offset opXor,offset opAdd,offset opInp,offset opOut
            dw offset opMov,offset opStp,offset opJnz

	

	this is the protection coded in the assembler of the memoCPU:
	
	the algorithm used is:
							read password
							make hash value of password
							if calculated hash=key
								nice
							else
								go_beggar

	the hash function is very easy, due to the fact that only one byte is used, the final hash space
	is very reduced, only 255 possible values, so, in theory 1 of each 255 strings will collide to the
	same value, you could test this yourself and see what i mean.

	i used this program to calculate the hash value:

code segment
org 100h
assume cs:code,ds:code

start:

    mov si,offset pass
next:
      lodsb
      xor var1,al				;first xors every letter of password, from the first to the last one
      or al,al
    jne next

    std
    dec si
    dec si
next2:
      lodsb						
      add var1,al				;second adds every letter with calculated xor, from last to first
      or al,al
    jne next2
    int 20h

     db 0
pass db 'password',0
var1 db 0
code ends
end start
	
	
	the protection coded in the assembler language of the memoCPU
	
	look carefully in the the data zone for an explanation of all the strange numbers

buc1:										<<<<------ this is position 0 of memory
   _mov 141,142,4    ;getchar from string
   _xor 143,142,8    ;xorit with key
   _add 145,143,12   ;inc key1
   _out 142    ,16   ;print
   _add 144,140,20   ;dec length
   _add 145,141,24   ;inc ptr
   _jnz 140,0,28     ;if length<>0 repeat

   _inp 162,146,32   ;get 8max bytes pass
   _mov 158,157,36   ;saves maxcnt
buc2:
   _mov 148,142,40   ;getchar from pass
   _xor 142,149,44   ;xor var2 with var1
   _add 144,146,48   ;dec length pass
   _add 145,148,52   ;inc ptr
   _jnz 146, 36,56   ;if length<>0 repeat

   _add 144,148,60   ;dec ptr
   _add 144,157,64   ;dec maxcnt2
   _mov 148,142,68   ;get last char from pass
   _add 142,149,72   ;var2+=last char from pass
   _jnz 157, 56,76

   _add 150,149,80   ;add -result to var2
   _jnz 149, 96,84
yes:
   _mov 160,151,88   ;ptr to st2=ptr to st3
   _mov 159,154,92   ;key3=key2
   _mov 161,152,96   ;length bad=length ok
no:
   _mov 151,142,100  ;getchar
   _xor 154,142,104  ;xorit with key
   _add 145,154,108  ;inc key3
   _out 142    ,112  ;print
   _add 144,152,116  ;dec length
   _add 145,151,120  ;inc ptr
   _jnz 152,96 ,124  ;if length<>0 repeat
   _stp

; data zone, begins at 140 decimal								
dat db lst1                                   ;140=length of st1
    db (offset st1-offset dat)+140            ;141=ptr to st1
    db 00                                     ;142=var1
    db 'r'                                    ;143=key1
    db 0ffh                                   ;144=-1
    db 1                                      ;145=1
mxc db 8                                      ;146=8
    db 143                                    ;147=143
    db 162                                    ;148=ptr to pass
    db 0                                      ;149=0             ;var2
    db -0f0h                                  ;150=-result
    db (offset st2-offset dat)+140            ;151=ptr to st2
    db lst2                                   ;152=length of st2
    db 'u'                                    ;153=key2
    db 'v'                                    ;154=key3
    db lst3                                   ;155=length of st3
    db (offset st3-offset dat)+140            ;156=ptr to st3
    db 00                                     ;157=maxcnt2
    db (offset mxc-offset dat)+140            ;158=ptr to maxcnt
    db 153                                    ;159=ptr to key2
    db 156                                    ;160=ptr to ptr to st2
    db 155                                    ;161=ptr to length st3
pas db 19,87,83,71,15,23,84,27,89,58          ;162=pass (initializated with garbage)
    db 'apocalypse'                           ;    rest of pass, hehehe
st1 db 0ah xor 'r'                            ;st1 xor key1
	

	okey man, get the point?, hehe
	
	finally the complete listing in assembler, to compile it do a tasm memocpu.asm /m4, tlink /t memocpu
	
	enjoy it!!!
	
;-------------------------------------------------------------------------
;-------------------------------------------------------------------------
; project name: memoCPU
; coder: anormal/kindergarten
; notes: just another CPU emulator designed with protection in mind
;
; ver: 0.001 : Mon 04-06-1998 : initial
; ver: 0.002 : Tue 04-07-1998 : +opcodes & macros
; ver: 0.003 : Wed 04-08-1998 : +emulated code
;                               +start debugging, tons (really) of fixes :)
;                               +and finished :)
;                               *1st working version
; time: 0.001 : 1:15
; time: 0.002 : 0:50
; time: 0.003 : 2:10
;
;
;-------------------------------------------------------------------------
;-------------------------------------------------------------------------
code segment
org 100h
assume cs:code,ds:code

; opcodes macros
_xor macro orig1,orig2,next_ip
    db   0,orig1,orig2,next_ip
endm

_add macro orig1,orig2,next_ip
    db   2,orig1,orig2,next_ip
endm

_inp macro dest,cnt,next_ip
    db   4,dest,cnt,next_ip
endm

_out macro orig1,next_ip
    db   6,orig1,0,next_ip
endm

_mov macro orig1,dest,next_ip
    db 8,orig1,dest,next_ip
endm

_stp macro
    dd 0000000ah
endm

_jnz macro orig1,ip1,ip2
    db 0ch,orig1,ip1,ip2
endm
;--------------------------------------------------------------------------
start:
.386
    sub  eax,eax
    sub  cx,cx
    sub  dx,dx
    mov  si,offset memory
    mov  di,si
    sub  bx,bx

nextCycle:
      mov  eax,[si]         ;get opcode from next_ip
      mov  bl,al
      call [bx+offset opcodeTable]
      shr  eax,8            ;the next_ip is in the 1st byte of eax
      mov  si,ax            ;si=next_ip
      add  si,di            ;+initial offset
    jmp  nextCycle

;emulation procedures ----------------------------------------------------
opXor:
    mov  bl,ah       ;get orig1
    mov  cl,[bx+di]  ;get value of orig1
    shr  eax,16
    mov  bl,al       ;get orig2
xorit:
    xor  [bx+di],cl  ;xor it
ret

opAdd:
    ;let's do it automodifying the code of opXor
    mov  byte ptr [xorit],bh         ;bh is always 0, 0 is the opcode for add :)
    call opXor
    mov  byte ptr [xorit],30h        ;restore the value of the xor opcode = 30
ret

opInp:
    mov  cl,ah                ;get dest
    shr  eax,16
    mov  bl,al
    push bx                   ;saves ptr to maxcnt
    mov  al,[bx+di]           ;get max counter
    mov  maxcnt,al
    mov  dx,offset maxcnt
    push ax
    mov  ah,0ah
    int  21h
    pop  ax
    mov  bl,cl              ;restore dest
    mov  cl,cnt
    mov  si,offset buffer
copy:
    mov  al,[si]
    mov  [bx+di],al
    inc  si
    inc  bx
    dec  cl
    jnz  copy
    pop  bx
    mov  cl,cnt
    mov  [bx+di],cl        ;updates maxcnt
    ret
ret


opOut:
    mov bl,ah
    mov dl,[bx+di]
    mov ah,02
    int 21h
    shr  eax,16
ret

opMov:
    mov  bl,ah       ;get orig1
    mov  bl,[bx+di]  ;get pointer to value of orig1
    mov  cl,[bx+di]  ;get value of orig1
    shr  eax,16
    mov  bl,al       ;get orig2
    mov  [bx+di],cl  ;xor it
ret


opStp:
    pop  ax                  ;pop the return address to the main loop
ret


opJnz:
    mov  bl,ah
    shr  eax,16
    cmp  [bx+di],bh    ;remember bh is always 0
    je   not0
    mov  ah,al         ;change the nextip
not0:
ret

opcodeTable dw offset opXor,offset opAdd,offset opInp,offset opOut
            dw offset opMov,offset opStp,offset opJnz

;begin of emulated code --------------------------------------------------

memory:
buc1:
   _mov 141,142,4    ;getchar from string
   _xor 143,142,8    ;xorit with key
   _add 145,143,12   ;inc key1
   _out 142    ,16   ;print
   _add 144,140,20   ;dec length
   _add 145,141,24   ;inc ptr
   _jnz 140,0,28     ;if length<>0 repeat

   _inp 162,146,32   ;get 8max bytes pass
   _mov 158,157,36   ;saves maxcnt
buc2:
   _mov 148,142,40   ;getchar from pass
   _xor 142,149,44   ;xor var2 with var1
   _add 144,146,48   ;dec length pass
   _add 145,148,52   ;inc ptr
   _jnz 146, 36,56   ;if length<>0 repeat

   _add 144,148,60   ;dec ptr
   _add 144,157,64   ;dec maxcnt2
   _mov 148,142,68   ;get last char from pass
   _add 142,149,72   ;var2+=last char from pass
   _jnz 157, 56,76

   _add 150,149,80   ;add -result to var2
   _jnz 149, 96,84
yes:
   _mov 160,151,88   ;ptr to st2=ptr to st3
   _mov 159,154,92   ;key3=key2
   _mov 161,152,96   ;length bad=length ok
no:
   _mov 151,142,100  ;getchar
   _xor 154,142,104  ;xorit with key
   _add 145,154,108  ;inc key3
   _out 142    ,112  ;print
   _add 144,152,116  ;dec length
   _add 145,151,120  ;inc ptr
   _jnz 152,96 ,124  ;if length<>0 repeat
   _stp

filler db 140-(offset filler - offset memory) dup (0)

; begins at 140 decimal
dat db lst1                                   ;140=length of st1
    db (offset st1-offset dat)+140            ;141=ptr to st1
    db 00                                     ;142=var1
    db 'r'                                    ;143=key1
    db 0ffh                                   ;144=-1
    db 1                                      ;145=1
mxc db 8                                      ;146=8
    db 143                                    ;147=143
    db 162                                    ;148=ptr to pass
    db 0                                      ;149=0             ;var2
    db -0f0h                                  ;150=-result
    db (offset st2-offset dat)+140            ;151=ptr to st2
    db lst2                                   ;152=length of st2
    db 'u'                                    ;153=key2
    db 'v'                                    ;154=key3
    db lst3                                   ;155=length of st3
    db (offset st3-offset dat)+140            ;156=ptr to st3
    db 00                                     ;157=maxcnt2
    db (offset mxc-offset dat)+140            ;158=ptr to maxcnt
    db 153                                    ;159=ptr to key2
    db 156                                    ;160=ptr to ptr to st2
    db 155                                    ;161=ptr to length st3
pas db 19,87,83,71,15,23,84,27,89,58          ;162=pass (initializated with garbage)
    db 'apocalypse'                           ;    rest of pass, hehehe
st1 db 0ah xor 'r'                            ;st1[1] xor key1
    db 0dh xor ('r'+01)						  ;st1[2] xor key1+1, etc...
    db 'm' xor ('r'+02)
    db 'e' xor ('r'+03)
    db 'm' xor ('r'+04)
    db 'o' xor ('r'+05)
    db 'C' xor ('r'+06)
    db 'P' xor ('r'+07)
    db 'U' xor ('r'+08)
    db ' ' xor ('r'+09)
    db 'b' xor ('r'+10)
    db 'y' xor ('r'+11)
    db ' ' xor ('r'+12)
    db 'a' xor ('r'+13)
    db 'n' xor ('r'+14)
    db 'o' xor ('r'+15)
    db 'r' xor ('r'+16)
    db 'm' xor ('r'+17)
    db 'a' xor ('r'+18)
    db 'l' xor ('r'+19)
    db '/' xor ('r'+20)
    db 'k' xor ('r'+21)
    db 'i' xor ('r'+22)
    db 'n' xor ('r'+23)
    db 'd' xor ('r'+24)
    db 'e' xor ('r'+25)
    db 'r' xor ('r'+26)
    db 'g' xor ('r'+27)
    db 'a' xor ('r'+28)
    db 'r' xor ('r'+29)
    db 't' xor ('r'+30)
    db 'e' xor ('r'+31)
    db 'n' xor ('r'+32)
    db 0ah xor ('r'+33)
    db 0dh xor ('r'+34)
    db 'E' xor ('r'+35)
    db 'n' xor ('r'+36)
    db 't' xor ('r'+37)
    db 'e' xor ('r'+38)
    db 'r' xor ('r'+39)
    db ' ' xor ('r'+40)
    db 'p' xor ('r'+41)
    db 'a' xor ('r'+42)
    db 's' xor ('r'+43)
    db 's' xor ('r'+44)
    db 'w' xor ('r'+45)
    db 'o' xor ('r'+46)
    db 'r' xor ('r'+47)
    db 'd' xor ('r'+48)
    db ':' xor ('r'+49)
    db ' ' xor ('r'+50)
    lst1 = $-offset st1
st2 db 0ah xor 'v'                            ;1xx=st2 xor key3
    db 0dh xor ('v'+01)
    db 'B' xor ('v'+02)
    db 'a' xor ('v'+03)
    db 'd' xor ('v'+04)
    db '!' xor ('v'+05)
    db '!' xor ('v'+06)
    lst2 = $-offset st2
st3 db 0ah xor ('u')                            ;1xx=st2  xor key2
    db 0dh xor ('u'+01)
    db 'G' xor ('u'+02)
    db 'o' xor ('u'+03)
    db 'o' xor ('u'+04)
    db 'd' xor ('u'+05)
    db '!' xor ('u'+06)
    db '!' xor ('u'+07)
    lst3 = $-offset st3
;end of emulated code ----------------------------------------------------

maxcnt db 12                    ;this 12 dont means nothing is just some trash
cnt    db 82                    ;more trash
buffer db 'heavensfacility'     ;more trash, hehehe, nice for lamers

code ends
end start





4. closing words: greetings, stupid things, etc... 

    Some final words:

    I only expect that some of this notes could serve to anyone :).     First of 
all i want to give thanks to solar_designer, for giving me hi file hackme.com 
(the one and only intelligent, original and portable crackme!!). This is the 
best "clever" protection scheme ive seen in many time. This file,hackme.com, is 
a TRUE hardwork, a cpu emulated with NOR gates.

    Hey solar!!: wherever you are, thanks!.

    Second, i want to give thanks to reverser for maintaining so astounding 
site, he has done of the HCU project a reality.

    I also want to greets to lord_byte for the initial testx series, the guys at 
#cracking, #ucf200, #pc, #revolt. Even when 80% all them are just a bunch of 
arrogant inmature kids... :)

    ejemm... enough greets... 

    If you have better ideas than myself (this is very easy), then write an 
essay and send it to reverser, @:]

    Finally, a stupid poem:

    Soul nopper 

    NOPS in the middle of your code, 
    suddenly a 75h becomes a EBh, 
    INTs 1, 0CChs, SMIs in my screen, 

    all i can afford, all i can forget, 

    the ctrl+d firing the ice in the bus... 
    the F12 PRETing until the end of the world ... 
    the BMSG shouting your voices... 

    and the only thing i can't crack... 

    the scent you left behind, 
    it's so long to XT, so long to BPM R, 
    IF i BPIO 378h THEN you will hang. 

    so, 

    my dreams and your smile, 
    my screen and your traps, 
    dust in the wind, clouds in the sky. 

(booarrrgg: you can vomit now :)




    You can reach me at anormal1@yahoo.com




 all rights reserved anormal/kindergarten, 1998.
Release to the public domain for free use and abuse to all cracking community, 
you could not use portions, or full algorithms of this page for commercial 
applications without written permission. Microsoft could buy a license to use 
this schemes in its products after paying to me, the author, a license of 
$1,000,000. Hey billy, thanks for all the blue screens.

