├── .gitignore ├── BinFind.cpp ├── BinFind.h ├── LICENSE ├── Makefile ├── Readme.md ├── Screenshot.png └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.vcxproj* 2 | [Dd]ebug 3 | [Rr]elease 4 | /test 5 | *.dSYM 6 | -------------------------------------------------------------------------------- /BinFind.cpp: -------------------------------------------------------------------------------- 1 | //TODO: 2 | // - Groups 3 | // - Crash when supplying a pattern byte as simply "<" or ">" 4 | // - Crash when matching pattern ending with 0 or 1/more operator such as "00 00?" 5 | // - Crash when matching past total buffer size (eg. with rep_1orMore) 6 | 7 | #define _CRT_SECURE_NO_WARNINGS 8 | 9 | #include "BinFind.h" 10 | 11 | #include 12 | 13 | #ifdef _MSC_VER 14 | #include 15 | #endif 16 | 17 | BinFindPatternByte::BinFindPatternByte(BinFindPattern* owner, const char* byte) 18 | { 19 | Owner = owner; 20 | 21 | BinFindPatternByteRepeat repeat = rep_1; 22 | BinFindPatternByteOperation oper = op_Equal; 23 | uint8_t value = 0; 24 | 25 | bool valueHigh = false; 26 | bool valueLow = false; 27 | 28 | const char* p = byte; 29 | while (*p != '\0') { 30 | const char c = *(p++); 31 | 32 | if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) { 33 | uint8_t singleChar = 0; 34 | 35 | char byteBuffer[2]; 36 | byteBuffer[0] = c; 37 | byteBuffer[1] = '\0'; 38 | sscanf(byteBuffer, "%hhx", &singleChar); 39 | 40 | if (!valueHigh) { 41 | value |= (singleChar << 4); 42 | valueHigh = true; 43 | } else if (!valueLow) { 44 | value |= singleChar; 45 | valueLow = true; 46 | } else { 47 | printf("ERROR: Byte value too big in: '%s'\n", byte); 48 | return; 49 | } 50 | continue; 51 | } 52 | 53 | if (c == '?') { 54 | if (valueHigh || valueLow) { 55 | repeat = rep_0or1; 56 | } else { 57 | oper = op_Unknown; 58 | } 59 | 60 | } else if (c == '<') { 61 | if (*p == '=') { 62 | oper = op_LessThanEqual; 63 | } else { 64 | oper = op_LessThan; 65 | } 66 | 67 | } else if (c == '>') { 68 | if (*p == '=') { 69 | oper = op_GreaterThanEqual; 70 | } else { 71 | oper = op_GreaterThan; 72 | } 73 | 74 | } else if (c == '*') { 75 | repeat = rep_0orMore; 76 | 77 | } else if (c == '+') { 78 | repeat = rep_1orMore; 79 | } 80 | } 81 | 82 | if (!valueLow) { 83 | value >>= 4; 84 | } 85 | 86 | Info = (repeat << 16) | (oper << 8) | value; 87 | } 88 | 89 | bool BinFindPatternByte::MatchesByte(uint8_t input) 90 | { 91 | BinFindPatternByteOperation oper = GetOperation(); 92 | uint8_t value = GetValue(); 93 | 94 | switch (oper) { 95 | case op_Equal: return input == value; 96 | case op_Unknown: return true; 97 | 98 | case op_LessThan: return input < value; 99 | case op_LessThanEqual: return input <= value; 100 | 101 | case op_GreaterThan: return input > value; 102 | case op_GreaterThanEqual: return input >= value; 103 | } 104 | 105 | printf("ERROR: Unknown pattern byte operation while matching %02hhx with %02hhx: %d\n", input, value, (int)oper); 106 | return false; 107 | } 108 | 109 | size_t BinFindPatternByte::Matches(uint8_t* input, bool &isOK) 110 | { 111 | BinFindPatternByteRepeat repeat = GetRepeat(); 112 | 113 | if (repeat == rep_1) { 114 | if (MatchesByte(*input)) { 115 | isOK = true; 116 | return 1; 117 | } 118 | 119 | } else if (repeat == rep_0or1) { 120 | if (MatchesByte(*input)) { 121 | isOK = true; 122 | return 1; 123 | } 124 | 125 | bool isSubOK = false; 126 | NextInOwner().Matches(input, isSubOK); 127 | if (isSubOK) { 128 | isOK = true; 129 | return 0; 130 | } 131 | 132 | } else if (repeat == rep_0orMore) { 133 | if (MatchesByte(*input)) { 134 | size_t ret = 0; 135 | while (MatchesByte(*(input++))) { 136 | ret++; 137 | } 138 | 139 | isOK = true; 140 | return ret; 141 | } 142 | 143 | bool isSubOK = false; 144 | NextInOwner().Matches(input, isSubOK); 145 | if (isSubOK) { 146 | isOK = true; 147 | return 0; 148 | } 149 | 150 | } else if (repeat == rep_1orMore) { 151 | size_t ret = 0; 152 | while (MatchesByte(*(input++))) { 153 | ret++; 154 | } 155 | 156 | isOK = ret > 0; 157 | return ret; 158 | 159 | } else { 160 | printf("ERROR: Unknown pattern byte repeat while matching at %p: %d\n", input, (int)repeat); 161 | } 162 | 163 | isOK = false; 164 | return 0; 165 | } 166 | 167 | BinFindPatternByte &BinFindPatternByte::NextInOwner() 168 | { 169 | return Owner->Bytes[Owner->CurrentOffset + 1]; 170 | } 171 | 172 | BinFindPattern::BinFindPattern(const char* pattern) 173 | { 174 | char byteBuffer[16]; 175 | int byteCur = 0; 176 | 177 | const char* p = pattern; 178 | while (*p != '\0') { 179 | const char c = *(p++); 180 | if (c == ' ') { 181 | byteBuffer[byteCur] = '\0'; 182 | Bytes.push_back(BinFindPatternByte(this, byteBuffer)); 183 | byteCur = 0; 184 | } else { 185 | byteBuffer[byteCur++] = (char)toupper(c); 186 | if (byteCur + 1 >= sizeof(byteBuffer)) { 187 | printf("ERROR: Trying to read past buffer size, stopping pattern compilation"); 188 | return; 189 | } 190 | } 191 | } 192 | 193 | if (byteCur > 0) { 194 | byteBuffer[byteCur] = '\0'; 195 | Bytes.push_back(BinFindPatternByte(this, byteBuffer)); 196 | } 197 | } 198 | 199 | void BinFindPattern::MatchBegin() 200 | { 201 | CurrentOffset = 0; 202 | } 203 | 204 | size_t BinFindPattern::MatchesNextByte(uint8_t* input, bool &isOK) 205 | { 206 | if (CurrentOffset >= Bytes.size()) { 207 | printf("ERROR: Trying to match bytes past pattern size!"); 208 | isOK = false; 209 | return 0; 210 | } 211 | 212 | BinFindPatternByte &byte = Bytes[CurrentOffset]; 213 | bool isSubOK = false; 214 | size_t sz = byte.Matches(input, isSubOK); 215 | if (isSubOK) { 216 | if (CurrentOffset == 0) { 217 | ResultHit = input; 218 | ResultSize = 0; 219 | } 220 | CurrentOffset++; 221 | 222 | ResultSize += sz; 223 | 224 | isOK = true; 225 | return sz; 226 | } 227 | 228 | CurrentOffset = 0; 229 | isOK = false; 230 | return 0; 231 | } 232 | 233 | bool BinFindPattern::MatchComplete() 234 | { 235 | return CurrentOffset == Bytes.size(); 236 | } 237 | 238 | BinFind::BinFind(uint8_t* buffer, size_t size) 239 | { 240 | m_buffer = buffer; 241 | m_size = size; 242 | } 243 | 244 | BinFind::~BinFind() 245 | { 246 | } 247 | 248 | std::vector BinFind::Find(const char* pattern) 249 | { 250 | std::vector ret; 251 | 252 | BinFindPattern compiled(pattern); 253 | if (compiled.Bytes.size() == 0) { 254 | return ret; 255 | } 256 | 257 | compiled.MatchBegin(); 258 | 259 | uint8_t* p = m_buffer; 260 | uint8_t* pf = nullptr; 261 | while ((size_t)(p - m_buffer) < m_size) { 262 | bool isOK = false; 263 | size_t sz = compiled.MatchesNextByte(p, isOK); 264 | 265 | if (isOK) { 266 | if (compiled.CurrentOffset == 1) { 267 | pf = p; 268 | } 269 | p += sz; 270 | 271 | if (compiled.MatchComplete()) { 272 | pf = nullptr; 273 | 274 | BinFindSection result; 275 | result.Pointer = compiled.ResultHit; 276 | result.Size = compiled.ResultSize; 277 | ret.push_back(result); 278 | 279 | compiled.MatchBegin(); 280 | } 281 | } else { 282 | if (pf != nullptr) { 283 | p = pf; 284 | pf = nullptr; 285 | } 286 | p++; 287 | } 288 | } 289 | 290 | return ret; 291 | } 292 | 293 | enum BinFind_ConsoleColor 294 | { 295 | col_None = 0, 296 | col_Blue = (1 << 0), 297 | col_Green = (1 << 1), 298 | col_Red = (1 << 2), 299 | col_Bright = (1 << 3), 300 | }; 301 | 302 | static void BinFind_SetColor(BinFind_ConsoleColor fg = col_None, BinFind_ConsoleColor bg = col_None) 303 | { 304 | #ifdef _MSC_VER 305 | static HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 306 | 307 | WORD attr = 0; 308 | 309 | if ((fg & col_Blue) != 0) { 310 | attr |= FOREGROUND_BLUE; 311 | } else if ((fg & col_Green) != 0) { 312 | attr |= FOREGROUND_GREEN; 313 | } else if ((fg & col_Red) != 0) { 314 | attr |= FOREGROUND_RED; 315 | } else if ((fg & col_Bright) != 0) { 316 | attr |= FOREGROUND_INTENSITY; 317 | } else if (fg == col_None) { 318 | attr |= FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED; 319 | } 320 | 321 | if ((bg & col_Blue) != 0) { 322 | attr |= BACKGROUND_BLUE; 323 | } else if ((bg & col_Green) != 0) { 324 | attr |= BACKGROUND_GREEN; 325 | } else if ((bg & col_Red) != 0) { 326 | attr |= BACKGROUND_RED; 327 | } else if ((bg & col_Bright) != 0) { 328 | attr |= BACKGROUND_INTENSITY; 329 | } 330 | 331 | SetConsoleTextAttribute(hConsole, attr); 332 | #else 333 | static BinFind_ConsoleColor _lastFg = col_None; 334 | static BinFind_ConsoleColor _lastBg = col_None; 335 | 336 | if (fg != _lastFg || bg != _lastBg) { 337 | printf("\e[0m"); 338 | } 339 | 340 | if (fg != _lastFg) { 341 | if (fg == col_Red) { 342 | printf("\e[31m"); 343 | } else if (fg == (col_Green)) { 344 | printf("\e[32m"); 345 | } else if (fg == (col_Blue)) { 346 | printf("\e[34m"); 347 | } else if (fg == (col_Red | col_Green)) { 348 | printf("\e[33m"); 349 | } else if (fg == (col_Red | col_Blue)) { 350 | printf("\e[35m"); 351 | } else if (fg == (col_Green | col_Blue)) { 352 | printf("\e[36m"); 353 | } else if (fg == (col_Red | col_Green | col_Blue)) { 354 | printf("\e[37m"); 355 | } 356 | _lastFg = fg; 357 | } 358 | 359 | if (bg != _lastBg) { 360 | if (bg == col_Red) { 361 | printf("\e[41m"); 362 | } else if (bg == (col_Green)) { 363 | printf("\e[42m"); 364 | } else if (bg == (col_Blue)) { 365 | printf("\e[44m"); 366 | } else if (bg == (col_Red | col_Green)) { 367 | printf("\e[43m"); 368 | } else if (bg == (col_Red | col_Blue)) { 369 | printf("\e[45m"); 370 | } else if (bg == (col_Green | col_Blue)) { 371 | printf("\e[46m"); 372 | } else if (bg == (col_Red | col_Green | col_Blue)) { 373 | printf("\e[47m"); 374 | } 375 | _lastBg = bg; 376 | } 377 | #endif 378 | } 379 | 380 | void BinFind_DumpMemory(uint8_t* buffer, size_t size, std::vector* highlights) 381 | { 382 | size_t offset = 0; 383 | 384 | BinFindSection* highlight = nullptr; 385 | 386 | while (offset < size) { 387 | printf("%p ", buffer + offset); 388 | 389 | for (int i = 0; i < 16; i++) { 390 | if (offset + i >= size) { 391 | break; 392 | } 393 | 394 | uint8_t* p = buffer + offset + i; 395 | if (highlight == nullptr) { 396 | for (auto §ion : *highlights) { 397 | if (p >= section.Pointer && p < section.Pointer + section.Size) { 398 | highlight = §ion; 399 | break; 400 | } 401 | } 402 | } 403 | 404 | if (highlight != nullptr) { 405 | BinFind_SetColor(col_None, col_Green); 406 | } 407 | 408 | printf("%02hhx", *p); 409 | 410 | if (highlight != nullptr) { 411 | if (p + 1 >= highlight->Pointer + highlight->Size) { 412 | highlight = nullptr; 413 | } 414 | } 415 | 416 | if (highlight == nullptr) { 417 | BinFind_SetColor(); 418 | } 419 | 420 | putchar(' '); 421 | 422 | if (i == 7) { 423 | putchar(' '); 424 | } 425 | 426 | BinFind_SetColor(); 427 | } 428 | offset += 16; 429 | putchar('\n'); 430 | } 431 | 432 | printf("%p\n", buffer + size); 433 | } 434 | -------------------------------------------------------------------------------- /BinFind.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | enum BinFindPatternByteOperation 9 | { 10 | op_Equal, 11 | op_Unknown, 12 | 13 | op_LessThan, 14 | op_LessThanEqual, 15 | 16 | op_GreaterThan, 17 | op_GreaterThanEqual, 18 | }; 19 | 20 | enum BinFindPatternByteRepeat 21 | { 22 | rep_1, 23 | 24 | rep_0or1, 25 | rep_0orMore, 26 | rep_1orMore, 27 | }; 28 | 29 | class BinFindPattern; 30 | 31 | class BinFindPatternByte 32 | { 33 | public: 34 | BinFindPattern* Owner; 35 | uint32_t Info; 36 | 37 | public: 38 | BinFindPatternByte(BinFindPattern* owner, const char* byte); 39 | 40 | bool MatchesByte(uint8_t input); 41 | size_t Matches(uint8_t* input, bool &isOK); 42 | 43 | inline BinFindPatternByteRepeat GetRepeat() { return (BinFindPatternByteRepeat)((Info & 0x00FF0000) >> 16); } 44 | inline BinFindPatternByteOperation GetOperation() { return (BinFindPatternByteOperation)((Info & 0x0000FF00) >> 8); } 45 | inline uint8_t GetValue() { return (uint8_t)(Info & 0x000000FF); } 46 | 47 | private: 48 | BinFindPatternByte &NextInOwner(); 49 | }; 50 | 51 | class BinFindPattern 52 | { 53 | public: 54 | std::vector Bytes; 55 | size_t CurrentOffset = 0; 56 | 57 | uint8_t* ResultHit = nullptr; 58 | size_t ResultSize = 0; 59 | 60 | public: 61 | BinFindPattern(const char* pattern); 62 | 63 | void MatchBegin(); 64 | size_t MatchesNextByte(uint8_t* input, bool &isOK); 65 | bool MatchComplete(); 66 | }; 67 | 68 | class BinFindSection 69 | { 70 | public: 71 | uint8_t* Pointer; 72 | size_t Size; 73 | }; 74 | 75 | class BinFind 76 | { 77 | private: 78 | uint8_t* m_buffer; 79 | size_t m_size; 80 | 81 | public: 82 | BinFind(uint8_t* buffer, size_t size); 83 | ~BinFind(); 84 | 85 | std::vector Find(const char* pattern); 86 | }; 87 | 88 | extern void BinFind_DumpMemory(uint8_t* buffer, size_t size, std::vector* highlights = nullptr); 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 github.com/codecat 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: main.cpp BinFind.cpp BinFind.h 2 | g++ -std=c++11 -ggdb main.cpp BinFind.cpp -o test 3 | 4 | clean: 5 | rm test 6 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # BinFind 2 | 3 | A C++ class for performing regex-like searches on binary data. 4 | 5 | ![](Screenshot.png) 6 | 7 | ## Usage 8 | 9 | Here's a basic example on how to use BinFind: 10 | 11 | ```c++ 12 | // Initialize a search in the given buffer 13 | BinFind find(buffer, sizeof(buffer)); 14 | 15 | // Match 1 or more 0x05, 0 or 1 0xFF, and 1 or more 0x04. 16 | auto ret = find.Find("05+ FF? 04+"); 17 | 18 | // Print out the results 19 | for (auto result : ret) { 20 | printf("* %p -> %p\n", result.Pointer, result.Pointer + result.Size); 21 | } 22 | ``` 23 | 24 | There's a simple example you can compile and run in `main.cpp` as well. 25 | 26 | You can also call `BinFind_DumpMemory` to dump a memory region with an optional vector of sections to highlight, which will result in the screenshot above. 27 | 28 | ## License 29 | 30 | Copyright (c) 2017 github.com/codecat 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in all 40 | copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 48 | SOFTWARE. 49 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecat/BinFind/7c624d73d2bc9d82d99a58c6b4e3856627410b8b/Screenshot.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "BinFind.h" 5 | 6 | static uint8_t buffer[] = { 7 | 0x00, 0x00, 0x00, 0x00, 8 | 0x05, 0x05, 0x04, 0x04, 9 | 0xAA, 0xFF, 0xBB, 0xC4, 10 | 0x04, 0x08, 0x0C, 0xA0, 11 | 12 | 0x00, 0x00, 0x00, 0x00, 13 | 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x00, 0x00, 0x00, 15 | 0x00, 0x00, 0x00, 0x00, 16 | 17 | 0x00, 0xFF, 0xFF, 0x00, 18 | 0x3C 19 | }; 20 | 21 | static void TestSearch(const char* pattern) 22 | { 23 | printf("\nPattern: \"%s\"\n", pattern); 24 | 25 | BinFind find(buffer, sizeof(buffer)); 26 | auto ret = find.Find(pattern); 27 | 28 | for (auto result : ret) { 29 | printf("* %p -> %p\n", result.Pointer, result.Pointer + result.Size); 30 | } 31 | 32 | BinFind_DumpMemory(buffer, sizeof(buffer), &ret); 33 | } 34 | 35 | int main() 36 | { 37 | printf(" Starting ptr: %p\n", buffer); 38 | printf(" Ending ptr: %p\n", buffer + sizeof(buffer)); 39 | 40 | TestSearch("05 05 04 04"); 41 | TestSearch("05+ 04+"); 42 | TestSearch(">=04"); 43 | TestSearch(">=04+"); 44 | TestSearch("0C A0 00+"); 45 | TestSearch("05 05 FF? 04 04"); 46 | 47 | #ifdef _MSC_VER 48 | getchar(); 49 | #endif 50 | 51 | return 0; 52 | } 53 | --------------------------------------------------------------------------------