├── .gitignore ├── LICENSE ├── PatternFinder.sln ├── PatternFinder ├── Pattern.cs ├── PatternFinder.csproj ├── Properties │ └── AssemblyInfo.cs └── Signature.cs ├── PatternFinderTest ├── App.config ├── PatternFinderTest.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.sdf 3 | bin/ 4 | obj/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Duncan Ogilvie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PatternFinder.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PatternFinder", "PatternFinder\PatternFinder.csproj", "{5FFB2393-5455-49C1-8D45-3EA5C0F22386}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PatternFinderTest", "PatternFinderTest\PatternFinderTest.csproj", "{349E4E31-0F3A-4542-91DA-784ADF9F8712}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {5FFB2393-5455-49C1-8D45-3EA5C0F22386}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {5FFB2393-5455-49C1-8D45-3EA5C0F22386}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {5FFB2393-5455-49C1-8D45-3EA5C0F22386}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {5FFB2393-5455-49C1-8D45-3EA5C0F22386}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {349E4E31-0F3A-4542-91DA-784ADF9F8712}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {349E4E31-0F3A-4542-91DA-784ADF9F8712}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {349E4E31-0F3A-4542-91DA-784ADF9F8712}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {349E4E31-0F3A-4542-91DA-784ADF9F8712}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /PatternFinder/Pattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Collections.Concurrent; 7 | 8 | namespace PatternFinder 9 | { 10 | public static class Pattern 11 | { 12 | public struct Byte 13 | { 14 | public struct Nibble 15 | { 16 | public bool Wildcard; 17 | public byte Data; 18 | } 19 | 20 | public Nibble N1; 21 | public Nibble N2; 22 | } 23 | 24 | public static string Format(string pattern) 25 | { 26 | var length = pattern.Length; 27 | var result = new StringBuilder(length); 28 | for (var i = 0; i < length; i++) 29 | { 30 | var ch = pattern[i]; 31 | if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f' || ch == '?') 32 | result.Append(ch); 33 | } 34 | return result.ToString(); 35 | } 36 | 37 | private static int hexChToInt(char ch) 38 | { 39 | if (ch >= '0' && ch <= '9') 40 | return ch - '0'; 41 | if (ch >= 'A' && ch <= 'F') 42 | return ch - 'A' + 10; 43 | if (ch >= 'a' && ch <= 'f') 44 | return ch - 'a' + 10; 45 | return -1; 46 | } 47 | 48 | public static Byte[] Transform(string pattern) 49 | { 50 | pattern = Format(pattern); 51 | var length = pattern.Length; 52 | if (length == 0) 53 | return null; 54 | var result = new List((length + 1) / 2); 55 | if (length % 2 != 0) 56 | { 57 | pattern += "?"; 58 | length++; 59 | } 60 | var newbyte = new Byte(); 61 | for (int i = 0, j = 0; i < length; i++) 62 | { 63 | var ch = pattern[i]; 64 | if (ch == '?') //wildcard 65 | { 66 | if (j == 0) 67 | newbyte.N1.Wildcard = true; 68 | else 69 | newbyte.N2.Wildcard = true; 70 | } 71 | else //hex 72 | { 73 | if (j == 0) 74 | { 75 | newbyte.N1.Wildcard = false; 76 | newbyte.N1.Data = (byte)(hexChToInt(ch) & 0xF); 77 | } 78 | else 79 | { 80 | newbyte.N2.Wildcard = false; 81 | newbyte.N2.Data = (byte)(hexChToInt(ch) & 0xF); 82 | } 83 | } 84 | 85 | j++; 86 | if (j == 2) 87 | { 88 | j = 0; 89 | result.Add(newbyte); 90 | } 91 | } 92 | return result.ToArray(); 93 | } 94 | 95 | public static bool Find(byte[] data, Byte[] pattern) 96 | { 97 | long temp; 98 | return Find(data, pattern, out temp); 99 | } 100 | 101 | private static bool matchByte(byte b, ref Byte p) 102 | { 103 | if (!p.N1.Wildcard) //if not a wildcard we need to compare the data. 104 | { 105 | var n1 = b >> 4; 106 | if (n1 != p.N1.Data) //if the data is not equal b doesn't match p. 107 | return false; 108 | } 109 | if (!p.N2.Wildcard) //if not a wildcard we need to compare the data. 110 | { 111 | var n2 = b & 0xF; 112 | if (n2 != p.N2.Data) //if the data is not equal b doesn't match p. 113 | return false; 114 | } 115 | return true; 116 | } 117 | 118 | public static bool Find(byte[] data, Byte[] pattern, out long offsetFound, long offset = 0) 119 | { 120 | offsetFound = -1; 121 | if (data == null || pattern == null) 122 | return false; 123 | var patternSize = pattern.LongLength; 124 | if (data.LongLength == 0 || patternSize == 0) 125 | return false; 126 | 127 | for (long i = offset, pos = 0; i < data.LongLength; i++) 128 | { 129 | if (matchByte(data[i], ref pattern[pos])) //check if the current data byte matches the current pattern byte 130 | { 131 | pos++; 132 | if (pos == patternSize) //everything matched 133 | { 134 | offsetFound = i - patternSize + 1; 135 | return true; 136 | } 137 | } 138 | else //fix by Computer_Angel 139 | { 140 | i -= pos; 141 | pos = 0; //reset current pattern position 142 | } 143 | } 144 | 145 | return false; 146 | } 147 | 148 | public static bool FindAll(byte[] data, Byte[] pattern, out List offsetsFound) 149 | { 150 | offsetsFound = new List(); 151 | long size = data.Length, pos = 0; 152 | while (size > pos) 153 | { 154 | if (Find(data, pattern, out long offsetFound, pos)) 155 | { 156 | offsetsFound.Add(offsetFound); 157 | pos = offsetFound + pattern.Length; 158 | } 159 | else 160 | break; 161 | } 162 | if (offsetsFound.Count > 0) 163 | return true; 164 | else 165 | return false; 166 | } 167 | 168 | public static Signature[] Scan(byte[] data, Signature[] signatures) 169 | { 170 | var found = new ConcurrentBag(); 171 | Parallel.ForEach(signatures, signature => 172 | { 173 | if (Find(data, signature.Pattern, out signature.FoundOffset)) 174 | found.Add(signature); 175 | }); 176 | return found.ToArray(); 177 | } 178 | } 179 | } -------------------------------------------------------------------------------- /PatternFinder/PatternFinder.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5FFB2393-5455-49C1-8D45-3EA5C0F22386} 8 | Library 9 | Properties 10 | PatternFinder 11 | PatternFinder 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | -------------------------------------------------------------------------------- /PatternFinder/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PatternFinder")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PatternFinder")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("9a112c98-6352-42ec-957d-25b59542436b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /PatternFinder/Signature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace PatternFinder 8 | { 9 | public class Signature 10 | { 11 | public Signature(string name, Pattern.Byte[] pattern) 12 | { 13 | Name = name; 14 | Pattern = pattern; 15 | FoundOffset = -1; 16 | } 17 | 18 | public Signature(string name, string pattern) 19 | { 20 | Name = name; 21 | Pattern = PatternFinder.Pattern.Transform(pattern); 22 | FoundOffset = -1; 23 | } 24 | 25 | public string Name { get; private set; } 26 | public Pattern.Byte[] Pattern { get; private set; } 27 | public long FoundOffset; 28 | 29 | public override string ToString() 30 | { 31 | return Name; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /PatternFinderTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /PatternFinderTest/PatternFinderTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {349E4E31-0F3A-4542-91DA-784ADF9F8712} 8 | Exe 9 | Properties 10 | PatternFinderTest 11 | PatternFinderTest 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {5ffb2393-5455-49c1-8d45-3ea5c0f22386} 53 | PatternFinder 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /PatternFinderTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using PatternFinder; 7 | 8 | namespace PatternFinderTest 9 | { 10 | class Program 11 | { 12 | public static void Test() 13 | { 14 | var pattern = Pattern.Transform("456?89?B"); 15 | var data1 = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; 16 | long o1; 17 | if (!(Pattern.Find(data1, pattern, out o1) && o1 == 2)) 18 | Console.WriteLine("Test 1 failed..."); 19 | var data2 = new byte[] { 0x01, 0x23, 0x45, 0x66, 0x89, 0x6B, 0xCD, 0xEF }; 20 | long o2; 21 | if (!(Pattern.Find(data2, pattern, out o2) && o2 == 2)) 22 | Console.WriteLine("Test 2 failed..."); 23 | var data3 = new byte[] { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 }; 24 | long o3; 25 | if (Pattern.Find(data3, pattern, out o3)) 26 | Console.WriteLine("Test 3 failed..."); 27 | Console.WriteLine("Done testing!"); 28 | } 29 | 30 | public static void SignatureTest() 31 | { 32 | var data = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; 33 | var signatures = new[] 34 | { 35 | new Signature("pattern1", "456?89?B"), 36 | new Signature("pattern2", "1111111111"), 37 | new Signature("pattern3", "AB??EF"), 38 | }; 39 | 40 | var result = Pattern.Scan(data, signatures); 41 | foreach (var signature in result) 42 | Console.WriteLine("found {0} at {1}", signature.Name, signature.FoundOffset); 43 | } 44 | 45 | static void Main(string[] args) 46 | { 47 | Test(); 48 | SignatureTest(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /PatternFinderTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("PatternFinderTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("PatternFinderTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("995416bd-deb2-4bac-8127-5b2141098420")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PatternFinder 2 | 3 | Parallel signature matcher/wildcard pattern finder in C#. Algorithm ported from [x64dbg](http://x64dbg.com). 4 | 5 | ## Examples 6 | 7 | Find a single pattern in a buffer like this: 8 | 9 | ``` 10 | long foundOffset; 11 | var pattern = Transform("11 ?6 89 9? 00 ?? 54"); 12 | if(Pattern.Find(buffer, pattern, out foundOffset)) 13 | Console.WriteLine("Found pattern at {0}!", foundOffset); 14 | else 15 | Console.WriteLine("Failed to find pattern..."); 16 | ``` 17 | 18 | Find a list of signatures in a buffer like this: 19 | 20 | ``` 21 | var signatures = new[] 22 | { 23 | new Signature("pattern1", "456?89?B"), 24 | new Signature("pattern2", "1111111111"), 25 | new Signature("pattern3", "AB??EF"), 26 | }; 27 | 28 | var result = Pattern.Scan(data, signatures); 29 | foreach (var signature in result) 30 | Console.WriteLine("Found signature {0} at {1}", signature.Name, signature.FoundOffset); 31 | ``` 32 | 33 | `Pattern.Scan` uses `Parallel.ForEach` for multi-thread support. 34 | --------------------------------------------------------------------------------