You are on page 1of 17

Chapter 1 LIGHTGREP: A MULTIPATTERN REGULAR EXPRESSION SEARCH TOOL FOR DIGITAL FORENSICS

Jon Stewart and Joel Uckelman


Abstract We describe lightgrep, our implementation of a multipattern regular expression search tool, which can eciently search huge data streams. lightgrep addresses several shortcomings of existing tools used in digital forensics by taking advantage of some recent (and some not-so-recent) developments in automata theory. We state the characteristics of regular expression searching in forensics, review current approaches, and then discuss lightgreps implementation in detail, based on direct simulation of nondeterministic nite automata, including a number of practical optimizations related to searching with large pattern sets.

Keywords: Digital forensics, regular expression, pattern matching, grep, nondeterministic nite automata, NFA, tagged NFA, virtual machine

1.

Introduction

Online keyword searching, commonly called grep, has several important applications in computer forensics. Investigators can search for text-based documents and fragments with potential relevance to a matter, identify structured artifacts such as Yahoo! Messenger chat logs and MFT entries, and recover deleted les using a header-footer search. Examiners often want to search for hundreds, sometimes thousands, of keywords and patterns. This paper discusses the properties of a number of implementations and then describes the implementation of lightgrep, a simple regular expression engine for digital forensics, inspired by Googles RE2 engine [5]. Several important aspects are explored in detail, and experimental results are given for intensive forensic searches.

2
<html> <head> <title>Welcome!</title> </head> <body> <p>Welcome to our friendly homepage on the internet!</p> <p>Send us <a href="mailto:osama@binladen.org"> email!</a></p> </body> </html> Figure 1. Some interesting HTML

2.

Statement of the Problem

Traditional regular expression libraries have focused on searching lineoriented text les with a single regular expression. The requirements for digital investigations are dierent, and present several challenges: Multipattern, with matches labeled by pattern Graceful performance degradation as number of patterns increases Support for large binary streams and long matches Multiple encodings (UTF-8, UTF-16, legacy code pages) A multipattern engine must identify all occurrences of the given patterns in a byte stream, even if some matches overlap. The patterns must be allowed full use of the regular expression syntax, and not be limited just to xed strings. For example, an investigator may run a search for the keywords <html>.*</html> (for carving HTML documents) and osama.{0,10}bin.{0,10}laden. A correct multipattern search implementation would report a hit for both keywords on the fragment in Figure 1. The search algorithm must degrade gracefully as the number of patterns increases, so that it is always faster to search for all patterns in a single pass of the data than to perform multiple search passes with disjoint subsets of patterns. An investigator should be condent that doubling the number of keywords will at most double the search time. Most investigators we know would gladly sacrice some bells-and-whistles in exchange for competitive and predictable performance. Worst-case guarantees are important in forensics as they aord investigators greater control over case management. It must be ecient to search byte streams many times larger than main memory and to track pattern matches hundreds of megabytes in

Stewart & Uckelman

size. In particular, the search algorithm used must mesh nicely with I/O concerns. Finally, since forensic data is by nature unstructured, it is necessary to search for occurrences of the same patterns in dierent encodings. This is especially important when searching for text in non-Western languages, where numerous encodings exist and it is unrealistic to demand that forensic investigators master the arcana of regional encodings.

3.

Background

Throughout, we assume a basic familiarity with automata theory, but provide here a brief gloss for non-specialists. A nite automaton consists of a set of states, one of which is initial, and some of which may be terminal. (E.g., in Figure 4 on page 7, state 0 is initial, while 3 and 5 are terminal.) Pairs of states may have arrows, known as transitions, from one state to the other. Each transition has a label, corresponding to some character in the input alphabet. A nite automaton reads characters from an input string. The current state of a nite automaton may change by following transitions having labels matching the characters which are read from the input string. If a terminal state is reached, the nite automaton is said to have matched the input; if, instead, a non-terminal state is reached which has no transition for the current character, the nite automaton has not matched the input. A nite automaton is called a deterministic nite automaton (DFA) if it contains no state having two outgoing transitions with the same label; otherwise, it is called a nondeterministic nite automaton (NFA). It is a basic fact about NFAs that every NFA is equivalent to a regular expression, and vice versa. We suggest that readers unfamiliar with DFAs and NFAs consult [15, Chapter 1] for an introduction.

4.

Current Approaches

Pattern searching is not a new problem, and a review of current approaches and tools is instructive for understanding pitfalls and how to address them.

4.1

strings | grep

Resourceful investigators often use the Unix strings command to extract ASCII strings from binary les and then search by piping the output to grep. This is a handy way to do a quick search, but using strings lters out unprintable characters and segments the data. As a result, searches for non-ASCII text as well as many artifacts and

4
recoverable le types is impossible, and investigators are limited to xed strings only if they want to search for multiple patterns. GNU grep does oer good performance for single patterns, however.

4.2

FTK

AccessDatas FTK [1] uses the open source Boost Regex library [12]. Boost Regex oers a rich feature set and competitive performance, and has been recently adopted into the C++ standard library. Boost Regex uses a backtracking-based algorithm, which can lead to poor performance on certain expressions, but Boost Regex terminates a search if the runtime becomes quadratic. Like most regular expression libraries, however, Boost Regex does not support a multipattern search.

4.3

EnCase

Guidance Softwares EnCase [8] oers multipattern search for regular expressions with a limited syntax and allows users to specify which encodings to consider for each keyword. Performance is acceptable with xed-string patterns and degrades gracefully as the number of patterns increases. However, search times increase substantially as regular expression operators and character classes are used in more of the patterns.1 Repetition is limited to a maximum of 255 bytes and EnCase seems unable to parse correctly some complex patternnknown which algorithm EnCase uses for the search, but results are consistent with a backtracking approach, as the increasing degree of alternation in a multipattern automata leads to performance loss with backtracking.

4.4

lex

The Unix lex utility2 can also be used to search for multiple regular expressions in a limited fashion. It does not have direct support for multiple encodings. lex compiles all patterns into a single DFA and reports on the longest match. The use of a DFA leads to good performance, but it also means that lex cannot match overlapping occurrences of dierent patterns. Most problematically, lex may backtrack unless all patterns are specied in a deterministic form, making its use with non-trivial or inexpertly constructed patterns infeasible. Additionally, lexs model of generating a C program to perform the search burdens investigators with having to compile the generated program and maintain a well-congured Unix environment, skills not held by many investigators. However, good results can be obtained with lex (e.g., by Garnkel [6]) when it is used

Stewart & Uckelman

for extracting a xed set of common, mutually-exclusive patterns (as is more in keeping with its traditional use-case of tokenization).

4.5

RE2

Recently, Google has released as open source the regular expression engine used for Google Code Search, RE2, and its author, Russ Cox, has described it in depth [4]. RE2 implements much, but not all, of both the POSIX and Perl regular expression syntax, and guarantees that performance is linear in the sizes of the pattern and the text and allows for ecient submatching of captured groups.3 RE2 works by converting the specied pattern to an NFA and then generating DFA states as needed, caching the DFA states for repeated usean algorithm known as DFAModules [14, Section 5.3.3]. RE2 represents the automata as specialized machine instructions and treats the current state as a thread operating at a certain instruction in the program; NFA searching requires the execution of multiple threads in lockstep examination of the text, character-by-character, and DFA searching utilizes only a single thread. This is consistent with the O(nm) and O(n) complexity of NFA and DFA simulation, respectively, where n represents the size of the text and m the number of states in the automata. Additionally, the start and end of matches on captured groups are tagged in the automata, as described by Laurikari [10]. Each thread maintains a small array recording the starting and ending osets of submatches, indexed by the transition tags. In this manner, RE2 is able to record submatch boundaries without backtracking. As with lex, RE2 can approximate multipattern search by combining patterns into alternating subpatterns of the form (t1 )|(t2 )|. . . |(tn ). However, because the submatch array size is O(m), performance begins to degrade substantially from copying the thread state as the number of patterns increases beyond 20004000. RE2 has other properties that limit its use for forensics applications. When using a DFA search, RE2 generates a reverse DFA that it runs backwards in the text when a match occurs, in order to nd the start of the match. This would be inecient on the very large matches it would be nice to accommodate for le carving. RE2 also assumes that the text is small enough to t in main memory, and has no facility for continuing searches from one segment of a text to another.

5.

lightgrep

Inspired by RE2, we have created lightgrep, a simple regular expression search tool for digital forensics. Simulating an NFA directly, it is

6
literal c If current character is c, increment instruction and suspend the current thread; kill the current thread otherwise Create a new thread at instruction n at the current oset, and increment instruction Goto instruction n Record a match for pattern n ending at current oset and increment instruction Kill the current thread, reporting a match if any Figure 2. Basic bytecode instructions

fork n jump n match n halt

capable of searching for thousands of patterns in a single pass of data and never suers from pathological performance problems. All occurrences of all patterns are correctly reported, and lightgrep never needs to refer backwards in the data, allowing for a streaming I/O model. The number of patterns is limited only by the amount of RAM on the system and lightgrep will cheerfully report that .* matches the entire byte stream, regardless of size. The key to correct multipattern searching is applying tagged transitions to the pattern matches, not to submatches.4 Instead of having an array of submatch positions, each state has scalar values for the starting oset of the match, the ending oset, and the value of the last tagged transition. We tag transitions to match states with the corresponding index numbers of the patterns. While the worst-case complexity of NFA search is O(nm), we have applied several practical optimizations to provide reasonable performance with large automata.

5.1

Implementation

Rather than using an NFA directly, we compile patterns into bytecode, using the instructions in Figure 2. A program in our bytecode language consists of a (numbered) sequence of bytecode instructions. Then, given a list of patterns to match and a stream of input, a bytecode program is executed by the bytecode interpreter in Figure 3 to produce a list of matches. Each thread is a tuple s, i, j, k , where s is the current instruction, i is the start (inclusive) of the match, j is the end (exclusive) of the match, and k is the index of the matched pattern. When a thread is created, it is initialized to 0, p, , , where p is the current position in the stream. Note that = 0. A zero (0) for the start or end of a match indicates that a match starts or ends at oset 0, while a null () indicates no match at all.

Stewart & Uckelman


1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:

for p := 0 to end of stream do create a new thread 0, p, , for all live threads t := s, i, j, k do repeat execute instruction s until t dies or is suspended end for end for for all live threads t := s, i, j, k do repeat execute instruction s until t dies end for
Figure 3. The bytecode interpreter

d/tag 0 2 b 0 a 1 abc b 4 c/tag 1


Figure 4. NFA matching a(bd)+ and abc

3 b a(bd)+

To make this concrete, we give an example. Suppose we have the stream qabcabdbd which we want to search for the patterns a(bd)+ and abc. The bytecode produced for this list of patterns appears in Figure 5. For comparison, the NFA corresponding to this pattern list can be seen in Figure 4. In the following, we walk through execution of the bytecode as we advance the stream one character at a time. The leftmost column is the thread ID5 , followed by the thread, followed by the explanation of what is being done in each step. 1 qabcabdbd
0 0 0, 0, , 0, 0, , thread 0 created literal a fails, dies

2 qabcabdbd

8
0 1 2 3 4 5 6 7 8 9 Figure 5. 1 1 1 0, 1, , 0, 1, , 0, 2, , literal fork 6 literal literal match 0 jump 2 literal literal match 1 halt a b d

b c

Bytecode matching a(bd)+ and abc

thread 1 created literal a succeeds advance instruction and suspend

3 qabcabdbd
2 2 1 3 1 1 1 3 3 0, 2, , 0, 2, , 1, 1, , 6, 1, , 2, 1, , 2, 1, , 3, 1, , 6, 1, , 7, 1, , thread 2 created literal a fails, dies fork 6 creates thread 3 thread 3 created advance instruction literal b succeeds advance instruction and suspend literal b succeeds advance instruction and suspend

4 qabcabdbd
4 4 1 3 3 0, 3, , 0, 3, , 3, 1, , 7, 1, , 8, 1, , thread 4 created literal a fails, dies literal d fails, dies literal c succeeds advance instruction and suspend

5 qabcqabdbd
5 5 3 3 3 3 0, 4, , 0, 4, , 8, 1, , 8, 1, 4, 0 9, 1, 4, 0 9, 1, 4, 0 thread 5 created literal a fails, dies match 1 set match pattern and end oset advance instruction halt, reports match on pattern 1 at [1, 4), dies

6 qabcqabdbd
6 6 6 0, 4, , 0, 4, , 1, 5, , thread 6 created literal a succeeds advance instruction and suspend

From here onward, we omit mentioning the creation of threads which will immediately die due to failing to match the current character.

Stewart & Uckelman

7 qabcqabdbd
6 7 6 6 6 7 7 1, 5, , 6, 5, , 2, 5, , 2, 5, , 3, 5, , 6, 5, , 7, 5, , fork 6 creates thread 7 thread 7 created advance instruction literal b succeeds advance instruction and suspend literal b succeeds advance instruction and suspend

8 qabcqabdbd
6 6 7 3, 5, , 4, 5, , 7, 5, , literal d succeeds advance instruction and suspend literal c fails, dies

9 qabcqabdbd
6 6 6 6 6 6 6 4, 5, , 4, 5, 8, 1 5, 5, 8, 1 5, 5, 8, 1 2, 5, 8, 1 2, 5, 8, 1 3, 5, 8, 1 match 0 set match pattern and end oset advance instruction jump 2 goto instruction 2 literal b succeeds advance instruction and suspend

10 qabcqabdbd
6 6 3, 5, 8, 1 4, 5, 8, 1 literal d succeeds advance instruction and suspend

11 Having reached the end of the stream, we run the remaining threads until they die:
6 6 6 6 6 6 4, 5, 8, 1 4, 5, 10, 1 5, 5, 10, 1 5, 5, 10, 1 2, 5, 10, 1 2, 5, 10, 1 match 0 set match pattern and end oset advance instruction jump 2 goto instruction 2 literal b fails, reports match of pattern 0 at [4, 9), dies

The run of this bytecode reports a match for abc at [1, 4) and a match for a(bd)+ at [4, 9).

5.2

Optimizations

5.2.1 Minimization. Minimizing thread creation from unnecessary alternation is the key to performance with an NFA simulation. Rather than treating each pattern as a separate branch of the NFA, we merge patterns into the NFA as they are parsed to form a trie.6 Merging must take into account not only the criteria of the transitions, but the sets of source and target states.

10
To facilitate minimization, we use the NFA form due to Glushkov [7] instead of the one due to Thompson [16]. Constructing a Glushkov NFA is somewhat more expensive computationally, but yields an NFA with m+1 states, whereas a Thompson NFA has O(2m) states. Additionally, a Glushkov NFA is free of epsilon transitions, simplifying both compilation and the resulting byte code.

5.2.2 Jump Tables for States with High Branching. Typically, one thread is forked to handle each successor of a given state. Some NFA states may have a large number of successors, making the creation of new threads very costly. For example, the rst state often has a large number, k, of outbound transitions when many patterns are specied. Therefore, every character read from the input stream will cause k new threads to be created, almost all of which will die immediately due to lack of a match. If we could quickly determine which thread will survive and spawn only that one, it would be a signicant practical improvement. To do this, we introduce the jumptable instruction. This instruction sits at the head of a list of 256 consecutive instructions, one for each possible value of the current byte. When the jumptable instruction is reached with byte b, execution jumps ahead b + 1 instructions and continues there. The instruction oset b+1 from jumptable will generally be a jump in the case of a match (in order to get out of the jump table), or a halt otherwise. If more than one transition is possible for byte b, then a list of appropriate fork and jump instructions is appended to the table and the jump instruction for byte b targets this table. In this manner, only threads which we know will succeed are spawned. The compiler takes care to specify jumps to states just beyond their literal instructions, ensuring that b is not evaluated twice. A sibling instruction, jumptablerange, is used when the dierence between the minimum and maximum accepted byte values is small, which operates by checking the byte value is in range and only then indexing into the table, thereby reducing the size of the table. 5.2.3 Reduced State Synchronization. The typical nondeterministic simulation of an NFA uses a bit vector (containing a bit for each state) to track which states are being visited for the current character in the stream in order to avoid duplicating work [2, Section 3.7.3]. The number of NFA states depends on the combined length of the search patterns in use; therefore, searching with a large number of patterns (even xed-string patterns) will force this bit vector to be quite long. Either we will be forced to clear the bit vector after each advance of the

Stewart & Uckelman

11

stream, or we will have to engage in some rather complex checking after each transition in order to update the bit vector. We observe that it is impossible for there to be two threads which arrive at the same state at the same character position unless that state has multiple transitions leading to it. Only those states with multiple predecessors require bits in the current state vector; the bits for other states are wasted. Our implementation in Section 1.5.1 made no provision for such deduplication. In order to handle this, we introduce the chkhalt instruction. We associate an index with each state having multiple incoming transitions. The chkhalt instruction is inserted before the outbound transition instructions associated with a state requiring synchronization. The index associated with the state is specied as an operand to chkhalt, and uses it to test the corresponding value in a bit-vector. The bit is set if it is currently unset, and execution proceeds. If the bit is already set, then the thread dies. In this manner, the size of the bit-vector is minimized and safe transitions, which occur frequently in practice, are left unguarded.

5.2.4 Complex Instruction Set. As noted with jumptable and chkhalt, it is easy to introduce new instructions to handle common cases. For example, either has two operands and continues execution if the current byte matches either one. Similarly, range has two operands and continues if the current byte has a value that falls within their range, inclusively. More complex character classes can be handled with bitvector, which is an instruction followed by 256 bits, each one set if the corresponding byte value is permitted. If several states have the same source and target states, their transitions can be collapsed into a single bitvector instruction. In general, if the introduction of a new instruction can eliminate sources of alternation, then it is likely worthwhile. 5.2.5 Compilation. lightgrep uses a hybrid breadth-rst search/depth-rst search scheme for layout of the generated instructions. Instructions for states are rst laid out in breadth-rst order of discovery, but when a parent state has only a single transition, discovery switches to a depth-rst search. This hybrid scheme has two advantages. First, subsequent states will generally be close to their parent states, due to breadth-rst discovery, and the total number of instructions used can be reduced signicantly in linear sequences of states, as no jump or fork instructions need be used between them.

12
40000 EnCase lightgrep sha1sum

35000

30000

Time (seconds)

25000

20000

15000

10000

5000

0 5 10 15 20 25 30 Keywords 35 40 45 50

Figure 6.
40000

Wall-clock time for EnCase and lightgrep

EnCase vs. lightgrep sha1sum vs. lightgrep

35000

30000 lightgrep Time (seconds)

25000

20000

15000

10000

5000

0 0 5000 10000 15000 20000 25000 30000 EnCase and sha1sum Time (seconds) 35000 40000

Figure 7.

Wall-clock time for EnCase and lightgrep

Stewart & Uckelman

13

5.3

Performance

In order to benchmark lightgrep, we created a list of 50 regular expressions suitable for use in investigations, with moderately aggressive use of regular expression operators. Some of the terms were for text, others for artifacts and les. We created increasing subsets of the terms, from ve terms to fty, in ve-term increments. Of the approaches detailed in Section 1.4, only EnCase has enough features in common with lightgrep for a head-to-head performance comparison to be meaningful, hence we have compared only EnCase and lightgrep in our experiment.7 With both EnCase and lightgrep, we ran each group of keywords against a 32GB Windows XP dd evidence le. The lesystems in the evidence le were not parsed. The workstation used had two Intel Xeon 5160 dual-core processors at 3.0GHz, 4GB RAM, and a 7200RPM SATA2 hard drive. Results may be seen in Figures 6 and 7. All times are wall-clock times. As can be seen from the gures, lightgrep dramatically outperforms EnCase on the test data, by more than a factor of ten in all cases. As a further benchmark, we show in the sha1sum series the time to hash the evidence le with SHA-1 using the sha1sum command (which is, of course, constant with respect to number of keywords). SHA-1 hashing reads every byte of the input and is I/O-bound, so the SHA-1 timings provide a lower bound for search performance. As can be seen, lightgrep comes very close to matching hash performance with small sets of keywords. Additionally, a lightgrep search was conducted with 114,743 xed strings from an English word list (not shown in the gures). This completed in 523 seconds, just 50 seconds above the time needed to hash the evidence. Because xed-strings collapse into a DFA for matching (but not searching), this indicates that performance improvements with complex patterns can be achieved by further determinization of the NFA.

6.

Observations

Here we make some additional observations regarding our lightgrep, focusing on interesting features we support, as well as a description of future work we have planned.

6.1

Interesting Features

6.1.1 Greedy vs. Non-greedy Matching. As discussed in [4], it is possible to introduce non-greedy repetition operators, such as *?, which result in the shortest possible matches instead of the longest. By ensuring that threads spawned by fork are executed before their associated parent threads, we can control which alternation is given priority.

14
Non-greedy matching proves quite useful in digital forensics. Our prior example of the pattern <html>.*</html> is not appropriate for carving HTML fragments from unallocated space in a lesystem. The pattern will match on the rst fragment, but a thread will continue trying to match and eventually match on the ends of subsequent fragments, if they exist, reporting one long match. By contrast, <html>.*?</html> generates one match for each fragment.

6.1.2 Positional Assertions. The vi text editor oers users the ability to specify positional assertions in patterns. For example, a pattern can assert that it must match the pattern on a certain line, in a certain column. When reimagined for searching binary data, we nd positional assertions useful for forensics applications. A le format may have an optional record that can be identied with a pattern, but known to occur only at a given oset. Further, we may wish to limit le carving to data that is sector-aligned. We introduce the syntax (?i@regex ) and (?i%j@regex ), where i is either an absolute or modulo byte oset and j is a divisor. For example, (?0%512@)PK will match sector-aligned zip le headers. 6.1.3 Multiple Encodings. Many regular expression libraries with Unicode support rely on data to be decoded to Unicode characters before consideration by the search routine, with the assumption that the data to be searched is stored in a single encoding. This is not an assumption that holds in forensics; when searching unstructured data, encodings may change capriciously from ASCII to UTF-16 to UTF-8 to a legacy codepage. lightgrep is explicitly byte-oriented. In order to search for alternate encodings of a pattern, its dierent binary representations must be generated as separate patterns in the NFA. Matches can then be resolved back to the user-specied term and appropriate encoding via a table. lightgrep currently can search for ASCII-specied patterns as ASCII and as UTF-16. Full support for various encodings is under active development, using the open source ICU library [9] to eliminate platform dependencies. In addition to specifying the particular encodings to be used for a given search term, users are allowed to choose an automatic mode, where the keywords characters are considered as Unicode code points, and then all unique binary representations are generated from the list of supported ICU encodings. We feel this will be a great help to investigators when searching for foreign-language keywords.

Stewart & Uckelman

15

6.2

Future Work

The primary areas of current development for lightgrep are robust acceptance testing to ensure condence in its results and support for generating alternative patterns for matching in multiple encodings. This is not without eort, but is straightforward engineering. An obvious optimization will be to multiplex the execution of virtual machine threads onto system threads, exploiting multicore processors. The malleability of the byte code representation for automata matching makes it amenable for use with newer matching algorithms that can often skip bytes in the text, based on precomputed shift tables. For example, Watson [17] describes a sublinear multipattern matching algorithm that combines a CommentzWalter xed-string search for prexes of matches with full automata evaluation for the complete pattern. Extending lightgreps virtual machine abstraction to support such an algorithm would be very feasible, and some initial experimentation indicates the speed of hashing can be matched with such techniques. Also, some pattern matching research related to network packet inspection and intrusion detection system rule-matching can be applied to digital media searching. For example, Becchi and Crowley [3] describe optimizations related to counted repetitions in patterns. lightgrep does not address searching for near-matches. Near-matching on large automata is accomplishable using the algorithm in [18], which is implemented by both the agrep and TRE [11] search utilities. nrgrep [13] also provides fuzzy matching, using a bit-parallel algorithm.

6.3

Conclusion

Tagged NFAs can easily be applied to the multipattern problem and moreover several practical optimizations can keep the observed performance below the worst-case O(nm) running time as the size of the automata increases. lightgrep uses these mechanisms to provide investigators with a sorely-needed capability, allowing evidence to be searched for large keyword sets in a single pass. We view its performance as establishing an upper bound for forensic keyword searches, and look forward to the day when this is the case for average investigators.

Notes
1. No patterns used in the experiments in Section 1.5.3 cause pathological behavior. 2. The same analysis applies to GNU flex. 3. RE2s syntax is limited strictly to patterns that can be searched without using backtracking, to limit the potential for denial of service attacks stemming from problematic patterns.

16
4. The two are orthogonal and can be supported simultaneously, though we do not nd submatching particularly compelling in forensics. 5. Thread IDs are provided in the example for ease of reference; they are not necessary for the implementation. 6. A trie, also known as a prex tree, is a tree where the root corresponds to the empty string, and each other node extends the string of its parent by one character. Tries are a kind of acyclic DFA. 7. In particular: strings | grep and FTK do not support multipattern search. lex does not support reporting overlapping matches from independent patterns. (E.g., the patterns ab and ba have overlapping matches on the text aba. lex will report only the match of ab at the start of the text, while we would prefer to be notied of both hits.) RE2 requires the entire text to t in RAM, but our disk image exceeded the size our RAM by a factor of eight.

References
[1] AccessData. Forensic Toolkit, May 2010. http://www.accessdata. com/forensictoolkit.html. [2] A. V. Aho, M. S. Lam, R. Sethi, and J. D. Ullman. Compilers: Principles, Techniques, & Tools. Pearson, second edition, 2007. [3] M. Becchi and P. Crowley. Extending nite automata to eciently match Perl-compatible regular expressions. In Proceedings of the International Conference on emerging Networking EXperiments and Technologies (CoNEXT), Madrid, Spain, December 2008. ACM. [4] R. Cox. Regular expression matching: the virtual machine approach, December 2009. http://swtch.com/~rsc/regexp/regexp2.html. [5] R. Cox. RE2: an ecient, principled regular expression library, May 2010. http://code.google.com/p/re2. [6] S. Garnkel. Automated computer forensics, July 2010. http://simson.net/wiki/index.php?title=Automated_ Computer_Forensics&old%id=574. [7] V. M. Glushkov. The abstract theory of automata. Russian Mathematical Surveys, 16(5):153, 1961. [8] Guidance Software. EnCase, May 2010. guidancesoftware.com. http://www.

[9] IBM. International Components for Unicode (ICU), May 2010. http://icu-project.org. [10] V. Laurikari. NFAs with tagged transitions, their conversion to deterministic automata and applications to regular expressions. In Proceedings of the 7th International Symposium on String Processing and Information Retrieval, pages 181187. IEEE, 2000. [11] V. Laurikari. TREthe free and portable approximate regex matching library, May 2010. http://laurikari.net/tre/.

Stewart & Uckelman

17

[12] J. Maddock. Boost.Regex, May 2010. http://www.boost.org/doc/ libs/1_43_0/libs/regex/doc/html/index.html. [13] G. Navarro. NR-grep: A fast and exible pattern matching tool. Software Practice and Experience (SPE), 31:2001, 2000. [14] G. Navarro and M. Ranot. Flexible pattern matching in strings: Practical on-line search algorithms for texts and biological sequences. Cambridge, 2007. [15] M. Sipser. Introduction to the Theory of Computation. PWS Publishing Company, 1997. [16] K. Thompson. Regular expression search algorithm. Communications of the ACM, 11(6):419422, 1968. [17] B. W. Watson. A new regular grammar pattern matching algorithm. In Proceedings of the 4th Annual European Symposium, pages 364 377. Springer-Verlag, 1996. [18] S. Wu and U. Manber. Agrepa fast approximate pattern-matching tool. In Proceedings USENIX Winter 1992 Technical Conference, pages 153162. USENIX Association, 1992.

You might also like