Patcheur Search & Replace

Nouveaux concepts:

Fichiers mappés en mémoire
Instructions de chaîne d'assembleur
Utilisation de variables locales dans vos procédures
Renvoi de codes de réussite/erreur

Nouvelles fonctions API:

CreateFileMapping
MapViewOfFile
UnmapViewOfFile
CloseMapFile

 


Dans ce tutoriel, nous allons renforcer notre exemple précédent en ajoutant une fonctionnalité permettant d'analyser le fichier cible afin de trouver la séquence d'octets exacte à patcher. Ainsi, les modifications mineures apportées à la version du programme qui n'affectent pas la routine de protection ne perturberont pas le patch. Avant d'aborder ces concepts théoriques, nous devons une fois de plus poser quelques bases.

Fichiers mappés en mémoire

La mise en correspondance de fichiers est une technique permettant de visualiser le contenu d'un fichier sans nécessiter l'allocation d'un bloc de mémoire important ni le chargement complet du fichier. Le fichier en question peut être n'importe quel fichier sur le disque que vous souhaitez mapper en mémoire, ou même le fichier de pagination système.

La première étape consiste à créer un objet de mise en correspondance de fichiers qui peut contenir tout ou une partie du fichier. Il est sauvegardé par le fichier sur le disque. Cela signifie que lorsque le système échange des pages de l'objet de mise en correspondance de fichiers, toutes les modifications apportées à l'objet de mise en correspondance de fichiers sont écrites dans le fichier sur le disque. Lorsque les pages de l'objet de mise en correspondance de fichiers sont réintégrées, elles sont restaurées à partir du fichier.

La taille de l'objet de mise en correspondance de fichiers est indépendante de la taille du fichier mappé. Si l'objet de mise en correspondance de fichiers est plus petit que le fichier, le système mappe uniquement le nombre spécifié d'octets du fichier. La création d'un objet de mise en correspondance de fichiers ne consomme pas de mémoire physique (RAM), elle la réserve uniquement. Il est donc courant de créer un objet de la même taille que le fichier, même si vous ne prévoyez pas de visualiser l'intégralité du fichier, car cela ne représente aucun coût supplémentaire en termes de ressources système.

Cependant, si l'objet de mise en correspondance est plus grand que le fichier, le système étend le fichier en ajoutant un rembourrage de zéros avant que la fonction CreateFileMapping ne retourne. Les objets de mise en correspondance sont de taille statique ; une fois créés, leur taille ne peut pas augmenter ni diminuer.

L'étape suivante consiste à mapper une vue de fichier de cet objet dans l'espace d'adressage (mémoire virtuelle) de votre application. Une vue de fichier peut contenir tout ou une partie seulement de l'objet de mise en correspondance de fichiers, mais ne peut pas être plus grande. La fonction MapViewOfFile renvoie un pointeur vers la vue de fichier. En déréférençant le pointeur, une application peut lire des données à partir du fichier et écrire des données dans le fichier.

L'écriture dans la vue de fichier entraîne des modifications de l'objet de mise en correspondance de fichiers. L'écriture réelle dans le fichier sur le disque est gérée par le système. Les données ne sont pas réellement transférées au moment où l'objet de mise en correspondance de fichiers est écrit. Au lieu de cela, une grande partie de l'entrée-sortie (E/S) du fichier est mise en cache pour améliorer les performances générales du système. Cela peut être contourné en utilisant la fonction FlushViewOfFile, qui copie le nombre spécifié d'octets de la vue de fichier vers le fichier physique immédiatement, sans attendre que l'opération d'écriture mise en cache se produise.

Lorsqu'un processus a terminé avec l'objet de mise en correspondance de fichiers, il doit détruire toutes les vues de fichier dans son espace d'adressage en utilisant la fonction UnmapViewOfFile pour chaque vue de fichier. Si certaines pages de la vue de fichier ont été modifiées depuis la création de la vue, le système écrit les pages modifiées du fichier sur le disque en utilisant la mise en cache.

La mise en correspondance de fichiers présente plusieurs avantages :

1. Accès au fichier plus rapide et plus facile.
2. Seule la partie nécessaire d'un fichier doit être mise en correspondance en mémoire.
3. Mémoire partagée entre deux applications ou plus.
4. Pas besoin d'utiliser des fonctions API de mémoire et des fonctions API d'E/S de fichier séparées.

La mise en correspondance de fichiers permet à un processus d'accéder aux fichiers plus rapidement et plus facilement en utilisant un pointeur vers une vue de fichier. L'utilisation d'un pointeur améliore l'efficacité car le fichier réside sur le disque, tandis que la vue de fichier réside en mémoire.

Elle permet également au processus de travailler efficacement avec un grand fichier de données, tel qu'une base de données, sans avoir à mapper l'intégralité du fichier en mémoire. Lorsque le processus a besoin de données à partir d'une partie du fichier autre que celle présente dans la vue de fichier actuelle, il peut désactiver la vue de fichier actuelle puis mapper une nouvelle vue. La mise en correspondance de fichiers est également utilisée comme moyen de partager des données entre des processus.

Le chargeur Windows utilise la mise en correspondance de fichiers pour charger des fichiers PE en mémoire. C'est très pratique car seules les parties nécessaires peuvent être lues sélectivement à partir du fichier sur le disque. Sous Win32, vous devriez utiliser la mise en correspondance de fichiers autant que possible.

 


L'interface utilisateur est exactement la même que pour le dernier tutoriel, donc réutilisez simplement le même script de ressources et collez le code de SnRpatcher.asm dans WinAsm :



1

La procédure List est exactement la même que la dernière fois et la procédure Patch est très similaire avec quelques dispositions prises pour appeler la procédure Scan si la séquence d'octets cible n'est pas trouvée à l'adresse attendue :

1

La procédure Scan mappe simplement une vue du fichier et la scanne octet par octet à la recherche de la séquence qui nous intéresse. Lorsqu'une correspondance est trouvée pour le premier octet, l'octet suivant est comparé avec notre séquence cible. S'il y a une correspondance, l'octet suivant est comparé, et ainsi de suite, mais si le deuxième octet ne correspond pas, la numérisation se poursuit. Une variable locale est utilisée pour stocker les résultats à retourner dans eax par la routine. Ces questions sont discutées plus en détail ci-dessous :

1

Variables locales

Les variables locales sont définies avec la syntaxe suivante LOCAL name:size qui doit être sur la ligne immédiatement suivant l'instruction proc. Contrairement aux variables globales que nous avons l'habitude d'utiliser, elles ne peuvent pas être initialisées dans le cadre de la définition. Dans l'exemple ci-dessus, une instruction mov est utilisée pour initialiser la variable locale à 0. L'utilisation de variables locales présente l'avantage qu'elles ne peuvent être vues que par la procédure dans laquelle elles se trouvent et sont détruites lorsque la procédure se termine.

Elles sont également faciles à utiliser car la directive LOCAL demande à MASM de gérer tout l'aspect délicat de l'équilibrage de la pile. Le chargement de l'adresse des variables locales doit être fait différemment des variables globales. Étant donné que l'adresse est un décalage par rapport à un cadre de pile temporaire, l'opérateur offset ne peut pas renvoyer l'adresse et l'instruction lea (load effective address) doit être utilisée à la place.

Instructions de chaîne d'assembleur

Il existe 5 instructions de chaîne pour le traitement rapide et efficace de chaînes et tableaux entiers. Le terme "instructions de chaîne" fait référence à une séquence de n'importe quels éléments, pas seulement des chaînes de caractères. Ces instructions fonctionnent directement sur des tableaux d'octets, de mots et de dwords. Toutes ont une syntaxe similaire :

MOVS - pour copier ("déplacer") des données de tableau d'une zone mémoire à une autre.
SCAS - pour rechercher ("scanner") des données de tableau en les comparant à la valeur dans eax.
CMPS - pour comparer des données de tableau et pointer vers l'adresse après une correspondance (ou une absence de correspondance).
STOS - pour remplir des données de tableau en "stockant" la valeur dans eax dans chaque élément.
LODS - pour charger des données de tableau dans eax.

Chaque instruction de chaîne opère sur un élément de tableau à la fois et a des formes mnémoniques se terminant par b, w ou d (byte, word ou dword) pour spécifier la taille de l'élément de chaîne sur lequel opérer. Ces instructions de chaîne nécessitent souvent trois registres pour les opérandes :

ESI - adresse source des données de tableau
EDI - adresse de destination des données de tableau
ECX - compte d'éléments (facultatif)

Elles dépendent également de la valeur du fanion de direction dans le registre FLAGS et peuvent utiliser tout ou partie de EAX.

L'instruction incrémente ou décrémente SI et DI après qu'un élément de chaîne a été accédé afin qu'il pointe vers l'élément de tableau suivant. Le fanion de direction détermine si SI et DI sont incrémentés (fanion effacé, 0) ou décrémentés (fanion positionné, 1). La taille de l'instruction détermine si SI et DI sont modifiés de 1, 2 ou 4 octets à chaque fois.

Toutes, sauf LODS, peuvent être utilisées avec les préfixes d'instruction repeat (LODS n'est pas utilisée avec un préfixe repeat, car le rechargement continu de EAX avec la même valeur ne sert à rien). Le préfixe repeat fait en sorte que l'instruction qui le suit se répète automatiquement le nombre de fois spécifié dans ECX ou jusqu'à ce qu'une condition devienne vraie :

REP - Répète l'instruction CX fois
REPE (REPZ) - Répète l'instruction un maximum de CX fois tant que les valeurs sont égales
REPNE (REPNZ) - Répète l'instruction un maximum de CX fois tant que les valeurs ne sont pas égales

Dans l'exemple ci-dessus, en utilisant repnz scasb, l'adresse de notre séquence d'octets cible est chargée dans ESI, l'adresse de la vue du fichier est chargée dans EDI et le nombre d'octets dans le fichier est chargé dans ECX. Ensuite, le premier octet de notre séquence cible est copié dans EAX. Cela est dû au fait que SCAS fonctionne en balayant EDI à la recherche de la valeur dans EAX. repnz scasb continue de balayer EDI tant qu'aucune correspondance n'est trouvée (REPeat while Not Zero ou Not Equal), ou jusqu'à ce que ECX atteigne zéro (la fin du fichier) lorsque la routine se termine. Si une correspondance est trouvée, le balayage s'arrête, l'état actuel (ESI, EDI et ECX) est sauvegardé sur la pile et la partie de comparaison de la routine commence.

EDI est décrémenté de 1 (puisque l'instruction scas s'est arrêtée après la correspondance), ECX est défini sur 4 (la longueur de notre séquence cible) et ESI contient toujours l'adresse de notre séquence d'octets cible. Repz cmpsb compare les octets successifs dans ESI et EDI tant qu'ils correspondent (REPeat while Zero ou Equal) et s'arrête s'il y a une incompatibilité. Si ECX a diminué à 0 lorsque cmps s'arrête, cela signifie que les 4 octets ont été appariés et la routine se termine. Si ECX > 0 (c'est-à-dire moins de 4 octets appariés), alors l'état précédent est déchargé de la pile et la routine de balayage reprend.

L'action précise de chacune des instructions de chaîne doit être illustrée par des exemples, je recommande donc de prendre l'un des livres électroniques de la page de référence à la fin de ce tutoriel et de lire des explications plus détaillées.

Limitations de la conception du patcher

Ce patcher est très simple. Il n'y a qu'un seul octet à modifier et nous avons pris le temps de nous assurer que notre séquence d'octets cible ne se produisait qu'une seule fois. Plus de code serait nécessaire si plusieurs modifications étaient nécessaires ou si la séquence d'octets se produisait plusieurs fois mais qu'une seule instance devait être modifiée. Diablo2oo2, l'auteur du patcher (dup2) le plus excellent, a publié le code source du moteur de recherche et de remplacement sur son site web. C'est une routine très aboutie qui mérite d'être examinée. J'ai inclus une capture d'écran ici pour référence rapide, mais elle peut être téléchargée dans un fichier zip ici :



dup.search.and.replace.patchengine.sourcecode.rar

 


Copyright (C)- xtx Team (2021)

XHTML valide 1.1 CSS Valide !