Skip to content

.:ASM tutorial day1 by rAsM:.

	- 0x00	Intro
	- 0x01	Les registres
	- 0x02	Les types de données
	- 0x03	Declaration de variables
	- 0x04	Les adresses
	- 0x05	L'instruction mov
	- 0x06	Les sauts
	- 0x07	Les instructions de comparaison
	- 0x08	Les labels
	- 0x09	L'instruction call
	- 0x0A	Les procedures ou les fonctions
	- 0x0B	La pile
	- 0x0C	Configurer FASM
	- 0x0D	Les APIs
	- 0x0E	Le premier programme



- 0×00 Intro:

Voici un petit tutorial pour les débutants en assembleur, j’y explique
somairement quelques notions de base le but est avant tout de fournir une
source asm commentée et que vous découvriez un peu par vous mêmes comment
ça fonctionne.

Vous aurez besoin de FASM pour windows et ollydbg v1.10.

La source day1 contient 2 petits bugs j’espère que vous
serez assez malins pour les trouver. ;) Mais tout d’abord lisez le texte
avant de commencer à faire joujou avec FASM et la source.



- 0×01 Les registres:

Ce sont des emplacements dans le processeur qui peuvent contenir des valeurs
et bien plus. Ils sont essenciels à la programmation car ils permetent de
manipuller les données.

En voici quelques uns avec leurs sudivisions par exemple eax se sudivise en
AX et AX en AH et AL.

	+---------------+
	|      EAX      | EAX, taille 32bits
	+-------+---+---+
	|       |  AX   | AX, taille 16 bits
	+-------+-------+
	|       | AH| AL| AL, AH, taille 8 bits
	+-------+---+---+

	+---------------+
	|      EBX      |
	+-------+---+---+
	|       |  BX   |
	+-------+-------+
	|       | BH| BL|
	+-------+---+---+

	+---------------+
	|      ECX      |
	+-------+---+---+
	|       |  CX   |
	+-------+-------+
	|       | CH| CL|
	+-------+---+---+

	+---------------+
	|      EDX      |
	+-------+---+---+
	|       |  DX   |
	+-------+-------+
	|       | DH| DL|
	+-------+---+---+

Ceux-ci servent à effectuer la plus part des opérations de notre programme.
Nous avons encore quelques registres spéciaux: ESP et EBP qui définissent la
pile ou stack, j’en parle plus loin.

Il existe encore ESI et EDI qui servent dans le programe day1 comme pointeurs
sur des chaines de caractères.

EIP sert de pointeur sur la prochaine instruction à éxecuter on ne peut pas
le modifier directement.

Et pour finir il nous reste le registre des flags, il fait 32bits en taille
chaque bit represente un flag. Et il ne peut être modifié directement. Mise à
part certaines instructions qui arment les flags, par exemple si le flag Z (zéro)
vaut 1 un saut tel que jz (jump if zero) sera exécuté.

On peut tout faire avec les registres sauf avec certains, vous verrez plus
loin comment leur affecter des valeurs, etc.


- 0×02 Les types de données:

Le DWORD, c’est un nombre qui a pour taille 32bits soit 4 octets.

Le WORD, il a pour taile 16bits soit 2 octets.

Le BYTE contient 8bits et est égal à 1 octet.

Vous l’aurez compris qu’avec EAX vous pouvez y mettre un DWORD, avec AX un
WORD et avec AL ou AH un BYTE.


- 0×03 Déclaration de variables:

Les variables sont des données contenues dans la mémoire pour y accéder nous
devons savoir ou elles se situent. Les emplacements dans la mémoire ont pour
taille un octet, chaqun de ces emplacements a une adresse. Dans les
processeurs 32 bits une adresse possede comme taille 32bits.

Dans nos programmes nous n’avons pas à faire directement à des adresses mais
à des noms que nous donnons aux variables pour y acceder facilement.

Les chaines de caractères MonString db ‘kikooolol’,0

Les chaines de bytes MonByte db 09h,08h,0

Les buffers (chaine de bytes vide) MonBuffer db 20 dup(?)
20 est la taille (nombre de bytes) et dup(?) l’initialise à 0


- 0×04 Les adresses:

Dans les processeurs 32 bits les adresses sont des nombres de 32 bits également
elles pointent sur des emplacements dans la mémoire. Ainsi chaque variable
possede son adresse. Comme vous l’aurez sans doute compris nous accédons aux
variables par une adresse mais FASM nous facilite grandement la tâche en nous
permetant d’utiliser un nom ou un label au lieu d’une adresse.


- 0×05 L’instruction mov:

Celle-ci est la base des intructions elle permet d’affecter des valeurs à
des registres ou bien à des variables.

	mov eax,1

EAX aura comme valeur 1.

	mov eax,1
	mov edx,eax

EDX aura comme valeur 1 et EAX aussi.

Imaginons une variable DWORD (MonDword dd 1234):

	mov eax,MonDword

EAX aura pour valeur l’adresse mémoire de MonDword à la quelle se trouve
la valeur 1234.

	mov eax, dword [MonDword]

Aura comme conséquences d’affecter la valeur 1234 à EAX. Lors de la lecture/
écriture de valeurs il faut spécifier la taille de ce qu’on va lire elle doit
être la même que le registre.

Imaginons maintenant que nous voulons lire la seconde lettre d’une chaine
de caracteres. (Machaine db “abcde”,0)

	mov al, byte [Machaine]

AL aura la valeur ascii de “a” soit 61h. Pour lire le second caractère il faut
ajouter 1 à l’adresse de Machaine.

	mov al, byte [Machaine+1]

Et comme ça AL aura la valeur “b” ou 62h.

On peut aussi utiliser un registre pour incrémenter l’adresse de la variable:

	mov ecx,1
	mov al, byte [Machaine+ecx]

Ou bien on peut utiliser un registre comme pointeur et incrémenter celui-ci:

	mov eax,Machaine
	inc eax		;incrémente de 1 EAX
	mov al,[eax]

On peut aussi utiliser d’autres opérandes:

	mov ecx,1
	mov al, byte [Machaine+ecx*2]		;AL aura la valeur de “c”
	inc ecx
	mov al, byte [Machaine+ecx*2]		:AL aura la valeur de “d”

Encore un code equivalent:

	mov eax,Machaine
	mov ecx,1
	mov al,byte [eax+ecx]

Sachez que ces modes d’accès aux données sont compatibles avec toutes les
instructions qui prement des adresses comme opérandes. Par exemple “cmp”.


- 0×06 Les sauts:

Ces instructions permetent de modifier EIP indirectement. En gros ca nous permet
de sauter du’un endroit du code à l’autre.

	jmp label
	...code1
	label:
	...code2

Lors de l’éxecution du jmp code1 ne sera pas éxecuté mais code2 le sera.
Il existe des sauts conditionels tels que jz (jmp if zero) jnz (jmp if not
zero) la condition depend du registre de flags si le flag Z est égal à 1
le saut jz sera executé.


- 0×07 Les instructions de comparaison

Cmp sert à comparer 2 opérandes et vont modifier les flags selon le
résultat de la comparaison.

	mov eax,200
	cmp eax,100
	jz egal
	...code1
	egal:
	...code2

Le code2 ne sera jamais executé car les opérandes ne sont pas égales donc
le flag Z sera à 0.


- 0×08 Les labels:

Les labels servent à définir une zone du code en gros votre code sera en mémoire
et aura une adresse précise que nous ne pouvons pas connaitre avant la compilation.
Donc au lieu de faire jmp 00400000 nous fesons jmp label.


- 0×09 L’instruction call:

Elle permet apeller une fonction une fois que la fonction est terminée, l’execution
du code retourne juste après le call.

	call label
	...code1
	label:
	...code2
	ret

On va sauter à code2 une fois l’intruction ret exectutée on retourne à code1.
Notez que le call ajoute dans la pile l’adresse de retour ce qui permet au
ret de retourner à code1 en utilisant cette adresse.


- 0×0A Les procédures ou fonctions:

L’intruction qui permet d’appeller une fonction est le call, par exemple:
call Monproc. On peut aussi utiliser un registre: genre call eax pour autant
que EAX contienne l’adresse de notre procédure.

Elles servent à simplifier le code surtout lorsqu’on doit éxécuter le même
code plusieurs fois à des endroits differents. Cela permet aussi le diviser
en petites fonctions pour faciliter la compréhesion de celui-ci.

Pour déclarer une procédure on utilise la macro proc:

	proc Maprocedure,parametre1:DWORD,parametre2:DWORD
	...notre code
	ret
	endp	;marque la fin de la procédure

DWORD indique à FASM la taille des paramètres. Le ret est l’instruction de fin
de la procedure elle permet de retourner juste après le call qui l’a appelée.


- 0×0B La pile:

C’est une zone mémoire delimitée par les registres esp et ebp, esp pointe sur
le bas de la pile et ebp à sa base, le haut.
En gros elle permet d’y stocker des variables temporairement.

	push eax

Ce code sauvegarde la valeur eax dans la pile on la recupere grace a:

	pop eax

Qu’arrive t’il a esp quand on met une valeur dans la pile:

	push 1234
	esp	00220000 pointe sur 00001234
     	esp+4	00220004 pointe sur 00000000
	esp+8	00220008 pointe sur 00000000

Si on enleve va valeur avec un pop eax.

     	esp-4	00220008 pointe sur 00001234
	esp	00220004 pointe sur 00000000
	esp+4	00220000 pointe sur 00000000

Donc quand on ajoute une valeur dans la pile on soustrait 4 à esp. Quand on
enlève une valeur on aditione 4 à esp. Pour plus de compréension regardez
dans ollydbg ce qui se passe lors des push/pop.

Maintenant qu’est ce qui se passe lors de l’appel d’une fonction:

	push 1	;parametre 1
	push 2	;parametre 2
	call mafonction

La pile aura ressemblera a ça:

	esp	00220000 pointe sur 00402200	;ceci est l'adresse de retour
     	esp+4	00220004 pointe sur 00000002
	esp+8	00220008 pointe sur 00000001

Donc le code de la fonction va ressember a ceci:

	push ebp
	mov ebp,esp
	...code
	leave
	ret 8

Le push ebp permet de sauvegarder ebp temporairement, en affectant la valeur
de ebp à esp on va créer une nouvelle pile temporaire qui ressemble a ça:

	esp = ebp 	0021fffc pointe sur l'ancienne valeur de ebp
	esp+4 		00220000 pointe sur l'adresse de retour

Maintenant la fonction peut utiliser la petite pile qu’elle a reservé pour
y metre des valeurs, par exemple:

	push 1234

	esp		0021fff8 pointe sur 1234
	ebp = esp+4 	0021fffc pointe sur l'ancienne valeur de ebp
	esp+8		00220000 pointe sur l'adresse de retour

Lorsque la fonction quite “leave” va utiliser EBP pour remetre esp à pointer
sur l’ancienne valeur de EBP et faire l’équivalent d’un pop ebp ce qui va
restaurer la valeur de EBP précedament sauvegardée. Ceci aura pour conséquence
de faire pointer ESP sur l’adresse de retour lors du ret on revient au code et
la pile n’a pas changé.

Ce qu’il faut en conclure ici: il ne faut pas empiller des valeurs sans les
retirer car à la fin de votre fonction, esp peut ne pas pointer sur l’adresse
de retour, et kaboom votre programe est mort.
Le “ret” peut prendre une operande ex: ret 8, il permet non seulement de retourner
au code comme d’enlever les parametres pushés avant le call.
(ret 8 -> 2 paramétres * 4)


- 0×0C Configurer FASM

Demarez fasmw.exe et fermez le. Il va créer un fichier FASMW.INI dans ce fichier
ajoutez ces deux lignes en prenant soin de mettre le path du dossier INCLUDE
de FASM.

	[Environment]
	Include = c:\…\FASM\INCLUDE


- 0×0D Les APIs

Ce sont des fonctions contenues dans les fichiers DLL qui permentent aux
programmes d’interagir avec windows. Dans le programme un vous verrez que
j’en fais usage (GetCommandLineA, MessageBoxA, ExitProcess).

Je ne vais pas en parler dans ce tutoriel, ça sera pour le prochain. En
attendant si vous voullez de la documentation dessus allez voir dans la
MSDN.


- 0×0E Le premier programe:

Ce petit programe va nous permetre de revoir un peu toutes ces notions.
Tout d’abord ouvrez la source avec FASM et compillez ensuite ouvrez le avec
ollydbg et il y aura un champ d’édition pour passer les arguments au
programe entrez “-abcd” et ouvrez.

Ensuite regardez le code commenté et avec Ollydbg executez chaque instruction
pas à pas (avec F7) pour comprendre que qui se passe. En ragardant le changement
des les registres, la pile (en bas à droite), etc.

Le but n’est pas de tout vous expliquer mais que vous apreniez à comprendre
la source par vous mêmes.

Bon travail :) Dans le day2 nous allons voir comment on fait un crackme.

AsmDay1_pack.zip

rAsM’s ring0 KeyGenMe!!!

ufoo000000oooo0oo le voici enfin comme promis j'espere qu'il y aura
par trop de BSODS; le readme en anglais:
Hello this is the result of hours of experimental coding...
It might not work on your config, i'm sorry about that.
The first goal was to code an original and funny crackme so
the algorithm part is very easy. I hope you will like it.

Read carefully:

Save all your work before running it, cause it could crash
your system.

The required configuration is:
	- windows xp sp2/3, not tested on vistass.
	- a PS/2 compatible keyboard.
	- single core or dual core CPU, not tested on
	hyperthreaded or multi processors systems.
	- resolution min 800x600, pixel mode must be 32
	bits.

You can use it on vmware single core vm by adding the line
 below to your .vmx file.

	svga.maxFullscreenRefreshTick = "2"

How to use it:

Enter your name and press register, then you will see an
interface wich ask you to imput the serial (only numbers)
In vmware the keyboard controller is bugged it's not
possible to know if keyboard is sending us a scancode or
a mouse packet, so dont move your mouse just enter the
serial. Serial differs from single and multi cpus/cores.

Greetz to:
0verCl0k, Baboon, kaze, __ed, Ivanlef0u, KERNEL_ERROR,
hastert...

rAsM_kGm.zip

Un peu de bonne musique:

.:keyboard polling demystified:.

	0x00 Introduction
	0x01 PS/2 keyboard
	0x02 No hooking
	0x03 IOAPIC
	0x04 Disable kdb IRQ
	0x05 Regenerate SCANCODE
	0x06 Call Kdb Interrupt
	0x07 Conclusion
	0x08 References



0×00 Introduction:

Dans cet article je vais expliquer comment fonctione un clavier PS/2 et
comment on peut faire un keylogger en ring0 sous windows xp sans aucun hook
ou un device filter.



0×01 PS/2 keyboard:

Le clavier PS/2 est relié à un microcontrolleur, sous les touches il y a
un reseau matriciel, lignes et collones, lorsqu’on presse une touche il
s’etablit un contact entre une ligne et une collone correspondant à la
touche pressée; le signal est traité par un micro controlleur qui pourra
tranmettre le SCANCODE de la touche.
Ces SCANCODEs sont des numéros qui correspondent à “la position” des
touches.

Le microcontrolleur ne transmet pas directement le SCANCODE au processeur
il envoie un irq (interruption matérielle) sur un pin du processeur
celui-ci en réponse à cet IRQ va déclancher une interruption.

IRQ 0 : Horloge Système
IRQ 1 : Clavier
IRQ 2 : N/A (cascade du second contrôleur)
IRQ 3 : Port série (COM2/COM4)
IRQ 4 : Port série (COM1/COM3)
IRQ 5 : LPT2 (carte de son)
IRQ 6 : Lecteur de disquettes
IRQ 7 : Port parallèle (LPT1)
IRQ 8 : Horloge temps réel
IRQ 9 : N/A (PCI)
IRQ 10 : N/A
IRQ 11 : N/A (USB)
IRQ 12 : N/A (PS/2)
IRQ 13 : Coprocesseur math.
IRQ 14 : Disque dur primaire
IRQ 15 : Disque dur secondaire

Sur les architectures récentes le controlleur: 82093AA I/O ADVANCED
PROGRAMMABLE INTERRUPT CONTROLLER (IOAPIC) est chargé de gérer les IRQs
et grâce à lui le systeme d’exploitation peut assigner un vecteur
d’interruption du processeur à un IRQ.

Puis le handler de ce vecteur d’interruption sera chargé de récupérer les
SCANCODES en accédant au ports du micro controlleur du clavier.

Le micro controlleur du clavier possede deux registres éssenciels:
le status register et le controller command register qui font tout les
deux la taille d’un octet.

Et un INPUT_BUFFER pour recevoir des commandes puis un OUTPUT_BUFFER pour
transmetre des données par exemple les SCANCODES.

Quand l’interruption dédiée au clavier est déclanchée le handler
devra lire le status register pour savoir qu’elles types de données
sont disponibles dans le OUTPUT_BUFFER.

Status register:

	+----+---+----+----+---+----+----+----+
	¦ 7  ¦ 6 ¦ 5  ¦ 4  ¦ 3 ¦ 2  ¦ 1  ¦ 0  ¦
	+----+---+----+----+---+----+----+----+
	¦PARE¦TIM¦AUXB¦KEYL¦C/D¦SYSF¦IMPB¦OUTB¦
	+----+---+----+----+---+----+----+----+

On accède à ce register par le port 64 qu’il faut lire, ensuite il
nous faudra tester les bits OUTB et AUXB; OUTB nous dit si un
SCANCODE ou autre, est présent dans le buffer de sortie AUXB va nous
permetre de savoir si les données dans le buffer de sortie ont été
envoiées par le clavier ou la souris.

(Si on code un handle d’interruption pour l’IRQ 1 on n’aura pas besoin
de tester le OUTPUT_BUFFER pour la souris.)

Voici le code qui teste le status register pour savoir si un
SCANCODE peut etre lu:

BOOLEAN IsThereScanCode()
{
	int i = 0x200;

	do{
		if((READ_PORT_UCHAR((PUCHAR)0x64) & BUFFER_FULL) == 1)
		{
			return TRUE;
		}
	}while(i-- > 0);
	return FALSE;
}

Si on a un scancode on le récupere en lisant le port 0×60:

unsigned char GetScanCode()
{
	return READ_PORT_UCHAR((PUCHAR)0x60);
}

Une fois le SCANCODE récupéré il nous faut le traduire, en une valeur
ascii qui correspond à la touche, en utilisant une table de keymap.
Cette table contient les valeurs ascii indexées par les SCANCODEs
dans l’ordre croissant.

(Si on code un handle d’interruption nous devons signaller au controlleur
que l’IRQ a bien été traitée pour qu’il puisse récupérer des nouveaux
SCANCODES.

	WRITE_PORT_UCHAR((PUCHAR)0x20,0x20);



0×02 No hooking:

Pour ne pas avoir à hooker l’IDT nous allons faire du keyboard polling,
comprendre par sondage du clavier. On va désactiver l’IRQ 1 du clavier
pour que l’interruption dédiée à cet IRQ ne soit pas déclanchée.

Ensuite on va creer une boucle qui va lire le status register. Dés
qu’un SCANCODE valide est disponible on le converti en ascii avec la
table de keymap et on l’affiche.

Puis nous allons renvoier le même SCANCODE au controlleur et appeller
l’interruption du clavier pour que le systeme d’exploitation puisse
traiter l’évenement comme si rien ne s’était passé.



0×03 IOAPIC:

Tout d’abord nous devons savoir quel vecteur d’interruption est assigné
à l’IRQ 1, ceci est possible en le demandant au IOAPIC controller. Je
vais expliquer son fonctionement:

Ce controlleur est accessible a l’adresse physique 0xFEC00000 depuis
deux registres le premier est l’IOREGSEL situé en 0xFEC00000 et le
second est l’OWIN situé en 0xFEC00010.
En envoyant une requette sur l’IOREGSEL on peut accéder à
l’I/O REDIRECTION TABLE REGISTERS qui contiennent les vecteurs des
interrutptions associées aux IRQs.

10-11h (IOREDTBL0)  28-29h (IOREDTBL12)
12-13h (IOREDTBL1)  2A-2Bh (IOREDTBL13)
14-15h (IOREDTBL2)  2C-2Dh (IOREDTBL14)
16-17h (IOREDTBL3)  2E-2Fh (IOREDTBL15)
18-19h (IOREDTBL4)  30-31h (IOREDTBL16)
1A-1Bh (IOREDTBL5)  32-33Fh (IOREDTBL17)
1C-1Dh (IOREDTBL6)  34-35h (IOREDTBL18)
1E-1Fh (IOREDTBL7)  36-37h (IOREDTBL19)
20-21h (IOREDTBL8)  38-39h (IOREDTBL20)
22-23h (IOREDTBL9)  3A-3Bh (IOREDTBL21)
24-25h (IOREDTBL10) 3C-3Dh (IOREDTBL22)
26-27h (IOREDTBL11) 3E-3Fh (IOREDTBL23)

Les numéros correspondent à des offsets qu’on doit specifier dans notre
requette envoyée par le registre IOREGSEL ensuite la valeur se trouvant
à ses offsets nous sera envoyée par le registre OWIN qu’il suffira de lire.

Chaque entrée dans cette table fait 64bits seul le premier DWORD nous
intérresse ici car c’est le premier octet contient notre vecteur.

	IOAPICGate = (PULONG)MapIOAPIC();
	IOAPICGate[0] = 12; //request offset of IOREDTBL1
	vector = (unsigned char)IOAPICGate[4] & 0xff;

Il est possible de modifier ces tables en requérant leur offset avec le
IOREGSEL et en ecrivant dans l’OWIN.
Pour plus d’informations concernant le IOAPIC voir la doc officielle ici:

http://download.intel.com/design/chipsets/datashts/29056601.pdf



0×04 Disable kdb IRQ:

Pour récupérer les SCANCODES il est important de s’assurer que
l’interruption dédié au clavier ne soit pas executée car dés qu’une
touche est pressee celle-ci sera declanchée et lira le OUTPUT_BUFFER
qui sera remis à 0. Il nous faut dont désactiver l’IRQ du clavier.
Pour ce faire nous pouvons écrire une valeur dans le command register
du keyboard controller ou bien nous pouvons masker l’IRQ dans
I/O REDIRECTION TABLE REGISTER du IOAPIC controller.

Command register:

	+-+-----+--+--+-----+----+---+---+
	¦7¦  6  ¦5 ¦ 4¦  3  ¦ 2  ¦ 1 ¦ 0 ¦
	+-+-----+--+--+-----+----+---+---+
	¦0¦XLATE¦ME¦KE¦IGNLK¦SYSF¦MIE¦KYE¦
	+-+-----+--+--+-----+----+---+---+

Si on met le bit KYE à zéro l’IRQ du clavier sera désactivée mais on
pourra toujours lire les SCANCODEs.

Tout d’abord on va lire le command register et modifier le bit KYE.

	//////////////////////////////////////////////////////////////////////
	///// Mask Kdb IRQ
	while((READ_PORT_UCHAR((PUCHAR)0x64) & INPUT_BUFFER_FULL));
	WRITE_PORT_UCHAR((PUCHAR)0x64, 0x20);
	//La commande 0x20 nous permet de récupérer le command register
	//dans le OUTPUT_BUFFER.

	while((!READ_PORT_UCHAR((PUCHAR)0x64) & OUTPUT_BUFFER_FULL));
	CommandReg = READ_PORT_UCHAR((PUCHAR)0x60);
	//On lit le command register.

	while((READ_PORT_UCHAR((PUCHAR)0x64) & INPUT_BUFFER_FULL));
	WRITE_PORT_UCHAR((PUCHAR)0x64, 0x60);
	//La commande 0x60 nous permet d'écrire de modifier le
	//command register par le biais du INPUT_BUFFER.

	while((READ_PORT_UCHAR((PUCHAR)0x64) & INPUT_BUFFER_FULL));
	WRITE_PORT_UCHAR((PUCHAR)0x60,CommandReg^1);
	//On écrit la valeur du command register recupérée avec
	//le bit KYE désactivé.
	/////
	//////////////////////////////////////////////////////////////////////

Pour réactiver l’IRQ il suffira de mette le bit KYE a 1.



0×05 Regenerate SCANCODE:

Nous allons écrire le SCANCODE préalablement récupéré dans le
OUTPUT_BUFFER comme ça nous pourrons appeller l’interruption du clavier
pour que windows traite le SCANCODE en toute transparence.

	while(READ_PORT_UCHAR((PUCHAR)0x64) & INPUT_BUFFER_FULL);

	WRITE_PORT_UCHAR((PUCHAR)0x64, 0xD2); //Commande pour mettre un SCANCODE dans
	//l'OUTPUT_BUFFER

	while(READ_PORT_UCHAR((PUCHAR)0x64) & INPUT_BUFFER_FULL);

	WRITE_PORT_UCHAR((PUCHAR)0x60, ScanCode); //Ecrit la valeur.

	// Sert a confirmer que le SCANCODE a bien été reçu.
	while(!(READ_PORT_UCHAR((PUCHAR)0x64) & OUTPUT_BUFFER_FULL));



0×06 Call Kdb Interrupt:

	unsigned char vector;
	unsigned char KdbInterrupt[] = {0xCD, 0×00, 0xC3};

	vector = GetIRQ1InterruptVector();
	KdbInterrupt[1] = vector;

	((void (*)(void))KdbInterrupt)();



0×07 Conclusion:

Je me suis inspiré d’un code de Chpie trouvé sur rootkit.com mais n’ayant
rien compris dans un premier temps j’ai décidé d’en savoir plus affin de
pouvoir vulgariser cette méthode intérressante. Puisqu’elle est à priori
indétectable, bien-sûr indépendament du driver :)

Un petit coucou au people de: #uct,#fat,#fret,#carib0u,#oldschool et
spécialement à kaze, Baboon, OverClok, fyury, Ivanlef0u…

ps: Je ne suis pas un gros geek :p



0×08 Références:

http://www.rootkit.com/newsread.php?newsid=854 //original code
http://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html //kdb specs
http://download.intel.com/design/chipsets/datashts/29056601.pdf //IOAPIC

kdbpoll_pack.zip
(Utilisez Dbgview pour voir les méssages du driver, si vous avez une configuration
multi CPU effacez cette ligne: #define SINGLE_CPU. Attention VMware ne
supporte pas l’IOAPIC donc laissez #define SINGLE_CPU.)

.:Solution to The secret of the invisible lookup table crackme:.

Tools: ollydbg, ida, syser+vm, rootkit unhooker.

	0x01 Introduction
	0x02 TableCrackme.exe
	0x03 Get the driver
	0x04 ZLFBEQY.SYS
	0x05 NewZwCreateFile
	0x06 KeyGen and conclusion

0×01 Introduction:

This crackmes uses a driver and hooks SSDT to compute serial, i’ll explain
how the serial check works and how to do a keygen.
I didn’t analyse the tblcrt.exe it’s very obfuscated and tricky :)
But i manage to get the driver file that it creates.

0×02 TableCrackme.exe:

This executable doesn’t have any antidebug trick but it might be a litle
hard to get the serial routine because it uses MFC.

Just load it with ollydbg and goto USER32.GetWindowTextA and set a breakpoint.
And enter your name longer than 4 chars and a serial with 8 chars.

Olly should breack in GetWindowTextA, trace until return to main module.

00401369   . 68 FF000000    PUSH 0FF
0040136E   . 8D8424 0C01000>LEA EAX,DWORD PTR SS:[ESP+10C]
00401375   . 50             PUSH EAX
00401376   . 8D4E 78        LEA ECX,DWORD PTR DS:[ESI+78]
00401379   . E8 00070000    CALL 
0040137E   . 68 FF000000    PUSH 0FF
00401383   . 8D4C24 0C      LEA ECX,DWORD PTR SS:[ESP+C]
00401387   . 51             PUSH ECX
00401388   . 8D8E CC000000  LEA ECX,DWORD PTR DS:[ESI+CC]
0040138E   . E8 EB060000    CALL 

First CALL gets your name and the second gets your serial.

A few instruction after, the crackme checks the length of the name:

004013A0   > 8A08           MOV CL,BYTE PTR DS:[EAX]
004013A2   . 83C0 01        ADD EAX,1
004013A5   . 84C9           TEST CL,CL
004013A7   .^75 F7          JNZ SHORT TableCra.004013A0
004013A9   . 2BC2           SUB EAX,EDX
004013AB   . 83F8 04        CMP EAX,4
004013AE   . 76 48          JBE SHORT TableCra.004013F8

So the must must be longer than 4 chars…

Now crackme does the same thing to serial:

004013B7   > 8A08           MOV CL,BYTE PTR DS:[EAX]
004013B9   . 83C0 01        ADD EAX,1
004013BC   . 84C9           TEST CL,CL
004013BE   .^75 F7          JNZ SHORT TableCra.004013B7
004013C0   . 2BC2           SUB EAX,EDX
004013C2   . 83F8 08        CMP EAX,8
004013C5   . 75 31          JNZ SHORT TableCra.004013F8

Serial must be 8 chars long…

Then serial is converted to a number:

004013C7   . 8D5424 04      LEA EDX,DWORD PTR SS:[ESP+4]
004013CB   . 52             PUSH EDX
004013CC   . 8D4424 0C      LEA EAX,DWORD PTR SS:[ESP+C]
004013D0   . 68 B8354000    PUSH TableCra.004035B8                   ; |format = “%x”
004013D5   . 50             PUSH EAX                                 ; |s
004013D6   . FF15 C8324000  CALL DWORD PTR DS:[<&MSVCR71.sscanf>]    ; \sscanf

And stored in [ESP+4].

Now comes the important routine:

004013E8   . 51             PUSH ECX                                 ; /Arg2
004013E9   . 8D9424 0C01000>LEA EDX,DWORD PTR SS:[ESP+10C]           ; |
004013F0   . 52             PUSH EDX                                 ; |Arg1
004013F1   . 8BCE           MOV ECX,ESI                              ; |
004013F3   . E8 58FEFFFF    CALL TableCra.00401250                   ; \TableCra.00401250

The parametres are name and a DWORD (serial converted to DWORD)

Let’s check this routine:

00401290  |> 0FBE1432       /MOVSX EDX,BYTE PTR DS:[EDX+ESI]
00401294  |. 33C0           |XOR EAX,EAX
00401296  |> 0FBE1C30       |/MOVSX EBX,BYTE PTR DS:[EAX+ESI]
0040129A  |. 03CA           ||ADD ECX,EDX
0040129C  |. 03DA           ||ADD EBX,EDX
0040129E  |. 03DB           ||ADD EBX,EBX
004012A0  |. 03C9           ||ADD ECX,ECX
004012A2  |. 33CB           ||XOR ECX,EBX
004012A4  |. 83C0 01        ||ADD EAX,1
004012A7  |. 03E9           ||ADD EBP,ECX
004012A9  |. 3BC7           ||CMP EAX,EDI
004012AB  |.^72 E9          |\JB SHORT TableCra.00401296
004012AD  |. 8B5424 10      |MOV EDX,DWORD PTR SS:[ESP+10]
004012B1  |. 83C2 01        |ADD EDX,1
004012B4  |. 3BD7           |CMP EDX,EDI
004012B6  |. 895424 10      |MOV DWORD PTR SS:[ESP+10],EDX
004012BA  |.^72 D4          \JB SHORT TableCra.00401290

Here a hash of the name will be computed and the result will be stored
in EBP. I’ve translated this code to C:

NameLength = strlen(Name);
for(i=0;i < NameLength;i++)
{
	for(j=0;j < NameLength;j++)
	{
		NameHash = NameHash + Name[i];
		NameHash = 2*NameHash;
		NameHash = NameHash^(2*(Name[i]+Name[j]));
		NameHashFinal = NameHashFinal+NameHash;
	}
}

Result for rAsM_ is 6EB462A8.

Then the crackme gets the temp path:

004012C4  |. 52             PUSH EDX                                 ; /Buffer
004012C5  |. 68 00020000    PUSH 200                                 ; |BufSize = 200 (512.)
004012CA  |. FF15 10304000  CALL DWORD PTR DS:[<&KERNEL32.GetTempPat>; \GetTempPathA

And concates this str with:l00KuPtAbl3.+serial

004012D7  |. 50             PUSH EAX                                 ; /<%08X>
004012D8  |. 8D4C24 1C      LEA ECX,DWORD PTR SS:[ESP+1C]            ; |
004012DC  |. 51             PUSH ECX                                 ; |<%s>
004012DD  |. 8D9424 2002000>LEA EDX,DWORD PTR SS:[ESP+220]           ; |
004012E4  |. 68 78344000    PUSH TableCra.00403478                   ; |Format = “%sl00KuPtAbl3.%08X”
004012E9  |. 52             PUSH EDX                                 ; |s
004012EA  |. FF15 08334000  CALL DWORD PTR DS:[<&USER32.wsprintfA>]  ; \wsprintfA

Here is the result: C:\DOCUME~1\rAsM\LOCALS~1\Temp\l00KuPtAbl3.12345678

Then the crackme uses CreateFile with this filename:
C:\DOCUME~1\rAsM\LOCALS~1\Temp\l00KuPtAbl3.12345678

004012F3  |. 6A 00          PUSH 0                                   ; /hTemplateFile = NULL
004012F5  |. 6A 02          PUSH 2                                   ; |Attributes = HIDDEN
004012F7  |. 6A 03          PUSH 3                                   ; |Mode = OPEN_EXISTING
004012F9  |. 6A 00          PUSH 0                                   ; |pSecurity = NULL
004012FB  |. 6A 01          PUSH 1                                   ; |ShareMode = FILE_SHARE_READ
004012FD  |. 68 00000080    PUSH 80000000                            ; |Access = GENERIC_READ
00401302  |. 8D8424 3002000>LEA EAX,DWORD PTR SS:[ESP+230]           ; |
00401309  |. 50             PUSH EAX                                 ; |FileName
0040130A  |. FF15 0C304000  CALL DWORD PTR DS:[<&KERNEL32.CreateFile>; \CreateFileA

A file like that doen't exist but CreateFile returns D71B18D0 if you
loaded tblcrt.exe instead of INVALID_HANDLE_VALUE (0xFFFFFFFF) there
should be a trick here, CreateFile should be hooked.

After the fake handle is compared to the hash of the name if the both
are the same goodboy messagebox is called.

00401310  |. 3BC5           CMP EAX,EBP
00401312  |. 75 1D          JNZ BADBOY

So i loaded rootkit unhooker and i saw that ZwCreateFile is hooked
by ZLFBEQY.SYS now we have to get the original file and analyse it.
Rootkit unhooker can dump it but dump cannot be loaded even the file
checksum is recomputed.

0x03 Get the driver:

I used the .local thechnic with a patched advapi32.dll i puted a 0xEBFE
on CreateServiceA entry, I attached with olly,then I got the filename of
the driver (it's a parameter of CreateServiceA) and i copied the file.

0x04 ZLFBEQY.SYS:

First of all, before to debug it with syser you have to patch all:

F7DC  NEG ESP
F7DC  NEG ESP

Because this causes bound exception and syser bsods. Replace all
F7DCF7DC with nops. And recompute the checksum of the file with
lordpe.

The driver imports this symbol: KeServiceDescriptorTable when the driver
is loaded KeServiceDescriptorTable receives a pointer to a
_SYSTEM_SERVICE_TABLE structure.

typedef struct _SYSTEM_SERVICE_TABLE
{
	PNTPROC	ServiceTable;   // pointer to SSDT
	PDWORD	CounterTable;
	DWORD	ServiceLimit;   // number of table entries
	PBYTE	ArgumentTable;
}

Then the driver gets ServiceLimit and ServiceTable and creates a
MemoryDescriptoList for the SSDT, it maps and lock the MDL to remove
write protection on SSDT.

After it does a InterlockedExchange to exange OldZwCreateFile for
NewZwCreateFile.

.text:00010570                 mov     eax, [esi+1]
.text:00010573                 mov     ecx, dword_10718
.text:00010579                 lea     ecx, [ecx+eax*4] ; Target
.text:0001057C                 mov     edx, offset sub_10310 ; Value <- NewZwCreateFile!!
.text:00010581                 call    ds:InterlockedExchange

EAX is the index of ZwCreateFile on table and ecx points to SSDT.
Now the hook is set if you want to debug it you can set a breakpoint
on the NewZwCreateFile.

0×05 NewZwCreateFile

We a next to the goal, i copied a clean listing from ida, i’ve removed
obfuscation too.

First of all we need to know the parameters of this api:

NTSTATUS    ZwCreateFile(
	OUT PHANDLE  FileHandle,
	IN ACCESS_MASK  DesiredAccess,
	IN POBJECT_ATTRIBUTES  ObjectAttributes,
	OUT PIO_STATUS_BLOCK  IoStatusBlock,
	IN PLARGE_INTEGER  AllocationSize  OPTIONAL,
	IN ULONG  FileAttributes,
	IN ULONG  ShareAccess,
	IN ULONG  CreateDisposition,
	IN ULONG  CreateOptions,
	IN PVOID  EaBuffer  OPTIONAL,
	IN ULONG  EaLength
	);

The filename is in a OBJECT_ATTRIBUTES structure:

typedef struct _OBJECT_ATTRIBUTES {
	ULONG  Length;
	HANDLE  RootDirectory;
	PUNICODE_STRING  ObjectName;
	ULONG  Attributes;
	PVOID  SecurityDescriptor;
	PVOID  SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;

PUNICODE_STRING ObjectName: is our filename without path so the crackme
it to a ascii string and checks the lenght of possible l00KuPtAbl3.12345678

.text:0001035F                 cmp     [ebp+DestinationString.Length], 14h
.text:00010364                 jb      loc_10442

Then length match it scans the filename for l00KuPtAbl3.

.text:00010370                 push    offset aL00kuptabl3_ ; "l00KuPtAbl3."
.text:00010375                 push    [ebp+DestinationString.Buffer] ; char *
.text:00010378                 call    ds:strstr

If the strstr return the good value the driver converts the serial to
a DWORD with this routine:

.text:00010395 loc_10395:                              ; CODE XREF: sub_10310+CEj
.text:00010395                 call    sub_10300
.text:00010395 ; ---------------------------------------------------------------------------
.text:0001039A                 db  95h ; ò
.text:0001039B ; ---------------------------------------------------------------------------
.text:0001039B                 mov     al, [edi+ebx+0Ch]
.text:0001039F                 cmp     al, 30h
.text:000103A1                 jl      short loc_103BE
.text:000103A3                 cmp     al, 39h
.text:000103A5                 jg      short loc_103BE
.text:000103A7                 add     esi, 0FFFFFFFDh
.text:000103AA                 movsx   eax, al
.text:000103AD                 shl     esi, 4
.text:000103B0                 add     esi, eax
.text:000103B2                 mov     [ebp+var_4], esi
.text:000103B5                 call    sub_10304
.text:000103B5 ; —————————————————————————
.text:000103BA                 db  56h ; V
.text:000103BB                 db  31h ; 1
.text:000103BC ; —————————————————————————
.text:000103BC                 jmp     short loc_103DA
.text:000103BE ; —————————————————————————

.text:000103BE loc_103BE:                              ; CODE XREF: sub_10310+91j
.text:000103BE                                         ; sub_10310+95j
.text:000103BE                 cmp     al, 41h
.text:000103C0                 jl      short loc_10436
.text:000103C2                 cmp     al, 46h
.text:000103C4                 jg      short loc_10436
.text:000103C6                 movsx   eax, al
.text:000103C9                 shl     esi, 4
.text:000103CC                 lea     esi, [esi+eax-37h]
.text:000103D0                 mov     [ebp+var_4], esi
.text:000103D3                 call    sub_10304
.text:000103D3 ; —————————————————————————
.text:000103D8                 db  76h ; v
.text:000103D9                 db  14h
.text:000103DA ; —————————————————————————
.text:000103DA

.text:000103DA loc_103DA:                              ; CODE XREF: sub_10310+ACj
.text:000103DA                 inc     edi
.text:000103DB                 cmp     edi, 8
.text:000103DE                 jb      short loc_10395

Now the the driver will transform the result like that:

.text:000103EC                 mov     edx, [ebp+var_4] ; our serial!!
.text:000103EF                 xor     edx, 8D37423Ah
.text:000103F5                 ror     edx, 5
.text:000103F8                 call    sub_10300
.text:000103F8 ; —————————————————————————
.text:000103FD                 db  19h
.text:000103FE ; —————————————————————————
.text:000103FE                 bswap   edx
.text:00010400                 ror     edx, 15h
.text:00010403                 call    sub_10304
.text:00010403 ; —————————————————————————
.text:00010408                 db  35h ; 5
.text:00010409                 db  47h ; G
.text:0001040A ; —————————————————————————
.text:0001040A                 add     edx, 0F5A73C0h
.text:00010410                 mov     [ebp+var_4], edx

0×06 KeyGen and conclusion:

To make keygen we only need to rip the routine found on TableCrackme.exe:

00401290  |> 0FBE1432       /MOVSX EDX,BYTE PTR DS:[EDX+ESI]
00401294  |. 33C0           |XOR EAX,EAX
00401296  |> 0FBE1C30       |/MOVSX EBX,BYTE PTR DS:[EAX+ESI]
0040129A  |. 03CA           ||ADD ECX,EDX
0040129C  |. 03DA           ||ADD EBX,EDX
0040129E  |. 03DB           ||ADD EBX,EBX
004012A0  |. 03C9           ||ADD ECX,ECX
004012A2  |. 33CB           ||XOR ECX,EBX
004012A4  |. 83C0 01        ||ADD EAX,1
004012A7  |. 03E9           ||ADD EBP,ECX
004012A9  |. 3BC7           ||CMP EAX,EDI
004012AB  |.^72 E9          |\JB SHORT TableCra.00401296
004012AD  |. 8B5424 10      |MOV EDX,DWORD PTR SS:[ESP+10]
004012B1  |. 83C2 01        |ADD EDX,1
004012B4  |. 3BD7           |CMP EDX,EDI
004012B6  |. 895424 10      |MOV DWORD PTR SS:[ESP+10],EDX
004012BA  |.^72 D4          \JB SHORT TableCra.00401290

And we do the reverse operations of the driver to the result of
this routine.

sub result,0F5A73C0h
rol result,15h
bswap result
rol result,5
xor result,8D37423Ah

Final words this crackme was very nice, I love ring0 crackmes, thanks to
warrantyVoider.

table_crackme_pack.zip

.:Solution to Archangel’s Against_Driver keygenme:.

Contact: http://rem-team.webou.net/forum
The tools used are: a virtual machine with windows xp, WinDbg and IDA.

0×00: The executable:

At the entry point we find a simple dialogbox creation routine:

.text:0040105F start proc near
.text:0040105F push offset ModuleName ; "ntdll.dll"
.text:00401064 call GetModuleHandleA
.text:00401069 mov hModule, eax
.text:0040106E push 0 ; lpModuleName
.text:00401070 call GetModuleHandleA
.text:00401075 push 0 ; dwInitParam
.text:00401077 push offset DialogFunc ; lpDialogFunc
.text:0040107C push 0 ; hWndParent
.text:0040107E push offset TemplateName ; "GUIDIALOG"
.text:00401083 push eax ; hInstance
.text:00401084 call DialogBoxParamA
.text:00401089 push 0 ; uExitCode
.text:0040108B call ExitProcess
.text:0040108B start endp

Now let’s see what the DialogFunc does:
This gets the address of RtlAddVectoredExceptionHandler located in ntdll.
(Remember GetModuleHandleA at entry-point)

.text:004010B3 push offset ProcName ; "RtlAddVectoredExceptionHandler"
.text:004010B8 push hModule ; hModule
.text:004010BE call GetProcAddress
.text:004010C3 mov dword_403308, eax

The next portion of the DialogFunc loads the driver in a “typical” way.

.text:004010C8 push 0F003Fh ; dwDesiredAccess
.text:004010CD push 0 ; lpDatabaseName
.text:004010CF push 0 ; lpMachineName
.text:004010D1 call OpenSCManagerA
.text:004010D6 or eax, eax
.text:004010D8 jz loc_401184
.text:004010DE mov hSCObject, eax
.text:004010E3 push offset FilePart ; lpFilePart
.text:004010E8 push offset BinaryPathName ; lpBuffer
.text:004010ED push 104h ; nBufferLength
.text:004010F2 push offset FileName ; "Engine.sys"
.text:004010F7 call GetFullPathNameA
.text:004010FC push 0 ; lpPassword
.text:004010FE push 0 ; lpServiceStartName
.text:00401100 push 0 ; lpDependencies
.text:00401102 push 0 ; lpdwTagId
.text:00401104 push 0 ; lpLoadOrderGroup
.text:00401106 push offset BinaryPathName ; lpBinaryPathName
.text:0040110B push 0 ; dwErrorControl
.text:0040110D push 3 ; dwStartType
.text:0040110F push 1 ; dwServiceType
.text:00401111 push 10030h ; dwDesiredAccess
.text:00401116 push offset DisplayName ; "Protection Engine"
.text:0040111B push offset ServiceName ; "Engine"
.text:00401120 push hSCObject ; hSCManager
.text:00401126 call CreateServiceA
.text:0040112B or eax, eax
.text:0040112D jz short loc_40117D
.text:0040112F mov hService, eax
.text:00401134 push 0 ; lpServiceArgVectors
.text:00401136 push 0 ; dwNumServiceArgs
.text:00401138 push hService ; hService
.text:0040113E call StartServiceA
.text:00401143 or eax, eax
.text:00401145 jz short loc_401176
.text:00401147 push 0 ; hTemplateFile
.text:00401149 push 0 ; dwFlagsAndAttributes
.text:0040114B push 3 ; dwCreationDisposition
.text:0040114D push 0 ; lpSecurityAttributes
.text:0040114F push 0 ; dwShareMode
.text:00401151 push 0C0000000h ; dwDesiredAccess
.text:00401156 push offset a_ProtectionEng ; "\\\\.\\Protection Engine"
.text:0040115B call CreateFileA
.text:00401160 cmp eax, 0FFFFFFFFh
.text:00401163 jz short loc_40116F
.text:00401165 mov hObject, eax
.text:0040116A jmp loc_4012FB

Now driver will be loaded and we should understand how the keygenme will use
name and serial. When we will press the Check button the crackme will get our
data and send it to the driver.

.text:00401221 push 7Fh ; cchMax
.text:00401223 push offset String2 ; lpString
.text:00401228 push 3EEh ; nIDDlgItem
.text:0040122D push [ebp+hDlg] ; hDlg
.text:00401230 call GetDlgItemTextA
.text:00401235 push 7Fh ; cchMax
.text:00401237 push offset byte_403080 ; lpString
.text:0040123C push 3EFh ; nIDDlgItem
.text:00401241 push [ebp+hDlg] ; hDlg
.text:00401244 call GetDlgItemTextA
.text:00401249 push offset String2 ; lpString2
.text:0040124E push offset String ; lpString1
.text:00401253 call lstrcpyA
.text:00401258 push offset unk_402194 ; lpString2
.text:0040125D push offset String ; lpString1
.text:00401262 call lstrcatA
.text:00401267 push offset byte_403080 ; lpString2
.text:0040126C push offset String ; lpString1
.text:00401271 call lstrcatA
.text:00401276 push offset String ; lpString
.text:0040127B call lstrlenA
.text:00401280 lea ecx, loc_4012B4
.text:00401286 push 0 ; lpOverlapped
.text:00401288 push offset BytesReturned ; lpBytesReturned
.text:0040128D push 14h ; nOutBufferSize
.text:0040128F push ecx ; lpOutBuffer
.text:00401290 push eax ; nInBufferSize
.text:00401291 push offset String ; lpInBuffer
.text:00401296 push 22E400h ; dwIoControlCode
.text:0040129B push hObject ; hDevice
.text:004012A1 call DeviceIoControl

I put as name: rAsM and as serial:qBpN¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
These two strings will be copied in a single buffer as:

00403100 72 41 73 4D 01 71 42 70 rAsM_qBp
00403108 4E BF BF BF BF BF BF BF N¿¿¿¿¿¿¿
00403110 BF BF BF BF BF BF BF BF ¿¿¿¿¿¿¿¿
00403118 BF ¿

Until here nothing interesting happens, now it’s time to communicate with the
driver the crackme will call DeviceIoControl to do this, the parameters:
lpInBuffer with our info and the out buffer will be loc_4012B4.

.text:004012B4 loc_4012B4: ; DATA XREF: DialogFunc+1F0_o
.text:004012B4 xor eax, eax
.text:004012B6 xchg eax, [eax]
.text:004012B8 xchg eax, [eax]
.text:004012BA xchg eax, [eax]
.text:004012BC xchg eax, [eax]
.text:004012BE xchg eax, [eax]
.text:004012C0 xchg eax, [eax]
.text:004012C2 xchg eax, [eax]
.text:004012C4 xchg eax, [eax]
.text:004012C6 xchg eax, [eax]

First hypothesis is that the driver will patch the code here.
Just after the DeciceIoControl call, crackme calls
RtlAddVectoredExceptionHandler and the exception handler is the badboy
message:

.text:004012A6 push offset sub_401000 ;Handler
.text:004012AB push 1
.text:004012AD mov eax, dword_403308
.text:004012B2 call RtlAddVectoredExceptionHandler
.text:00401000 sub_401000 proc near ; DATA XREF: DialogFunc+216_o
.text:00401000 push ebp
.text:00401001 mov ebp, esp
.text:00401003 push 10h ; uType
.text:00401005 push offset Caption ; "ERROR"
.text:0040100A push offset Text ; "The code you've entered is invalid"
.text:0040100F push 0 ; hWnd
.text:00401011 call MessageBoxA

So we understand that the code will be modified in function of our name and
serial if the modified code is invalid an exception would happens and badboy
message will be called. We must debug the driver to understand what happens.

0×01 The Driver:

Just before we learn that the crackme communicate with his driver by
DeviceIoControl, we must understand what happens in the driver when
DeviceIoControl is called. At DriverEntry (driver entry point) the driver
must fill a structure with the pointers of the functions it supports. The
most interesting function is the one which is called by the I/O system to
answer to the DeviceIoControl.

INIT:0001083D mov eax, [ebp+DriverObject]
INIT:00010840 mov dword ptr [eax+38h], offset sub_10541
INIT:00010847 mov dword ptr [eax+40h], offset sub_10541
INIT:0001084E mov dword ptr [eax+70h], offset sub_10581 < this one
INIT:00010855 mov dword ptr [eax+34h], offset sub_10565

Now we should place a breakpoint (0xCC) at the function for this take your
favorite hex editor and don’t forget to recalculate the checksum with your pe
editor (lord_Pe can do this) if the checksum is bad driver cannot be loaded.

.text:00010581 push ebp ;replace by int3

To debug this driver I used WinDbg as remote debugger and vmware for the
virtual machine. You can also use SoftIce in local or remote and Syser in
local. But the advantage to do remote debugging of debugging in the vm is you
can use IDA in parallel.

Ok we need to launch the keygenme in the vm with the patched driver and
WinDbg will break. We must restore the old instruction (push ebp) to do
this, use this command: “a” + “push ebp” + enter.
We are here: (use command “u” to disassemble code)

.text:00010581 push ebp
.text:00010582 mov ebp, esp
.text:00010584 add esp, 0FFFFFFF8h
.text:00010587 push esi
.text:00010588 push edi
.text:00010589 push ebx
.text:0001058A and [ebp+var_8], 0
.text:0001058E mov esi, [ebp+Irp]
.text:00010591 mov eax, esi
.text:00010593 mov eax, [eax+60h]
.text:00010596 mov edi, eax
.text:00010598 cmp dword ptr [edi+0Ch], 22E400h <=IO_CTL
.text:0001059F jnz short loc_1061B
.text:000105A1 cmp dword ptr [edi+4], 14h <=OutBuffer size
.text:000105A5 jnz short loc_10612
.text:000105A7 cmp dword ptr [edi+8], 100h <=InBuffer size
.text:000105AE ja short loc_10612

This code does nothing interesting it get the IRP structure which contains
the IOCTL it’s a number like a command ID. The driver checks it if it’s equal
to 22E400h the command is accepted.
Then the driver checks the InBuffer and out buffer size if the size is valid
we continue.
Here we are at the serial check:

.text:000105B0 push dword ptr [edi+8] < InBuffer size
.text:000105B3 mov edi, [esi+0Ch] < ptr to InBuffer
.text:000105B6 call sub_10280 < copy name in another buffer
.text:000105BB pop eax
.text:000105BC call sub_102A5 < copy serial in another buffer
.text:000105C1 call sub_102D8 < clears drx
.text:000105C6 call sub_102CA < saves edi
.text:000105CB call sub_102D8 < clears drx
.text:000105D0 call sub_102E9 < xors name and serial by 0x10
.text:000105D5 call sub_102D1 < restores edi
.text:000105DA call sub_10332 < clears drx
.text:000105DF call sub_102CA < saves edi
.text:000105E4 call sub_10341 < xors name and serial by 0xAC
.text:000105E9 call sub_102D1 < restores edi
.text:000105EE call sub_1038A < clears drx
.text:000105F3 call sub_10399 < xors name buffer by serial buffer
.text:000105F8 call sub_103C1 < construct the code
.text:000105FD call sub_1052A < copy the code to OutBuffer

InBuffer:

81fce8a8 72 41 73 4d 01 71 42 70-4e bf bf bf bf bf bf bf rAsM.qBpN.......
81fce8b8 bf bf bf bf bf bf bf bf-bf 12 00 82 00 00 00 00 ................

> The first call takes one parameters, the Inbuffer size, let’s see what
happens inside: First we got a loop to check the length of the name, result
will be in ebx.(Remember that name and serial are separated by the byte 0×01)

.text:00010284 loc_10284:
.text:00010284 cmp byte ptr [ebx+edi], 1
.text:00010288 jnz short loc_102A1
.text:000102A1 loc_102A1: ; CODE XREF: sub_10280+8_j
.text:000102A1 inc ebx
.text:000102A2 jmp short loc_10284

After the “strlen” the name is copied in a buffer:

.text:0001028A xchg ebx, edx
.text:0001028C lea ecx, unk_106E0 <=address of buffer
.text:00010292
.text:00010292 loc_10292: ; CODE XREF: sub_10280+1D_j
.text:00010292 cmp ebx, edx
.text:00010294 jz short locret_102A4
.text:00010296 mov al, [ebx+edi]
.text:00010299 mov [ecx], al
.text:0001029B inc ecx
.text:0001029C inc ebx
.text:0001029D jmp short loc_10292

Result:

f8d236e0 72 41 73 4d 00 00 00 00-00 00 00 00 00 00 00 00 rAsM............
f8da56f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

> We continue with the second call, this one does strlen as before and copies
the serial into another buffer.

Result:

f8da5760 71 42 70 4e bf bf bf bf-bf bf bf bf bf bf bf bf qBpN............
f8da5770 bf bf bf bf 00 00 00 00-00 00 00 00 00 00 00 00 ................

> The third call clears the debug registers; it’s useless if you use int3.

> Next call saves edi.

> The fifth call clears the drx one more time.

> Then the next call do some strlen of the name and xor it by 0×10 same thing
to the serial:

.text:000102E9 lea eax, unk_106E0
.text:000102EF push eax
.text:000102F0 push eax
.text:000102F1 pop edi
.text:000102F2 xor eax, eax
.text:000102F4 or ecx, 0FFFFFFFFh
.text:000102F7 repne scasb
.text:000102F9 not ecx
.text:000102FB dec ecx
.text:000102FC dec ecx
.text:000102FD mov edx, ecx
.text:000102FF pop eax
.text:00010300 xor ecx, ecx

Result: strlen-1 in edx

The xor loop:

.text:00010302 cmp ecx, edx
.text:00010304 ja short loc_1030D
.text:00010306 xor byte ptr [ecx+eax], 10h
.text:0001030A inc ecx
.text:0001030B jmp short loc_10302

Result:

f8da56e0 62 51 63 5d 00 00 00 00-00 00 00 00 00 00 00 00 bQc]............
f8da56f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

Same thing to the serial, result:

f8da5760 61 52 60 5e af af af af-af af af af af af af af aR`^............
f8da5770 af af af af 00 00 00 00-00 00 00 00 00 00 00 00 ................

> The next call restores edi.

> We continue and one more time the drx are cleared.

> Then the driver xors name and serial by 0xAC:

f8da56e0 ce fd cf f1 00 00 00 00-00 00 00 00 00 00 00 00 ................
f8da56f0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
f8da5760 cd fe cc f2 03 03 03 03-03 03 03 03 03 03 03 03 ................
f8da5770 03 03 03 03 00 00 00 00-00 00 00 00 00 00 00 00 ................

> Restores edi.

> Clears the drx.

> The nest call xors name buffer by serial buffer:

.text:00010399 lea eax, unk_106E0
.text:0001039F lea edx, unk_10760
.text:000103A5 xor ecx, ecx
.text:000103A7
.text:000103A7 loc_103A7: ; CODE XREF: sub_10399+1A_j
.text:000103A7 cmp ecx, 78h
.text:000103AA jz short locret_103B5
.text:000103AC mov bl, [edx]
.text:000103AE xor [eax], bl
.text:000103B0 inc eax
.text:000103B1 inc edx
.text:000103B2 inc ecx
.text:000103B3 jmp short loc_103A7
.text:000103B5 ; —————————————————————————
.text:000103B5
.text:000103B5 locret_103B5: ; CODE XREF: sub_10399+11_j
.text:000103B5 retn

Result:

f8da56e0 03 03 03 03 03 03 03 03-03 03 03 03 03 03 03 03 ................
f8da56f0 03 03 03 03 00 00 00 00-00 00 00 00 00 00 00 00 ................

> We are next to the end :) this call is big but there is nothing difficult
to understand, it will add or sub to each byte of the result a value. The
result of all these operations will be the code to call the goodboy
messagebox. Archangel was nice to us because every byte must be 0×03 before
the operations. :)

Result:

f8da56e0 6a 00 68 60 21 40 00 68-6b 21 40 00 6a 00 e8 8d j.h`!@.hk!@.j...
f8da56f0 00 00 00 90 00 00 00 00-00 00 00 00 00 00 00 00 ................

> Finally the driver must copy this to the OutBuffer (edi):

.text:0001052A sub_1052A proc near ; CODE XREF: sub_10581+7C_p
.text:0001052A lea eax, unk_106E0
.text:00010530 xor ecx, ecx
.text:00010532
.text:00010532 loc_10532: ; CODE XREF: sub_1052A+14_j
.text:00010532 cmp ecx, 15h
.text:00010535 jz short locret_10540
.text:00010537 mov bl, [eax]
.text:00010539 mov [ecx+edi], bl
.text:0001053C inc eax
.text:0001053D inc ecx
.text:0001053E jmp short loc_10532
.text:00010540 ; —————————————————————————
.text:00010540
.text:00010540 locret_10540: ; CODE XREF: sub_1052A+B_j
.text:00010540 retn
.text:00010540 sub_1052A endp

0×02 Return to the executable:

After the DeviceIoControl the code has been patched and goodboy messabox will
be called. If serial is invalid code will be invalid too and do some
exception, the handler will call badboy messagebox.

.text:004012B4 push 0 ; uType
.text:004012B6 push offset aWellDone ; "Well Done!"
.text:004012BB push offset aYesYouDidItNow ; "Yes, you did it, now write the
solution"...
.text:004012C0 push 0 ; hWnd
.text:004012C2 call MessageBoxA
.text:004012C7 nop

0×03 Keygen:

Recapitulate, name and serial are xored by 0×10 and by 0xAC and the results
are xored by each other. Serial must always be 20 chars, the name can be
shorter.
To find the serial part corresponding to the name we xor the name by 0×03 the
other operations aren’t important because they are effected in serial and
name they “simplify” themselves.
The rest of the serial is easy, we know after the two xors that result must
be 0×03 so we must xor 0×03 by 0xAC and 0×10, the result is 0xBF.

Name: rAsM
Serial: qBpN¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿

0×04 Conclusion:

This crackme was good; I was a bit disappointed about the anti-xxx
protections but this is good to initiate somebody to reversing ring0
applications. There is a little bug with names shorter than 20 chars you can
register them only one time because the buffers in the driver are not
cleared.

Greetz to Archangel the autor of this nice crackme, all the members of remteam
and specialy to Guetta, haiklr, 0vercl0ck, Ivanlef0u, Baboon,
Squallsurf, Kaze, Silma, Virtualabs and Kaine.

Download Pack:
against_driver_pack.zip

.:bootvid.dll secrets:.

Cet article explique comment utiliser les fonctions exportées par bootvid.dll
affin d’afficher du texte ou autre pendant le démarage de windows.

Tout d’abord intéressons nous à certaines fontions exportées par cette dll:

#ifndef _bootvid_h_
#define _bootvid_h_
...
VOID
VidDisplayStringXY(
  PUCHAR s,
  ULONG x,
  ULONG y,
  BOOLEAN Transparent
  );
...
#endif

VidDisplayStringXY:
Comme son nom l’indique cette fonction nous permet d’afficher une chaine de
caractères ascii à un certain endroit de l’écran, ceci en définissant des
coordonnées x et y.

Au demarage de windows il n’est pas possible d’utiliser un exécutable ring3,
il nous faut programmer un driver qui se chargera au boot affin d’afficher
notre texte. Il est important de noter que lors de l’appel de cette fonction,
un exception est declanchée. Donc il faudra installer un handler qui va gérer
celle-ci et empêcher un crash.

Voici le code qui appelle cette focntion:

	__try
	{
		VidDisplayStringXY("helloworld! :)",180,408,TRUE);
	}
	__except(1){}

Voila c’est tout pour aujourd’hui :) Je vous laisse éssaier le reste des
fonctions qui ont l’air très intérressantes. Les headers ont été trouvés sur
un site chinois; comme par hasard! :p

Les sources sont disponibles ici:
bootvid_pack.zip

.:A trip through PCI Bus:.

	0x01 Aperçu général du bus PCI.
	0x02 Communication avec le bus.
	0x03 Former une requête.
	0x04 Enumération des périphériques.
	0x05 Récupération des BARs.
	0x06 Le framebuffer.

0×01 Aperçu général du bus PCI:

Le Peripheral Component Interconnect (PCI) sert à ajouter des cartes
d’extension sur la carte mère. Il rend possible la communication des
cartes entre elles et l’écriture en mémoire sans passer par le processeur.

Chaque carte est un dispositif plug and play, il se charge automatiquement.
En effet celle ci peut demander des domaines de port I/O et aussi de la
mémoire RAM, le BIOS inspecte la configuration du bus et traite les requêtes
des divers périphériques connectés.

0×02 Communication avec le bus:

Pour communiquer avec le bus PCI il faut utiliser deux ports I/O:
- le premier est le CONFIG_ADDRESS (0xCF8)
- le second est le CONFIG_DATA (0xCFC)
Tout d’abord il faut formuler une requête sur 4 octets et envoyer celle-ci
sur le port 0xCF8 ensuite le bus va transférer la réponse à notre requête
sur le port 0xCFC.

0×03 Former une requête:

Tout d’abord intéressons nous au format de celle-ci:

  +------+--------+-----+------+--------+--------+---+
  |    31|   30-24|23-16| 15-11|    10-8|     7-2|1-0|
  +------+--------+-----+------+--------+--------+---+
  |Enable|Reserved|  Bus|Device|Function|Register| 00|
  +------+--------+-----+------+--------+--------+---+

Une fois ce format connu il suffit de faire une fonction qui prend touts
ces paramètres et qui construit notre requête.
Cette fonction a été trouvée sur osdev et a ete adaptée pour mon driver.

VOID PCISendConfig (unsigned short bus, unsigned short slot,
					unsigned short func, unsigned short offset)
{
	unsigned long address;
	unsigned long lbus = (unsigned long)bus;
	unsigned long lslot = (unsigned long)slot;
	unsigned long lfunc = (unsigned long)func;

	/* create configuration address */
	address = ((lbus << 16) | (lslot << 11) |(lfunc << 8 ) | (offset & 0xfc) |
		((UINT32)0x80000000));

	/* write out the address */
	WRITE_PORT_ULONG(((PULONG)0xCF8), address);
}

0×04 Enumération des périphériques:

Nous avons vu comment envoyer une requette au bus PCI maintenant regardons
les informations qu’il peut nous renvoyer.

  +----------+------------+------------+-------------+---------------+
  | register | bits 31-24 | bits 23-16 |  bits 15-8  |    bits 7-0   |
  +----------+------------+------------+-------------+---------------+
  |    00    |         Device ID       |           Vendor ID         |
  +----------+-------------------------+-----------------------------+
  |    04    |          Status         |            Command          |
  +----------+------------+------------+-----------------------------+
  |    08    | Class code |  Subclass  |          Revision ID        |
  +----------+------------+------------+-------------+---------------+
  |    0C    |    BIST    | Header type|Latency Timer|Cache Line Size|
  +----------+------------+------------+-------------+---------------+
  |    10    |Base address #0 (BAR0)                                 |
  +----------+-------------------------------------------------------+
  |    14    |Base address #1 (BAR1)                                 |
  +----------+-------------------------------------------------------+
  |    18    |Base address #2 (BAR2)                                 |
  +----------+-------------------------------------------------------+
  |    1C    |Base address #3 (BAR3)                                 |
  +----------+-------------------------------------------------------+
  |    20    |Base address #4 (BAR4)                                 |
  +----------+-------------------------------------------------------+
  |    24    |Base address #5 (BAR5)                                 |
  +----------+-------------------------------------------------------+
  |    28    |Cardbus CIS Pointer                                    |
  +----------+-------------------------+-----------------------------+
  |    2C    |       Subsystem ID      |    Subsystem Vendor ID      |
  +----------+-------------------------+-----------------------------+
  |    30    |Expansion ROM base address                             |
  +----------+-------------------------------------------------------+
  |    34    |Reserved                                               |
  +----------+-------------------------------------------------------+
  |    38    |Reserved                                               |
  +----------+------------+------------+-------------+---------------+
  |    3C    |Max latency |  Min Grant |Interrupt PIN| Interrupt Line|
  +----------+------------+------------+-------------+---------------+

Maintenant nous voulons récupérer le framebuffer qui est un des 6 Base Address
de notre carte graphique branchée sur le bus PCI. Il nous faut dans un premier
temps énumérer les différents périphériques sur le bus PCI…

Pour ce faire nous allons faire 2 boucles qui incrémenteront le bus et le slot,
on récupérera le Vendor ID et on vérifie sa validité puis on récupère le
Class code, si celui-ci est égal a 03 c’est qu’on a affaire a une carte vidéo.
Nous allons parcourir 32 slots par bus et ceci sur 255 bus.

Donc les paramètres seront le bus, le slot, function sera nul et offset sera
égal à 2 car il correspond au registre Vendor ID.

#define Display_controller  0x03
#define MAX_PCI_BUS 255
#define MAX_PCI_SLOT 32

for(bus = 0; bus < MAX_PCI_BUS; bus++)
{
	for(slot=0;slot < MAX_PCI_SLOT; slot++)
	{
		PCISendConfig(bus,slot,0,2); //to get vendor ID
		if(0xFFFF != PCIGetConfigWord()) //check valid vendor ID
		{
			PCISendConfig(bus,slot,0,8); //to get Class Code
			if(Display_controller == (PCIGetConfigLong()>>0×18))
                           {
				// Get BARs
			}
		}
	}
}

0×05 Récupération des BARs:

Maintenant il ne nous reste plus qu’a récupérer les BARs ainsi que leur
taille puis nous prendrons le plus grand, celui a le plus de chances d’être
notre framebuffer.

Cependant il est impératif d’en connaitre un peu plus sur ces adresses.
Le BAR est une adresse physique, pour pouvoir y accéder il nous faudra la
convertir en une adresse virtuelle.
Pour récupérer la taille il faut écrire dans le BAR 0xffffffff ensuite on
lit ce même BAR et on récupère la taille qu’il faudra convertir en nombre non
signé.

//Get bars
PCISendConfig(bus,slot,0,0x10); //to get BAR#0
pbar_regs->BAR0 = PCIGetConfigLong();
...

//Get bar size
PCISendConfig(bus,slot,0,0x10); //to get sz BAR#0
WRITE_PORT_ULONG(((PULONG)0xCFC), 0xffffffff);
pbar_regs->szBAR0 = PCIGetConfigLong();
pbar_regs->szBAR0 = ConvertBARsz(pbar_regs->szBAR0);
...

//restore bars
PCISendConfig(bus,slot,0,0x10); //to get BAR#0
WRITE_PORT_ULONG(((PULONG)0xCFC),pbar_regs->BAR0);

0×06 Le framebuffer:

Voila nous arrivons à la fin de notre parcours il nous faut « convertir »
l’adresse physique du BAR en une adresse virtuelle pour ce faire nous allons
utiliser l’api MmMapIoSpace.

PHYSICAL_ADDRESS bigestBAR;
unsigned long szbigestBAR;
unsigned long framebuffer;
...

framebuffer = MmMapIoSpace(bigestBAR,szbigestBAR,FALSE);

Il nous suffit juste d’écrire à l’adresse retournée pour afficher ce qu’on
veut. (Bien sûr, avant il vous faudra connaitre la résolution de l’écran et
le mode) :)

pci_pack.zip