c++ Speicher nach einem Wert durchsuchen .



  • AirTrake schrieb:

    das ich das

    Ein Deutschkurs würde sie vielleicht viel mehr beeindrucken.
    http://www.linuxmanpages.com/man2/ptrace.2.php



  • Mein Deutsch ist echt nicht gut. Da werde ich dir nicht
    wiedersprechen können ...
    Danke für dein Link



  • #include <sys/ptrace.h>
    long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);

    Hmm ...

    int i=1;
            int *p=&i;
            int ha;
            ha = ptrace(PTRACE_POKETEXT, 1000,p, NULL);
            printf("%d",ha);
    

    So giebt er -1 aus.



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x, bzw. C++11) in das Forum Linux/Unix verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Zumindest sind wir ehrlich ... na gut, dann sage ich mal mein Gedicht auf.

    Direkten Speicherzugriff gibt es nur in der System- und Treiberprogrammierung. Früher, in den Tagen von DOS und UNIX (obwohl, bei UNIX bin ich mir nicht sicher, bin mit MS-DOS aufgewachsen), da konnte ein Programm noch direkt auf die Speicherzellen im RAM zugreifen. Ging deshalb, weil DOS ein Single-Process-OS ist, bei dem immer nur eine Anwendung gleichzeitig laufen konnte. Und wenn diese sich in eine Endlosschleife verhing, musste man den Rechner neustarten.

    Zwischendurch gab es auch merkwürdige Konstrukte wie Segmente, die man beim Zugriff beachten musste. Die Prozessoren unterstützten damals 16 Bit, 2^16 Bytes <=> 65536 Bytes = 64 Kilobytes, die man adressieren konnte, und mit der 20-Bit Technologie kammen noch mal 4 Bit dazu (also 1 MB Speicher, aufgeteilt in 64-Kilobyte-Segemente. FAR-(Zugriff außerhalb des eigenen Segmentes, 20 Bit) und NEAR-Zeiger (Zugriff auf eigenes Segment, 16 Bit) haben einige Programmierer in den Wahnsinn getrieben. 😃

    Dann kam die 32-Bit-Technologie, und die Multi-Threading-Technologie kam auf. Nun ja, was man so nennen will - Windows nannte das ganze "kooperatives Mutli-Threading", soll heißen, die Anwendung bestimmt, wann Prozessorzeit freigegeben wird und wann nicht. Damit konnte man tatsächlich eine gleichzeitige Ausführung simulieren, aber gegen Abstütze gefeit war das Ganze immer noch nicht. Außerdem kam es gerne vor, dass sich Programme gegenseitig in den Speicher schrieben, weil jedes dachte, dass es alleine im Speicher war. 🤡

    Und schließlich wurde die Virtuelle Adressverwaltung und preemptives Multi-Threading in das System eingefügt. Nicht mehr die Anwendung bestimmt, wo es Speicher erhält, sondern das OS/der Kernel. Wenn Anwendung abstützt, läuft das OS weiter. Kernel verwaltet Speicherzugriffe, wenn für Prozess an Adresse X kein Speicher reserviert wurde, Access Violation. Um das zu realisieren, hat man dem Programm einfach vorgegaukelt, es sei immer noch die einzige Anwendung im Speicher. Und diese Anwendung bekam aufgrund von 32-Bit-Technologie 2^32 Bytes = 4 Gigabytes an virtuellen Speicher, verjubeln konnte es den, wie es lustig war. Wenn man auf eine virtuelle Speicheradresse zugriff, schaut der Kernel für einen nach, ob für den Prozess und für die Adresse physikalischer Speicher reserviert ist, und erlaubt/verbietet den Zugriff. Das Schöne ist, dass mehrere Programme an ein und die selbe Stelle geladen werden können, da jeder Prozess über den selben Scope verfügt und nur der Kernel die tatsächliche Speicheradresse kennt, können 10 Dienste auf Adresse 0x10A0 2BE2 zugreifen und schreiben, ohne sich in die Quere zu komme.

    Dein Programm befindet sich also in einer Art "virtuellen Maschine". Zugriff auf andere Prozesse sind so nicht möglich - dazu musst du entsprechende Routinen des OS aufrufen, die dann für dich nachschauen, ob für Prozess XY physikalischer Speicher reserviert wurde. Wenn du also Beispielsweise folgendes machst:

    #include <iostream>
    
    int main()
    {
        //Erste valide Stelle des Speichers.
        unsigned int*Pointer(1);
    
        //Speicher auslesen
        while(1!=0xFFFFFFFF)
        {
            std::cout<<"Speicheradresse "<<std::hex<<Pointer++<<" hat den Wert "<<std::dec<<*Pointer<<"\n";
        }
        std::cin.get();
        return 0;
    }
    

    bekommst du unter Garantie einen Speicherzugriffsfehler. Denn nicht du bestimmst, ob die virtuelle Adresse 0x10A0 2BE2 mit physikalischem Speicher verknüpft wurde, sondern das OS.



  • AirTrake schrieb:

    int i=1;
            int *p=&i;
            int ha;
            ha = ptrace(PTRACE_POKETEXT, 1000,p, NULL);
            printf("%d",ha);
    

    So giebt er -1 aus.

    Das ist wenig verwunderlich.

    ha = ptrace(PTRACE_POKETEXT, 1000, p, NULL);
    

    bedeutet: Schreibe NULL an Speicherstelle p im virtuellen Adressraum des Prozesses mit PID 1000. Ich weiß jetzt nicht, ob und ggf. welcher Prozess bei dir die PID 1000 hatte, als das Programm lief, und ich hege die Vermutung, dass du es versäumt hast, dich vorher an diesen anzuhängen. Was ich aber weiß, ist, dass p aus dem eigenen Adressraum genommen ist und mit ziemlicher Sicherheit keine Entsprechung in Prozess 1000 hat.

    Anschauungsbeispiel mit eigens erzeugtem Kindprozess:

    #include <sys/ptrace.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    #include <stdio.h>
    
    int main(void) {
      int p_toparent[2];
      pid_t pid;
    
      pipe(p_toparent);
    
      pid = fork();
    
      if(pid == 0) {
        /* Kindprozess */
        long n = 1000;
        long *p = &n;
    
        close(p_toparent[0]);
    
        write(p_toparent[1], &p, sizeof(p));
    
        /* "Synchronisation" - der Vaterprozess sollte nicht länger als eine Sekunde brauchen,
         * sich die Adresse aus der Pipe zu holen. In ernsthaften Anwendungsfällen wäre dieses
         * Vorgehen natürlich haarsträubend, aber die Arbeit, das vernünftig aufzuziehen, mache
         * ich mir hierfür nicht.
         */
        sleep(1);
    
        printf("C: %lx\n", n);
      } else {
        /* Elternprozess */
        long data = 0xdeadbeef; /* Der rüberzuschreibende Wert */
        long *n_child;
    
        close(p_toparent[1]);
    
        printf("P: %lx\n", data);
    
        /* Hier wird der Wert, den der Kindprozess in die Pipe geschrieben hat, in n_child
         * gelesen.
         * n_child ist danach die Adresse von n im virtuellen Adressraum des Kindprozesses,
         * nicht im eigenen! Dementsprechend kann n_child nicht direkt verwendet werden, sondern
         * dient lediglich als Argument für ptrace.
         */
        read(p_toparent[0], &n_child, sizeof(n_child));
    
        /* Dann kann man sich anhängen, darauf warten, dass das SIGSTOP bearbeitet wurde,
         * danach im Adressraum des Kindprozesses rumfuhrwerken und sich wieder abhängen.
         */
        ptrace(PTRACE_ATTACH, pid, 0, 0);
        wait(NULL);
        ptrace(PTRACE_POKEDATA, pid, n_child, data);
        ptrace(PTRACE_DETACH, pid, 0, 0);
    
        sleep(2);
      }
    
      return 0;
    }
    

    Prinzipiell kann man sich auf die gleiche Weise an alle Prozesse anhängen, bei denen man die entsprechenden Rechte hat (als normaler Benutzer sind das im Zweifel die von dir selbst gestarteten); wenn der andere Prozess nicht darauf vorbereitet ist, ist es aber natürlich sehr viel schwieriger, interessante Stellen in dessen Adressraum herauszufinden.



  • Naja ich lass es dann mal mit dem Zeug. 🙂
    Muss mir jetzt wieder eine andere Idee überlegen ..

    Danke für die Antworten 👍



  • /dev/mem



  • Häng dich mit ptrace an den Prozess, parse /proc/[PID]/maps um die Liste von allen Speicherbereichen zu bekommen, lies alle Bereiche aus /proc/[PID]/mem und suche darin nach deinem Wert.



  • Der aus dem Westen ... schrieb:

    Direkten Speicherzugriff gibt es nur in der System- und Treiberprogrammierung. Früher, in den Tagen von DOS und UNIX (obwohl, bei UNIX bin ich mir nicht sicher, bin mit MS-DOS aufgewachsen), da konnte ein Programm noch direkt auf die Speicherzellen im RAM zugreifen.

    Virtual Memory kam in der Unix-Welt Ende der 70er Jahre unter Berkeley Unix (müsste 3BSD auf der VAX gewesen sein).

    Der Rest deines (übrigens äußerst wertvollem) Beitrag trifft in der Form auch nur auf x86-Systeme richtig zu (auch wenn die beschriebenen Techniken natürlich auch anderswo Verwendung finden), das sollte man vielleicht dazu sagen.

    Zur Frage des Threadstarters noch:
    Unter vielen Unix-Implementierungen (unter anderem eben auch unter Linux) gibt es (wie rüdiger bereits angedeutet hat) unter /dev/mem (siehe mem(4)) eine Gerätedatei, über die man (entsprechende Rechte vorausgesetzt) die Inhalte des physikalischen Speichers auslesen und durchaus auch ändern kann, wobei letzteres natürlich recht heikel ist. Zum Rumspielen durchaus interessant, es gibt aber nur sehr wenige Klassen von Programmen, in denen diese Funktionalität tatsächlich sinnvoll verwendet werden kann.


Anmelden zum Antworten