Nouveaux Concepts: Utilisation des contrôles ListBox et CheckBox dans votre boîte de dialogue Ouverture, lecture, écriture et fermeture de fichiers Le Pointeur de Fichier Nouvelles fonctions API: CreateFile CopyFile SetFilePointer ReadFile WriteFile CloseHandle
|
Dans ce tutoriel, nous allons coder un simple patcher autonome qui ouvre un fichier cible dans le dossier courant (le patch doit être placé dans le même dossier que la cible), vérifie une certaine séquence d'octets à un offset donné et les remplace. Cela est appelé un patch d'offset. Si la séquence d'octets correcte n'est pas trouvée, le patcher signalera une erreur. Il devrait également nous avertir si le fichier a déjà été patché. Il devrait y avoir une option pour sauvegarder le fichier cible avant le patching et si les attributs du fichier cible incluent lecture seule, cela doit être défini en lecture-écriture. Tout d'abord, nous devons couvrir un peu de théorie. Ouverture d'un fichier
La fonction CreateFile crée ou ouvre un fichier et renvoie un handle qui peut être utilisée pour accéder au fichier. Elle peut également être utilisée pour ouvrir des objets, y compris des dispositifs de disque, des consoles et des répertoires. Le paramètre dwCreationDisposition contrôle son comportement : CREATE_NEW - crée un nouveau fichier, échoue si un fichier portant le même
nom existe déjà Chaque fichier ouvert a un pointeur de fichier qui spécifie le prochain octet à lire ou l'emplacement pour recevoir le prochain octet écrit. Lorsqu'un fichier est ouvert pour la première fois, Windows place le pointeur de fichier au début du fichier. À mesure que chaque octet est lu ou écrit, Windows avance le pointeur de fichier. Une application peut également déplacer le pointeur de fichier en utilisant la fonction SetFilePointer. Lecture depuis et écriture dans un fichier Une application lit depuis et écrit dans un fichier en utilisant les fonctions ReadFile
et WriteFile qui requièrent l'handle du fichier ouvert comme décrit ci-dessus. ReadFile et WriteFile lisent
et écrivent un nombre spécifié d'octets à l'emplacement indiqué par le pointeur de fichier.
Les données sont lues et écrites exactement telles quelles ; les fonctions ne formatent pas les données.
Lorsque le pointeur de fichier atteint la fin d'un fichier et que l'application tente de lire depuis le fichier, aucune erreur ne se produit, mais aucun octet n'est lu. Par conséquent, lire zéro octet sans erreur signifie que le programme a atteint la fin du fichier. Écrire zéro octet ne fait rien. Fermeture d'un fichier La fonction CloseHandle ferme et invalide les handles et libère les ressources système pour presque tous les objets. Le système fermera toutes les handles ouvertes à la sortie, mais il est toujours recommandé aux programmes de fermer leurs handles avant de se terminer. Notre exemple de fichier cible Nous utiliserons à nouveau SuperCleaner 2.9 comme exemple de fichier cible, mais je n'aborderai pas la façon de le cracker, seulement comment coder le patcher. La section du code cible dans SuperCleaner.exe lorsqu'il est visualisé dans Olly est la suivante : Nous devons patcher cette ligne : 0042374F 75 41 JNZ SHORT SuperCle.004234C2 par ceci : 0042374F EB 41 JMP SHORT SuperCle.004234C2 Cela implique de changer seulement 1 octet (75 par EB) à RVA 2347F (ImageBase de 400000 soustrait de VA pour obtenir RVA - voir mon tutoriel sur les PE pour plus de détails). Nous pourrions maintenant convertir la RVA de l'adresse de patch en mémoire en un offset dans le fichier sur le disque pour le patcher (encore une fois, voir le tutoriel sur les PE). Une méthode plus simple consiste à rechercher le fichier avec un éditeur hexadécimal pour trouver la séquence correcte.
En ce qui concerne la recherche d'une séquence d'octets unique, il y a 5 occurrences de 75 41 dans le fichier cible mais seulement 1 occurrence de 75 41 E8 BA, nous utiliserons donc ce DWORD pour notre séquence cible. Dans ce cas, les choses sont faciles car la RVA de l'adresse de patch est la même que l'offset sur le disque. Si trouvé à l'offset attendu, notre fichier cible doit être la bonne version.
|
Nous allons coder une petite application basée sur une boîte de dialogue avec 3 boutons pour Appliquer, À propos et Quitter. Nous utiliserons une case à cocher pour créer une sauvegarde et une liste pour afficher nos rapports de succès ou d'erreurs, ce qui est plus pratique pour l'utilisateur que de devoir cliquer sur OK sur de nombreux messages. Lancez WinAsm comme d'habitude et ajoutez un script de ressources. Configurez-le comme suit : La conception est inspirée des patchs produits par dup2 (Universal Patcher de Diablo2oo2). J'ai utilisé un logo (inclus dans cette archive), mais le patcher peut être entièrement personnalisé comme dans l'exemple du keygen. Lorsque vous positionnez la case à cocher, vous devez faire glisser un rectangle suffisamment grand pour l'étiquette texte ainsi que la case à cocher elle-même. Le script (patcher.rc) est inclus dans la section du code source. Maintenant, collez le code source offsetpatcher.asm : Remarquez les 2 prototypes de fonction pour notre fonction de patch et une petite routine pour mettre à jour notre liste. Remarquez également notre séquence de bytes cible, les bytes de patch et le décalage dans la section .data. Les séquences de bytes sont en fait des tableaux - nous en discuterons dans le chapitre sur les Structures de données. REMARQUE : Les nombres hexadécimaux doivent toujours commencer par un chiffre décimal (0-9). Si nécessaire, ajoutez un zéro initial pour distinguer les symboles et les nombres hexadécimaux qui commencent par une lettre. Nous avons également le nom de notre cible et le nom que nous utiliserons pour le fichier de sauvegarde. Dans la section .data?, nous avons des tampons pour lire les octets au décalage cible et aussi pour prendre le nombre d'octets lus et écrits par les fonctions ReadFile et WriteFile. La procédure de la boîte de dialogue devrait être assez explicite, mais contient des appels à nos 2 fonctions List et Patch : La fonction List prend un pointeur vers la chaîne à imprimer dans la liste et envoie des messages à la ListBox pour ajouter la chaîne et également pour faire défiler pour garder le nouveau message en vue : La fonction Patch commence par vérifier les attributs du fichier cible pour s'assurer qu'ils ne sont pas en lecture seule (FILE_ATTRIBUTE_READONLY). Si c'est le cas, l'instruction suivante les définit à FILE_ATTRIBUTE_NORMAL pour permettre l'écriture dans le fichier. Ensuite, nous essayons d'ouvrir le fichier avec CreateFile. Si le fichier est déjà utilisé, nous obtenons une erreur de handle invalide, nous le signalons et quittons. Si nous réussissons, nous regardons la case à cocher. Si elle est cochée, nous copions le fichier et le renommons avec notre nom de sauvegarde, puis signalons la sauvegarde réussie dans la liste. Enfin, nous utilisons SetFilePointer avec le décalage de patch et nous lisons 4 octets pour voir si nous avons la bonne version cible, la version incorrecte ou un fichier déjà patché. Si nous avons la bonne version du fichier, nous déplaçons le pointeur de fichier de nouveau au début du décalage de patch et utilisons WriteFile pour patcher notre byte cible, en signalant les erreurs ou le succès dans la liste avant de retourner. REMARQUE : Le caractère barre oblique inversée peut être utilisé pour étendre les arguments longs des appels de fonction sur plusieurs lignes. Ici, l'appel à CreateFile a un argument par ligne. Cette routine fonctionne bien, mais que se passe-t-il si une nouvelle version du fichier cible est publiée avec une protection identique mais des décalages légèrement modifiés ? Dans le prochain tutoriel, nous améliorerons notre patcher en ajoutant la fonctionnalité permettant de rechercher la séquence de bytes cible à un décalage différent pour permettre un patch réussi. De plus, nous examinerons la mise en mémoire d'un fichier, qui est une alternative beaucoup plus élégante à l'utilisation du pointeur de fichier. |
Copyright (C)- xtx Team (2021)