IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

lecture d'une valeur dans une adresse mémoire [Débutant]


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Janvier 2008
    Messages
    36
    Détails du profil
    Informations forums :
    Inscription : Janvier 2008
    Messages : 36
    Par défaut lecture d'une valeur dans une adresse mémoire
    Bonjour,

    dans le cadre du développement d'une appli, je bloque sur la lecture de la valeur d'une adresse mémoire d'un process. Le but du logiciel est un peu plus complexe que ca, mais c'est simplement la lecture d'une adresse qui pêche.
    J'ai parcouru pas mal d'exemples et j'utilise la fonction ReadProcessMemory fourni par la dll "kernel32.dll".

    Pour vérifier le bon fonctionnement de mon code, je compare mon résultat avec ce que j'obtiens avec le fameux logiciel cheatEngine. Je prends une adresse au hasard pour un process et je compare les valeurs.

    Dans mon code, j'utilise la fonction OpenProcess (toujours fourni par la même dll) afin de retrouver mon process. En debug, je vois bien que le process sur lequel je pointe est le bon car il a bien l'id indiqué dans le gestionnaire des tâches.

    Mon erreur se situe donc certainement dans l'utilisation de ReadProcessMemory dont voici l'utilisation:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
            public byte[] ReadMem(int pOffset, int pSize)
            {
                byte[] buffer = new byte[pSize];
                ReadProcessMemory(this.processHandle, pOffset, buffer, pSize, 0);
                return buffer;
            }
    Mon buffer est déseperement vide, et en tracant l'erreur avec l'instruction Marshal.GetLastWin32Error(), j'obtiens l'erreur "Seule une partie d’une requête ReadProcessMemory ou WriteProcessMemory a été effectuée".

    Merci d'avance pour votre aide

  2. #2
    Membre extrêmement actif
    Inscrit en
    Avril 2008
    Messages
    2 573
    Détails du profil
    Informations personnelles :
    Âge : 65

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 573
    Par défaut
    bonjour pixigol

    Tu as une erreur dans la signature de methode (common mistake ).
    Une adresse en memoire en l'occurence l'offset de debut ou tu veux lire doit etre un IntPtr car un offset est une adresse (quand on n'utilise pas du code unsafe il faut utiliser un IntPtr pour les adresses)
    Et egalement un IntPtr pour les Handles car les handles sont des Index du Managed Heap qui -masque helas- pour nous une adresse memoire (comme les handles systeme de Win32) ....
    Tu dois de plus liberer les handles de process.....

    Pour des assembly .net je ne vois pas l'interet de lire une adresse memoire car cette adresse peut etre modifie sans preavis par le Managed Heap...
    voici le code sans l'appel OpenProcess;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
     
     
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Globalization;
     
    namespace WinFormReadMemory
    {
        public partial class frmReadMemory : Form
        {
            //portee globale
            private IntPtr processHandle;
            public frmReadMemory()
            {
                InitializeComponent();
            }
     
     
            //textBox1  : affichage apres conversion en char 
            //textBox2  : affichage  en hex
     
            private void button1_Click(object sender, EventArgs e)
            {
                // Get process by name (ici instance en cours d'execution  notepad).
                Process notepad = Process.GetProcessesByName("devenv")[0];
                processHandle = notepad.Handle; 
     
                // ici un exemple de specification d'offset memoire particulier
                // IntPtr Offset = new IntPtr(132290);
     
                // pointe à l'adresse debut de l'image memoire executable 
                // commence toujours par  2 premiers fameux octets "MZ" 
                // (signature des exe binaires DOS & WIN32 par Microsoft) 
                // C'est le fameux forrmat Coff de MS...suivi du message "This program cannot be run in DOS mode." 
                // pour les exes destines à WIN32...  
     
     
                IntPtr ptrEntry = notepad.MainModule.BaseAddress;
                IntPtr Offset = ptrEntry;
                byte[] buffer = new byte[1000];
                uint size = (UInt32)buffer.Length;
                label1.Text = ptrEntry.ToString("X");
                uint ptrNumBytesRead = 0;
     
                if (ReadProcessMemory(processHandle, Offset, buffer, size, ptrNumBytesRead))
                {
     
                   char[] arrChar = new char[buffer.ToArray().Length];
                   for (int i = 0; i < buffer.Length;i++ )
                   {
                       arrChar[i] =(char) buffer[i];
                       textBox1.Text = textBox1.Text + arrChar[i].ToString() ;
                       textBox2.Text = textBox2.Text + buffer[i].ToString("X");
                   }
     
                }
                else
                {
                    MessageBox.Show("failed ...");
                }
                CloseHandle(processHandle);
     
     
            }
     
     
            private void button2_Click(object sender, EventArgs e)
            {
                // Get process by name .
                // Un process .Net cette fois car notepad.exe est un binaire natif...
     
                Process winTestReadMemory = Process.GetProcessesByName("WinTestReadMemory")[0];
                processHandle = winTestReadMemory.Handle;
     
                IntPtr ptrEntry = winTestReadMemory.MainModule.BaseAddress;
                IntPtr Offset = ptrEntry;
                byte[] buffer = new byte[1000];
                uint size = (UInt32)buffer.Length;
                label1.Text = ptrEntry.ToString("X");
                uint ptrNumBytesRead = 0;
     
                if (ReadProcessMemory(processHandle, Offset, buffer, size, ptrNumBytesRead))
                {
                    IEnumerable<byte> q = buffer.OfType<byte>();
                    char[] arrChar = new char[q.ToArray().Length];
                    for (int i = 0; i < q.ToArray().Length; i++)
                    {
                        arrChar[i] = (char)q.ToArray()[i];
                        textBox1.Text = textBox1.Text + arrChar[i].ToString();
                        textBox2.Text = textBox2.Text + q.ToArray()[i].ToString("X");
                    }
     
                }
                else
                {
                    MessageBox.Show("failed ...");
                }
                CloseHandle(processHandle);
            }
            //liberer le handle obtenu ....
            public void CloseHandle()
            {
                if (processHandle != IntPtr.Zero)
                {
                    bool result = CloseHandle(processHandle);
                    if (!result)
                    {
                        throw new Exception("unable to cmlose process...");
                    }
                    processHandle = IntPtr.Zero;
                }
     
            }
            [DllImport("Kernel32.dll")]
            public static extern bool ReadProcessMemory(
                [In] IntPtr hProcess,
                [In] IntPtr lpBaseAddress, [Out] byte[] lpBuffer, uint nSize, [Out] uint lpNumberOfBytesRead);
     
            [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)]
            public static extern bool CloseHandle(IntPtr handle);
     
     
     
        }
    }
    bon code..............

  3. #3
    Membre averti
    Inscrit en
    Janvier 2008
    Messages
    36
    Détails du profil
    Informations forums :
    Inscription : Janvier 2008
    Messages : 36
    Par défaut
    Merci beaucoup pour ta réponse.
    Effectivement en testant ton code ca fonctionne.
    Pour être certain d'avoir bien compris, tes deux exemples lisent les 1000 premiers bytes utilisés en mémoire par les 2 applications (notepad et WinTestReadMemory) puisqu'on passe en parametre offset = xxx.MainModule.BaseAddress?

    Enfin question plus précise, comment fait on pour lire les valeurs d'une zone mémoire correspondant à une adresse donnée.
    Je pensais (certainement tres naivement) qu'il suffisait de faire (par exemple pour l'adresse 0x00D67B60, ce qui fait 14056288 en decimale:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    PtrInt Offset = new PtrInt(14056288);
    byte[] bufResultat = new byte[4];
    uint ptrNumBytesRead = 0;
    ReadProcessMemory(processHandle, Offset, bufResultat, 4, ptrNumBytesRead));
    Ainsi en comparant le resultat de ce code avec ce que donne CheatEngine je saurais que je lis correctement la bonne valeur et je pourrais enfin m'attaquer à mon algo proprement dit.

  4. #4
    Membre extrêmement actif
    Inscrit en
    Avril 2008
    Messages
    2 573
    Détails du profil
    Informations personnelles :
    Âge : 65

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 573
    Par défaut
    bonjour pixigol
    Naivement certainemement .
    NB: observe ceci,c'est important ,les adresses de base generes par le linker sont toujours paires en x86-32 bits...pour atterir en limite de page (65 k soit 65535 ou FFFF hexa)....
    NB:observe egalement ,que lorsque on fait IntPtr.Add(BaseAdress,Offset),c'est important ,le resultat peut etre impair..Cela signifie que l'on se deplace d'octet en octet....
    Si l'on veut generer une BaseAdress valide par rapport au systeme il faut l'incrementer de 4 octets....


    En .Net il faut utiliser le class static IntPtr qui dispose des methodes necessaires:add,substract,zero etc...
    Voici l'infrastructure Add(ou Substract si tu fais du va-et -vient dans le module) que tu dois utiliser sans te soucier de l' hexadecimal ou du decimal. En C# on ne doit pas se soucier de cela(sinon ce n'est plus un compilateur) on utilise simplement le decimal , et il se de la corvee du jour...

    Voici le meme code revu qui lit une zone memoire à l'offset MainModule.BaseAddress + 5000 (5000 est decimal) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
     
     
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    namespace WinFormReadMemory
    {
        public partial class frmAdresse : Form
        {
            //portee globale
            private IntPtr processHandle;
            public frmAdresse()
            {
                InitializeComponent();
            }
            private void button1_Click_1(object sender, EventArgs e)
            {
     
                // Get process by name (ici instance en cours d'execution  notepad).
                Process notepad = Process.GetProcessesByName("notepad")[0];
                processHandle = notepad.Handle;
     
     
     
                IntPtr ptrEntry = notepad.MainModule.BaseAddress;
                // atteindre l'offset 300
                int displacement = 5000;
                IntPtr Offset =IntPtr.Add (ptrEntry,displacement);
     
                byte[] buffer = new byte[1000];
                uint size = (UInt32)buffer.Length;
                label1.Text = ptrEntry.ToString("X");
                uint ptrNumBytesRead = 0;
     
                if (ReadProcessMemory(processHandle, Offset, buffer, size, ptrNumBytesRead))
                {
     
                    char[] arrChar = new char[buffer.ToArray().Length];
                    for (int i = 0; i < buffer.Length; i++)
                    {
                        arrChar[i] = (char)buffer[i];
                        textBox1.Text = textBox1.Text + arrChar[i].ToString();
                        textBox2.Text = textBox2.Text + buffer[i].ToString("X");
                    }
     
                }
                else
                {
                    MessageBox.Show("failed ...");
                }
                CloseHandle(processHandle);
     
     
            }
            //liberer le handle obtenu ....
            public void CloseHandle()
            {
                if (processHandle != IntPtr.Zero)
                {
                    bool result = CloseHandle(processHandle);
                    if (!result)
                    {
                        throw new Exception("unable to cmlose process...");
                    }
                    processHandle = IntPtr.Zero;
                }
     
            }
            [DllImport("Kernel32.dll")]
            public static extern bool ReadProcessMemory(
                [In] IntPtr hProcess,
                [In] IntPtr lpBaseAddress, [Out] byte[] lpBuffer, uint nSize, [Out] uint lpNumberOfBytesRead);
     
            [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true)]
            public static extern bool CloseHandle(IntPtr handle);
     
     
        }
    }
    L'ennui est que la seule adresse de reference connue pour un module est l'adresse du EntryPoint (1ere e instruction executable du module qui est generee par le JIT au chargement).
    Cet executable est susceptible d'etre deplace en memoire par le GC apres notre acces...
    Il faut retrouver le nouvel emplacement en appelant le handle à chaque fois c.à.d que c'est un veritable jeu de cache-cache)...
    Si c'est un type objet on ne peut meme pas "pinner" son handle (lui assigner un emplacement fixe) car le pinning est reserve au types de base appeles blittables pratiquement les types valeurs...)...

    Bon code.....

  5. #5
    Membre extrêmement actif
    Inscrit en
    Avril 2008
    Messages
    2 573
    Détails du profil
    Informations personnelles :
    Âge : 65

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 573
    Par défaut
    rebonjour pixigol
    Oups !..On pense un chose et on en ecrit une autre(la langue est le 1er mal invente par l'homme disait le fabuliste grec ,jajouterai l'ecriture aussi sur cette liste)..
    Phrase corrigee .Il faut lire:
    L'ennui est que la seule adresse de reference connue pour un module est l'adresse EntryPoint ( EntryPoint + longueur en octet du Header du module =>nous donne 1ere instruction executable de l'image memoire generee par le JIT au chargement).
    Les donnees sont dans un segment de memoire gere en private par le system...
    Bon code............

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 13/04/2015, 11h17
  2. [WD16] Lecture donnée d'une base dans une valeur d'une autre base
    Par gef13 dans le forum WinDev
    Réponses: 2
    Dernier message: 16/11/2011, 09h05
  3. Recherche une valeur d'une cellule dans une colonne d'une autre feuille
    Par kourria dans le forum Macros et VBA Excel
    Réponses: 8
    Dernier message: 21/06/2007, 13h48
  4. Réponses: 1
    Dernier message: 25/09/2006, 17h15
  5. Mettre une valeur d'une table dans une variable
    Par Raphou96 dans le forum Access
    Réponses: 5
    Dernier message: 06/02/2006, 15h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo