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 Fravia???. 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 goldieīs CDs: "timeless", "saturnzreturn").
Fraviaīs is a nice site, isnīt it???. He, he... well in this essay iīll try to show you a new kind of protection scheme. I say "new", because i only have seen this one 3 times, iīll 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?. Iīm 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 heīll 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. Itīs 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.
Letīs 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, donīt 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 doesnīt use index (like the stop) causes
not effect.
After the cpu is powered on, the initial program counter is just after the inmediateīs 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 ;itīs <>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 ;weīll 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 itīs how we are going to compile the protection, and how to use
the language.
Well, very simple, weīll 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 iīve 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 donīt 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 weīll 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 havenīt used this feature, i donīt 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!
letīs 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 ;weīll 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 donīt 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 iīve 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 Fravia 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 Fravia, @:]
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.