<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4736761969082226436</id><updated>2024-09-14T12:53:25.966-04:00</updated><category term="Programming"/><category term="EnScript"/><category term="Forensics"/><category term="Security"/><category term="Cloud"/><title type='text'>Codeslack</title><subtitle type='html'>Software, programming, computer forensics</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>87</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1306902073284144357</id><published>2023-09-20T12:06:00.003-04:00</published><updated>2023-09-21T21:41:54.298-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>Running Rules — A Rough Ranking</title><content type='html'>&lt;p&gt;A caveat: this post is more brain dump than research. I would not be surprised if multiple academic papers have laid out competing hierarchies and frameworks for classifying rules engines. However, doing research is hard. It takes me days of searching to find good papers, and then it takes me weeks of re-reading to grok them. So what follows is entirely NIH.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Problem definition&lt;/h3&gt;&lt;p&gt;Say you have a big pile of data. There&#39;s a file, it has some records in it. The records are serialized in some format, like json, protobufs, pcaps, or, more ridiculously, binary xml records inside of a chunked binary file format (how I miss &lt;a href=&quot;https://forensics.wiki/windows_event_log_%28evt%29/&quot; target=&quot;_blank&quot;&gt;LfLe&lt;/a&gt;!). Maybe there&#39;s some compression involved. And what your boss/department/company/industry would like to know is whether individual records in that data set match any number of rules in a set and, if so, which ones.&lt;/p&gt;&lt;p&gt;Note how uncomplicated of a task this seems. We don&#39;t need to sum any of these fields across records, compute statistics on them, join them with lots of other data sets (well, we do and will; cyber calls this phase &quot;enrichment&quot; but it happens much later), or such. Our problem is, for each of &lt;i&gt;n&lt;/i&gt; records, which of our &lt;i&gt;m&lt;/i&gt; rules matches on the record?&lt;/p&gt;&lt;p&gt;(Note also how this is actually a join, but since we&#39;re so often processing complex records in complicated formats as it lies and using rules with a rich and complicated set of predicates, refining the computation to the point where it can be performed as a join is damn near impossible.)&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Rules&lt;/h4&gt;&lt;p&gt;Ok, records, sure, records are easy. Tuples of fields, maybe there&#39;s some typing, maybe the fields have names, maybe there are complex types, but, still, we all understand these records: they&#39;re logs, CSV, JSON, maybe protobufs/Avro/Thrift or pcaps. What are the rules?&lt;/p&gt;&lt;p&gt;A rule could be very simple, e.g., &quot;Value = 5&quot;. A simple evaluation rule like this can be called a predicate; it&#39;s performing a single evaluation against a single field in the record and the result of the predicate is either true or false. Predicates can be joined together into complex rules with Boolean logic. The operators in the predicates will likely permit equality checks and comparisons. Often there will be &lt;a href=&quot;http://docs.oasis-open.org/cti/stix/v2.0/cs01/part5-stix-patterning/stix-v2.0-cs01-part5-stix-patterning.html#_Toc496717744&quot; target=&quot;_blank&quot;&gt;functions&lt;/a&gt; (which are also just operators without fancy syntax) like &quot;contains&quot;, &quot;in&quot;, &quot;like&quot;, &quot;matches&quot;, which help with strings and sets. Perhaps if you&#39;re unlucky, you&#39;ll be working in an &lt;a href=&quot;https://yara.readthedocs.io/en/stable/modules/pe.html#c.dll_characteristics&quot; target=&quot;_blank&quot;&gt;extensible rules language&lt;/a&gt; where a predicate can involve parsing kilo/megabytes of raw data to compute some fields on the fly. Then you&#39;ll tire of such complexity, and the accompanying slowdown in runtime and development time that comes with it, and you&#39;ll adopt the hot new rules language that seems &lt;a href=&quot;https://github.com/SigmaHQ/sigma-specification/blob/main/Sigma_specification.md#rx-yaml&quot; target=&quot;_blank&quot;&gt;simpler and friendlier&lt;/a&gt;, the kind assembled by field practitioners who can&#39;t be bothered with knowing that describing computer language syntax was solved &lt;a href=&quot;https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form&quot; target=&quot;_blank&quot;&gt;64 years ago&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Rules evaluate different fields in the records, often comparing them against literal values. It may be possible for rules to compare different fields in the same record, e.g., &quot;&lt;span style=&quot;font-family: courier;&quot;&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;record.created_timestamp &amp;lt; record.lastmodified_timestamp&lt;/span&gt;&lt;/span&gt;&quot;, though this is rare. What we will &lt;i&gt;not&lt;/i&gt; consider is the ability for rules to compare/evaluate multiple/different records with possible aggregations. That&#39;s what SQL is for.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Implementation types, ranked&lt;/h3&gt;&lt;p&gt;So, how do we write a program to run the rules against our data? What follows is a ranking of implementation approaches I can envision, from least optimized to most. This ranking primarily considers throughput, i.e., how many records can evaluated against a given set of rules in a measure of time.&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Ad hoc&lt;/h4&gt;&lt;p&gt;Rules seem hard and abstract. Unrestricted Boolean logic? Comparisons? Code Monkey think this is hard! (Code Monkey output not functional or elegant.)&lt;/p&gt;&lt;p&gt;If rules can be restricted to simpler and specialized forms, then an ad hoc rules engine may only need to consider hard-coded logic for looking up hash values in a hash set, parsing PE file headers, or conducting string searches. Perhaps a few different types of rules can express a small number of commonly desired Boolean forms, so a rule could simply OR constituent ad hoc evaluation functions, or maybe AND then, or negate them. I did this with EnCase eDiscovery/EnCase Cybersecurity, where the user could choose from three different Boolean forms for combining logic on filesystem metadata, file content search terms, and file hashes. Those three options handled all the practical use cases.&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Parse trees&lt;/h4&gt;&lt;p&gt;Ad hoc engines are pretty limited, however, and parsers aren&#39;t &lt;i&gt;that&lt;/i&gt; hard, once you know how to write one. So, if you can parse formal rules, with Boolean logic, then you can build a parse tree data structure. And if you can build a parse tree, then you can execute it according to the types of nodes in the parse tree. The engine will generally involve a stack data structure and one or more switch statements for executing what to do for each type of node in the parse tree (e.g., an OR operator). Sometimes the parse tree may be turned into an abstract form, with enums and objects; other times it&#39;ll just be character literals that are being compared directly.&lt;/p&gt;&lt;p&gt;Performance will vary amongst implementations, and especially depend on the implementation language/platform. An engine written in Python will be a lot slower than one written in Rust or C. Still, throughput may only be 10MB/s or less, and will degrade considerably as the rules add up — each rule will need to be parsed and evaluated, and there&#39;s usually not any work done to consolidate logic or even just useful setup work between rules. If parsing is not a separate step from evaluation, then the parsing logic itself will need to be re-evaluated with each record.&lt;/p&gt;&lt;p&gt;A possible way out of writing a proper parser and evaluation engine is to recast the rules into some other language that can be embedded and executed itself; I also did this in EnCase eDiscovery/EnCase Cybersecurity where the EnScript engine itself was exposed as an API to EnScript, and then I could just generate EnScript snippets on the fly as EnCase Filters and execute that code against objects. And when I first started thinking about evaluating rules, in 2005, I realized that if the industry was smart it would simply write its rules in Prolog and life would be easy.&lt;/p&gt;&lt;p&gt;Did the industry adopt Prolog?-&amp;nbsp;&lt;/p&gt;&lt;p&gt;no&lt;/p&gt;&lt;p&gt;(That was a Prolog joke.)&lt;/p&gt;&lt;p&gt;Instead the industry first adopted OpenIOC (yay xml) and then whatever-the-hell YARA is. And now there&#39;s sigma, which is even less well-defined than these two predecessors.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Examples: &lt;a href=&quot;https://github.com/Yamato-Security/hayabusa/blob/main/src/detections/rule/matchers.rs&quot; target=&quot;_blank&quot;&gt;Hayabusa&lt;/a&gt;, &lt;a href=&quot;https://crates.io/crates/tau-engine&quot; target=&quot;_blank&quot;&gt;chainsaw&lt;/a&gt; (a bit more elegant)&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Virtual machines&lt;/h4&gt;&lt;p&gt;If you can generate a parse tree and you refactor your code enough, you&#39;ll often find you have a loop that pops nodes off a stack, evaluates those nodes, and then pushes them back onto the stack until the stack contains only one element and there&#39;s nothing left in the rule to parse: that one element then is the result.&lt;/p&gt;&lt;p&gt;If you have that code, then it is not a big leap to represent those nodes as invented byte code, or &lt;a href=&quot;https://en.wikipedia.org/wiki/UCSD_Pascal&quot; target=&quot;_blank&quot;&gt;&lt;i&gt;p-code&lt;/i&gt;&lt;/a&gt;, and then to execute that byte code. As it happens, this leap can make the evaluation engine astonishingly straightforward — there&#39;s a loop around decoding the current instruction where the opcode&#39;s evaluated by a switch statement and the cases perform the operation. The complexity of direct parse tree evaluation can be spread out across a separate compilation step, which transforms the parse tree into the byte code.&lt;/p&gt;&lt;p&gt;Generally these virtual machines fall into two classes: stack-based and stackless. Stack-based virtual machines are typically simple and low-level, where the operations are extremely simple and evaluation follows the parse tree representation. Stackless VMs may use a byte code representation that is higher-level and domain specific, with instructions that correspond more directly to complicated functions (e.g., &quot;contains&quot; resulting in a string find). This is not a precise distinction, of course; the precise distinction is that stackless VMs lack a stack... but lacking a stack often results in greater complexity being put into the operations.&lt;/p&gt;&lt;p&gt;Virtual machines have some nice properties that bring performance benefits. Decoding and evaluation results in simple code, and optimizing compilers love simple code, which they can tease out into sequences of instructions that pipelined CPUs can evaluate efficiently. There&#39;s often far less branching in the resulting code, so there are fewer stalls. And critically, the byte code tends to be compact and stored altogether in memory, resulting in excellent L1 cache hit characteristics.&lt;/p&gt;&lt;p&gt;With a separate compilation step, the virtual machine approach can also be amenable to optimization passes. For example, across a set of rules there may be duplicate predicates. A rule engine that understands all the rules at once could deduplicate evaluation of these predicates. It could also reorder evaluation of predicates to try to prove rules don&#39;t match (the common case) with early exits.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://dl.ifip.org/db/conf/ifip11-9/df2011/StewartU11.pdf&quot; target=&quot;_blank&quot;&gt;Lightgrep&lt;/a&gt; uses the virtual machine, in a stackless complicated way. It&#39;s based in no small part on Russ Cox&#39;s excellent blog post &lt;i&gt;&lt;a href=&quot;https://swtch.com/~rsc/regexp/regexp2.html&quot; target=&quot;_blank&quot;&gt;Regular Expression Matching: the Virtual Machine Approach&lt;/a&gt;&lt;/i&gt;. Whole programming languages can be implemented with virtual machines, too, like &lt;a href=&quot;https://github.com/python/cpython/blob/main/Python/executor_cases.c.h&quot; target=&quot;_blank&quot;&gt;Python&lt;/a&gt;. Another notable example that&#39;s generated considerable work and blog posts over the years is &lt;a href=&quot;https://thesephist.com/posts/lua/&quot; target=&quot;_blank&quot;&gt;Lua&lt;/a&gt;. One could do a lot worse than embedding Lua and calling it a day.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Other examples: &lt;a href=&quot;https://github.com/jqlang/jq/blob/master/src/execute.c#L403&quot; target=&quot;_blank&quot;&gt;jq&lt;/a&gt;, &lt;a href=&quot;https://github.com/PCRE2Project/pcre2/blob/master/src/pcre2_match.c#L787&quot; target=&quot;_blank&quot;&gt;PCRE&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;JITs&lt;/h4&gt;&lt;p&gt;If you&#39;re parsing rules and compiling them to byte code, well, why not compile them to machine code instead and make it the CPU&#39;s problem? This is called a JIT, for just-in-time.&lt;/p&gt;&lt;p&gt;Do you like speed? JITs can be blazing fast. They lower evaluation to the level of the machine, where there is no engine, i.e., getting your crappy Code Monkey software out of the way of the CPU is probably the best way to go fast.&lt;/p&gt;&lt;p&gt;But JITs also have a cost — welcome to tradeoffs! First, rules tend to be fairly compact, a few dozen characters. Those rules can unroll to hundreds or thousands of machine code instructions. Invented virtual machine byte code can still be compact, but machine code deals with registers, raw arithmetic, and loads/stores. So, typically, JITs will need more memory to represent the rules, and this can turn into a problem when you have a lot of rules.&lt;/p&gt;&lt;p&gt;Additionally, compiling the rules to machine code can take time, so there are larger startup costs. This is especially the case if your JIT has optimization passes. This often leads to faster evaluation, but could wind up being a false economy, where a simpler engine would get through processing all the data before a JIT even starts running.&lt;/p&gt;&lt;p&gt;What&#39;s more, an off-the-shelf engine with optimizers can itself be dozens of megabytes in size. You&#39;re going to embed LLVM into your tool? Good luck fitting that on a floppy and/or downloading to point-of-sale endpoints running on a cheap Comcast link.&lt;/p&gt;&lt;p&gt;Portability also becomes a concern. If you prudently decide &lt;i&gt;not&lt;/i&gt; to embed LLVM, now you need to find a JIT that&#39;s portable if you want to support more than x86. Good luck! I&#39;m sure there are solutions, but I don&#39;t know of a clear answer off the top of my head.&lt;/p&gt;&lt;p&gt;JITs also have security concerns. A normal executable gets compiled and it runs. Modern operating systems will treat its code and data separately, with the code protected by the virtual memory manager as read-only and the data treated as non-executable. With JITs, data and code are the same thing! The JIT will need to write out machine code to memory and then execute it directly; this requires some low-level operating system trickery.&lt;/p&gt;&lt;p&gt;Examples: &lt;a href=&quot;https://pcre.org/original/doc/html/pcrejit.html&quot; target=&quot;_blank&quot;&gt;PCREjit&lt;/a&gt;, &lt;a href=&quot;http://lua-users.org/lists/lua-l/2011-02/msg00742.html&quot; target=&quot;_blank&quot;&gt;luajit&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=ZJOgDovsIsg&quot; target=&quot;_blank&quot;&gt;redgrep&lt;/a&gt;, Ken Thompson&#39;s &lt;a href=&quot;https://dl.acm.org/doi/pdf/10.1145/363347.363387&quot; target=&quot;_blank&quot;&gt;original regular expression search engine&lt;/a&gt; for IBM 7049&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;SIMDized&lt;/h4&gt;&lt;p&gt;Modern CPUs have a lot of capability. Pipelined CPUs became common forty years ago. Thirty years ago CPUs went super-scalar. Twenty years ago CPUs went multi-core.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;A rule engine that uses a JIT, especially a simple one without optimization passes, will struggle with pipelining as it&#39;s likely to embed many branches that mispredict with some regularity (a 90% branch prediction rate is still pretty bad). It will likely not execute with &lt;a href=&quot;https://en.wikipedia.org/wiki/Instructions_per_cycle&quot; target=&quot;_blank&quot;&gt;IPC&lt;/a&gt; rates &amp;gt; 1.0 (unless you&#39;re running on one of Apples magic M chips, that put jillions of transistors towards a monstrous &lt;a href=&quot;https://dougallj.wordpress.com/2021/04/08/apple-m1-load-and-store-queue-measurements/&quot; target=&quot;_blank&quot;&gt;ROB&lt;/a&gt;). And for sure you will not get multiple cores utilized, as multithreading is always an exercise left to the user (and rightfully so).&lt;/p&gt;&lt;p&gt;These three limitations need to be addressed in a rules engine if you want to fly as fast as the hardware can go. Good luck! I don&#39;t have good answers for you other than lots and lots of hard work.&lt;/p&gt;&lt;p&gt;And even if you are able to write a fantastic rules engine that JITs out code with high branch prediction and IPC rates, and uses threads smartly to process [batches of, you&#39;ll need to amortize your locking to avoid stalling] records in parallel, you still won&#39;t be using your CPU to its fullest potential.&lt;/p&gt;&lt;p&gt;18 years ago Intel released SSE3, and SSE3 quietly revolutionized SIMD optimization. It took awhile for &lt;a href=&quot;https://en.algorithmica.org/hpc/simd/shuffling/&quot; target=&quot;_blank&quot;&gt;folks to figure out the magic new pshufb instruction&lt;/a&gt;, but each new SIMD ISA since has added lots of capability (though ARM&#39;s NEON ISA is more comparable to SSE 4.2 or AVX, with 128 bit registers while just about everyone with an Intel CPU has at least 256 bit registers and many have 512 bit registers). Contemporary CPUs can now often retire multiple SIMD instructions per cycle, if they&#39;re scheduled correctly.&lt;/p&gt;&lt;p&gt;Predicates in rules evaluate to true and false. Rules combine predicates with Boolean logic. If you&#39;re thinking what I&#39;m thinking, then finding ways to represent predicates and rules in SIMD bit vectors is likely to provide a lot of benefit.&lt;/p&gt;&lt;p&gt;However, it&#39;s easy to forget the MD in SIMD. A good SIMD engine will likely only gain high throughput by biting off a chunk of records, decomposing them into their fields, and evaluating those fields with SIMD operations in parallel, then, many instructions later, taking a look at the result and separating matching records.&lt;/p&gt;&lt;p&gt;One major trouble with adopting SIMD is portability. Even amongst x86 CPUs, there are multiple targets ISAs (AVX-512 is a &lt;i&gt;family&lt;/i&gt; [and a Tolstoy-esque unhappy one at that] of differing ISAs, then there&#39;s AVX-2, AVX, SSE 4.2 for the Sandy Bridge diehards like me...), and different CPU microarchitectures may have very different performance characteristics for the instructions in those ISAs. And then there&#39;s ARM NEON. Oy.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Another major trouble with using SIMD operations in a rules engine is that the predicates in a modern cybersecurity ruleset are many, but most evaluate to false a great deal of the time. So a bit vector representation of all the predicates across all the rules will still be large (thousands of bits), much too large for a single register, and most of the bits will be unset. So, there&#39;s natural sparsity to cope with — maybe a bit vector representation isn&#39;t worth it? Another major trouble will be the dependencies between predicates in the rules; how do you benefit from early exit with a SIMD approach?&lt;/p&gt;&lt;p&gt;The only answer when it comes to SIMD optimization is empirical bloodymindedness.&lt;/p&gt;&lt;p&gt;Examples: none that I know of, naïve SIMDization is an uncanny valley of death&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Multi-algorithm SIMD with ad hoc components&lt;br /&gt;&lt;/h4&gt;&lt;p&gt;Bloodymindedness it is then. Relying on simple SIMD operations to evaluate sparse bit vectors with thousands of bits could lose out to a normal JIT engine, maybe even a VM. The way to apply SIMD is to look for amenable types of predicates and rules and target those with specialized SIMD algorithms.&lt;/p&gt;&lt;p&gt;Here &lt;a href=&quot;https://www.intel.com/content/www/us/en/developer/articles/technical/introduction-to-hyperscan.html&quot; target=&quot;_blank&quot;&gt;Hyperscan&lt;/a&gt; reigns supreme and alone, and with its integration into Suricata pcap scanning has benefited from it for a number of years. &lt;a href=&quot;https://github.com/praetorian-inc/noseyparker&quot; target=&quot;_blank&quot;&gt;Noseyparker&lt;/a&gt; also integrates Hyperscan for secrets scanning. But Hyperscan, as an Intel project, is unsurprisingly Intel-only. There&#39;s been an attempt or two to port Hyperscan to ARM, but Hyperscan&#39;s development itself seems tenuous and the porting attempts can be characterized as &quot;academic&quot; in comparison.&lt;/p&gt;&lt;p&gt;DuckDB is also worthy of mention here. As a columnar database, it is a good fit for SIMD operations, and it benefits greatly from them. Any Python programmer &lt;a href=&quot;https://github.com/Neo23x0/LOKI&quot; target=&quot;_blank&quot;&gt;considering writing a rules engine&lt;/a&gt; should do everyone a favor and instead focus on ingesting their data into DuckDB. (I would also extend this recommendation to many C programmers who&#39;ve written popular engines, as well as to those that naïvely bundle such engines into &lt;a href=&quot;https://www.nextron-systems.com/thor-lite/&quot; target=&quot;_blank&quot;&gt;larger scanners&lt;/a&gt;.)&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Further Considerations&lt;/h3&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Record parsing&lt;/h4&gt;&lt;p&gt;Rules don&#39;t exist in a vacuum. After all, what we care about are the &lt;i&gt;records&lt;/i&gt;. And records are inevitably represented in their own form (CSV, json, in-memory data structures, etc.). Dealing with those records and getting their necessary fields out to where they can be evaluated by predicates in our ruleset is not free. In fact, it may &lt;i&gt;dominate&lt;/i&gt; the runtime, perhaps by orders of magnitude, especially if we&#39;re not careful with how the data&#39;s encoded (ugh cybox) and which libraries are used (&lt;a href=&quot;https://docs.python.org/3/library/json.html#json.load&quot; target=&quot;_blank&quot;&gt;🤡&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;An interesting approach is &lt;a href=&quot;https://arxiv.org/pdf/1902.08318.pdf&quot; target=&quot;_blank&quot;&gt;simdjson&lt;/a&gt;. Simdjson uses SIMD operations in parsing json records in parallel, but it also embeds some [very] rudimentary rules capability with &lt;a href=&quot;https://github.com/simdjson/simdjson/blob/master/doc/basics.md#json-pointer&quot; target=&quot;_blank&quot;&gt;JSON Pointer&lt;/a&gt; support. That allows it to evaluate json records without first doing a full parse.&lt;/p&gt;&lt;p&gt;Avoiding full parsing has of course been a key aspect in regular expression searching. Fast regular expressions don&#39;t parse text into lines, parse lines into Unicode codepoints, and run codepoints through their Unicode-aware regexp engines; no, fast regular expression engines look for &lt;a href=&quot;https://lists.freebsd.org/pipermail/freebsd-current/2010-August/019310.html&quot; target=&quot;_blank&quot;&gt;byte literals in megabyte-sized blocks of bytes&lt;/a&gt; and only when there are hits—and some refuse to believe there are hits &lt;a href=&quot;https://ridiculousfish.com/blog/posts/old-age-and-treachery.html&quot; target=&quot;_blank&quot;&gt;without repeated evidence&lt;/a&gt;—do they bother to look for line breaks and consider the full regular expression(s).&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Heavyweight processing components&lt;/h4&gt;&lt;p&gt;Additionally, some predicates may be call-outs to entire subcomponents 
doing complicated things (looking at you, YARA). Searching strings or files, hashing, parsing parts of file formats, these are operations that go well beyond evaluating records of tuples/dicts in csv or json. The rules engine must call out to these subcomponents, and a considerable portion of runtime will be spent in those components and not in the evaluation of rules.&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Predicate cost weighting&lt;/h4&gt;&lt;p&gt;To repeat, runtime may be dominated in components evaluating complicated predicates completely outside the normal ken of a rules engine. Therefore, the &lt;i&gt;ideal&lt;/i&gt; rules engine will have some notion of which types of predicates are costlier than others, and the engine will then analyze the rules and the predicate costs to evaluate the cheapest predicates first that permits early-exit.&lt;/p&gt;&lt;p&gt;For an example of a trick I&#39;ve used for decades, though not in a general purpose rules engine, consider cryptographic hashing for file matching; if I know the file sizes of the set of [contraband/IP/malware] files I&#39;m trying to match against then I can first check to see whether the input file has a matching file size; only if it does will the input file need to be read and hashed. This will get you several orders of magnitude of speedup when scanning disk images or live endpoints, so long as you have all the original file sizes and their cardinality is not too great.&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;A Dismal Conclusion&lt;/h3&gt;&lt;p&gt;Do I have a conclusion? No, not really. Rule engines exist, but few are separate from their rule language, with tight coupling that consigns them to the same fate as that of the rule language and the commercial entities that sponsor them. Most of the best engines tap out with a virtual machine approach, avoiding the hard engineering involved in JITs and SIMD, but also missing out on the 10+ GB/s of throughput that&#39;s possible with contemporary CPUs and DRAM. A few crazy people have made it to that promised land but for specialized domains. And going crazy is meaningless if you don&#39;t consider the input format of the records you want to process.&lt;/p&gt;&lt;p&gt;Developing and maintaining an ideal rules engine is a hard problem, requiring genius, expertise, bloodymindedness, and the long-term focus/dedication (with concomitant financing) to scrap existing code and build new implementations from scratch in response to new hardware capabilities. Am I working on one? No. Would I like to? Sort of. Will I? Maybe, but likely without success, because genius, expertise, bloodymindedness, and long-term focus are a lot to ask, and I am lazy, tired, and distracted.&lt;/p&gt;&lt;p&gt;So, continue along with your YARA and SIGMA rules, and their 386-era throughput.&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1306902073284144357/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2023/09/running-rules-rough-ranking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1306902073284144357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1306902073284144357'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2023/09/running-rules-rough-ranking.html' title='Running Rules — A Rough Ranking'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-569887975906067579</id><published>2022-10-30T23:33:00.003-04:00</published><updated>2022-10-30T23:33:49.831-04:00</updated><title type='text'>Learning Timely &amp; Differential Dataflow, Part I of K</title><content type='html'>&lt;p&gt;Differential Dataflow is both a &lt;a href=&quot;https://github.com/TimelyDataflow/differential-dataflow&quot;&gt;Rust library&lt;/a&gt; and a &lt;a href=&quot;https://www.microsoft.com/en-us/research/publication/differential-dataflow/&quot;&gt;dataflow computation paradigm&lt;/a&gt;. This blogpost is about the former.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Except... it&#39;s not?&lt;/p&gt;&lt;p&gt;Differential Dataflow seems to be an advanced construct built on top of Timely Dataflow, which is also both a &lt;a href=&quot;https://github.com/TimelyDataflow/timely-dataflow&quot;&gt;Rust library&lt;/a&gt; and a &lt;a href=&quot;https://www.microsoft.com/en-us/research/publication/differential-dataflow/&quot;&gt;dataflow computation paradigm&lt;/a&gt;. The rough idea is that Differential Dataflow can accept new input items after having processed a bunch of data and update derived result sets/output with a minimum of computation; it does this by paying attention to changes/deltas/differentials between nodes in a dataflow graph. (NB: I do not understand it beyond this rough idea; some day I&#39;ll get around to reading the paper, but there are programs to write.)&lt;/p&gt;&lt;p&gt;In order to compute differences in a dataflow graph, though, one first needs a dataflow graph system, and that&#39;s what Timely Dataflow provides.&lt;/p&gt;&lt;p&gt;So, here&#39;s how to learn Timely.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 1: Do not read the Differential Dataflow docs.&lt;/h3&gt;&lt;p&gt;(This may seem alternately obvious and counterintuitive.) They&#39;re less than fantastic at distinguishing what Differential Dataflow provides above and beyond Timely, yet any use of the differential dataflow crate will have one quickly using the timely dataflow API, and likely more heavily than the former.&lt;br /&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 2: Read the Timely Dataflow docs.&lt;/h3&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://timelydataflow.github.io/timely-dataflow/introduction.html&quot;&gt;Timely Dataflow online book&lt;/a&gt; (pamphlet)&lt;/li&gt;&lt;li&gt;Blog posts — An Introduction to Timely Dataflow&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-14.md&quot;&gt;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-14.md&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-18.md&quot;&gt;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-18.md&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-21.md&quot;&gt;https://github.com/frankmcsherry/blog/blob/master/posts/2015-09-21.md&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://docs.rs/timely/latest/timely/&quot;&gt;https://docs.rs/timely/latest/timely/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 3: Investigate magic in examples&lt;br /&gt;&lt;/h3&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;wtf does &lt;a href=&quot;https://docs.rs/timely/latest/timely/execute/fn.execute_from_args.html&quot;&gt;timely::execute_from_args&lt;/a&gt; do?&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://docs.rs/timely/latest/timely/dataflow/operators/inspect/trait.Inspect.html&quot;&gt;inspect&lt;/a&gt; — this seems more as a debugging aid? or does it force computation?&lt;/li&gt;&lt;li&gt;is &lt;a href=&quot;https://docs.rs/timely/latest/timely/dataflow/operators/aggregation/aggregate/trait.Aggregate.html&quot;&gt;aggregate&lt;/a&gt; just &lt;a href=&quot;https://hadoop.apache.org/docs/current/api/&quot;&gt;reduce&lt;/a&gt; by a different name? is it more? is it less?&lt;/li&gt;&lt;ul&gt;&lt;li&gt;Hash is presumably used for grouping values from the different keys together, but read the source to confirm this is correct, kind of akin to Hadoop&#39;s &lt;a href=&quot;https://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/RawComparator.html&quot;&gt;RawComparator&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;But is Hash collision resistant? Does it just use for rough partitioning and distinguish between different keys? Or do collisions (on a 64 bit int) signify equality and good luck working with strings and such?&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 4: Write some toy programs.&lt;/h3&gt;&lt;p&gt;Many (most?) of the example programs in the docs have to do with iterative graph computations. I can see how they&#39;re the motivating class of problems for Timely, as older systems fall down here (ahhhh, remember the days of &lt;a href=&quot;http://docs.cascading.org/cascading/3.2/userguide/ch01-introduction.html#_what_is_cascading?&quot;&gt;cascading&lt;/a&gt; MapReduce jobs?), but it would be nice to have simpler examples with clearer analogues to other systems (by which I mean, WordCount).&lt;/p&gt;&lt;p&gt;In particular, Timely and Differential Dataflow are &lt;i&gt;Rust&lt;/i&gt; &lt;i&gt;libraries&lt;/i&gt;, so they are a readymade data processing engine that play nicely in the Rust ecosystem. The downsides of Hadoop/Spark/Flink/et al have always been their &lt;a href=&quot;http://www.frankmcsherry.org/assets/COST.pdf&quot;&gt;cost&lt;/a&gt; of scaling down and being so firmly mired in the muck of Java, requiring enormous shell scripts to manage classpaths and the like.&lt;/p&gt;&lt;p&gt;Rust scales down to a single machine quite nicely, and Timely purports to do so as well. Therefore, it&#39;s nice to use for data processing even if all the advanced time/windowing features are used.&lt;/p&gt;&lt;p&gt;Programs to write:&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;WordCount&lt;/li&gt;&lt;li&gt;WordCount w/ Combiner (or does Rust just do this?)&lt;/li&gt;&lt;li&gt;Map-side join&lt;/li&gt;&lt;li&gt;Reduce-side join&lt;/li&gt;&lt;li&gt;Composite aggregation, of count and sum (how many files do we have, and what are their total size?)&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Step 5: Questions&lt;/h3&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Does Timely actually support distributed processing, with multiple nodes???&lt;/li&gt;&lt;li&gt;How hard is it to submit patches to the docs?&lt;/li&gt;&lt;li&gt;How much attention is it getting now that Materialize exists and is focused on a higher-level database product?&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/569887975906067579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2022/10/learning-timely-differential-dataflow.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/569887975906067579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/569887975906067579'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2022/10/learning-timely-differential-dataflow.html' title='Learning Timely &amp; Differential Dataflow, Part I of K'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-8410294922946242922</id><published>2020-01-07T10:14:00.000-05:00</published><updated>2020-01-07T10:14:28.783-05:00</updated><title type='text'>Evaluating SANS Courses</title><content type='html'>I&#39;ve only taken one DFIR training course, the EnCase Advanced class back in 2003, the third week into my new job at Guidance Software at their old Green Street location. It was taught by Jessica Bair and Ric Stonesifer, and, knowing nothing about forensics or EnCase, it was a good fast-paced introduction to the field. From there, I learned by doing, maintaining Guidance&#39;s core set of artifact EnScripts and then supporting the company&#39;s professional services examiners in their casework.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I&#39;ve forgotten a great deal about old artifacts since then, and have not kept up on new artifacts as much lately. And, of course, there are far more systems and approaches to forensics than existed 15 years ago—macOS, iOS, Android, memory forensics, malware analysis, etc. My dimly-recalled Windows 98 artifact knowledge no longer applies.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Live training is a good option for me because I can turn my phone and email off and simply focus for a week. So, I&#39;m thinking about taking a SANS course this year. Which one should I take? These are the possibilities:&lt;/div&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;FOR500, Windows Forensics. I do know a lot about Windows forensics, so I&#39;m worried I might get a little bored, but this would also be a good tune-up.&lt;/li&gt;
&lt;li&gt;FOR 508, Advanced IR &amp;amp; Threat Hunting. I&#39;ve assisted on a number of large-scale IR scenarios, but always in a focused, limited capacity where I&#39;ve been asked to write code to do a specific thing (usually at scale). Working through the complete chain of an investigation could give me new insight into current problem areas to focus on as a developer, and especially provide me with greater intuition into the needs of IR examiners.&lt;/li&gt;
&lt;li&gt;FOR 572, Network Forensics. I&#39;ve focused almost exclusively on host-based forensics, and have always thought that network forensics is not well integrated into traditional forensics investigations (especially IR and IP theft scenarios). I did a bit of libpcap network programming in 2002, but there&#39;d be much here that&#39;d be new to me.&lt;/li&gt;
&lt;li&gt;FOR 578, Cyber Threat Intelligence. I&#39;m trying to learn much more about threat intelligence and see it as directly applicable to my job. OTOH, the course seems a bit thin; working more hands-on with threat intel data sets would be good practice, but I worry about there being too much focus on jargon (I still am skeptical that &quot;kill chain&quot; means anything that isn&#39;t obvious) and on high-level intelligence analysis and techniques (e.g., ACH) and less on tech. It&#39;s fine for training classes to address non-technical aspects of a subject, but I personally would get bored and then not get much out of the class.&lt;/li&gt;
&lt;li&gt;FOR 610, Malware Analysis. Here&#39;s something I don&#39;t know much about. But would I be better off taking a more in-depth reverse-engineering course from a different organization? Spending time on little utilities like pescan and pdfid would not be useful to me; fundamental deep dives into PE and ELF, unpacking, reverse engineering, call graph analysis, and so on, definitely would be great.&lt;/li&gt;
&lt;li&gt;FOR 498, Battlefield Forensics. I worked on bulk_extractor and a few other MEDEX projects, and a lot of this seems specific to acquiring different devices and whatnot. Probably not for me.&lt;/li&gt;
&lt;li&gt;FOR 518, Mac and iOS Forensics. I know a few macOS artifacts and use a Mac as my daily computer, but there&#39;d be a lot here for me to learn.&lt;/li&gt;
&lt;li&gt;FOR 585, Smartphone Forensics. I know next to nothing about smartphone forensics. This might be inspiring, as my impression of existing mobile forensics tools has not been favorable.&lt;/li&gt;
&lt;li&gt;FOR 526, Memory Forensics. I also know very little about memory forensics. I do have a pretty good sense of how operating systems and the memory hierarchy work. The fact that this covers multiple operating systems and associated techniques is promising, though.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
Are there other courses I should look at, including outside of the SANS curricula? Training works best for me when it&#39;s very fast-paced and requires hands-on work. Otherwise, my ADD-addled brain will stop paying attention and I&#39;ll just daydream.&lt;/div&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/8410294922946242922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2020/01/evaluating-sans-courses.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/8410294922946242922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/8410294922946242922'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2020/01/evaluating-sans-courses.html' title='Evaluating SANS Courses'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-6855930581932512478</id><published>2019-12-31T11:13:00.006-05:00</published><updated>2019-12-31T11:13:59.766-05:00</updated><title type='text'>2020 Goals</title><content type='html'>My tech skill development goals for 2020:&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Build something useful in Rust, with Rust proficiency as a side-effect&lt;/li&gt;
&lt;li&gt;Develop competence with low-level optimization&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;tooling: nonius, perf, flame graphs, some dtrace/strace&lt;/li&gt;
&lt;li&gt;assembly: casual familiarity reading x86, comfort with SSE/AVX and associated libraries&lt;/li&gt;
&lt;li&gt;hardware: learn recent microarchitectures (Haswell–) for Intel and AMD, gain empirical expertise with contemporary memory hierarchies including NVMe&lt;/li&gt;
&lt;li&gt;libraries: add a few C++ libraries to my trusted working set beyond Boost, focused on addressing particular performance issues (e.g., replacements for std::unordered_map, B-trees, a trie, a Bloom filter lib, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;Do everything with some set of Docker, CloudFormation, Vagrant, and Terraform&lt;/li&gt;
&lt;li&gt;Apply some data science/machine learning techniques to real problems&lt;/li&gt;
&lt;li&gt;Become dangerously inept with ReactJS and Django as a frontend/backend webapp platform&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
With the exception of learning how to use ReactJS with Django in order to build a simple webapp, these goals are focused on developing working expertise, building off my strengths in batch processing/backend/systems development. None of these goals involve surface-level reading about new technologies in order to gain conceptual familiarity. I&#39;ve &lt;i&gt;read&lt;/i&gt; about a lot of these technologies for years. It&#39;s high time to incorporate them into my daily development activities.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/6855930581932512478/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2019/12/2020-goals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/6855930581932512478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/6855930581932512478'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2019/12/2020-goals.html' title='2020 Goals'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1627986405273486693</id><published>2014-05-30T22:45:00.002-04:00</published><updated>2014-05-30T22:45:56.553-04:00</updated><title type='text'>GNU Autotools Grievances</title><content type='html'>&lt;h2&gt;
Part 1, of Infinity&lt;/h2&gt;
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;Adding or renaming a source file requires changing Makefile.am, which means you have to run autoreconf again, which generates a new configure script, which you then must re-run, even though nothing about your configuration has changed.&lt;/li&gt;
&lt;li&gt;Pulled some changes, now out of date, and you want to run make clean? Have to run ./configure again.&lt;/li&gt;
&lt;li&gt;Why, yes, my system has strlen. Thank you for checking. Again. And again. And again.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Is there a &lt;i&gt;sane&lt;/i&gt;&amp;nbsp;response to C development on a system without strlen?&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;NB: Folks running UNICOS don&#39;t get to vote.&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;li&gt;Every function, header, and library you check for in autoconf results in generating a C/C++ program and compiling, all done through shell script, natch.&lt;/li&gt;
&lt;li&gt;The GNU documentation advises against globbing all source files in a given directory for your Makefile and recommends, instead, listing them all individually. Who does this?&lt;/li&gt;
&lt;li&gt;Autoconf is really just a bunch of shell macros, which means they introduce new shell variables. So when you see a variable referenced somewhere and you search for it to find where it&#39;s set, good luck to you.&lt;/li&gt;
&lt;li&gt;libtool! Huh! Good God, y&#39;all... What is it good for? Absolutely nothing!&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Seriously, this is a gigantor shell script whose purpose in life, to the best of my understanding, is to supply the right GCC options for building object code for shared libraries, and then linking them together.&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;That is, it&#39;s an enormous shell script middleman to save you the hassle of knowing &quot;-fPIC&quot;, &quot;-shared&quot;, and &quot;.so&quot;. No, really.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;20 years ago (maybe even 10; maybe), the platform portability problems libtool was “designed” (a strong word for anything written in shell) to fix might have been an issue. EVERYONE USES LINUX NOW. And those who use FreeBSD still use the same tools.&lt;/li&gt;
&lt;li&gt;IT HIDES YOUR EXECUTABLES in .folders and generates shell scripts which you think would be your executables, but aren&#39;t, and their sole job in life is to fuxor the PATH and then invoke the hidden executables. Good luck using some debugger other than gcc.&lt;/li&gt;
&lt;/ul&gt;
&lt;li&gt;The configure script is not in any way parallelizable. Running it on a wide enough machine can actually make it the slowest part of building.&lt;/li&gt;
&lt;li&gt;Want to make a debug build? Rerun configure.&lt;/li&gt;
&lt;li&gt;Want to use an alternate compiler on your system? Make sure you pass CC and CXX as variables on the command-line every single time you run configure. (Props to SCons for remembering default choices in its build_variables.py file.)&lt;/li&gt;
&lt;li&gt;Have you ever tried reading through the phantasmagoria that is the shell scripts generated by autoconf? It&#39;s seriously the inspiration behind Plato&#39;s Allegory of the Cave.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1627986405273486693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2014/05/gnu-autotools-grievances.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1627986405273486693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1627986405273486693'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2014/05/gnu-autotools-grievances.html' title='GNU Autotools Grievances'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-2535942433722697012</id><published>2012-12-02T12:17:00.001-05:00</published><updated>2012-12-02T12:17:55.803-05:00</updated><title type='text'>System Reconfig</title><content type='html'>A year and a half ago, I bought a new system. Meet Virgil:&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgckWLgNs76J1mGVNvVCRhwQbTQ3AmHQlX_dwMjP-Qruw5sGxy8nyVXUjL92OvN1oeJAE3FNexNHOn-pkl8D9cnrUO6QNS4zg-JevOx2zKmKY4TBc_MSxYLOo8oPkuwpANPaJ5lEESzaYyB/s1600/IMG_20121202_121151.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgckWLgNs76J1mGVNvVCRhwQbTQ3AmHQlX_dwMjP-Qruw5sGxy8nyVXUjL92OvN1oeJAE3FNexNHOn-pkl8D9cnrUO6QNS4zg-JevOx2zKmKY4TBc_MSxYLOo8oPkuwpANPaJ5lEESzaYyB/s320/IMG_20121202_121151.jpg&quot; width=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Virgil&#39;s main specs:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;2x &lt;a href=&quot;http://ark.intel.com/products/48768/Intel-Xeon-Processor-E5645-(12M-Cache-2_40-GHz-5_86-GTs-Intel-QPI)&quot; target=&quot;_blank&quot;&gt;Xeon E5645&lt;/a&gt; (2.4GHz, 6 cores, 12MB L3, hyperthreading—apiece)&lt;/li&gt;
&lt;li&gt;the legendary &lt;a href=&quot;http://www.bit-tech.net/hardware/motherboards/2010/08/17/evga-classified-sr-2-review/1&quot; target=&quot;_blank&quot;&gt;EVGA SR-2&lt;/a&gt; motherboard&lt;/li&gt;
&lt;li&gt;24GB RAM&lt;/li&gt;
&lt;li&gt;a pair of WD RE4 2TB hard drives&lt;/li&gt;
&lt;li&gt;in a &lt;a href=&quot;http://www.techpowerup.com/reviews/LianLi/PC-V2120/&quot; target=&quot;_blank&quot;&gt;Lian Li PC-V2120&lt;/a&gt; case&lt;/li&gt;
&lt;li&gt;all kinds of I/O ports and a ho-hum-but-CUDA-capable GPU&lt;/li&gt;
&lt;/ul&gt;
The SR-2 motherboard is a non-standard size and will not fit in a normal ATX case. It necessitated the mondo case. At the time, the SR-2 was about the only desktop board that allowed for dual Xeons (otherwise you needed to buy a true server; blech). A year and a half after purchase, it is still far more computer than I need and I sometimes feel bad considering how much it idles. With its cores at only 2.4GHz, some laptops can beat Virgil at single-threaded tasks, but Virgil excels at parallel compiles, data processing, and the like. With Xeon fans, Virgil sounds like a red-lined Ferrari when its under load.&lt;br /&gt;
&lt;h4&gt;
Operating Systems&lt;/h4&gt;
I first started off running Ubuntu, which I mostly liked. However, the upgrade to 12.04 failed completely—Virgil could not boot, and I had no interest in trouble-shooting the failure. Additionally, I found Ubuntu&#39;s Unity interface a little tiresome. I probably appreciated it more than most Linux users, but it involved a bit more mousing around than I wanted.&lt;br /&gt;
&lt;br /&gt;
A colleague is an expert Fedora user, so I installed Fedora. I figure it&#39;d be worthwhile to learn an alternate approach and I could always piggyback off his knowledge. However, I hated Fedora. Gnome 3 is a disaster, Xfce felt like 1998 all over again, and Fedora demanded far too much sysadmin time from me. I didn&#39;t want to re-learn how to setup Samba configuration files again, as if it was still 2002; I wanted to click a checkbox and have my home folder shared so I could access it from VMs without any fuss. This was possible on Ubuntu... not so much on Fedora.&lt;br /&gt;
&lt;br /&gt;
Additionally, I gradually realized that the Fedora installer had messed up my RAID. Under Ubuntu, I&#39;d configured the two drives to act as an LVM RAID-1. When I installed Fedora, it could not recognize the partition, which was hugely annoying, but it did seem to recognize the underlying LVM RAID. I was wrong, though: it turned the RAID-1 into a &lt;i&gt;spanned&lt;/i&gt; volume. I no longer had a redundant 2TB drive; I had a 4TB drive with twice the probability of failure as a single drive and no redeeming performance qualities.&lt;br /&gt;
&lt;br /&gt;
Of course, by the time I realized this (disclosure: I am really bad at IT/sysadmin), I&#39;d already accumulated a little over 2TB of data, thanks in large part of VirusShare. It would not be so simple to go back to the RAID-1.&lt;br /&gt;
&lt;h4&gt;
Reconfig&lt;/h4&gt;
So I bought another 2TB WD RE4 hard drive and a 256GB OCZ Vertex 4 SSD. I had two main goals: first, I wanted to install Mint as the main Linux system and I wanted to do it in such a way that I could trash it, install some other system, and be up and running again in a day or less, without too much time spent sysadmin&#39;ing; second, I wanted a RAID for my main data, and I wanted the volume somewhat separate from /home so I could simply remount it as a subdirectory in my home directory under a new system, perhaps issue a recursive chown &amp;amp; chgrp, and be good to go. (Fedora and Ubuntu use different numbering schemes for UIDs, of course; it should be possible to preserve one&#39;s UID from system to system with a little bit of work, but the underlying theme of this blog post is how I am too lazy to do such things).&lt;br /&gt;
&lt;br /&gt;
I first installed the SSD in the system and installed the newly released Mint 14 (with the Cinnamon UI) on a single / partition. I then backed up all of my data to an external 3TB drive, installed the 3rd WD drive, and blew my old data away.&lt;br /&gt;
&lt;br /&gt;
Although the SR-2 board supports various RAID options in hardware, I decided I wanted a software RAID so I could survive a motherboard failure. Virgil has compute power to spare, and these are only 7200 RPM drives so I shouldn&#39;t suffer significant performance loss by going the software RAID route. I first constructed an unformatted partition on each of the drives, leaving a small buffer before and after in case I someday need to add another drive that differs slightly in its sector count. I followed the instructions to construct a RAID5 array from the three partitions, according the &lt;a href=&quot;https://raid.wiki.kernel.org/index.php/RAID_setup&quot; target=&quot;_blank&quot;&gt;Linux RAID wiki&lt;/a&gt;:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mdadm --create --verbose /dev/md0 --level=5 --devices=3 /dev/sda1 /dev/sdc1 /dev/sdd1&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mdadm --detail --scan &amp;gt;&amp;gt; /etc/mdadm/mdadm.conf&lt;/span&gt;&lt;/blockquote&gt;
This created a 4TB RAID5 across the disks, with a default-selected chunk size of 512KB.&lt;br /&gt;
&lt;h4&gt;
XFS&lt;/h4&gt;
I narrowed my choices for the filesystem to EXT4 or XFS. Since I have a mix of very large files (evidence and other archives) and very small files (source code), I wanted an extent-based filesystem. Despite its advanced feature-set, reading up on BtrFS does not fill one with confidence about its reliability. Of course, one can find data loss stories about EXT4 and XFS as well. In the end I decided to go with XFS as it is an older design that&#39;s been strenuously tested on far more advanced RAID hardware, but it&#39;s still under active development and maintenance. This is an intuitive choice more than anything.&lt;br /&gt;
&lt;br /&gt;
So I issued the command:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mkfs -t xfs /dev/md0&lt;/span&gt;&lt;/blockquote&gt;
and immediately was presented with an error that the 512KB chunk size was too large for XFS. It seems that the default chunk size of 512KB is a recent change in the mdadm tools, likely geared towards EXT4. However, the maximum chunk size that XFS can handle is 256KB and XFS developers still recommend a 32KB chunk size in the general case. So, I deleted the RAID and recreated it:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mdadm --create --verbose /dev/md0 --level=5 --devices=3 /dev/sda1 /dev/sdc1 /dev/sdd1&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mdadm --detail --scan &amp;gt;&amp;gt; /etc/mdadm/mdadm.conf&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mkfs -t xfs /dev/md0&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mount /dev/md0 /mnt/raid&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;mkdir /mnt/raid/jon/data&lt;/span&gt;&lt;/blockquote&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;ln -s /mnt/raid/jon/data /home/jon/data&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
I now had my XFS RAID5 and had it easily accessible inside my home directory, while still having the overall volume available for other uses.&lt;br /&gt;
&lt;br /&gt;
One thing I noticed in reading about Linux RAIDs is that many people use LVM on top of the raw RAID device and then create different filesystems on top of LVM. If one were managing a system with a few large RAIDs for a large number of users, I could see how LVM would be useful. However, I cannot see any utility for it when you just want a single RAID volume. There&#39;s likely no performance loss to using LVM, but it adds another layer of complexity... and I am too lazy for unnecessary complexity.&lt;br /&gt;
&lt;h4&gt;
The Disposable System&lt;/h4&gt;
The RAID solves my data problem, but what about being able to get up and running with another system in the future? This enters into the realm of configuration management. There are two new configuration management systems that are taking the open source world by storm, Puppet and Chef. Both are implemented in Ruby and both seem like good pieces of software. They&#39;re both geared towards sysadmins who need to manage many systems. Essentially what both do is take configuration files specifying what packages, files, user accounts, and other configuration details should apply to a given system, and then they make the system conform to that description.&lt;br /&gt;
&lt;br /&gt;
The main differentiator between Puppet and Chef is that Puppet has its own syntax for its config files whereas Chef&#39;s config files are valid Ruby source code. What I want to do is simply track the packages I install over time, so that I can get them reinstalled quickly if I need to. After looking at the syntax for each of Puppet and Chef, I decided that Puppet would be slightly more simple.&lt;br /&gt;
&lt;br /&gt;
So, what I have been doing is building a directory of Puppet config files, each containing a list of software packages. The config files are roughly categorized, so I have one for basic C/C++ development, another for Python, one for system tools, one for GUI applications, etc. When I find myself in need of a package, I add it to the appropriate config file, and I can then bring my system into compliance with:&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
&lt;span style=&quot;font-family: Courier New, Courier, monospace; font-size: x-small;&quot;&gt;sudo puppet apply &lt;i&gt;config-file.pp&lt;/i&gt;&lt;/span&gt;&lt;/blockquote&gt;
Puppet figures out which, if any, packages are missing and then invokes apt-get to download and install them. If I switched back to Fedora, or to FreeBSD or some other crazy system, I would likely need to remap some of these package names slightly, but at least I&#39;m tracking them. Additionally, I will sometimes create an EC2 instance to accomplish a specific task, and having categorized Puppet config files should help me bootstrap barebones instances in short order. I track my config files with git, natch, and mirror them on Github as I do with all my other source code.&lt;br /&gt;
&lt;br /&gt;
Data has mass; I now have a RAID that should be able to survive a move to a different environment without having to make copies. And by using Puppet to reduce system management into, essentially, software development, I have reduced the mass of my operating system to almost nothing. All I need to do on a new system is get it to mount my RAID, drop my ssh keys, install Puppet, and then wait while all the rest of the software I depend on is downloaded and installed automatically.&lt;br /&gt;
&lt;h4&gt;
etckeeper&lt;/h4&gt;
Speaking of Git, another useful software package for anyone running Linux is &lt;a href=&quot;http://joeyh.name/code/etckeeper/&quot; target=&quot;_blank&quot;&gt;etckeeper&lt;/a&gt;. etckeeper is a software package that interacts with your package management system and tracks changes to /etc. If you inadvertently screw up some aspect of your system via editing of various /etc config files, you can always go back in time to restore things. In this respect, it&#39;s sort of like System Restore, except that it works.&lt;br /&gt;
`&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/2535942433722697012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/12/system-reconfig.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2535942433722697012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2535942433722697012'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/12/system-reconfig.html' title='System Reconfig'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgckWLgNs76J1mGVNvVCRhwQbTQ3AmHQlX_dwMjP-Qruw5sGxy8nyVXUjL92OvN1oeJAE3FNexNHOn-pkl8D9cnrUO6QNS4zg-JevOx2zKmKY4TBc_MSxYLOo8oPkuwpANPaJ5lEESzaYyB/s72-c/IMG_20121202_121151.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-2831471277411704611</id><published>2012-11-14T09:40:00.001-05:00</published><updated>2012-11-14T09:54:51.583-05:00</updated><title type='text'>Representing Encoding Chains</title><content type='html'>Here&#39;s a problem I&#39;ve been mulling over the last couple days: what is a good way to represent &lt;i&gt;encoding chains&lt;/i&gt;?&lt;br /&gt;
&lt;h4&gt;
What&#39;s an encoding chain?&lt;/h4&gt;
When we search for text, it&#39;s always in some kind of encoding, something that specifies how logical characters are represented as &quot;physical&quot; bytes (a discussion of the physicality of bytes is best left for happy hour). For example, &quot;A&quot; is represented as byte 0x41 in the ANSI or UTF-8 encodings but as the two-byte sequence 0x41 0x00 in UTF-16LE (aka &quot;two-byte Unicode&quot;) and UCS-2 (aka &quot;fake two-byte Unicode&quot;; never use UCS-2).&lt;br /&gt;
&lt;br /&gt;
To search for a keyword in a file, we either need to know the encoding used for the file and convert the bytes in the file to logical characters to perform the comparison to the keyword &lt;i&gt;or&lt;/i&gt; we need to convert the characters in the keywords into their binary form using the file&#39;s encoding and then search for those bytes in the file. Most text editors do the former; &lt;a href=&quot;http://www.lightgrep.com/&quot; target=&quot;_blank&quot;&gt;Lightgrep&lt;/a&gt; does the latter, as we don&#39;t usually know the encodings used for files, especially when searching fragments in unallocated space and whatnot.&lt;br /&gt;
&lt;br /&gt;
An &lt;i&gt;encoding chain&lt;/i&gt; goes beyond a single encoding: it&#39;s what happens when we want to deal with data that&#39;s been subject to a series of transformations, including a normal characters-to-bytes encoding.&lt;br /&gt;
&lt;h4&gt;
Examples&lt;/h4&gt;
A useful artifact in forensics is the &lt;a href=&quot;http://blog.didierstevens.com/programs/userassist/&quot; target=&quot;_blank&quot;&gt;UserAssist registry key&lt;/a&gt;. These keys store recently run programs, recording the name of the program, when it was last run, and the number of times it was run. A bewildering aspect of values under the UserAssist key is that the names of the programs are &lt;a href=&quot;http://en.wikipedia.org/wiki/ROT13&quot; target=&quot;_blank&quot;&gt;ROT13&lt;/a&gt; &quot;encrypted&quot;. This means that &quot;A&quot; becomes &quot;N&quot;, &quot;B&quot; becomes &quot;O&quot;, and so on.&lt;br /&gt;
&lt;br /&gt;
ROT13 seems like a silly form of obfuscation... until you need to search for UserAssist registry keys pertaining to a particular program. Many forensic tools will parse registry hives and some will even decrypt the UserAssist keys for you, but if you&#39;d like to look for traces of keys in the pagefile or in unallocated, you are out of luck when it comes to tool support. You must manually apply ROT13 to the name of the program (&quot;notepad&quot; becomes &quot;abgrcnq&quot;) and then apply the UTF-16LE encoding to it (&quot;abgrcnq&quot; becomes &quot;a\x00b\x00g\x00r\x00c\x00n\x00q\x00&quot;). Tedious, no?&lt;br /&gt;
&lt;br /&gt;
Another example (featuring more love for Didier Stevens) is when looking for obfuscated strings in malware. &lt;a href=&quot;http://blog.didierstevens.com/programs/xorsearch/&quot; target=&quot;_blank&quot;&gt;XORSearch&lt;/a&gt; is a program Messrs. Stevens recently released which lets you search for strings that have been obfuscated with XOR, ROR (rotate-right), or ROL (rotate-left) transformations. These types of transforms are often used by malware authors to obfuscate strings like URLs.&lt;br /&gt;
&lt;br /&gt;
When searching, it would be very nice to say &quot;look for &#39;notepad&#39; but in ROT13&#39;d UTF-16LE&quot; or &quot;look for &#39;http://sirhaxal0t.ru/&#39; in UTF-8 that&#39;s been XOR&#39;ed&quot;. The permutation of &quot;ROT13, then UTF-16LE&quot; or &quot;UTF-8, then XOR&quot; is what I mean when I say &quot;encoding chain.&quot; We want to apply a chain of encodings to our search terms.&lt;br /&gt;
&lt;h4&gt;
Characters v. Bytes&lt;/h4&gt;
ROT13 is a transformation that applies to &lt;i&gt;characters&lt;/i&gt;, with &quot;A&quot; becoming &quot;N&quot; and so on. In contrast, XOR is a transformation that applies to &lt;i&gt;bytes&lt;/i&gt;. Character transforms apply to the string first before it&#39;s encoded into bytes, whereas byte transforms apply to the bytes after the encoding has happened. Here&#39;s a table that clearly delineates transformations and encodings:&lt;br /&gt;
&lt;br /&gt;
&lt;table border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;th&gt;Character Transforms&lt;/th&gt;&lt;th&gt;Encodings&lt;/th&gt;&lt;th&gt;Byte Transforms&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ROT13, &lt;a href=&quot;http://en.wikipedia.org/wiki/Percent-encoding&quot;&gt;URL encoding&lt;/a&gt;, &lt;a href=&quot;http://www.w3schools.com/html/html_entities.asp&quot;&gt;HTML entities&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;ASCII, UTF-8, UTF-16LE, EBCDIC, &lt;a href=&quot;http://en.wikipedia.org/wiki/Shift_JIS&quot;&gt;Shift-JIS&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/ISO/IEC_8859-5&quot;&gt;ISO-8859-5&lt;/a&gt;, &lt;a href=&quot;http://demo.icu-project.org/icu-bin/convexp&quot;&gt;etc.&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;XOR, ROR, ROL, &lt;a href=&quot;http://www.stevebunting.org/udpd4n6/forensics/outlookcompencryptionsearch.htm&quot;&gt;Outlook Compressible Encryption&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/Base64&quot;&gt;Base64&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
You may have noticed that there are a few constraints to the encoding chains we&#39;d like to apply. First, there&#39;s an ordering constraint in that character transforms must come first, followed by the encoding, and finally by byte transforms. An encoding chain like &quot;XOR -&amp;gt; ROT13 -&amp;gt; UTF-8&quot; is invalid and nonsensical. Second, while it&#39;s possible to have multiple character transforms or byte transforms in a chain, &lt;a href=&quot;http://www.youtube.com/watch?v=kq4SqgxIKM0&quot; target=&quot;_blank&quot;&gt;there can be only one&lt;/a&gt; encoding. An encoding changes the &lt;i&gt;type&lt;/i&gt; of thing we&#39;re working with from characters (ideally Unicode &lt;a href=&quot;http://en.wikipedia.org/wiki/Code_point&quot; target=&quot;_blank&quot;&gt;codepoints&lt;/a&gt;) to bytes, and that type change can only occur once.&lt;br /&gt;
&lt;h4&gt;
The Ultimate Problem&lt;/h4&gt;
We&#39;ve been working on adding support for Outlook Compressible Encryption to Lightgrep, and that&#39;s necessitated thinking about encoding chains and how to expose the abstraction. At this point the main technical work is done and we are arguing about how to &lt;i&gt;represent&lt;/i&gt; the encoding chains.&lt;br /&gt;
&lt;br /&gt;
Whither:&lt;br /&gt;
&lt;br /&gt;
&quot;outlook(base64(utf8(rot13())))&quot;&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;br /&gt;
&quot;ROT13 | UTF-8 | Base64 | Outlook&quot;&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;br /&gt;
&quot;ROT13 &amp;gt; UTF-8 &amp;gt; Base64 &amp;gt; Outlook&quot;&lt;br /&gt;
&lt;br /&gt;
?&lt;br /&gt;
&lt;br /&gt;
We&#39;d like to have a good notation for specifying these chains. One interesting thing to note is that some transformations may want parameters. For example, ROR and ROL need to know how many bits of shift to apply. It would be silly to have ROR1, ROR2, ROR3, ROR4, ROR5, ROR6, and ROR7, when we could just have &quot;ROR(2)&quot;, for example. With parameters, we could come up with a generic rotate character transformation, and then ROT13 would be &quot;ROT(13)&quot;. (If you were investigating a classically trained hacker, you may want to look for text obfuscated by the &lt;a href=&quot;http://penelope.uchicago.edu/Thayer/E/Roman/Texts/Suetonius/12Caesars/Julius*.html#56.6&quot; target=&quot;_blank&quot;&gt;Caesar cipher&lt;/a&gt;, i.e., ROT(3).)&lt;br /&gt;
&lt;h4&gt;
So...&lt;/h4&gt;
...which representation above do you prefer? Or can you come up with something better?&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
* Addendum *&lt;/h4&gt;
You can expect Outlook Compressible Encryption support in Lightgrep fairly soon, but it will be some time before you see base64. Base64 is not so much a byte transformation as it is a &lt;i&gt;bit&lt;/i&gt; transformation, taking 6 bits of data and turning them into 8. Lightgrep needs some work in order to work well with bits.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/2831471277411704611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/11/representing-encoding-chains.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2831471277411704611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2831471277411704611'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/11/representing-encoding-chains.html' title='Representing Encoding Chains'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-798482860947298119</id><published>2012-09-17T10:46:00.003-04:00</published><updated>2012-09-17T10:47:12.164-04:00</updated><title type='text'>Forensic Programming Rules of Thumb</title><content type='html'>I have a set of mental guidelines I use when writing digital forensics programs, and it&#39;s high time I write them down. These are by no means a complete list of useful programming techniques, but I think they&#39;re all worth keeping in mind.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Algorithms&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
1. Don&#39;t be afraid to use RAM. Most investigators&#39; machines have several gigabytes of RAM installed, so you might as well use it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
2. Remember, though, that RAM is fixed while disk space is nearly infinite. If you can&#39;t set an upper limit on the amount of RAM used by your program, create a cache data structure, size it appropriately, then spill to disk if you exceed it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
3. Prefer streaming models to building up complex data structures in RAM. Streaming models will generally use a fixed amount of memory and scale to large evidence sets.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
4. Choose algorithms based on the best worst-case behavior, rather than the best average-case. Investigators don&#39;t need the best possible performance from their software as much as they need predictable performance, and the ability for the software to cope when faced with The Big One. An algorithm that has bad worst-case performance is not mission capable.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Timestamps&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
1. Convert local time to UTC as soon as possible.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
2. Always report timestamps in UTC. I&#39;m generally not a fan of programmers telling users what to do, but I&#39;ll make an exception in this case; when users ask for local time output, firmly reply &quot;no&quot; and tell them to get comfortable working in UTC.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
3. Do not separate times from dates in output. Timestamps are points in time; separating times from dates imposes the periodicity of the earth&#39;s revolution onto your investigation, and that&#39;s often not germane. Even if it is germane, having dates and times separated makes it a huge PITA to calculate timestamp differences in, e.g., Excel.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
4. Timestamps generally exist within a &quot;context,&quot; for lack of a better word. Contexts are often cascading/recursive. For example, the context for an OLE timestamp with a Word 2003 document is &quot;Word 2003 OLE&quot;, followed by the filesystem (e.g. UTC for NTFS, local time adjustment for FAT32), followed by the system time settings, followed by, say, the timezone of where the evidence was seized. It&#39;s important to remember these contexts when handling local time. Even if you convert local time to UTC, one of these contexts could later be found to be incorrect. Consider the timestamps as existing in a database; it would be better to query for the appropriate rows to adjust by this context then through some ad hoc mechanism.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Artifacts&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
1. Never assume anything about your input. Ooh, you found a magic header for your artifact? So what!? The data could have been generated by a cuckoo clock. Sanity check everything, especially when reading in variable length buffers/strings with sizes denoted in other fields. Return true or false depending on whether the artifact parsed successfully. If it mostly parsed but failed some kind of test, return the partial results along with false.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
2. Keep your artifact parsing code separate from your I/O code (if possible; better to operate on provided memory buffers and let other code feed it data) and separate from your reporting code. This way you can foster code reuse when someone else needs to parse the same artifact but in a different context.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
3. Put the immediate results from parsing into a struct or, perhaps better, a name-value data structure, such as a Python dict. Don&#39;t serialize into a string or otherwise obliterate the type information you have at that moment; that&#39;s for your output code to handle. Again, this fosters code reuse.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
4. Always refer to bytes on disk. When you output parsed artifacts, always give the user reference to where that artifact existed on disk. This way the user has a chance in hell of finding it for themselves and validating that your software parsed it correctly.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
5. Remember that parsing is not the same thing as analysis. The output from parsing an artifact should be in a form that makes it convenient for the examiner to analyze. This could mean, for example, that it&#39;s easy to get the output into Excel or Access or pipe it into different command-line utilities. It&#39;s way better to produce output that is well-structured than pretty—well-structured data can always be prettied up, while it&#39;s often hard to recreate structure from pretty data.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
6. It&#39;s better to identify an artifact by searching for it than by assuming it exists within a well-structured file. If you can identify artifacts by searching, then you can parse them out of RAM dumps, virtual memory swapfiles, and unallocated space.&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/798482860947298119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/09/forensic-programming-rules-of-thumb.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/798482860947298119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/798482860947298119'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/09/forensic-programming-rules-of-thumb.html' title='Forensic Programming Rules of Thumb'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1117806924460791862</id><published>2012-08-24T17:18:00.001-04:00</published><updated>2012-08-24T18:22:13.678-04:00</updated><title type='text'>Happy Birthday, Mr. Pigeon</title><content type='html'>Yesterday, I asked a question on Twitter:&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;
Alright lazy web, yes or no: can I use md5() for lazy man&#39;s equality testing of strings ranging from 3 bytes to 3GB?&lt;br /&gt;
— Jon Stewart (@codeslack) &lt;a data-datetime=&quot;2012-08-23T22:49:39+00:00&quot; href=&quot;https://twitter.com/codeslack/status/238770005016190976&quot;&gt;August 23, 2012&lt;/a&gt;&lt;/blockquote&gt;
I will admit, at the time I was hoping that one of the respondents would be Jesse Kornblum. I was not disappointed. There was a little back on forth on twitter, and then Jesse decided to write a blog post, &quot;&lt;a href=&quot;http://jessekornblum.livejournal.com/285811.html&quot; target=&quot;_blank&quot;&gt;Being Unreasonable About MD5&lt;/a&gt;.&quot; In it, he advised me to use a &lt;a href=&quot;http://en.wikipedia.org/wiki/SHA-2&quot; target=&quot;_blank&quot;&gt;SHA-2&lt;/a&gt;-based hash, or wait until &lt;a href=&quot;http://csrc.nist.gov/groups/ST/hash/sha-3/index.html&quot; target=&quot;_blank&quot;&gt;SHA-3&lt;/a&gt; is determined.&lt;br /&gt;
&lt;br /&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif02d9ATYzCHHP5UkRY5uH9dzrgznsGLiD8lsWIC-nz2izbv5dw_K5-4tvacrrx0g3A0D4dfsGjvaqBXJWv6WKgp-f02b2x1uqdg4QZkri1CEIwRjINTRWDZOTisq7AUKCkNWGjMoHyrYM/s1600/rocky-iv+ivan+drago+punches+rocky+balboa.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img alt=&quot;If using awesome Rocky IV images in blog posts is wrong, I don&#39;t want to be right.&quot; border=&quot;0&quot; height=&quot;231&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif02d9ATYzCHHP5UkRY5uH9dzrgznsGLiD8lsWIC-nz2izbv5dw_K5-4tvacrrx0g3A0D4dfsGjvaqBXJWv6WKgp-f02b2x1uqdg4QZkri1CEIwRjINTRWDZOTisq7AUKCkNWGjMoHyrYM/s400/rocky-iv+ivan+drago+punches+rocky+balboa.jpg&quot; title=&quot;&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;DFIR Slugfest!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
It being 2012, we should all know that MD5 is &quot;broken.&quot; The first MD5 collision was announced in 2004 (&lt;a href=&quot;http://www.cs.colorado.edu/~jrblack/papers/md5e-full.pdf&quot; target=&quot;_blank&quot;&gt;some analysis&lt;/a&gt; [PDF]). The ability to generate collisions since then has only gotten better. Earlier this summer we were treated to the sheer awesomeness of &lt;a href=&quot;http://arstechnica.com/security/2012/06/flame-crypto-breakthrough/&quot; target=&quot;_blank&quot;&gt;Flame exploiting an MD5 collision&lt;/a&gt; which could not have been generated based on publicly known techniques (*cough* USA!, USA! *cough*).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://img2.timeinc.net/ew/dynamic/imgs/101208/ROCKY-IV-09_510.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;218&quot; src=&quot;http://img2.timeinc.net/ew/dynamic/imgs/101208/ROCKY-IV-09_510.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Here&#39;s the problem, though: I&#39;m looking for a hash function that I can use for lazy equality comparisons, not for use in digital signatures. That is, I want to see whether a new string of input I&#39;ve read in from a program is the same as a previous string of input I&#39;ve read in.&lt;br /&gt;
&lt;br /&gt;
Why do I want to do this? Well, I want to grep hard drives for email addresses and spit out a list of all the unique ones I&#39;ve seen. I also want to do this with web addresses/URLs, credit card numbers, Social Security Numbers, and so on. One possible way of doing this (a really lazy way, and it might not even be the best way; that&#39;s a separate story altogether) is to hash all the email addresses with MD5 and call it a day. MD5 will surely work for this, won&#39;t it?&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
In 1834, Johann Dirichlet postulated what has come to be known as the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pigeonhole_principle&quot; target=&quot;_blank&quot;&gt;Pigeonhole Principle&lt;/a&gt;. It is one of those theorems in mathematics where the proof of it is simply common sense: if you have &lt;i&gt;n&lt;/i&gt; pigeonholes, and &lt;i&gt;n+1&lt;/i&gt; pigeons, you&#39;re guaranteed to have 2 pigeons shacking up.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.flickr.com/photos/davrozs/2998599900/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot; title=&quot;Feral Pigeon (Columba livia domestica) by dave and rose, on Flickr&quot;&gt;&lt;img alt=&quot;Feral Pigeon (Columba livia domestica)&quot; height=&quot;334&quot; src=&quot;http://farm4.staticflickr.com/3178/2998599900_f2c01d9aba.jpg&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
MD5 is a 128 bit hash algorithm. Thus, it has&amp;nbsp;&lt;i&gt;2^128&lt;/i&gt; different pigeonholes. According to the pigeonhole principle, we need to have &lt;i&gt;(2^128) + 1&lt;/i&gt; items before we&#39;re guaranteed to have a collision.&lt;br /&gt;
&lt;br /&gt;
In case you&#39;re wondering, 2^128 =&amp;nbsp;340,282,366,920,938,463,463,374,607,431,768,211,456. &lt;i&gt;&lt;insert about=&quot;about&quot; beach=&quot;beach&quot; etc.=&quot;etc.&quot; grains=&quot;grains&quot; here=&quot;here&quot; in=&quot;in&quot; known=&quot;known&quot; of=&quot;of&quot; on=&quot;on&quot; or=&quot;or&quot; sand=&quot;sand&quot; something=&quot;something&quot; stars=&quot;stars&quot; the=&quot;the&quot; universe=&quot;universe&quot;&gt;&amp;nbsp;&lt;/insert&gt;&lt;/i&gt;There certainly aren&#39;t that many unique email addresses we&#39;re ever going to encounter, so we should be good right?&lt;br /&gt;
&lt;br /&gt;
Well, actually, let&#39;s take a moment to guess how many unique email addresses there are. I don&#39;t have a central repository of all the email addresses ever used, but I can use myself as an example and estimate out from there. Here are all the email addresses I can remember having:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;(I don&#39;t remember the first one, but it was @nebbs.nersc.gov on a &lt;a href=&quot;http://www.cisl.ucar.edu/computers/gallery/cray/xmp.jsp&quot; target=&quot;_blank&quot;&gt;Cray X-MP&lt;/a&gt;, which was rad.)&lt;/li&gt;
&lt;li&gt;beefstew@aol.com (yes, really)&lt;/li&gt;
&lt;li&gt;stew1@iastate.edu&lt;/li&gt;
&lt;li&gt;stew1@avalon.net&lt;/li&gt;
&lt;li&gt;jonathan.l.stewart@uiowa.edu&lt;/li&gt;
&lt;li&gt;jstewart@cs.uiowa.edu&lt;/li&gt;
&lt;li&gt;stew1@ellipsis.cx&lt;/li&gt;
&lt;li&gt;jonathan_l_stewart@hotmail.com&lt;/li&gt;
&lt;li&gt;jonathan_l_stewart@yahoo.com&lt;/li&gt;
&lt;li&gt;jonathan.l.stewart@gmail.com&lt;/li&gt;
&lt;li&gt;jonathan.l.stewart@acm.org&lt;/li&gt;
&lt;li&gt;jonstewart17@comcast.net (DIAF, Comcast)&lt;/li&gt;
&lt;li&gt;jon@lightboxtechnologies.com&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
Lucky number 13. Of course, I &lt;i&gt;may&lt;/i&gt;&amp;nbsp;have used a few burner email addresses over the years and there could be a couple of legitimate ones I&#39;ve forgotten, too. So, let&#39;s round that up to 50, just for sake of argument.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Also, let&#39;s assume that I&#39;m the most average person on Earth. How many people are there? Supposedly 7.035 billion, as of today, August 24, 2012. Extrapolating out, we could be dealing with 351 billion email addresses, if every single person on the planet has had 50 email addresses. However, there are almost certainly email addresses that are associated with computers and departments and mailing lists and such, not just people. So, well, let&#39;s double that: 702 billion email addresses. That&#39;s our estimated population.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Right off the bat, we can see that those clinging to 32-bit integers are going to need a bigger boat. If I happened to have a hard drive with all the email addresses on it and I was using a 32-bit hash, the pigeonhole principle tells me I&#39;d have a lot of collisions.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Still, though, MD5 only has to deal with 702,000,000,000 email addresses and fit the hashes from our email addresses into&amp;nbsp;340,282,366,920,938,463,463,374,607,431,768,211,456 possible values. Continuing along the primrose path of overly simplistic math, this means we should have a probability of 702 billion/2^128, which is&amp;nbsp;2.062992585693e-27, or 1 chance in 2.06 octillion. That&#39;s a lot of room for MD5 to play in.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg82sXGXLALtWH979ocTQXs5vkABWzK1zUf6PSuBpTFlzq1JKpGgY2k6lZo8zuwEkjg5mR3xrjf0QrJXEiapZz397eETAItq7BfAjhHYVQ0TKLQxMc39QxSBnEM7ZaxymNZ2O53Ysk5BNGw/s1600/rocky-4-1985-14-g.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg82sXGXLALtWH979ocTQXs5vkABWzK1zUf6PSuBpTFlzq1JKpGgY2k6lZo8zuwEkjg5mR3xrjf0QrJXEiapZz397eETAItq7BfAjhHYVQ0TKLQxMc39QxSBnEM7ZaxymNZ2O53Ysk5BNGw/s400/rocky-4-1985-14-g.jpg&quot; width=&quot;260&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Nyet so fast, Komrade!&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;i style=&quot;font-weight: bold;&quot;&gt;However,&lt;/i&gt;&amp;nbsp;thus far I&#39;ve failed to take into account the &lt;a href=&quot;http://en.wikipedia.org/wiki/Birthday_problem&quot; target=&quot;_blank&quot;&gt;Birthday Paradox&lt;/a&gt;. Have you ever been in a group of people and been surprised to find that two of them have the same birthday? As it turns out, odds are even that two guests at a 23-person dinner party will have the same birthday and 99% that two people in a group of just 57 will have the same birthday. This is because we are comparing pairs of birthdays to each other, each person trying to pair themselves to all the rest. The number of pairs of n things is &lt;i&gt;(n*(n-1))/2&lt;/i&gt;. As people start piling up, so do the combinations and the number of days in a year are quickly overwhelmed.&lt;br /&gt;
&lt;br /&gt;
The Birthday Paradox leads to the &lt;a href=&quot;http://en.wikipedia.org/wiki/Birthday_attack#Mathematics&quot; target=&quot;_blank&quot;&gt;Birthday Attack&lt;/a&gt;: start testing for collisions by brute force and, over time, you will get lucky and have a pair collide. The number of items we have to test, &lt;i&gt;Q&lt;/i&gt;, before finding the first collision in a fair hash function is roughly equal to the square root of &lt;i&gt;(pi * H)/2&lt;/i&gt;, where &lt;i&gt;H&lt;/i&gt; is the total number of available pigeonholes. For MD5, &lt;i&gt;H=2^128&lt;/i&gt; and thus &lt;i&gt;Q = sqrt((3.1415926 * 2^128)/2)&lt;/i&gt;. What is that number?&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/ygQvB6OjHOU&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;

&lt;br /&gt;
&lt;br /&gt;
It&#39;s ~23,119,565,135,021,060,000. That is, 23 quintillion. That number, while large, is a &lt;i&gt;lot&lt;/i&gt;&amp;nbsp;smaller than 340,282,366,920,938,463,463,374,607,431,768,211,456. That&#39;s the number of strings we have to hash before we have close to a 100% chance of having a hash collision on MD5. What about a 1% chance? Well, fortunately, Wikipedia calculated that number in the table that you see under the &lt;a href=&quot;http://birthday%20attack/&quot; target=&quot;_blank&quot;&gt;Birthday Attack&lt;/a&gt; article. For a fair hash with 128 bits, if we try hashing a tenth of that many items, the probability we&#39;ll have a collision is 1 in 100. If we generate ~820,000,000,000,000 strings (820 trillion), the chance we&#39;ll have a collision is 1 in a billion. If we generate 1000 times fewer strings, ~820,000,000,000 (820 billion), the chance we&#39;ll have a collision is 1 in a quadrillion.&lt;br /&gt;
&lt;br /&gt;
820 billion is pretty close to the 702 billion distinct email addresses we estimated above. So, if MD5 is fair, we can expect we have about a 1 in quadrillion chance that we&#39;ll have a collision.&lt;br /&gt;
&lt;br /&gt;
Have you noticed how fast and loose I&#39;ve played with the math here? And how much I&#39;ve relied on Wikipedia?&amp;nbsp;&lt;b&gt;Disclaimer: I am really bad at math.&lt;/b&gt; When trying to figure out math like this, I generally reserve the right to be off by a factor of 1000. Therefore, you only get to correct me if you can show that I&#39;m off by more than a factor of 1000.&lt;br /&gt;
&lt;br /&gt;
Additionally, all these numbers above assume that MD5 is perfectly fair. In fact, we know that it isn&#39;t, because of the collision attacks. If I reserve the right to be off by a factor of 1000 and we know that MD5 is also kind of caput, that means we need to estimate our probability to be somewhat higher. So, one in a trillion? One in 100 billion? Maybe a little less?&lt;br /&gt;
&lt;br /&gt;
Now we&#39;re in judgment call territory. For my application of deduping email addresses, well, meh. I&#39;ll certainly take the odds of a collision when hashing all of the email addresses ever at one in quadrillion, and even at one in a trillion. The nice thing about using MD5 for this is that it&#39;s reasonably fast (Xeons even have a special instruction for calculating MD5 on the chip) and the keys will fit in 16 bytes, the size of an SSE2 register.&lt;br /&gt;
&lt;br /&gt;
However, what about all possible files? Are there more possible files than email addresses? Probably. What about all distinct 512-byte sectors, for those who like sector block hashing? Almost certainly. Are we starting to bump up into limits where we have to &lt;i&gt;think&lt;/i&gt; about whether it&#39;s safe or not? Yes. Is that a bad thing? Yes. Like Rocky, we do not want to think, we do not want the judge to think, we do not want the jury to think; we want to &lt;i&gt;win&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
So, unless you want to think about all these things above, including whether you want to explain one in a trillion, then you should stop using MD5.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Although&lt;/i&gt;, to keep things in perspective, the risk of everyone dying next year due to a comet or asteroid strike is only &lt;a href=&quot;http://discovermagazine.com/1996/may/afistfulofrisks763&quot; target=&quot;_blank&quot;&gt;1 in 20,000&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;...&lt;br /&gt;
&lt;br /&gt;
Ok. Now you may correct my shoddy math in the comments.&lt;br /&gt;
&lt;br /&gt;
&lt;script charset=&quot;utf-8&quot; src=&quot;//platform.twitter.com/widgets.js&quot;&gt;&lt;/script&gt;

&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1117806924460791862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/08/happy-birthday-mr-pigeon.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1117806924460791862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1117806924460791862'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/08/happy-birthday-mr-pigeon.html' title='Happy Birthday, Mr. Pigeon'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif02d9ATYzCHHP5UkRY5uH9dzrgznsGLiD8lsWIC-nz2izbv5dw_K5-4tvacrrx0g3A0D4dfsGjvaqBXJWv6WKgp-f02b2x1uqdg4QZkri1CEIwRjINTRWDZOTisq7AUKCkNWGjMoHyrYM/s72-c/rocky-iv+ivan+drago+punches+rocky+balboa.jpg" height="72" width="72"/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5359400492267532153</id><published>2012-08-11T21:19:00.000-04:00</published><updated>2012-08-11T21:19:55.236-04:00</updated><title type='text'>Random Sampling in Forensics</title><content type='html'>&lt;i&gt;&lt;span style=&quot;font-size: x-small;&quot;&gt;(NB: Some links are to PDFs.)&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
I had the pleasure of attending the &lt;a href=&quot;http://www.dfrws.org/2012/program.shtml&quot; target=&quot;_blank&quot;&gt;Digital Forensics Research Workshop&lt;/a&gt; this past week. DFRWS is different than the other forensics conferences I&#39;ve attended, in that speakers must submit formal academic papers for review by a committee. Less than one-third of submitted papers make the cut.&lt;br /&gt;
&lt;br /&gt;
This is how most academic conferences work, and there are a few consequences stemming from the emphasis on peer-reviewed papers. One is that the quality of the material is generally high. This is good. Another is that many leading practitioners—who, as forensics generally demands, also double as researchers—don&#39;t present, since writing a formal academic paper is an enormous PITA. This is less good.&lt;br /&gt;
&lt;br /&gt;
I enjoyed a number of presentations at DFRWS this year (chief among them was Ralf Brown&#39;s about &lt;a href=&quot;http://www.dfrws.org/2012/proceedings/DFRWS2012-4.pdf&quot; target=&quot;_blank&quot;&gt;language identification&lt;/a&gt;). The one that stood out for me, though, was a blend of theory and practice. This was &lt;a href=&quot;http://www.dfrws.org/2012/proceedings/DFRWS2012-11.pdf&quot; target=&quot;_blank&quot;&gt;&lt;i&gt;The Use of Random Sampling in Investigations Involving Child Abuse Material&lt;/i&gt;&lt;/a&gt;, presented by &lt;a href=&quot;http://www.writeblocked.org/&quot; target=&quot;_blank&quot;&gt;Mike Wilkinson&lt;/a&gt; and co-authored by Brian Jones and Syd Pleno.&lt;br /&gt;
&lt;br /&gt;
What Mike, Brian, and Syd did is, frankly, revolutionary. They established procedures at the New South Wales police force&#39;s State Electronic Evidence Branch so that child pornography possession cases make use of statistical sampling early in the analysis of the case. By using sampling, they have cut their case backlog from 3+ months to zero.&lt;br /&gt;
&lt;br /&gt;
By using an EnScript that selects a random sample of images from suspects&#39; hard drives, investigators can estimate the total amount of child pornography on the system with a high degree of confidence. A report based on the sampling review is created and, while situations no doubt vary, the report is typically presented to the suspect along with a plea bargain... and most take the plea. Such cases take only a few hours of processing.&lt;br /&gt;
&lt;br /&gt;
Of course, such a methodology is only appropriate for possession cases, not distribution or where active child abuse is suspected. Digital investigators benefit because they don&#39;t have to sit for hours reviewing thousands (or millions) of corrosive images, and they have more time for other cases. Other investigators benefit because their computer evidence gets turned around faster. Even the falsely accused benefit, because they are quickly exonerated.&lt;br /&gt;
&lt;br /&gt;
Part of why the procedure works is because it&#39;s well-adapted to the law in New South Wales, Australia. However, with care and consideration, it is hard to believe that the basic idea could not be applied in the United States.&lt;br /&gt;
&lt;br /&gt;
I encourage you to read their paper and review their presentation. Kudos to Brian, Syd, and Mike, and to the New South Wales police for adapting to the increasing strain on digital investigators with an elegant and effective solution.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5359400492267532153/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/08/random-sampling-in-forensics.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359400492267532153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359400492267532153'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/08/random-sampling-in-forensics.html' title='Random Sampling in Forensics'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5806461270453015361</id><published>2012-06-14T11:37:00.000-04:00</published><updated>2012-06-14T11:37:26.308-04:00</updated><title type='text'>Lightgrep 1.0</title><content type='html'>I&#39;ve just released &lt;a href=&quot;http://www.lightgrep.com/&quot; target=&quot;_blank&quot;&gt;Lightgrep for EnCase&lt;/a&gt; 1.0. Lightgrep is a new multipattern regular expression search engine that I&#39;ve been developing with my colleagues over the last two years, and our EnCase integration makes it the fastest way to conduct searches over digital evidence.&lt;br /&gt;
&lt;br /&gt;
I&#39;m hoping I&#39;ll be writing more blogposts now, as I allow my mind to wander a bit more.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5806461270453015361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/06/lightgrep-10.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5806461270453015361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5806461270453015361'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/06/lightgrep-10.html' title='Lightgrep 1.0'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5359241770578111687</id><published>2012-05-19T09:53:00.000-04:00</published><updated>2012-05-19T09:53:10.480-04:00</updated><title type='text'>Lightgrep for EnCase at CEIC 2012</title><content type='html'>I&#39;ll be at &lt;a href=&quot;http://www.ceicconference.com/&quot;&gt;CEIC 2012&lt;/a&gt; next week, arriving Monday and leaving mid-morning on Thursday. &lt;a href=&quot;http://www.geoffblack.com/&quot;&gt;Geoff Black&lt;/a&gt; and I will be talking again about the use of &lt;a href=&quot;http://www.ceicconference.com/AJAX/courseScheduleLightbox.aspx?id=1000018743&quot;&gt;statistical sampling in eDiscovery matters&lt;/a&gt;, based on a matter we worked on in which the&amp;nbsp;&lt;a href=&quot;http://e-discoveryteam.com/2011/04/24/top-ten-e-discovery-issues-by-judge-andrew-peck-and-david-lender/&quot;&gt;Hon. Andrew J. Peck&lt;/a&gt; was the presiding judge. Our talk is at 4:30pm on Monday.&lt;br /&gt;
&lt;br /&gt;
We are also putting the finishing touches on &lt;a href=&quot;http://www.lightgrep.com/&quot;&gt;Lightgrep for EnCase&lt;/a&gt; and we&#39;d love to show it to folks attending CEIC. Lightgrep is a new regular expression search engine we wrote, that searches evidence for many keywords very quickly. We&#39;ve created an EnScript wrapper around the low-level search engine that allows you to use it instead of EnCase&#39;s own keyword search facility. It will bookmark search hits and create an Excel report with details about the search, in addition to several other features.&lt;br /&gt;
&lt;br /&gt;
Lightgrep shines when you have many keywords or need more advanced grep functionality than EnCase provides. Last year we had some investigators use an early version on a case with over a million keywords, so we think it sets a new standard for scalability. Lightgrep also supports more grep features, and we try to make them work as close to Perl as possible. We don&#39;t have all of Perl&#39;s functionality just yet, but we&#39;ll keep adding more grep operators over time (and we do a &lt;u&gt;lot&lt;/u&gt; of testing to make sure we get the same hits Perl does).&lt;br /&gt;
&lt;br /&gt;
Unicode support is what we&#39;ve been working on most recently, and I&#39;m pretty excited about it. Out of the gate, we&#39;ll have support for ASCII (really, &lt;a href=&quot;http://msdn.microsoft.com/en-us/goglobal/cc305145&quot;&gt;Windows code page 1252&lt;/a&gt;), &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-8&quot;&gt;UTF-8&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/UTF-16&quot;&gt;UTF-16LE&lt;/a&gt; (two-byte Unicode on Windows), UTF-16BE, and UTF-32LE and BE. We are able to look for every character in the Unicode standard, including brand new ones in&amp;nbsp;&lt;a href=&quot;http://www.unicode.org/versions/Unicode6.1.0/&quot;&gt;Unicode version 6.1.0&lt;/a&gt;, like the &lt;a href=&quot;http://www.fileformat.info/info/unicode/char/1f4a9/index.htm&quot;&gt;infamous U+1F4A9&lt;/a&gt;. We won&#39;t support other code pages just yet, but the code is mostly developed and we simply need our test suites to catch up.&lt;br /&gt;
&lt;br /&gt;
The cool thing about Lightgrep&#39;s Unicode support is that you can use &lt;a href=&quot;http://www.regular-expressions.info/unicode.html&quot;&gt;Unicode properties&lt;/a&gt; in patterns. You can specify \p{Digit} or \p{Letter} and look for only valid digits or letters, but in any language. You can also specify different scripts, which is amazingly useful when looking for text that&#39;s not in the Latin alphabet. For example, \p{Cyrillic}+ and \p{Arabic}+ will find all words in Cyrillic (i.e., Russian) and Arabic, respectively.&lt;br /&gt;
&lt;br /&gt;
If you&#39;d like to see Lightgrep in action at CEIC, just email me, at &lt;a href=&quot;mailto:jon@lightboxtechnologies.com&quot;&gt;jon@lightboxtechnologies.com&lt;/a&gt;, and we&#39;ll find a good time to chat. I&#39;ll have a few dozen thumb drives with a Lightgrep trial version installer, so don&#39;t be shy.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgesArg_oFdq5GHXbvn-BAYs8Yw9wPL28oxhHt4RvB1NRnfeVfSymU5a5hb8KSkoBuwA18obvu9Ijf23FNmYI4yK_MPBwGenFv9Q5FAkMoOOpRPg0YPFveSvtDjJ-a320Ye5qKvbwQUQvXH/s1600/Lightbox_thumbdrive.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgesArg_oFdq5GHXbvn-BAYs8Yw9wPL28oxhHt4RvB1NRnfeVfSymU5a5hb8KSkoBuwA18obvu9Ijf23FNmYI4yK_MPBwGenFv9Q5FAkMoOOpRPg0YPFveSvtDjJ-a320Ye5qKvbwQUQvXH/s320/Lightbox_thumbdrive.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5359241770578111687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/05/lightgrep-for-encase-at-ceic-2012.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359241770578111687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359241770578111687'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/05/lightgrep-for-encase-at-ceic-2012.html' title='Lightgrep for EnCase at CEIC 2012'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgesArg_oFdq5GHXbvn-BAYs8Yw9wPL28oxhHt4RvB1NRnfeVfSymU5a5hb8KSkoBuwA18obvu9Ijf23FNmYI4yK_MPBwGenFv9Q5FAkMoOOpRPg0YPFveSvtDjJ-a320Ye5qKvbwQUQvXH/s72-c/Lightbox_thumbdrive.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5121772430987600859</id><published>2012-03-22T17:38:00.000-04:00</published><updated>2012-03-22T17:38:00.851-04:00</updated><title type='text'>Differential EnScript</title><content type='html'>My former colleague Jamie Levy &lt;a href=&quot;http://gleeda.blogspot.com/2012/03/differential-enscript.html&quot;&gt;released a new EnScript today&lt;/a&gt;, for doing differential analysis of hard drives. You should check it out.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5121772430987600859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/03/differential-enscript.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5121772430987600859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5121772430987600859'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/03/differential-enscript.html' title='Differential EnScript'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-2470608054891809375</id><published>2012-03-21T09:39:00.000-04:00</published><updated>2012-03-21T09:46:56.852-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EnScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><title type='text'>Don&#39;t call virtual functions in a destructor</title><content type='html'>Sometimes I like to define the outline of an operation in a base class and then have it delegate to virtual functions in derived classes. This is the &lt;a href=&quot;http://en.wikipedia.org/wiki/Template_method_pattern&quot;&gt;Template Method design pattern&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;class Base {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; uint Counter;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; void foo() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; ++Counter;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; _foo();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; pure void _foo();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;class Derived {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; virtual void _foo() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;In Derived::_foo&quot;);&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Last night I tried to gin up a sort of &quot;stack guard,&quot; something that would let me perform an operation once I got to a certain point in a function, but which also would be executed if the function returned early. When something needs to be done at the end of a block of code, I usually define a new class that has code in its destructor. For example, a lock class could take a critical section and acquire it in its constructor and release it in its destructor, and you just need to create the object in the appropriate place to perform your thread-safe operation. I wanted the same thing, but just a bit more complex, where I could call the release function manually once I&#39;d reached a certain point and then be assured it would not be called in the destructor.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;class Committer {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; bool Committed;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; ~Committer() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; if (!Committed) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; commit();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; void commit() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; _commit();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; pure void _commit();&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;class SafeDB: Committer {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; SafeDB(Database db): Committer() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; // open a database connection&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; virtual void _commit() {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; &amp;nbsp; // commit any pending transactions&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Simple enough. You can quibble about whether it&#39;s really worth it to define a class hierarchy for such simple functionality, but I&#39;ve been trying hard to increase the amount of abstraction I use in EnScript to reduce code bloat and time wasted debugging.&lt;br /&gt;
&lt;br /&gt;
The problem is that this doesn&#39;t work and generates run-time errors about null references. Why?&lt;br /&gt;
&lt;br /&gt;
Answer: SafeDB&#39;s destructor is called first, and then Committer&#39;s destructor is called. As part of executing SafeDB&#39;s destructor, it nulls out whatever class members are a part of SafeDB &lt;i&gt;before&lt;/i&gt; calling Committer&#39;s destructor. Hence, by the time you get to Committer&#39;s destructor, you really only have a Committer object, not a SafeDB object. The virtual table is still active, however, so the call to SafeDB::_commit() happens successfully, but it finds itself confronted with no class variables. &lt;b&gt;Boom!&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
So, this is now another little corner of the EnScript world that&#39;s been explored. Time to light out for the territory...</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/2470608054891809375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/03/dont-call-virtual-functions-in.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2470608054891809375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2470608054891809375'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/03/dont-call-virtual-functions-in.html' title='Don&#39;t call virtual functions in a destructor'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5779881617037688182</id><published>2012-01-31T14:40:00.001-05:00</published><updated>2012-01-31T14:41:16.808-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EnScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><title type='text'>ThreadClass::WAITSAFE</title><content type='html'>
A New Year&#39;s Resolution, if you can call it that, is to blog more in the moment, as I&#39;m figuring stuff out. I &lt;i&gt;think&lt;/i&gt; I just figured out how the ThreadClass::WAITSAFE option works, so here goes...&lt;br /&gt;
&lt;br /&gt;
ThreadClass::WAITSAFE is an enum value under ThreadClass::WaitOptions. It can be passed to several of the different Wait() methods related to the threading classes. The gloss on it says &quot;Exits early if thread stops running,&quot; but there aren&#39;t any examples that show it in action and it seems to be the default parameter for some of the Wait() methods, but not for all of them. Specifically, it&#39;s not the default parameter for either of the ThreadClass::WaitClass methods.&lt;br /&gt;
&lt;br /&gt;
To test it, we&#39;re going to have three threads. One is just going to spin its wheels—it&#39;s the thread we&#39;d have doing real work, so we&#39;ll call it the &quot;doer.&quot; Another thread will launch the doer and wait for it to finish, which we&#39;ll call the &quot;waiter.&quot; This seems kind of silly, except that in a real application, the waiter could have a pool of doer threads, and could be coordinating their work and that may justify a separate thread of its own (especially if it&#39;s happening while you have a dialog up). Finally, we&#39;ll have our main thread that creates and launches the waiter, sleeps for a bit, then tells it to cancel. The waiter in turn will cancel the doer thread, and exit once the doer has exited. Here&#39;s the code:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
&lt;br /&gt;
class Doer: ThreadClass {&lt;br /&gt;
&amp;nbsp; Doer(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; while (IsRunning()) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer sleeping...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; SystemClass::Sleep(1000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer exiting.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class Waiter: ThreadClass {&lt;br /&gt;
&amp;nbsp; Waiter(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Doer d();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; ThreadClass::WaitClass w();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; d.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; w.AddObject(d);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting on doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; bool waitRet = w.WaitAll();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Woke up waiter&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; if (!IsRunning()) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter was cancelled. Stopping doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter exiting, waitRet = &quot; + waitRet);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class MainClass {&lt;br /&gt;
&amp;nbsp; void Main() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::ClearConsole();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Waiter t();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Sleeping for 10 seconds&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::Sleep(10000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Cancelling waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting for waiter to quit...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter returned successfully. Done.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;

&lt;div&gt;
This code produces the following output:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
Starting waiter...&lt;br /&gt;
Sleeping for 10 seconds&lt;br /&gt;
Starting doer...&lt;br /&gt;
Waiting on doer...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Cancelling waiter...&lt;br /&gt;
Waiting for waiter to quit...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
The problem is that this script never terminates successfully. The Waiter class uses WaitAll() to wait for the Doer thread to finish, but the Doer thread is just hanging out, enjoying its infinite loop until someone calls StopRunning() on it... which never happens. This is where ThreadClass::WAITSAFE comes in. Here&#39;s some modified code:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
class Doer: ThreadClass {&lt;br /&gt;
&amp;nbsp; Doer(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; while (IsRunning()) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer sleeping...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; SystemClass::Sleep(1000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer exiting.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class Waiter: ThreadClass {&lt;br /&gt;
&amp;nbsp; Waiter(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Doer d();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; ThreadClass::WaitClass w();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; d.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; w.AddObject(d);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting on doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; bool waitRet = w.WaitAll(&lt;b&gt;ThreadClass::INFINITE, ThreadClass::WAITSAFE&lt;/b&gt;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Woke up waiter&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; if (!IsRunning()) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter was cancelled. Stopping doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter exiting, waitRet = &quot; + waitRet);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class MainClass {&lt;br /&gt;
&amp;nbsp; void Main() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::ClearConsole();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Waiter t();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Sleeping for 10 seconds&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::Sleep(10000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Cancelling waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting for waiter to quit...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter returned successfully. Done.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
This time, we pass ThreadClass::INFINITE (the default) and ThreadClass::WAITSAFE (&lt;i&gt;not&lt;/i&gt; the default) as parameters to the WaitAll() method in the Waiter. This is what we get for output:
&lt;br /&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
&lt;br /&gt;
Starting waiter...&lt;br /&gt;
Sleeping for 10 seconds&lt;br /&gt;
Starting doer...&lt;br /&gt;
Waiting on doer...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Cancelling waiter...&lt;br /&gt;
Waiting for waiter to quit...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Woke up waiter&lt;br /&gt;
Waiter was cancelled. Stopping doer...&lt;br /&gt;
Doer exiting.&lt;br /&gt;
Waiter exiting, waitRet = 0&lt;br /&gt;
Waiter returned successfully. Done.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
Huzzah! Calling StopRunning() in the main thread on the waiter causes the waiter to wake up from WakeAll(), now that we&#39;re using ThreadClass::WAITSAFE. Once the Waiter wakes up, it checks IsRunning() to check whether it&#39;s been given the signal to quit, via StopRunning(), and then does likewise with the Doer thread. The Doer exits gracefully, then the Waiter exits gracefully, and then the whole script exists. Quite nice.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Notice that we&#39;ve output the return value from WaitAll(), and it seems to have been false in this case. The question this raises is, would it return true if Doer exited before Waiter was cancelled with StopRunning()? Here&#39;s some modified code where the Doer exits after about 5 seconds, while the main thread still waits about 10 seconds to tell the Waiter to stop.&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
&lt;br /&gt;
class Doer: ThreadClass {&lt;br /&gt;
&amp;nbsp; Doer(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &lt;b&gt;uint i;&lt;/b&gt;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; while (IsRunning()&lt;b&gt; &amp;amp;&amp;amp; i++ &amp;lt; 5&lt;/b&gt;) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer sleeping...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; SystemClass::Sleep(1000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Doer exiting.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class Waiter: ThreadClass {&lt;br /&gt;
&amp;nbsp; Waiter(): ThreadClass() {}&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; virtual void Run() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Doer d();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; ThreadClass::WaitClass w();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; d.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; w.AddObject(d);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting on doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; bool waitRet = w.WaitAll(ThreadClass::INFINITE, ThreadClass::WAITSAFE);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Woke up waiter&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; if (!IsRunning()) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter was cancelled. Stopping doer...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; d.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter exiting, waitRet = &quot; + waitRet);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
class MainClass {&lt;br /&gt;
&amp;nbsp; void Main() {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::ClearConsole();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Waiter t();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Starting waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Start();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Sleeping for 10 seconds&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; SystemClass::Sleep(10000);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Cancelling waiter...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.StopRunning();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiting for waiter to quit...&quot;);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; t.Wait();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Console.WriteLine(&quot;Waiter returned successfully. Done.&quot;);&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
And here&#39;s the output:&lt;/div&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &#39;Courier New&#39;, Courier, monospace; font-size: x-small;&quot;&gt;&lt;br /&gt;
Starting waiter...&lt;br /&gt;
Sleeping for 10 seconds&lt;br /&gt;
Starting doer...&lt;br /&gt;
Waiting on doer...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer sleeping...&lt;br /&gt;
Doer exiting.&lt;br /&gt;
Woke up waiter&lt;br /&gt;
Waiter exiting, waitRet = 1&lt;br /&gt;
Cancelling waiter...&lt;br /&gt;
Waiting for waiter to quit...&lt;br /&gt;
Waiter returned successfully. Done.&lt;br /&gt;
&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
So, as you can see, WaitAll() still returns when the Doer thread exits, and this time it returns true. Waiter then exits from its Run() function before the main thread has the chance to cancel it. The upshot is that we can use the return value from WaitAll() to tell whether the thread waiting has had StopRunning() called on it (return value is false) or whether simply the operations it&#39;s waiting on have completed (return value is true).&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Oh frabjous day, everything works!&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5779881617037688182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2012/01/threadclasswaitsafe.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5779881617037688182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5779881617037688182'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2012/01/threadclasswaitsafe.html' title='ThreadClass::WAITSAFE'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5359317773826580687</id><published>2011-12-14T17:07:00.003-05:00</published><updated>2011-12-14T17:07:51.765-05:00</updated><title type='text'>SANS 360 Lightning Talk</title><content type='html'>I&#39;ve published a &lt;a href=&quot;http://www.lightboxtechnologies.com/2011/12/14/sans-360-lightning-talk-factory-forensics/&quot;&gt;write-up&lt;/a&gt;&amp;nbsp;on the Lightbox blog of my SANS 360 talk, &quot;Factory Forensics.&quot; You could watch the video, of course, but then you&#39;d have to suffer through my stumbling and general awkwardness. The turnout was great, and I was humbled by the quality of the speakers.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5359317773826580687/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/12/sans-360-lightning-talk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359317773826580687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5359317773826580687'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/12/sans-360-lightning-talk.html' title='SANS 360 Lightning Talk'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1914892876120955254</id><published>2011-08-09T23:34:00.000-04:00</published><updated>2011-08-09T23:34:20.721-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EnScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><title type='text'>ExecuteClass pitfall</title><content type='html'>Of the ways that EnScript can interact with other programs, using ExecuteClass to launch programs from the command-line is probably my favorite. It&#39;s simple to use, simple to debug, and doesn&#39;t leave you with an unholy mess of code, like COM.&amp;nbsp;However, I recently ran into a pitfall with ExecuteClass and hopefully sharing it here will keep others from running into it or, at least, recognizing it when they&#39;re at their wits&#39; end.&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;The problem? ExecuteClass becomes angry when the target program writes a lot of data to stdout or stderr, and you wouldn&#39;t like ExecuteClass when it&#39;s angry.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;ExecuteClass currently has the Output property, a String, for giving you back stdout output from the application. My somewhat shaky belief is that this is updated once the application has ended—I haven&#39;t tested this in a while, though. Additionally, notice that there&#39;s no property representing stderr.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;If the target application you are running generates lots of output to stdout or to stderr, you will find that the application will hang after a while. Why? Well, in the words of the illustrious &lt;a href=&quot;http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html#pipes&quot;&gt;Beej&lt;/a&gt;, &quot;&lt;span class=&quot;Apple-style-span&quot; style=&quot;background-color: white; font-family: &#39;Times New Roman&#39;, Times, Georgia, serif; font-size: 16px;&quot;&gt;On many systems, pipes will fill up after you write about 10K to them without reading anything out.&quot;&lt;/span&gt; Once the target application fills up whatever pipe EnCase has connected to stdout or stderr, it will block on the next write to the stream... and EnCase will never consume from the pipe, causing the target application to hang indefinitely.&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;background-color: white; font-family: &#39;Times New Roman&#39;, Times, Georgia, serif; font-size: 16px;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div&gt;One possible workaround to this is not to call your target application, but to call cmd.exe instead, with the argument being the command you want to run, redirecting output to a file. I haven&#39;t yet tested this, but it should work. The other, of course, is to change the target application to write output to a file itself. As for the underlying win32 calls involved, I found an &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms682499(v=vs.85).aspx&quot;&gt;MSDN article&lt;/a&gt; with a sample program demonstrating how child processes are created and how to read data from stdout and stderr.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1914892876120955254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/08/executeclass-pitfall.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1914892876120955254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1914892876120955254'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/08/executeclass-pitfall.html' title='ExecuteClass pitfall'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1831712344420971558</id><published>2011-08-05T11:37:00.002-04:00</published><updated>2011-08-09T13:14:31.587-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Forensics"/><title type='text'>DFRWS 2011</title><content type='html'>I spent this last week in New Orleans at the &lt;a href=&quot;http://www.dfrws.org/2011/&quot;&gt;Digital Forensic Research Workshop&lt;/a&gt;. DFRWS is primarily a &amp;nbsp;research workshop for academics, to which they can submit formal papers for acceptance by peer review and then eventual publication. Folks whose papers are accepted then give presentations about their work.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Highlights&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.roussev.net/&quot;&gt;Vassil Roussev&lt;/a&gt; presented on the work he&#39;s done on fuzzy hashing, comparing his &lt;a href=&quot;http://roussev.net/sdhash/&quot;&gt;sdhash&lt;/a&gt; tool to Jesse Kornblum&#39;s venerable ssdeep in an information retrieval-type experiment. In general, sdhash does a significantly better job of identifying similar files, with the main trade-off being that hash values are not fixed-length (I haven&#39;t played with sdhash enough to know whether performance is a significant factor, either). Fuzzy hashing is a great example of applying computer science theory to forensics. More of this, please. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/09-341.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.rbeverly.net/research/&quot;&gt;Robert Beverly&lt;/a&gt; presented on work at the Naval Postgraduate School in finding artifacts of common structures used by programmers in networking code. It turns out that these ephemeral structures in RAM can often be found on disk (pagefile/hiberfil/unallocated), allowing the astute investigator to construct a partial remembrance of networks past. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/14-346.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Michael Cohen of &lt;a href=&quot;http://www.pyflag.net/cgi-bin/moin.cgi&quot;&gt;PyFlag&lt;/a&gt; fame presented on &lt;a href=&quot;http://code.google.com/p/grr/&quot;&gt;GRR&lt;/a&gt;. GRR&amp;nbsp;stands for &quot;GRR Rapid Response&quot;, but you can probably substitute &quot;Google&quot; for the G if you&#39;d like to avoid the left-recursion. GRR is an open source tool for collecting and analyzing data from remote machines on an enterprise network, created for internal investigations at Google. It is the closest thing I&#39;ve seen to my old friend &lt;a href=&quot;http://www.guidancesoftware.com/ediscovery.htm&quot;&gt;ESAC&lt;/a&gt;, and built on a far-richer software stack. It&#39;s early days, but I&#39;ll be following development of GRR very closely and I recommend that you do as well (it&#39;s much cheaper to buy &lt;a href=&quot;http://www.posthumorous.com/&quot;&gt;Cory&lt;/a&gt; a beer than to buy your overly-friendly enterprise software salesperson a new Ferrari). The one aspect of GRR I&#39;m leery of is the use of AFF&#39;s RDF-based data model, because I think RDF is a bit abstruse for most problems in forensics (or, at least, doesn&#39;t have the pay-off you&#39;d like in return for learning RDF, a technology only &lt;a href=&quot;http://www.aber.ac.uk/media/Documents/S4B/sem01.html&quot;&gt;semioticians&lt;/a&gt; could love). (&lt;a href=&quot;http://dfrws.org/2011/proceedings/16-348.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cs.georgetown.edu/~clay/&quot;&gt;Clay Shields&lt;/a&gt; presented on some work he&#39;s done using, essentially, document indexing for supporting enterprise investigations. By creating bag-of-word style feature vectors for documents on hosts, he can perform offline queries to help discover machines involved in an investigation. Indexing and the bag-of-words do have a host of problems that require careful consideration, but this would no doubt be a useful technique for many investigations. I&#39;d love to see such functionality put into GRR. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/06-338.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Christopher King and Tim Vidas from Carnegie Mellon presented on some aspects of Solid State Disks. This is a paper everyone should read carefully once it&#39;s been posted. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/17-349.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;James Okolica and &lt;a href=&quot;http://www.afit.edu/directory/faclook.cfm?id=200&quot;&gt;Gilbert Peterson&lt;/a&gt; figured out how to find and extract data from the Windows clipboard in RAM images. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/18-350.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Judson Powers from ATC-NY gave lightning talks both on MacOS X Lion&#39;s new full disk &quot;encryption&quot; (the quotes are needed, my fellow Mac weenie brethren) and &lt;a href=&quot;http://www.atc-nycorp.com/News/20110615DropboxReader.html&quot;&gt;Dropbox artifacts&lt;/a&gt;.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Last but certainly not least, &lt;a href=&quot;http://www.cs.cmu.edu/~ralf/&quot;&gt;Ralf Brown&lt;/a&gt; talked about his &lt;a href=&quot;http://sourceforge.net/projects/ziprec/&quot;&gt;ZipRec&lt;/a&gt; tool, which is able to find and recover data from corrupt DEFLATE streams (i.e., from zip files and many other compressed formats). It&#39;ll be very interesting to see how this tool develops and what other topics in forensics Ralf turns to. (&lt;a href=&quot;http://dfrws.org/2011/proceedings/19-351.pdf&quot;&gt;pdf&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;b&gt;Format&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Not being an academic, I find these sorts of conferences kinda&#39; weird. Their primary purpose is to provide a forum for academic researchers to present their work. I don&#39;t have skin in that game, and there were, frankly, some pretty boring moments for me... and, in the case of the legal panel, some pretty boring moments for everyone (a panel of class action plaintiffs attorneys, replete with hair product and suits, for an audience of bits-and-bytes forensics researchers is an odd pairing; the caliber of the audience &amp;nbsp;demands the likes of Craig Ball, Ralph Losey, Jason Baron, or other folks capable of engaging with the best forensics researchers; throwing a prosecutor into the mix would be good, too).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;While my ADD-addled brain would like a more-compressed, faster-paced format (can someone organize a conference consisting of forensics lightning talks? that&#39;d be great), DFRWS is the place to be to learn about the future of forensics. Most folks there are working on substantive problems and I had some great conversations with various researchers throughout the conference. I definitely need to start planning now so I can submit a paper or two for next year and participate a bit more actively. DEFCON and Black Hat may get the press, and DFRWS probably missed out on a few practitioners in attendance because of the scheduling conflict, but I think DFRWS is the best mash up of computer science and digital forensics I&#39;ve yet seen.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;The Fail Whale&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Finally, an expedition was organized to &lt;a href=&quot;http://www.beachonbourbon.com/&quot;&gt;The Beach&lt;/a&gt;, a bar on Bourbon Street that has a mechanical killer whale (instead of a mechanical bull). Life&#39;s not all about hex, code, and catching bad guys; it&#39;s also about taming wild beasts.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPyt06iuQJ49RWtMxSwzJnH9eOw9Swl2ywhzt9GWNCRUTMIYOZJCfqyFQLmt07r9MnaRWH6mfwiukmocmjY392JQ3_jRJVIrpu6r5KtItcIxQmkYEvMoO4d4NNUjN7ON8yqVPlLs46S1P0/s1600/photo.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPyt06iuQJ49RWtMxSwzJnH9eOw9Swl2ywhzt9GWNCRUTMIYOZJCfqyFQLmt07r9MnaRWH6mfwiukmocmjY392JQ3_jRJVIrpu6r5KtItcIxQmkYEvMoO4d4NNUjN7ON8yqVPlLs46S1P0/s320/photo.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1831712344420971558/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/08/dfrws-2011.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1831712344420971558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1831712344420971558'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/08/dfrws-2011.html' title='DFRWS 2011'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPyt06iuQJ49RWtMxSwzJnH9eOw9Swl2ywhzt9GWNCRUTMIYOZJCfqyFQLmt07r9MnaRWH6mfwiukmocmjY392JQ3_jRJVIrpu6r5KtItcIxQmkYEvMoO4d4NNUjN7ON8yqVPlLs46S1P0/s72-c/photo.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-9115303426618106726</id><published>2011-05-22T23:04:00.000-04:00</published><updated>2011-05-22T23:04:55.534-04:00</updated><title type='text'>EnCase 7: First Impressions</title><content type='html'>I was able to play with &lt;a href=&quot;http://www.guidancesoftware.com/encase-forensic-v7-launch.htm&quot;&gt;EnCase Version 7&lt;/a&gt;&amp;nbsp;for the first time this past week at &lt;a href=&quot;http://www.ceicconference.com/&quot;&gt;CEIC&lt;/a&gt;&amp;nbsp;(which was a great conference, btw). It will take me some time to explore v7 fully and make definite conclusions about much of its functionality. Additionally, just as restaurant reviewers don&#39;t visit a new restaurant until its had some time to find its rhythm, formally reviewing v7 seems pointless until 7.02 or 7.03. Still, I do have some immediate impressions.&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;What I Love&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;Tabs&lt;/b&gt;. EnCase now just has a single row of tabbed windows along the top. Bookmarks? That&#39;s a tab. Search results? That&#39;s a tab. The familiar tree-table view is then contained &lt;i&gt;within&lt;/i&gt; the tab. I&#39;ve heard some griping that the user interface has changed too much, but the tabs are a break-through and I&#39;ll tell you why: your windows stay as you left them.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;In previous versions of EnCase, which type of data you were looking at (entries, records, etc.) and the way you looked at data (table, report, gallery, timeline) were global settings. So if you wanted to go from the table view in Entries to the report view in Bookmarks, you had to switch views as well as the tab. Not only is it now far easier to navigate between different windows, but also the individual display settings and contexts for those windows are maintained. I took to this change like a fish to water, and I believe daily users will find it far more effective. Kudos to GSI for rethinking one of the fundamental aspects of the EnCase look-and-feel and pulling it off splendidly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;The EnScript Class Browser&lt;/b&gt;. If you write EnScripts, the upgrade is clear. If you depend on EnScripts in v6, you&#39;ll need to upgrade, too, because developers are not going to want to support v6 after upgrading. V7 includes a detachable version of the EnScript Types view, so you can edit your code and have the documentation in front of you &lt;i&gt;at the same time&lt;/i&gt;. This really should have been possible long, long ago, but I&#39;m glad it&#39;s here now. The fact that it generates a separate class browser for your own code is a nice touch.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;Search Results&lt;/b&gt;. I haven&#39;t played around with the search results view enough to know whether it&#39;s fully baked, but it seemed darned close. The results of conditions/filters/searches/scripts now live in a separate tab, where they persist. This is similar to bookmarks, but Search Results can contain different types of data, and I believe retain a notion of what generated them (at least if generated through a search). Additionally, like everything else in v7, Search Results are persisted to disk, supposedly allowing for smoother operation with large numbers of items (as opposed to filling up RAM and dealing with the data greedily). The distinction between Bookmarks and Search Results then becomes that Bookmarks are for items you&#39;ve selected for your report.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;What I don&#39;t think I&#39;ll love&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;Reports&lt;/b&gt;. The new reporting functionality is certainly better than nothing, but I was disappointed nonetheless. First, looking it over, it&#39;s clear that the reporting is built similarly to ExportClass in EnScript, which offers very limited display functionality in exchange for portability between HTML and RTF. Second, as with everything else in EnCase, the metaphor is the tree. You must organize your report into chapters, sections, subsections, and so on. Third, there is actually a &lt;i&gt;new&lt;/i&gt; report description &lt;i&gt;language&lt;/i&gt; built into this, for you to use for customization. There are dozens of html templating languages and technologies... so why not another one?!?!&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Here&#39;s my view: this should have had hooks into ASP.Net, and RTF should have been sacrificed on the altar of ancient technologies. I don&#39;t really like ASP.Net (or any other Microsoft web technology; give me Python and Django), but it&#39;s readily available on Windows and millions of people know it. EnCase would have been open to far more tinkering in one fell swoop. As for RTF, its primary purpose in EnCase is to let you write reports in Microsoft Word. That&#39;s useful, I know, really useful even, but not nearly as useful as having first-class HTML reporting support with the ability to save out as PDF. Plus, Microsoft Word supports copying-and-pasting from a browser into Word, so that&#39;s still available. Full-fledged HTML5 support would more than make up for the loss of RTF, an unloved also-ran in the document format wars.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;What&#39;s more, reporting could have been rethought from the ground up. What about something like Evernote or Onenote or TiddlyWiki? The new reporting functionality will be useful to those who want better reporting in EnCase; the Love just isn&#39;t there, though. If I need to create great reports, I&#39;ll still export the data and use some other tool/technology.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;Tags&lt;/b&gt;. &lt;i&gt;Maybe &lt;/i&gt;I won&#39;t love tags. I thought it was really cool you could tag a file just by clicking in the right place in the Tags column. I need to see whether this works in Records and the other views, and what the EnScript support is like. Tags are clearly a head-nod in the direction of review platform capabilities, but review needs specialized—and simplified—controls and functionality. Additionally, it&#39;s not clear to me how tags interact with Bookmarks and whether the two should be separate. Is a tag a type of bookmark? Can bookmarks be organized by tag? Is it easy to see overviews of tags? What about multi-case tag review? Will there be any kind of automatic classification done by tag? Tags beg a lot of questions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;The Death of forall()&lt;/b&gt;. In EnScript in v7, iterators are the new hotness and forall() is the old and busted. Or so we are told. The problem? forall() is the lone great innovation of EnScript. Recursive descent in every other programming language is a PITA; forall() makes it a joy. What&#39;s more, the iterator design in V7, while technically solid on its own, dovetails so nicely into forall() it&#39;s a shame the two aren&#39;t integrated. There&#39;s no reason that iterators could not be used with forall(), regardless of whether the data&#39;s in memory or on disk. forall() means &quot;for all&quot; and the extra bit of syntactic sugar guarantees that the iteration will occur successfully without worrying about termination.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;In fact, more often than not, forall() maps onto &lt;a href=&quot;http://en.wikipedia.org/wiki/Map_(higher-order_function)&quot;&gt;Map&lt;/a&gt;. The body of forall() loops are often data-parallel, and with a little bit of work in the EnScript language, forall() could have become a concurrent map(). Trivial parallel processing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;That said, if ItemIteratorClass supports virtual functions, such that we can write our own iterators, then this will be a worthy trade. With our own iterators, EnScripters can do lots of things, like define reverse iterators and create generic interfaces to common routines that use different data structures.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;Wait and See&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;EnCase V7 is hands-down the most significant upgrade to EnCase I&#39;ve witnessed. It&#39;s astonishing the amount of work that&#39;s gone into it, and I&#39;m greatly looking forward to using it. The amount of work that went into addressing the everything-in-RAM design of prior versions of EnCase can only be described as Herculean; that GSI&#39;s developers went so far beyond such back-end changes and put large amounts of time into rethinking EnCase for the new decade is heartening.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;There&#39;s a lot to learn, and it will take me some time to puzzle it all out. Conclusions are for another time. I will say that, for the first time in a long time, I&#39;m looking forward to upgrading EnCase and exploring it. I think it will be fun.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/9115303426618106726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/05/encase-7-first-impressions.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/9115303426618106726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/9115303426618106726'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/05/encase-7-first-impressions.html' title='EnCase 7: First Impressions'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5470265265673774580</id><published>2011-02-23T20:00:00.000-05:00</published><updated>2011-02-23T20:00:47.749-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cloud"/><title type='text'>Next Generation of Hadoop</title><content type='html'>The last couple of weeks has seen some new information trickle out from Yahoo! &lt;a href=&quot;http://developer.yahoo.com/blogs/hadoop/posts/2011/02/mapreduce-nextgen/&quot;&gt;about their efforts to improve scalability on Hadoop&lt;/a&gt;. (For a sense of a scale to the uninitiated, by &quot;improve scalability&quot; I mean, &quot;scale beyond clusters of 4,000 servers.&quot;) Yahoo! is calling this effort Next Generation Hadoop, or, Hadoop .Next.&lt;br /&gt;
&lt;br /&gt;
To paraphrase &lt;a href=&quot;http://query.nytimes.com/gst/fullpage.html?res=9D0CE5D8173CF932A3575AC0A9649C8B63&amp;amp;pagewanted=all&quot;&gt;V.S. Naipaul on the New Yorker and fiction&lt;/a&gt;, Yahoo! knows nothing about enterprise software marketing, nothing.&lt;br /&gt;
&lt;br /&gt;
To those paying attention, it&#39;s clear there&#39;s been tension between Yahoo!, Hadoop&#39;s first and most important patron, and Cloudera, the usurper. It&#39;s not hard to play armchair psychologist and speculate about the forces behind the tension, but I certainly don&#39;t know enough to comment on it intelligently. What is undeniable, though, is that release momentum hiccuped at a critical period in Hadoop&#39;s adoption by the industry at large, and that momentum has only recently been restored. Yahoo!&#39;s &lt;a href=&quot;http://developer.yahoo.com/blogs/hadoop/posts/2011/01/announcement-yahoo-focusing-on-apache-hadoop-discontinuing-the-yahoo-distribution-of-hadoop/&quot;&gt;recent statement &lt;/a&gt;about abandoning their own Hadoop distro and working to improve Apache trunk was very good news.&lt;br /&gt;
&lt;br /&gt;
Still, remember that Hadoop is not yet that magical 1.0. It&#39;s changed enormously over the past year or two, and for the better. It&#39;s silly it&#39;s not considered 1.0 already, and it&#39;s clear that a 1.0 designation is coming down the pike.&lt;br /&gt;
&lt;br /&gt;
THEREFORE: It makes no sense whatsoever to talk about &quot;Next Generation Hadoop.&quot; All the technical reasons are sound, but here&#39;s what this sounds like to me: &quot;We haven&#39;t hit 1.0 yet, but we&#39;ve already come down with a terminal case of &lt;a href=&quot;http://c2.com/cgi/wiki?SecondSystemEffect&quot;&gt;Second System Effect&lt;/a&gt;.&quot; Moreover, this sense of foreboding is not at all helped by the fact that the presentations and documents about Hadoop .Next have not included any information about the most important facet of a Hadoop refactoring: the &lt;a href=&quot;http://hadoop.apache.org/common/docs/r0.20.0/api/index.html&quot;&gt;API&lt;/a&gt;. So, not only do I have to worry about deciding between implementing &lt;i&gt;Mapper&lt;/i&gt; or inheriting from Mapper, I&#39;m now worried that I&#39;ll have to abandon Mapper for ConfigurableGenericJobTask and ConfigurableGenericJobTaskFactoryInterface and rewrite all my code to suit. Viva la Revolution.&lt;br /&gt;
&lt;br /&gt;
I&#39;m not really that worried. Mostly, Hadoop has been evolving according to a &lt;a href=&quot;http://en.wikipedia.org/wiki/Pierre_Teilhard_de_Chardin&quot;&gt;Teilhardian&lt;/a&gt; roadmap. However, it&#39;d be fantastic if the community released 1.0 in 2011 and phased in Hadoop .Next incrementally, without making it &lt;i&gt;seem&lt;/i&gt; so disruptive.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5470265265673774580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/02/next-generation-of-hadoop.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5470265265673774580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5470265265673774580'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/02/next-generation-of-hadoop.html' title='Next Generation of Hadoop'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-5318773926879022943</id><published>2011-02-15T18:43:00.001-05:00</published><updated>2011-02-15T18:47:15.559-05:00</updated><title type='text'>Fast Unique Files filter for EnCase</title><content type='html'>Related to my post &lt;a href=&quot;http://codeslack.blogspot.com/2010/11/fast-hash-matching.html&quot;&gt;Fast Hash Matching&lt;/a&gt; post from November and &lt;a href=&quot;http://www.forensickb.com/2010/11/encase-filter-that-uses-mssql-for.html&quot;&gt;Lance&#39;s original post&lt;/a&gt;, here&#39;s the code to an EnCase filter for the Entries view that will show you only the first occurrence of each file by hash value:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;include &quot;GSI_Basic&quot;&lt;br /&gt;
&lt;br /&gt;
class MainClass {&lt;br /&gt;
&amp;nbsp; NameListClass &amp;nbsp;HashList;&lt;br /&gt;
&amp;nbsp; BinaryTreeClass Tree;&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; MainClass() :&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; HashList(),&lt;br /&gt;
&amp;nbsp; &amp;nbsp; Tree(HashList, new NodeStringCompareClass())&lt;br /&gt;
&amp;nbsp; {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; if (SystemClass::CANCEL == SystemClass::Message(&lt;/div&gt;&lt;div id=&quot;:1il&quot; style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;wbr&gt;&lt;/wbr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SystemClass::ICONINFORMATION |&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SystemClass::MBOKCANCEL, &quot;Unique Files By Hash&quot;,&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp;&quot;Note:\nFiles must be hashed prior to running this filter.&quot;))&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br /&gt;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; SystemClass::Exit();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; bool Main(EntryClass entry) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; HashClass hash = entry.HashValue();&lt;br /&gt;
&amp;nbsp; &amp;nbsp; if (Tree.FastFind(hash)) {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; return false;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; &amp;nbsp; else {&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; Tree.FastInsert(new NameListClass(null, hash), hash);&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; return true;&lt;br /&gt;
&amp;nbsp; &amp;nbsp; }&lt;br /&gt;
&amp;nbsp; }&lt;br /&gt;
}&lt;/div&gt;&lt;div id=&quot;:1il&quot;&gt;&lt;/div&gt;&lt;div id=&quot;:1il&quot;&gt;The code in the comments on Lance&#39;s blog is close, but not quite correct, maybe due to the comment form or some such. You need to hash all files before you run this. As discussed in my earlier post, this is by no means the fastest possible way to do this, but I recently had someone ping me about needing exactly this, and it makes sense to put it up for everyone.&lt;/div&gt;&lt;div id=&quot;:1il&quot;&gt;&lt;br /&gt;
This filter is utterly dependent on BinaryTreeClass in GSI_Basic.EnScript, a support file that comes with EnCase and can be found at Program Files\EnCase 6\EnScript\Include\GSI_Basic.EnScript.&lt;br /&gt;
&lt;br /&gt;
I also have put an ini file of the filter that you can import directly into EnCase (right-click in the filter tree, choose Import...), &lt;a href=&quot;https://docs.google.com/a/lightboxtechnologies.com/uc?export=download&amp;amp;id=0B9Td2gyPIBBOM0hVNG9EQU5LWjNzNVB3UG93YWMyTWRCc1lNPQ&amp;amp;revision=true&quot;&gt;available here&lt;/a&gt; on Google Docs.&lt;/div&gt;&lt;div id=&quot;:1il&quot;&gt;&lt;br /&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/5318773926879022943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/02/fast-unique-files-filter-for-encase.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5318773926879022943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/5318773926879022943'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/02/fast-unique-files-filter-for-encase.html' title='Fast Unique Files filter for EnCase'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-2064233546277159222</id><published>2011-02-15T17:00:00.000-05:00</published><updated>2011-02-15T17:00:21.438-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EnScript"/><category scheme="http://www.blogger.com/atom/ns#" term="Programming"/><title type='text'>Dynamic Features in EnScript</title><content type='html'>EnScript is first and foremost a static language. It&#39;s statically typed and statically compiled, which means that EnScript figures out everything in your script before it starts to execute it, and if it cannot figure out which code should be called or finds any other syntactic problems, it generates a compilation error. It also means that EnScript executes reasonably efficiently, since the compiler has figured out which functions to call in advance, and this is done in a relatively lightweight manner.&lt;br /&gt;
&lt;br /&gt;
(It should be noted that EnScript does not do much in the way of code-optimization, however, so dynamic languages that make use of &lt;a href=&quot;http://en.wikipedia.org/wiki/Just-in-time_compilation&quot;&gt;just-in-time compilation&lt;/a&gt; and other &lt;a href=&quot;http://code.google.com/apis/v8/design.html&quot;&gt;aggressive&lt;/a&gt; &lt;a href=&quot;http://steve-yegge.blogspot.com/2008/05/dynamic-languages-strike-back.html&quot;&gt;techniques&lt;/a&gt; may yet execute faster than EnScript.)&lt;br /&gt;
&lt;br /&gt;
Despite being squarely in the static camp of languages, EnScript does have some features which can be considered &quot;dynamic.&quot; By dynamic, I mean that you can write an EnScript that is able to interact with the EnScript engine and script code, at least in some ways.&lt;br /&gt;
&lt;br /&gt;
The dynamic features in EnScript are:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Typecasting&lt;/li&gt;
&lt;li&gt;Class reflection&lt;/li&gt;
&lt;li&gt;Property accessors&lt;/li&gt;
&lt;li&gt;Subordinate execution through ProgramClass&lt;/li&gt;
&lt;/ul&gt;These dynamic features make EnScript far more powerful than it initially seems. I&#39;ll be writing about these features over the next few weeks. Since typecasting is short and sweet, I&#39;ll cover that now.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Upcasting&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Let&#39;s say you&#39;re working with a NameListClass object. Since NameListClass inherits from NodeClass, you can always treat a NameListClass object as a NodeClass object without doing anything special:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;NameListClass list();&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;list.Parse(&quot;one two three&quot;, &quot; &quot;);&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;NodeClass nodeRef = list; // up-cast&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;Console.WriteLine(nodeRef.Count());&lt;/div&gt;&lt;br /&gt;
This is an example of upcasting, where we treat a derived object as its parent type by manipulating it through a parent-type reference. EnScript knows that NameListClass inherits from NodeClass, so there&#39;s no need for you to do anything special, and this cannot possibly fail. There is no ambiguity.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Downcasting&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
What if we want to do the reverse? Say we have a function that takes a NodeClass object as a parameter, and we&#39;d like to treat it specially if it&#39;s a NameListClass object? Here&#39;s the answer:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;void foo(&lt;b&gt;NodeClass&lt;/b&gt; node) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; &lt;b&gt;NameListClass&lt;/b&gt; listRef = &lt;b&gt;NameListClass::TypeCast(node)&lt;/b&gt;;&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; if (listRef) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; }&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;}&lt;/div&gt;&lt;br /&gt;
As it turns out, every class in EnScript has a static function named TypeCast(). TypeCast() takes an ObjectClass reference and returns a reference to an object for its particular type, e.g., NameListClass::TypeCast() returns a NameListClass object reference. Because every object inherits from ObjectClass implicitly (even NodeClass), you can pass just about anything into TypeCast(), and because NameListClass::TypeCast() returns a reference to a NameListClass object, EnScript&#39;s compiler doesn&#39;t complain about assigning the result to the listRef reference here. TypeCast() lets us bridge the gap, allowing us to treat our NodeClass as a NameListClass.&lt;br /&gt;
&lt;br /&gt;
Note here that we then check listRef to see whether its null. Why? Well, what if someone called foo() and passed in an EntryClass object? An EntryClass object is not a NameListClass object, so NameListClass::TypeCast() will return null. By doing so, you can safely test your downcast. Of course, if you didn&#39;t do the check, the code would compile but you&#39;d end up with a null reference error when someone called foo() with anything other than a NameListClass object.&lt;br /&gt;
&lt;br /&gt;
One more thing about TypeCast(): Don&#39;t pass it a null reference. TypeCast() itself will generate a null reference error if you pass it null. Therefore, to be truly safe, foo() should look like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;void foo(NodeClass node) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; &lt;b&gt;if (node) {&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; NameListClass listRef = NameListClass::TypeCast(node);&lt;br /&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; if (listRef) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ...&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; &lt;b&gt;}&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;}&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;Single Object Inheritance is the Root of All Evil&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If you make your own class hierarchies, you will find yourself having to make use of TypeCast() quite a bit. Let&#39;s say you have an AnimalClass as a base class and two derived classes, DogClass and CatClass. Further, we want to have a method to check whether two AnimalClass objects are equal (...uh, whatever that means...). So, we would have to create a virtual function in AnimalClass, isEqual(), that looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;class AnimalClass {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; pure bool isEqual(AnimalClass other);&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;}&lt;/div&gt;&lt;br /&gt;
and then override the function in DogClass and CatClass. Clearly, a dog is not ever going to be equal to a cat. But not all cats are equal to each other, either. We will need to check their favorite brand of kitty food, kitty litter, markings, meowing habits, and whatnot to decide two cats are equal. So, even though isEqual() receives an AnimalClass object, we&#39;ll need to treat it as a CatClass to inspect all the member variables that are specific to cats and make this determination.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;class CatClass {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; String FaveWetFood, FaveDryFood, FaveLitter;&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; virtual bool isEqual(AnimalClass other) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; CatClass cat = CatClass::TypeCast(other);&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (cat) {&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return FaveWetFood == cat.FaveWetFood&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;&amp;amp; FaveDryFood == cat.FaveDryFood&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;amp;&amp;amp; FaveLitter == cat.FaveLitter;&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return false; // must be a dog or a ferret&lt;/div&gt;&lt;div style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;&amp;nbsp; }&lt;/div&gt;&lt;span style=&quot;background-color: #f3f3f3; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The TypeCast() ends up being necessary because there&#39;s something common we want to do for all animals (e.g., make equality comparisons), so the functionality must go in the base class and the function signature must involve an AnimalClass object. In languages like Java, SmallTalk, and EnScript, programmers end up having to do this a lot.&lt;br /&gt;
&lt;br /&gt;
In C++, which has templates, you can often make use of the &lt;a href=&quot;http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern&quot;&gt;Curiously Recurring Template Pattern&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/2064233546277159222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/02/dynamic-features-in-enscript.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2064233546277159222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/2064233546277159222'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/02/dynamic-features-in-enscript.html' title='Dynamic Features in EnScript'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-1687600614854611767</id><published>2011-02-11T02:44:00.004-05:00</published><updated>2011-02-11T22:32:49.313-05:00</updated><title type='text'>sed quis custodiet ipsos custodes?</title><content type='html'>Fifty years on, Eisenhower&#39;s farewell address seems quite relevant to the times, and the readers of this blog.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;0&quot; src=&quot;http://c.gigcount.com/wildfire/IMP/CXNID=2000002.0NXC/bT*xJmx*PTEyOTc*MDM4ODE4NzAmcHQ9MTI5NzQwMzg4NjQ3MCZwPTEyNTg*MTEmZD1BQkNOZXdzX1NGUF9Mb2NrZV9FbWJlZCZn/PTImbz1kODJjZDc5N2QyNTk*MTdhOGQyNjA2NmVlZWIzNzZjNiZvZj*w.gif&quot; style=&quot;height: 0px; visibility: hidden; width: 0px;&quot; width=&quot;0&quot; /&gt;&lt;object classid=&quot;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&quot; codebase=&quot;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,124,0&quot; height=&quot;278&quot; id=&quot;ABCESNWID&quot; width=&quot;344&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://abcnews.go.com/assets/player/walt2.6/flash/SFP_Walt_2_65.swf&quot; /&gt;&lt;param name=&quot;quality&quot; value=&quot;high&quot; /&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot; /&gt;&lt;param name=&quot;allowNetworking&quot; value=&quot;all&quot; /&gt;&lt;param name=&quot;flashvars&quot; value=&quot;configUrl=http://abcnews.go.com/video/sfp/embedPlayerConfig&amp;configId=406732&amp;clipId=12367106&amp;showId=12367106&amp;gig_lt=1297403881870&amp;gig_pt=1297403886470&amp;gig_g=2&quot; /&gt;&lt;param name=&quot;allowfullscreen&quot; value=&quot;true&quot; /&gt;&lt;embed src=&quot;http://abcnews.go.com/assets/player/walt2.6/flash/SFP_Walt_2_65.swf&quot; quality=&quot;high&quot; allowScriptAccess=&quot;always&quot; allowNetworking=&quot;all&quot; allowfullscreen=&quot;true&quot; pluginspage=&quot;http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash&quot; type=&quot;application/x-shockwave-flash&quot; width=&quot;344&quot; height=&quot;278&quot; flashvars=&quot;configUrl=http://abcnews.go.com/video/sfp/embedPlayerConfig&amp;configId=406732&amp;clipId=12367106&amp;showId=12367106&amp;gig_lt=1297403881870&amp;gig_pt=1297403886470&amp;gig_g=2&quot; name=&quot;ABCESNWID&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
A quibble:&lt;br /&gt;
&lt;blockquote&gt;Yet, in holding scientific research and discovery in respect, as we should, we must also be alert to the equal and opposite danger that public policy could itself become the captive of a scientific-technological elite.&lt;/blockquote&gt;&lt;br /&gt;
In 2011, when any &lt;a href=&quot;http://www.majorityleader.gov/YouCut/Review.htm&quot;&gt;village idiot/realtor&lt;/a&gt; can comment on &lt;a href=&quot;http://www.majorityleader.gov/YouCut/Review.htm&quot;&gt;which NSF grants do not have merit&lt;/a&gt;, it is safe to say that public policy is in no danger of becoming captive to the scientific-technological elite. (By the way, &lt;a href=&quot;http://vimeo.com/16423966&quot;&gt;Jacques Barzun&lt;/a&gt;&#39;s &lt;i&gt;&lt;a href=&quot;http://www.amazon.com/House-Intellect-Perennial-Classics/dp/0060102306&quot;&gt;The House of Intellect&lt;/a&gt;&lt;/i&gt; is a great exploration of anti-intellectualism in the U.S. [I hope I look that good when I&#39;m 102, and I&#39;m unsurprised the Presbyterians played host.])&lt;br /&gt;
&lt;br /&gt;
However, a core tenet of the contemporary homeland defender&#39;s creed is &quot;we don&#39;t set policy.&quot; The scientific-technological elite, to which you, dear reader, no doubt belong, strives to &lt;a href=&quot;http://www.faqs.org/docs/artu/ch01s04.html&quot;&gt;uphold the Unix ideal&lt;/a&gt;, providing mechanism, not policy... capability, not culpability. This is a cop-out. You can&#39;t sit Grandma down in front of a bash prompt and tell her she&#39;ll be just fine with &lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;man man&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
Our clients, those whom we serve, likely do not understand what it is we do, what we know, what we can do, and, most importantly, what &lt;i&gt;should&lt;/i&gt; be done. If we in the Cyber Industrial Complex* do not act with restraint, if we do not &quot;conduct our struggle on the high plane of dignity and discipline,&quot; then we risk legitimizing the &lt;a href=&quot;http://www.newyorker.com/reporting/2010/06/07/100607fa_fact_khatchadourian&quot;&gt;paranoid delusionals&lt;/a&gt;, and the &lt;a href=&quot;http://arstechnica.com/tech-policy/news/2011/02/how-one-security-firm-tracked-anonymousand-paid-a-heavy-price.ars&quot;&gt;lawless actions&lt;/a&gt; of their defenders. &quot;Thy will be done&quot; may be the easy interpretation our masters desire, but when our masters are not omniscient, or lack sufficient wisdom, then, ever mindful of our own weaknesses and ever penitent, we should strive to be &lt;a href=&quot;http://en.wikipedia.org/wiki/Themis&quot;&gt;of good counsel&lt;/a&gt;. Machine learning, SNA, disinformation, and old school psyops are good tools, but only if we have good targets.&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana; font-size: x-small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Verdana; font-size: x-small;&quot;&gt;* Military Cyber Complex is maybe more apt, but less poetic.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update:&lt;/b&gt; Penitence requires more than an &lt;a href=&quot;http://www.palantir.com/statement-from-dr-alex-karp&quot;&gt;apology&lt;/a&gt;, but it&#39;s a good start.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update 2:&lt;/b&gt; &lt;a href=&quot;http://www.nytimes.com/2011/02/12/us/politics/12hackers.html&quot;&gt;The paper of record&amp;nbsp;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Comment&lt;/i&gt;: What about severing ties to Hunton &amp;amp; Williams? Severing ties to a tiny firm like HB Gary Federal seems like kicking a dead dog.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/1687600614854611767/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/02/sed-quis-custodiet-ipsos-custodes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1687600614854611767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/1687600614854611767'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/02/sed-quis-custodiet-ipsos-custodes.html' title='sed quis custodiet ipsos custodes?'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-654467624054648416</id><published>2011-01-22T14:23:00.000-05:00</published><updated>2011-01-22T14:23:14.718-05:00</updated><title type='text'>Empty hash values don&#39;t get sorted?</title><content type='html'>Weird thing in EnCase 6.18 I came across today: when sorting by the Hash Value field, entries without hash values (0-byte contents) seem immune to sorting, and seem to remain in a stable ordering, relative to each other. You can see this in this screenshot, where the evidence is in a logical evidence file with folders that don&#39;t have content. I don&#39;t know whether this is specific to logical evidence files, but it&#39;s somewhat annoying.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5LlrD-zBnF11O4cjHUfXhqzbtd_ZrZiLZVUvXnETWPm23Xl6NVGVrB7Z8qpA8HnMUktW-CGN5_sasLODiNZq4ucxdDmHFgJ1NJSVJg4dxWOZCvqsqdrBpkUgENztnPNtBNd1E5stRIsxS/s1600/EmptyHashValues.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;339&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5LlrD-zBnF11O4cjHUfXhqzbtd_ZrZiLZVUvXnETWPm23Xl6NVGVrB7Z8qpA8HnMUktW-CGN5_sasLODiNZq4ucxdDmHFgJ1NJSVJg4dxWOZCvqsqdrBpkUgENztnPNtBNd1E5stRIsxS/s640/EmptyHashValues.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/654467624054648416/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2011/01/empty-hash-values-dont-get-sorted.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/654467624054648416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/654467624054648416'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2011/01/empty-hash-values-dont-get-sorted.html' title='Empty hash values don&#39;t get sorted?'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5LlrD-zBnF11O4cjHUfXhqzbtd_ZrZiLZVUvXnETWPm23Xl6NVGVrB7Z8qpA8HnMUktW-CGN5_sasLODiNZq4ucxdDmHFgJ1NJSVJg4dxWOZCvqsqdrBpkUgENztnPNtBNd1E5stRIsxS/s72-c/EmptyHashValues.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4736761969082226436.post-8131400618919668955</id><published>2010-12-04T22:49:00.000-05:00</published><updated>2010-12-04T22:49:54.966-05:00</updated><title type='text'>Wanted: strpbrk()</title><content type='html'>I&#39;m looking for a good EnScript API method for testing whether a string contains any characters in a second string.&lt;br /&gt;
&lt;br /&gt;
That is, I want the standard C function &lt;span style=&quot;font-size: x-small;&quot;&gt;&lt;span style=&quot;font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;&quot;&gt;strpbrk()&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
For some reason, I feel like earlier versions of EnCase had a function that did this. (By earliere versions, I mean v5 or v4.)&lt;br /&gt;
&lt;br /&gt;
String::Trim() would almost do the job, except that it replaces the occurrences, which isn&#39;t what I want. The second parameter of Trim() is an int for passing in a combination of the String::TRIMOPTIONS enums. So, I tried passing in 0 instead of one of the enums, hoping this would find all occurrences but not replace them. Sadly, passing in 0 finds no occurrences.&lt;br /&gt;
&lt;br /&gt;
You can use SearchClass with a character class regexp (e.g., [abc]) or NameListClass::Parse(), but both of them are pretty heavyweight.&lt;br /&gt;
&lt;br /&gt;
In happier news, though, you can use EnScript to convert doubles to strings in scientific notation.</content><link rel='replies' type='application/atom+xml' href='http://codeslack.blogspot.com/feeds/8131400618919668955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://codeslack.blogspot.com/2010/12/wanted-strpbrk.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/8131400618919668955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4736761969082226436/posts/default/8131400618919668955'/><link rel='alternate' type='text/html' href='http://codeslack.blogspot.com/2010/12/wanted-strpbrk.html' title='Wanted: strpbrk()'/><author><name>Jon Stewart</name><uri>http://www.blogger.com/profile/00439761904910491653</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhD5kG59266Uzg-7naosiJzYjcA-2s-d7rVOp-bUvO6Ftmmg_aTqrFrrNj4J0aK8RbXBEJsRDfeAziloWwdKnaqsOlnwORGsUuPXl2BPX1F77NmdmNgLZDGYzrvv0Sca1s/s80-r/0a1b6f1.jpg'/></author><thr:total>2</thr:total></entry></feed>