Wie erhalte ich einen Byte-Array zurück?

  • C++/CLI

Es gibt 23 Antworten in diesem Thema. Der letzte Beitrag () ist von jvbsl.

    Wie erhalte ich einen Byte-Array zurück?

    Guten Abend,

    für mein C++-Lernprojekt benötige ich eine Funktion, die mir einen Byte-Array zurückliefert, den ich dann mit weiteren Funktionen verarbeiten/auswerten kann. Diese Funktion ist essentiell für mein Projekt und darf nicht fehlen.
    Ich habe ein wenig bei Google gesucht und musste feststellen, dass dies wohl nicht so einfach wie in VB.NET ist.

    Ich habe bislang nur wenig Erfahrung in C++, wenn Ihr mir also auch abseits meiner gestellten Frage wichtige Tipps geben könnt, nehme ich diese dankend an! :)
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford
    @Vultrax Wo kommt denn der Inhalt für das Array her?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany - Danke erstmal für Deine Antwort!

    Die ReadProcessMemory-Funktion gibt einen Array von Bytes in der Größe von Size zurück.

    msdn.microsoft.com/en-us/libra…op/ms680553(v=vs.85).aspx

    C-Quellcode

    1. BOOL WINAPI ReadProcessMemory(
    2. _In_ HANDLE hProcess,
    3. _In_ LPCVOID lpBaseAddress,
    4. _Out_ LPVOID lpBuffer,
    5. _In_ SIZE_T nSize,
    6. _Out_ SIZE_T *lpNumberOfBytesRead
    7. );

    Meine Funktion sieht in VB.NET ungefähr so aus (die ganzen Checks habe ich mal entfernt):

    VB.NET-Quellcode

    1. Public Function RMS(ByVal Address As Long, ByVal Size As Integer) As Byte()
    2. Dim Bytes(Size - 1) As Byte
    3. RPM(Handle, Address, Bytes, Size, Nothing)
    4. Return Bytes
    5. End Function

    Den Array kann ich nun mit weiteren Funktionen verarbeiten.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Vultrax“ ()

    Moment. Die ReadProcessMemory-Funktion gibt kein Array zurück, sondern du gibst ihr ein Array und sie schreibt dann die Daten in das Array hinein.
    Möchtest Du die Funktion, die Du da in VB geschrieben hast, zu C++ konvertieren?
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Vultrax Gugst Du PInvoke: pinvoke.net/default.aspx/kernel32/ReadProcessMemory.html
    da hast Du noch ein Beispiel in C#.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Niko Ortner

    Du hast recht, das habe ich etwas doof beschrieben.
    Ja, da ich diese Funktion benötige.

    @RodFromGermany

    Ich habe bereits ein funktionsfähiges Read-/WriteProcessMemory-Modul in VB.NET. Ich möchte nur ein paar Teile davon in C++ übersetzen, damit ich an einem neuen Projekt arbeiten kann.


    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „Vultrax“ ()

    In C++ können keine byte-arrays zurück gegeben werden. Du kannst einen Speicherbereich zurückgeben (in dem Fall einen pointer oder irgendeine Klasse welche einen Pointer wrappt).


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Nun, ich, als jemand, der sich mit den neumodischen Idiomen in C++ nicht auskennt, würde das so in der Richtung machen:

    C-Quellcode

    1. byte* RMS(long Address, int Size) // Ob die Größen der Datentypen "long" und "int" hier mit denen in VB zusammenstimmen weiß nur Gott
    2. {
    3. byte* Bytes = new Byte [Size];
    4. RPM(Handle, Address, Bytes, Size, null);
    5. return Bytes;
    6. }

    Bei diesem Code musst Du beim Aufrufen darauf achten, den Speicher wieder freizugeben.

    C-Quellcode

    1. byte* Bytes = RMS(0xdeadbeef, 64);
    2. // Irgendwas mit den Bytes machen
    3. delete[] Bytes; // Speicher freigeben.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Bitte verzeiht mir meine verzögerte Antwort, habe euch nicht vergessen.

    @thefiloe

    Warum eigentlich nicht? Mir ist auch nicht klar, warum es den Datentyp "Byte" in C++ nicht gibt.
    Nebenbei: Der Showroom-Link in deiner Signatur funktioniert nicht, da solltest du vielleicht nochmal drüber schauen.

    @Niko Ortner

    Vielen Dank für Dein Beispiel, dass hat mir schon sehr weitergeholfen!

    Nur zum Verständnis: Es wird also ein Pointer (die Adresse zu dem Speicherbereich mit dem Array) zurückgegeben, worauf man dann mit einer Funktion zugreifen kann, korrekt?

    Grundfunktion:

    C-Quellcode

    1. unsigned char *readMemory(int processId, LPCVOID address, SIZE_T size)
    2. {
    3. unsigned char *buffer = new unsigned char[size];
    4. ReadProcessMemory(getProcessHandle(processId), address, buffer, size, 0);
    5. return buffer;
    6. }

    readInt (equivalent zu meiner VB.NET-ReadInteger Funktion):

    C-Quellcode

    1. int readInt(int processId, LPCVOID address)
    2. {
    3. unsigned char *buffer = readMemory(processId, address, 4);
    4. int value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24;
    5. delete[] buffer;
    6. return value;
    7. }

    readFloat (equivalent zu meiner VB.NET-ReadFloat Funktion):

    C-Quellcode

    1. float readFloat(int processId, LPCVOID address)
    2. {
    3. unsigned char *buffer = readMemory(processId, address, 4);
    4. float value;
    5. *((unsigned char*)(&value) + 3) = buffer[3];
    6. *((unsigned char*)(&value) + 2) = buffer[2];
    7. *((unsigned char*)(&value) + 1) = buffer[1];
    8. *((unsigned char*)(&value) + 0) = buffer[0];
    9. delete[] buffer;
    10. return value;
    11. }

    Für mich gibt es nun noch ein paar Fragen:

    1). Warum kann char in C++ signiert (negative Werte halten) sein?

    2). Mit "float value = buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] << 24;" lässt sich mein Byte-Array nicht in einen Float-Wert umwandeln, warum? Da ich den Code zum umwandeln in Float nur aus dem Internet habe und leider nicht verstehe, wäre es wirklich cool, wenn mir jemand erklären könnte, was dort genau passiert.

    3). Der Operator "<<" springt zu den richtig Bits, oder? Der entsprechende Wert wird dann bei diesem Bit gesetzt, richtig?

    Wenn man meinen Code noch optimieren kann, bitte nur her damit! :)
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Vultrax schrieb:

    Warum kann char in C++ signiert (negative Werte halten) sein?
    Das ist das selbe wie sbyte in C#.
    Du hast einen Wertevorrat, der 256 Werte umfasst, und je nach Interpretation des MSB gehen die von 0...255 oder -128...127.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

    @RodFromGermany

    Ich hätte mir durchaus mal die anderen Datentypen anschauen können ... Da ich in Byte keine negativen Werte abspeichern konnte, ging ich davon aus, dass dieser Datentyp (ohne S) nur positiv sein kann (wieder was dazugelernt!).
    Das ist auch schon eine lange Frage von mir: Was ist den nun der maximale Wert von Byte? Das IDE sagt mir auch 255, was normalerweise auch korrekt sein muss, da 255 = 0xFF. Im Internet konnte ich bei anderen Themen aber auch schon 256 lesen, was sich dann aber immer mit meinem aktuellen Verständnis kabbelt.
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Vultrax“ ()

    byte hat eigentlich ansich keine Feste größe und kann von der Platform abhängig sein, aber im allgemeinen konses hat man 8 Bit wenn man von einem Byte redet und innerhalb von 8 Bit kann man 256 verschiedene Werte speichern, dieser Wertebereich geht im unsigned Fall von 0-255.
    Du willst in C++ außerdem auch vermutlich die benannten Datentypen verwenden:
    uint8_t, int8_t, uint16_t,int16_t ...
    wobei die Zahl jeweils die größe in bits angibt.
    Denn ein int kann ansich zwischen 16-32 Bit sein und nicht wie in .Net wo ein int eindeutig ein 32-Bit Integer Wert ist.

    Außerdem wäre es sinnvoll wenn du dir mal die aktuellen C++ Standards anguckst...
    Dein Aktueller Code ist mehr C als C++
    en.cppreference.com/w/cpp/memory/unique_ptr
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    Denn ein int kann ansich zwischen 16-32 Bit sein
    int war in C einst die Prozessorbreite (16 oder 32, 64 gab es da noch nicht) und es galt: sizeof(int) <= sizeof(long).
    Mit diesem Hick-Hack war natürlich eine explizite Angabe der Breite eines Typs von Vorteil.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Dies gilt auch immernoch für Mikroprozessoren z.b.. Außerdem ist es auch nicht gegeben, dass es etwas mit der Registergröße zu tun hat. Um std konform zu sein muss es nur in bestimmten ranges sein sowie deine oben gegebene Bedingung(natürlich auch für die anderen datentypen) erfüllen
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    @jvbsl

    Man verwendet also diese Datentypen, um sicherzustellen, dass sie auch wirklich die benötigte Größe haben? Wenn ich ein plattformunabhängiges Programmen schreiben möchte, sollte ich aber int verwenden?
    Kannst du mir zeigen, wo ich die Fehler in meinem Code gemacht habe?
    Danke für die Seite, da werde ich auf jeden Fall mal drüber schauen!

    @RodFromGermany & @jvbsl

    Könnt Ihr mir Eure Meinung zu "using namespace std is bad practice" sagen?

    Folgendes habe ich dazu gefunden:
    1. bit.ly/2yCnRAC (lonecpluspluscoder.com)
    2. bit.ly/2HJo4mp (stackoverflow.com)
    3. bit.ly/2JWKSmU (YouTube-Video)

    Was wäre besser? So wie verstanden habe, sollte man NICHT "using namespace std;" inkludieren und stattdessen einfach immer "std::" (der entsprechende Namespace) dran schreiben.
    1. std::cout << "Text" << std::endl;
    2. cout << "Text" << endl;

    Noch ein paar sonstige Fragen die ich habe:
    1. Da man das Speicher-Management in C++ komplett übernimmt, stellt sich mir die Frage, ob man bei einer ausgeführten Funktion noch irgendwas bereinigen muss?
    2. Kann ich Header auch in einem eigenen Header inkludieren? Wenn MyHeader <iostream.h> und MyHeader2.h ebenfalls <iostream.h> inkludiert hat, müsste das doch mein Programm ziemlich groß machen, oder nicht?


    Vielen Dank, dass Ihr Euch Zeit nehmt, mir zu helfen!
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Vultrax“ ()

    Nein wenn du Plattformunabhängig haben willst musst eben die festen größen verwenden, damit die größe die du brauchst garantiert ist und auch deutlich dranne steht was fu brauchst..

    YouTube nie als quelle verwenden.
    Außerdem geht es darum das using nicht in headern zu verwenden, aber selbst da muss ich wiedersprechen, du musst es einfach in einem untergeordneten scope machen um das Zeug außerhalb nicht zu beeinflussen...

    1. Muss man eben nicht mehr guck dir dafür obigen link und alles andere zu smart pointern an...
    2. Nein, diese header enthalten etwas, was alle header enthalten sollten und zwar ein präprozessor, sodass die datei eben nur einmal inkludiert wird. Bei entsprechenden code optimierungen wird außerdem viel ungenutztes entfernt...
    Das sind die #ifdef dinge am anfang(bzw non standard #pragma once)
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Also zu Deinen Implementierungen kann ich Dir noch was sagen:

    In C und C++ kannst Du dem Compiler sagen: "Ich hab hier einen Pointer. Du weißt, dass er auf einen char zeigt. Heißt ja auch char*. Aber vergiss das jetzt. Tu einfach so, als würde der Pointer auf einen float zeigen. Also als wäre es ein float*."
    Das hat keinen Einfluss auf den tatsächlichen Wert des Pointers zur Laufzeit. Es ist immer noch einfach nur eine Zahl, die angibt, wo sich das Teil, auf das gezeigt wird, im Speicher befindet.

    C-Quellcode

    1. int readInt(int processId, LPCVOID address)
    2. {
    3. unsigned char *buffer = readMemory(processId, address, 4);
    4. int value = *((int*)buffer);
    5. delete[] buffer;
    6. return value;
    7. }

    C-Quellcode

    1. float readFloat(int processId, LPCVOID address)
    2. {
    3. unsigned char *buffer = readMemory(processId, address, 4);
    4. float value = *((float*)buffer);
    5. delete[] buffer;
    6. return value;
    7. }

    Das setzt allerdings voraus, dass die Bytes in der richtigen Reihenfolge vorliegen. Deine readInt-Funktion geht von LittleEndian aus. Meine readInt-Funktion verwendet die Endianess, die von der Hardware verwendet wird. Wenn Du also einen Int auslesen willst, von dem Du weißt, dass er im LittleEndian-Format im Speicher liegt, der Code aber auf einer BigEndian-Maschine ausgeführt wird, dann kommt ein falscher Wert heraus.

    Und vielleicht kannst Du jetzt schon erkennen, dass der Code für alle Varianten ziemlich gleich aussieht. Mit Templates kann man das zusammenfassen:

    C-Quellcode

    1. template<typename T>
    2. T readStruct(int processId, LPCVOID address)
    3. {
    4. unsigned char *buffer = readMemory(processId, address, sizeof(T));
    5. T value = *((T*)buffer);
    6. delete[] buffer;
    7. return value;
    8. }
    (Der Code ist ungetestet. C++ kompilieren ist anstrengend.)


    [1] Nur zum Verständnis: Es wird also ein Pointer (die Adresse zu dem Speicherbereich mit dem Array) zurückgegeben,
    [2] worauf man dann mit einer Funktion zugreifen kann, korrekt?

    [1] Korrekt.
    [2] Man kann darauf zugreifen. Ich bin mir nicht ganz sicher, was Du mit "mit einer Funktion" meinst.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @jvbsl

    jvbsl schrieb:

    Nein wenn du Plattformunabhängig haben willst musst eben die festen größen verwenden, damit die größe die du brauchst garantiert ist und auch deutlich dranne steht was fu brauchst..

    Ich habe Plattformunabhängigkeit immer so verstanden, dass sich das geschriebene Programm an die Umgebung anpasst. Wenn ich mein Programm nun in einer Umgebung laufen lasse, in der int 16-Bit groß ist, würde dies ja bei einer festgelegten Größe zu Problemen führen. Da ich mich informieren würde, welche Größe die bestimmten Datentypen in dieser Umgebung haben, müsste ich keinen Code anpassen, da meine ReadInteger-Funktion mit 16-Bit, 32-Bit oder 64-Bit einwandfrei funktionieren würde.

    jvbsl schrieb:

    YouTube nie als quelle verwenden.

    Da hast Du schon recht, YouTube ist eigentlich auch keine gute Quelle.

    jvbsl schrieb:

    du musst es einfach in einem untergeordneten scope machen um das Zeug außerhalb nicht zu beeinflussen...

    Kannst Du mir ein Beispiel zeigen, wie man es richtig macht? Bei diesem Thema lese echt zu viele verschiedene Meinungen ...
    Sollte man Using nur in Klassen, Funktionen usw. verwenden?

    jvbsl schrieb:

    Muss man eben nicht mehr guck dir dafür obigen link und alles andere zu smart pointern an...

    Die meisten Beispiele für den Smart-Pointer sind mir nicht ganz verständlich. Ich habe schon etwas Zeit gebraucht, bis ich verstanden habe wie man einen Smart-Pointer in die Prozedur einbindet und wenigstens erstmal an die Daten die Daten kommt.

    Funktion:

    C-Quellcode

    1. ​uint8_t *getMemoryBytes(HANDLE processHandle, LPCVOID address, SIZE_T size)
    2. {
    3. uint8_t *buffer = new uint8_t[size];
    4. ReadProcessMemory(processHandle, address, buffer, size, NULL);
    5. return buffer;
    6. }

    Verwendung:

    C-Quellcode

    1. ​ std::unique_ptr<uint8_t> buffer(getMemoryBytes(hProcess, (LPCVOID)0x1000249, 4));
    2. for (int i = 0; i < 4; i = ++i)
    3. {
    4. std::cout << (int)buffer.get()[i] << std::endl;
    5. }

    Ich hoffe, dass das richtig gemacht habe.

    Es stellen sich mir auch hier zwei Fragen zum Speicher-Management:
    1). Delete[] würde in diesem Fall dennoch ordnungsgemäß die Daten löschen (wenn ich keinen Smart-Pointer verwenden würde)?
    2). Ich habe mit Delete[] und dem Smart-Pointer überprüft, was mit den im Speicher abgelegten Daten passiert wenn diese gelöscht werden, und habe für mich merkwürdigerweise festgestellt, dass der Speicher an den entsprechenden Adressen nicht ungültig wird, sondern nur irgendwelche Zahlen geschrieben werden. Ist dies so gewollt?

    jvbsl schrieb:

    Das sind die #ifdef dinge

    Diese habe ich auch in mein Header eingebunden. Ich habe gelesen, dass man #pragma once nicht verwenden sollte, da es noch unausgereift ist und harte Bugs beinhalten soll?

    @Niko Ortner

    Was ich wirklich noch nicht verstehe ist char* oder float*. Es ist ein Pointer zu einem Datentyp, dass verwirrt mich stark.

    Niko Ortner schrieb:

    Das setzt allerdings voraus, dass die Bytes in der richtigen Reihenfolge vorliegen.

    Stimmt, dass hätte ich auf jeden Fall berücksichtigen sollen.

    Niko Ortner schrieb:

    Und vielleicht kannst Du jetzt schon erkennen, dass der Code für alle Varianten ziemlich gleich aussieht. Mit Templates kann man das zusammenfassen:

    Das mit den Templates ist echt eine super Sache, wusste ich noch nicht!
    Ich habe auch festgestellt, dass man die Funktion noch deutlich einfacher gestalten kann:

    C-Quellcode

    1. ​template<typename T>
    2. T getMemoryValue(HANDLE processHandle, LPCVOID address)
    3. {
    4. T buffer;
    5. ReadProcessMemory(processHandle, address, buffer, sizeof(buffer), NULL);
    6. return buffer;
    7. }


    Tut mir leid, dass ich Euch wieder etwas verspätet antworte :)
    Ich bin wirklich sehr dankbar dass ich Euch die Zeit nehmt mir zu helfen!
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford
    #pragma once funktioniert ohne Probleme. Man sollte es nicht verwenden weil es compilerspezifisch ist. Das #ifdef... läuft bei jedem Compiler, #pragma nur mit Microsoft. Was verwirrt dich an einem Pointer zu einem Datentypen? Du musst einen Pointer ja auch irgendwie deklarieren können und auf die Daten zugreifen können. Mit einem void* kann man nicht viel machen. Da steckt nur die Adresse drin und nicht die Repräsentation der Daten, also kann man auch nicht darauf zugreifen. C++ ist typensicher.
    @Gonger96

    Dann werde ich wohl auch weiterhin bei #ifndef bleiben.

    Ich kann es nur schwer beschreiben ... Ein Datentyp kenne ich nur in dem Zusammenhang, dass ich damit die Typen meiner Daten definiere. Ein Pointer hält doch nur die Adresse zu den Daten?
    *((int*)buffer) ??
    "Denken ist die schwerste Arbeit, die es gibt. Das ist wahrscheinlich auch der Grund, warum sich so wenig Leute damit beschäftigen." - Henry Ford

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Vultrax“ ()