Reversing Packed Targets
An Excersize in reversing for reversing's sake
packers
Packers
19 May 1998
by The RudeBoy [PC]
Courtesy of Fravia's page of reverse engineering
 
fra_00xx
980519
RudeBoy
0100
PU
PC
Simple, yet interesting little essay about basic DOS-type unpacking skills.
There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner (X)Intermediate ( )Advanced ( )Expert

An essay discussing simple .com file packing and unpacking
Reversing Packed Targets
An Excersize in reversing for reversing's sake
Written by The RudeBoy [PC]


Introduction
This essay is meant to explain .com file packing and unpacking.
Not much else to say...


Tools required
TRacer 1.98
IDA 3.7x
C Compiler (optional)
Hex Editor (optional)
PKLite

Target's URL/FTP
rb15fcrk.com - f's crack for ReBirth 1.5

Program History
Not Important

Essay
A friend of mine downloaded this crack off the web so that he could better evaluate
ReBirth.  He sent it to me as well, and when I ran it I noticed that it had a "nag".
In order to run the patch, you had to hit the "Page Up" key.  I decided that I would 
remove the nag from this program.

To do this, i started up IDA and loaded the .com file.  What I saw was interesting. 
The program started off with a jmp, followed by a bunch of data that seemed to make
no sense.  Generally this is a good sign that the file is packed.  I now had a few 
options: i could try a generic unpacker (and learn nothing in the process), i could
use TR or debug to unpack the prog (and learn nothing), or, i could reverse the 
unpacking routine and write my own unpacker for this kind of file.  I decided to go
for the third option.

To reverse the unpacking routine, I followed the jump at the entry point.  The first
thing it does is check the DOS version, then jumps to the next part of the code regardless
of what version of DOS is running.  This is where we want to start paying attention:


loc_0_3576:				
		mov	bx, 10h     ; 
		add	bx, 0F0h    ; 10h + F0h = 100h*
		push	bx      
		mov	al, 0AAh    ; Encryption key into al
		add	al, 7	    ; change the key by adding 7
		push	cx
		mov	cx, 3465h   ; # of bytes to decrypt into cx

loc_0_3586:	
		jmp	loc_0_358A  ; jmp to decryption loop
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
		db 0EAh	; ê
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

loc_0_358A:				
		xor	cs:[bx], al ; xor the byte at cs:[bx] with our encryption key
		push	si      ; random code
		mov	si, 64h     ; ??
		sub	si, 27DBh   ; ??
		pop	bp		    ; ??	
		inc	al          ; increment the encryption key
		inc	bx          ; look at the next byte in the program
		loop	loc_0_3586 ; loop until all bytes have been processed

* 100h is the starting location in memory of a .com file

So this code is pretty simple, get the encryption key, xor a byte by it, increment
the encryption key, and use it tdecryptpt the next byte in the file.
If thencryptionon routine ended here there would be a problem.  When the program is
packed, The first 3 bytes are changed to a jmp to this section of code.  Xor'ing this
jump by the encryption key will still leave us with 3 odd bytes at the start of the file.
So we go to the next section of the code:


		mov	bx, 100h
		mov	word ptr [bx], 0F0B8h
		mov	byte ptr [bx+2], 80h


Simple enough, the packer stores the first 3 bytes directly in the decryption routine.
Now, to write our own unpacker for these files.  I used C to do this, but you can use
whatever language is comfortable to you.  You can take at a look at the source to my
unpacker here.

Now, on to removing that nag...
Wait, this file is packed again...this time with PKLite.  Reversing PKLite is beyond
the scope of this essay, but PKLite has an option built into it to decompress packed
files. Just run pklite -x [filename] and the file is now unpacked.

Again, on to removing the nag...but wait, the file is packed yet again...
This time the file is compressed with the same packer used the first time, so, if you
wrote your unpacker properly you can make short work of this final layer of packing.

Now time to *finally* remove the nag.  IDA does not produce a very usable listing for 
this prog, so fire up TR, and step through this prog.  You will find a few sections
of code dedicated to printing characters and strings to the screen, then a few 
memory allocation calls then:


		mov	ds, cs:word_0_2B9	; setting up a far call
		push	ds			;
		pop	es			;
		assume es:seg000		;
		call	dword ptr cs:unk_0_2B7  ; NagUser();


Trace into the NagUser() function and here is what you will see:


		or	ax, ax	    ;Test value in ax
		jz	loc_0_BA4   ;jump
		clc	
		retf	
loc_0_BA4:				
		xor	ax, ax
		int	16h	    ; KEYBOARD - READ CHAR FROM BUFFER, WAIT IF EMPTY
				    ; Return: AH = scan code, AL = character
		cmp	ah, 49h	    ; compare the key pressed to "Page Up"
		jz	loc_0_BAF   ; jump if equal

Bingo! That's the code we're looking for. In order to kill the nag, we have to do
two things. First, stop the int 16h, because it waits for a keypress.  Second, force
the jz after the compare to always jump.  There are many ways to do these things, I
will let you figure them out for yourself.



/*********************************************************************************
	funp.cpp
	Unpacker for "f" encrypted .com files
	This code is not very neat...i threw it together pretty quickly, so please
	excuse the sloppyness.
*********************************************************************************/
#include <stdio.h>
#include <stdlib.h>

void main (int argc, char *argv[]) {
    FILE *packed, *unpacked;
	int codeloc, count, i, curbyte;
	char key;
	printf("f-Unpacker by The RudeBoy [PC]\n");
	if (argc < 3)
	{
		printf("USAGE: funp [packed file] [unpacked file]\n");
		exit(-1);
	}
	// Open the files
    if((packed = fopen(argv[1], "rb")) == NULL){
        printf("Error: cannot locate %s\n", argv[1]);
        exit(-1);
    }                
	if ((unpacked = fopen(argv[2], "wb")) == NULL)
	{
        printf("Error: cannot open %s\n", argv[2]);
        exit(-1);
	}

	//Seek in one byte, to get the location of the decryption code
	"if(fseek(packed,1.class" tppabs="http://Fravia.org/if(fseek(packed,1.class" , SEEK_SET)){
        printf("Error in %s: cannot seek to 0x1\n", argv[1]);
        exit(-1);
    }
	codeloc = 0;
	//Get the location of the decryption code (relative to the current location in the file)
	if( fread(&codeloc, sizeof(short), 1, packed) != 1)
	{
		printf("Error in %s: could not read data\n", argv[1]);
		exit(-1);
	}
	codeloc += 3; //So we have the total length from the start of the file to the code
	if(fseek(packed,codeloc + 0x17 , SEEK_SET)){
        printf("Error in %s: cannot seek to 0x%X\n", argv[1], codeloc);
        exit(-1);
    }
	//Get the initial decryption key
	if( fread(&key, sizeof(char), 1, packed) != 1)
	{
		printf("Error in %s: could not read data\n", argv[1]);
		exit(-1);
	}
	//add 7
	key += 7;
	count = 0;
	if(fseek(packed,codeloc + 0x1C , SEEK_SET)){
        printf("Error in %s: cannot seek to 0x%X\n", argv[1], codeloc);
        exit(-1);
    }
	//Get the number of encrypted bytes
	if( fread(&count, sizeof(short), 1, packed) != 1)
	{
		printf("Error in %s: could not read data\n", argv[1]);
		exit(-1);
	}
	//Go to the beginning of the file to start the decryption
    fseek(packed,0 , SEEK_SET);
	//Decryption loop
	for(i = 1;i<=count;i++)
	{
		//Get the next byte from the packed file
		curbyte = fgetc(packed);
		//xor it by the key
		curbyte ^= key;
		//and write it to the decrypted file
		fputc( curbyte, unpacked);
		//increment the key, or make it 0 if it equals 0xFF
		if(key == 0xFF)
			key = 0;
		else
			key++;
	}
	//Go to the location in the packed file where the "real" first 3 bytes are found
	if(fseek(packed,codeloc + 0x38 , SEEK_SET)){
        printf("Error in %s: cannot seek to 0x%X\n", argv[1], codeloc);
        exit(-1);
    }
	fseek(unpacked,0 , SEEK_SET);
	//Get each of those bytes and write them to the unpacked file
	curbyte = fgetc(packed);
    fputc( curbyte, unpacked);
	curbyte = fgetc(packed);
    fputc( curbyte, unpacked);
	fseek(packed,3 , SEEK_CUR);
	curbyte = fgetc(packed);
    fputc( curbyte, unpacked);
	
	//close the files
    fclose(packed);
    fclose(unpacked);
	
	printf("Done.\n");

}
//----------------------------------------------------------------------------------




Ob Duh
Ob duh doesn't apply, it's reversing for reversing sake , here

You are deep inside Fravia's page of reverse engineering, choose your way out:

projunpa
Back to the packers' section

redhomepage redlinks redsearch_forms red+ORC redstudents' essays redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_Fravia
redIs reverse engineering legal?