Menschlich lesbaren String in Byte-Array finden

  • Allgemein

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Menschlich lesbaren String in Byte-Array finden

    Guten Abend,
    ich versuche grad aus Dateien (also *.exe und *.dll) Strings herauszufiltern. Die sind meistens irgendwo in der .rdata-Section drin, welche ich auch als Byte-Array zur Verfügung hab'. Bei Unicode Strings scheinen die Chars durch Spaces getrennt zu sein. Ich könnte prüfen ob bestimmte Folgen druckbar sind, aber das Byte Array dafür komplett zu nem String zu machen scheint mehr ziemlich Resourcen fressend. Wisst ihr wie ich da am Besten vorgehe ?

    Grüße
    Moin,

    geh doch mit 'ner For Each durch, gib das an deine Prüf-Logik (z.B. Char.IsLetterOrDigit) und füg das, wenn es True ergibt an einen Text.StringBuilder an. Dann hast du alle Zeichen da drin.

    PS: Die Zeichen werden übrigens durch NULL-Chars getrennt.
    Mit freundlichen Grüßen,
    Thunderbolt
    @Agita
    So hatte ichs auch eigentlich geplant.
    Das Problem ist jetzt, ich weißt nicht wo die Strings enden. Wenn ich so prüfe und einem Stringbuilder anfüge, dann hab ich alle Strings in einem drin. Ausserdem kann ich Unicode Strings so garnicht finden
    es ist zwar nur php ... aber evtl hilfts dir ja weiter.. hab das eben grad auf die schnelle gemacht

    PHP-Quellcode

    1. <?php
    2. // unwichtig... ich lade einfach nur die datei und erstelle mir auch ein array
    3. $fp = fopen("sdfdf.txt", "r");
    4. $bytes = array();
    5. for($i=0; $i<10; $i++)
    6. $bytes[] = ord(fgetc($fp));
    7. fclose($fp);
    8. // nun gehts los
    9. // dim pos = 0
    10. $pos = 0;
    11. // dim len = bytes.length
    12. $len = count($bytes);
    13. // könnt evtl auch ersetzt werden durch
    14. // while(true)
    15. while($pos < $len)
    16. {
    17. // ende des strings oder der datei
    18. // if (bytes(pos) = 0) then break
    19. if ($bytes[$pos] == 0) break;
    20. // binärcode
    21. // weiß nicht wie vb function aussieht
    22. $bin = decbin($bytes[$pos]);
    23. // bits auffüllen sodass ein ganzes byte ensteht (8 bits)
    24. // weiß nicht ob du das auch machen musst.
    25. // in php wird aus "00100100" zb "100100"
    26. // also müssen da noch zwei "0" davor
    27. $bin = str_repeat("0", 8-strlen($bin)).$bin;
    28. // regex
    29. // versuche herauszufinden wieviele "1" am anfang stehen
    30. if(preg_match("/^1+/", $bin, $matches))
    31. $uses = strlen($matches[0]);
    32. // regex failed ... also ists nur 1 byte
    33. else
    34. $uses = 1;
    35. echo "$uses<br>";
    36. $pos += $uses;
    37. }

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

    *g* da sind doch kommentare =)

    ich mach nix anderes als in der schleife das nächste byte zu nehmen
    dann wandle ich das byte in binär um

    dann prüfe ich mittels regex wieviele 1sen am anfang stehen
    bei "11100101" bekomme ich zb "111"

    dann ermittle ich einfach die string länge von "111"
    siehe da -> 3 ... das heisst dieses und die nächsten 2 bytes bilden ein zeichen

    wenn regex fehl schlägt, dann nur weil am anfang keine 1 sondern eine 0 steht
    und dann ists automatisch nur ein byte

    --
    bin grad dabei für mich was zu basteln, daher das fehlende elan, aber werd gleich danach ma vb.net anschmeissen =)

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

    VB.NET-Quellcode

    1. Module Module1
    2. Sub Main()
    3. Dim data = IO.File.ReadAllBytes("ConsoleApplication1.exe").ToList
    4. Dim pos As Integer = 0
    5. Dim bin As String
    6. Dim reg As New Text.RegularExpressions.Regex("^1+")
    7. Dim use As Integer
    8. Dim uni As Char
    9. Dim datacount = data.Count
    10. While (pos < datacount)
    11. ' this byte is 0 .. skip it
    12. If (data(pos) = 0) Then
    13. pos += 1
    14. Continue While
    15. End If
    16. ' convert to 8 bits binary string
    17. bin = Convert.ToString(data(pos), 2).PadLeft(8, "0"c)
    18. ' benutzte bytes anzahl finden
    19. If reg.IsMatch(bin) Then
    20. use = reg.Match(bin).Value.Length
    21. Else
    22. use = 1
    23. End If
    24. ' dieses byte hat nix mit unicode zu tun...
    25. If (use > 6) Then
    26. pos += 1
    27. Continue While
    28. End If
    29. ' uni enthällt hier das zeichen des oder der bytes
    30. uni = CChar(Text.Encoding.UTF8.GetString(data.GetRange(pos, use).ToArray))
    31. Console.Write((uni))
    32. ' erhöhe array pointer um anzahl der benutzten bytes für dieses zeichen
    33. pos += use
    34. End While
    35. Console.ReadLine()
    36. End Sub
    37. End Module
    Danke fürs Beispiel, ich werds gleich mal ausprobieren. Ich habs etwas anders gelöst:
    Spoiler anzeigen

    C-Quellcode

    1. static void Main(string[] args)
    2. {
    3. byte[] data = File.ReadAllBytes(@"...");
    4. foreach (string s in GetStrings(data, 3))
    5. Console.WriteLine(s);
    6. }
    7. static bool IsPrint(int c)
    8. {
    9. return (c >= 32 && c <= 126);
    10. }
    11. static string[] GetStrings(byte[] data, int length)
    12. {
    13. // Ansi
    14. List<string> foundstrings = new List<string>();
    15. StringBuilder sb = new StringBuilder();
    16. for (int i = 0; i < data.Length; i++ )
    17. {
    18. if (!IsPrint(data[i])) continue;
    19. while (IsPrint(data[i]))
    20. {
    21. sb.Append((char)data[i]);
    22. i++;
    23. }
    24. if (sb.Length >= length) foundstrings.Add(sb.ToString());
    25. sb.Remove(0, sb.Length);
    26. }
    27. // Unicode
    28. for (int i = 0; i < data.Length; i++)
    29. {
    30. if (i + 1 >= data.Length) break;
    31. int val = Encoding.Unicode.GetChars(new byte[] { data[i], data[i + 1] })[0];
    32. if (!IsPrint(val)) continue;
    33. while (IsPrint(val))
    34. {
    35. sb.Append((char)val);
    36. i += 2;
    37. val = Encoding.Unicode.GetChars(new byte[] { data[i], data[i + 1] })[0];
    38. }
    39. if(sb.Length >= length) foundstrings.Add(sb.ToString());
    40. sb.Remove(0, sb.Length);
    41. }
    42. return foundstrings.ToArray();
    43. }


    Hier kommen aber immernoch so ca 20-30 falsche Ergebnisse raus. Der Rest ist korrekt. Gibt es noch eine andere möglichkeit zu sehen ob z.B. "AAgSFJGFDJJJFD" semantisch sinnvoll ist ?
    Ein Beispiel verdeutlicht es glaub ich. Ich suche nach Wörtern, also solche die ein Mensch lesen kann (die irgendeinen Sinn ergeben). Mit meiner aktuellen Funktion kommt Folgendes heraus:
    Spoiler anzeigen

    Quellcode

    1. !This program cannot be run in DOS mode.
    2. .text
    3. `.rsrc
    4. @.reloc
    5. pro
    6. +K+
    7. +o+6
    8. *BSJB
    9. v4.0.30319
    10. #Strings
    11. #US
    12. #GUID
    13. #Blob
    14. <Module>
    15. ConsoleApplication2.exe
    16. Program
    17. ConsoleApplication2
    18. mscorlib
    19. System
    20. Object
    21. Main
    22. IsPrint
    23. GetStrings
    24. .ctor
    25. args
    26. data
    27. length
    28. System.Runtime.Versioning
    29. TargetFrameworkAttribute
    30. System.Reflection
    31. AssemblyTitleAttribute
    32. AssemblyDescriptionAttribute
    33. AssemblyConfigurationAttribute
    34. AssemblyCompanyAttribute
    35. AssemblyProductAttribute
    36. AssemblyCopyrightAttribute
    37. AssemblyTrademarkAttribute
    38. AssemblyCultureAttribute
    39. System.Runtime.InteropServices
    40. ComVisibleAttribute
    41. GuidAttribute
    42. AssemblyVersionAttribute
    43. AssemblyFileVersionAttribute
    44. System.Diagnostics
    45. DebuggableAttribute
    46. DebuggingModes
    47. System.Runtime.CompilerServices
    48. CompilationRelaxationsAttribute
    49. RuntimeCompatibilityAttribute
    50. System.IO
    51. File
    52. ReadAllBytes
    53. String
    54. Join
    55. WriteAllText
    56. System.Collections.Generic
    57. List`1
    58. System.Text
    59. StringBuilder
    60. Append
    61. get_Length
    62. ToString
    63. Add
    64. Remove
    65. Encoding
    66. get_Unicode
    67. Byte
    68. GetChars
    69. ToArray
    70. z\V
    71. .NETFramework,Version=v4.5
    72. FrameworkDisplayName
    73. .NET Framework 4.5
    74. ConsoleApplication2
    75. !Copyright
    76. $b2c223cc-5eb7-4ef0-b11b-b09f563f877a
    77. 1.0.0.0
    78. WrapNonExceptionThrows
    79. RSDSE[
    80. _CorExeMain
    81. mscoree.dll
    82. <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    83. <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    84. <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
    85. <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    86. <security>
    87. <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
    88. <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
    89. </requestedPrivileges>
    90. </security>
    91. </trustInfo>
    92. </assembly>
    93. m!!m!)m!1m!9m!Am!Im!Qm!Ym&am!im!qm!ym+
    94. ConsoleApplication2.exe
    95. VS_VERSION_INFO
    96. VarFileInfo
    97. Translation
    98. StringFileInfo
    99. 000004b0
    100. CompanyName
    101. FileDescription
    102. ConsoleApplication2
    103. FileVersion
    104. 1.0.0.0
    105. InternalName
    106. ConsoleApplication2.exe
    107. LegalCopyright
    108. Copyright
    109. OriginalFilename
    110. ConsoleApplication2.exe
    111. ProductName
    112. ConsoleApplication2
    113. ProductVersion
    114. 1.0.0.0
    115. Assembly Version
    116. 1.0.0.0

    Solche Strings wie in Zeile 93, 99 oder 6, 7, 8 würde ich gern herausfiltern. Ich weiß nur beim Besten Willen nicht wie.
    Das Ganze wird später meinem Projektchen hinzugefügt (s. Signatur ^^). Ich denke der Nutzer wird dann einmal über die Suchlänge entscheiden können und über die Section. Also .rdata, .data usw. oder die komplette Datei. Da könnten die Ergebnisse schon zu 90% korrekt sein. Also wird im Idealfall nurn Abschnitt durchsucht, im Zweifelsfall aber alles.