malware
Getting Started with Savarin
(disclaimer: the author of Savarin, Matthieu Kaczmarek, is a colleague working in the office next door and a friend of mine)
Savarin is a free online binary classification service (you can think of it as automatic diff’ing against large databases of programs). It is in beta, not fully polished yet, but you can still squeeze some interesting results out of it. Here is your daily shot of binary analysis, freshly brewed.
You will need:
- 2 different malware samples in the same malware family. We are going to use Sasser.A (already in Savarin’s database) and an unpacked Sasser.G (md5 b973853d0863070aca89ce00d4ee0fb9 [offensivecomputing.net])
- IDA with IDAPython for the actual diff’ing (I have IDA 5.5, I don’t know if this works with the free version)
Let’s go:
- open Savarin
- in “Classification against custom database”, choose SasserA
- upload the Sasser.G sample
- in the results page, click More to see the similarity with other binaries in the Sasser family
- you can see that the sample is 41.95% similar to a sample with md5 edc66a4031f5a41f9ddf08595a1d4c92
At this point, you have a classification of a sample against a (small) database of programs. You can therefore see the distance between this sample and other samples. If you ask me, it’s a lot better to see that unknownsample.exe is 80% similar to badguy.exe and 90% similar to badguy2.0.exe than just “infected” or “not infected”.
For the actual diff’ing, follow these steps:
- open the Sasser.G sample in IDA
- download the IDAPython analysis report on Savarin’s analysis page (this report contains all the data needed to visualize the binary differences in IDA)
- execute the IDAPython analysis report
- right now, the situation is pretty anticlimactic since you should see no change apart from a few lines in the console. Wait until next step for the interesting stuff. Yes, you had nothing to do in this step, so what?
- type SavColor(‘md5.edc66a4031f5a41f9ddf08595a1d4c92’, 0x0088ff) in the IDAPython console (it is the md5 value of the Sasser.A sample)
- type SavComment(‘md5.edc66a4031f5a41f9ddf08595a1d4c92’) in the IDAPython console
- this is it, now you can browse the Sasser.G sample, and the common parts with Sasser.A will be colored. Additionally, for two matching instructions you will see the corresponding address in the Sasser.A sample.
The Fine Screenshots:
A look at anti-virtualization in malware samples
In previous posts, I described PuppetMaster, a way to dynamically detect and control CPU-based VMM detection methods in malware samples. We ran it on 2 sets of malware samples, and here are the results.
1. 60k samples from a Nepenthes honeypot
- 62498 samples on the honeypot
- 59554 of them being executable files
- 48404 were analysed “correctly”
- 13409 samples were terminated due to a 2 minutes timeout
The number of samples trying to detect virtualization is surprisingly low:
- 71 (0.15%) binaries used at least one anti-virtualization technique
- 65 (0.13%) binaries used the SIDT anti-virtualization technique
- 0 (0.00%) binaries used the STR anti-virtualization technique
- 0 (0.00%) binaries used the SLDT anti-virtualization technique
- 0 (0.00%) binaries used the SGDT anti-virtualization technique
- 14 (0.03%) binaries used the VMware channel anti-virtualization technique
2. 25k samples from uh… somewhere
These samples were shared by Paul Royal, so thanks Paul :)
- 25118 samples
- 23104 of them being executable files
- 18670 were analysed “correctly”
- 8298 samples were terminated due to a 2 minutes timeout
Again, the number of samples trying to detect virtualization is very low:
- 117 (0.63%) binaries used at least one anti-virtualization technique
- 56 (0.30%) binaries used the SIDT anti-virtualization technique
- 0 (0.00%) binaries used the STR anti-virtualization technique
- 2 (0.01%) binaries used the SLDT anti-virtualization technique
- 6 (0.03%) binaries used the SGDT anti-virtualization technique
- 58 (0.31%) binaries used the VMware channel anti-virtualization technique
Conclusion
There are a few potential reasons why the numbers are so low:
- the samples used other techniques that we do not support (such as detecting the VMware tools, or hardware version)
- or the samples we got are really not representative of malware samples in the wild. Indeed, our 60k samples contain mostly Allaple samples.
- or anti-virtualization techniques are not that common in actual malware samples…
It would be interesting to run the test on better malware repositories, unfortunately this is not something obvious to get our hands on. So if you have a big malware repo ready to be dissected, and you would like to share them with an academic lab for free, I’d be glad to hear from you: reynaudd at loria dot fr.
Do We Really Need Malware Analysis?
Recently I’ve been wondering, how is malware analysis different from traditional program analysis? The fundamental reason is that programs can generally self-modify themselves. There is a direct consequence: with malware we have to admit that we don’t have static access to the program listing (thus preventing standard program analyses). And since turning self-modifying code into normal code is undecidable, we end up only with technical, partial solutions. This is why virtually every paper on malware analysis will only be a report on how a given technology/implementation is better/faster/stronger than the others.
This has a corollary too: since we have only partial solutions, in some cases they don’t work. And malware authors actively exploit that fact, by implementing techniques to defeat our implementations. This opened a sub-research field: the production of techniques to defeat the analysis-defeating techniques. Yes, there is some irony in this, for instance think about packing -> emulation-based unpacking -> anti-emulation techniques -> other-wonderful-unpacking-techniques…
Now, you might wonder, how did we get into this quagmire? As Schneier pointed it out before me, this is an accident – a historic by-product of the way the IT industry evolved. The x86 architecture allowed self-modifying code, and operating systems did nothing to prevent or regulate that. And bam, a research niche was born.
Puppetmaster Strikes Back
Vincent Mussot and I implemented new virtualization counter-countermeasures in puppetmaster. This time we can detect and thwart 6 tests out of 7 in ScoopyNG. In addition to the SIDT test, we counter the SLDT, SGDT and STR techniques in a similar way: instrument the binary until one of these instructions is found, intercept the memory address it writes to and patch the return value.
The 2 other tests use the VMware backdoor (the in eax instruction, with a magic value in eax and edx). We thwart it by detecting the backdoor trigger, and changing the magic values (this way an exception is raised, as if there were no backdoor). We then restore the magic values to add a little bit of stealth.
You can compile and run puppetmaster with the latest version (27887) of Pin on Windows and Linux. The makefile for the free version of Visual C++ is given below.
// puppetmaster.cpp // Usage: pin -t <puppetmaster.dll> -- <binary> [arguments]\n // Currently supported anti-virtualisation techniques: SIDT, SLDT, SGDT, STR, VMWare channel // works with pin-2.6-27887-msvc9 #include <string> #include "pin.H" int needRestore = 0; VOID poisonSIDT(ADDRINT memIp) { char *data = (char *)memIp; unsigned int* m = (unsigned int *)(data+2); *m = 0xd00dbeef; // if ((idt_base >> 24) == 0xff) -> vmware detected } void poisonSLDT(ADDRINT memIp) { char *data = (char *)memIp; unsigned int* m = (unsigned int *)(data); *m = 0xdead0000; // if (ldt_base != 0xdead0000) -> vmware detected } void poisonSGDT(ADDRINT memIp) { char *data = (char *)memIp; unsigned int* m = (unsigned int *)(data+2); *m = 0xdeadbabe; // if ((gdt_base >> 24) == 0xff) -> vmware detected } void poisonSTR(ADDRINT memIp) { char *data = (char *)memIp; unsigned int* m = (unsigned int *)(data); *m = 0xbebaadde; // if ((mem[0] == 0x00) && (mem[1] == 0x40)) -> vmware detected } void poisonVMWareChannel() { unsigned int EAX_save; unsigned short int DX_save; __asm { mov EAX_save, eax mov DX_save, dx } if ((EAX_save == 0x564D5868) && (DX_save == 0x5658)){ __asm { mov dx, 0x0004 } needRestore = 1; } else needRestore = 0; } void restoreVMWareChannel() { if (needRestore == 1) { __asm { mov eax, 0x564D5868 mov dx, 0x5658 } needRestore = 0; } } VOID Instruction(INS ins, VOID *v) { string buffer = INS_Disassemble(ins); if (buffer.substr(0,4) == "sidt") INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)poisonSIDT, IARG_MEMORYWRITE_EA, IARG_END); else if (buffer.substr(0,4) == "sldt") INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)poisonSLDT, IARG_MEMORYWRITE_EA, IARG_END); else if (buffer.substr(0,4) == "sgdt") INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)poisonSGDT, IARG_MEMORYWRITE_EA, IARG_END); else if (buffer.substr(0,3) == "str") INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)poisonSTR, IARG_MEMORYWRITE_EA, IARG_END); else if (buffer.substr(0,6) == "in eax") { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)poisonVMWareChannel, IARG_END); INS_InsertCall(ins, IPOINT_AFTER, (AFUNPTR)restoreVMWareChannel, IARG_END); } } int main(int argc, char * argv[]) { PIN_Init(argc, argv); INS_AddInstrumentFunction(Instruction, 0); PIN_StartProgram(); return 0; }
Here is the Nmakefile for the Nmake utility:
# Nmakefile ###################################################################################### # This is the NMAKE file for building and testing PIN toos contained in one of the # subdirectories of the PinTool project or PIN kit. # # For description of targets and options, see Nmakefile in the root directory. ###################################################################################### !if "$(PIN_HOME)"=="" PIN_HOME=.. !endif # Define tools to be buit and tested ###################################################################################### COMMON_TOOLS=puppetmaster.dll # Include building and testing rules from the root Nmakefile. ###################################################################################### INCLUDE_SUB_RULES=1 !INCLUDE $(PIN_HOME)\Nmakefile
Build instructions:
- download and install Pin and Visual C++
- under $PIN_HOME\source\tools, create a puppetmaster directory
- put puppetmaster.cpp and the Nmakefile in that directory
- from that directory, run ..\nmake.bat puppetmaster.dll
Now you can run it and use your newly acquired ninja skills on ScoopyNG:
<pre>C:\pin-2.6-27887-msvc9-ia32_intel64-windows\source\tools\puppetmaster>pin -t obj-ia32\puppetmaster.dll -- ScoopyNG.exe #################################################### :: ScoopyNG - The VMware Detection Tool :: :: Windows version v1.0 :: [+] Test 1: IDT IDT base: 0xd00dbeef Result : Native OS [+] Test 2: LDT LDT base: 0xdead0000 Result : Native OS [+] Test 3: GDT GDT base: 0xdeadbabe Result : Native OS [+] Test 4: STR STR base: 0xdeadbabe Result : Native OS [+] Test 5: VMware "get version" command Result : Native OS [+] Test 6: VMware "get memory size" command Result : Native OS :: tk, 2008 :: :: [ www.trapkit.de ] :: ####################################################
Note: we do not support the last test in ScoopyNG because Pin does not currently support far rets in different code segments. But as far as I can tell the bug that this last test uses has been patched, I was not able to trigger it. It should probably be considered deprecated.
Reversing CUDA Software
After my Ruxcon talk on GPGPU malware, some people doubted that malware could use GPUs at all and that even if malware used GPUs, they would just be like normal malware (and since I did not provide any code sample at the conference, I can understand the frustration).
Here is a small code sample to convince the unconvinced: it contains encrypted strings, that are sent on the GPU to be decrypted. And once decrypted, they are executed in a shell.
#include <stdio.h> #include <cuda.h> #define MAX_SIZE 255 // caution: kickass encryption ahead __global__ void decodeOnDevice(char *a) { char cap; int i = 0; while(a[i] && i<MAX_SIZE) { cap = a[i] & 32; a[i] &= ~cap; a[i] = ((a[i] >= 'A') && (a[i] <= 'Z') ? ((a[i] - 'A' + 13) % 26 + 'A') : a[i]) | cap; i++; } } int main(void) { char *temp_host; // pointers to host memory char *temp_device; // pointers to device memory char commands[2][MAX_SIZE]; int i; // allocate arrays on host temp_host = (char *)malloc(MAX_SIZE); // allocate arrays on device cudaMalloc((void **) &temp_device, MAX_SIZE); // initialize host data memset(commands[0], 0, MAX_SIZE); memset(commands[1], 0, MAX_SIZE); // these are the encoded commands memcpy(commands[0], "rpub Jung vf lbhe anzr, unaqfbzr xavtug?", strlen("rpub Jung vf lbhe anzr, unaqfbzr xavtug?")); memcpy(commands[1], "rpub - Fve Tnynunq... gur Punfgr.", strlen("rpub - Fve Tnynunq... gur Punfgr.")); for(i = 0; i<2; i++) { memset(temp_host, 0, MAX_SIZE); memcpy(temp_host, commands[i], strlen(commands[i])); // send data from host to device cudaMemcpy(temp_device, temp_host, MAX_SIZE, cudaMemcpyHostToDevice); // data copied on device, invoking kernel decodeOnDevice <<< 1, 1 >>> (temp_device); // retrieve data from device cudaMemcpy(temp_host, temp_device, MAX_SIZE, cudaMemcpyDeviceToHost); // execute the decoded command system(temp_host); } }
PIN me if you can
Or how to escape PIN in 5 instructions, using the self-modification technique seen in the previous post. Ready ? Go:
#include <stdio.h> main() { asm("call foo\n\t" "foo: pop %rax\n\t" "movl $0x4004e7, 10(%eax)\n\t" // put @nottraced() in the next mov "movl $0x4004fb, %eax\n\t" // @traced(), will be overwritten // by @nottraced() if not instrumented "call *%rax\n\t"); }
// we don't want PIN to analyse this nottraced() { printf("trace me if you can!\n"); }
// we want PIN to analyse this, a dummy function traced() { printf("you're not supposed to get here\n"); }
As usual: compile, make the .text section and the program header writable, and run.
reynaudd@lhs-2:~/test/packed$ ./escape2 trace me if you can!
reynaudd@lhs-2:~/test/packed$ pin -t ../pin-2.5-24110-gcc.4.0.0-ia32_intel64-linux/source/tools/ManualExamples/obj-intel64/inscount0.so -- ./escape2 you're not supposed to get here
‘Nuff said.
UPDATE: as the authors of PIN pointed out, this situation in handled correctly by PIN with the option -smc_strict. That’s because for performance reasons (and standards compliance), PIN makes the assumption that there is at least a taken branch between a modification of the code and its execution (i.e. no basic block modifies itself). My example violates this assumption.
New Firefox Malware
Howdy,
Apparently BitDefender stumbled upon a Firefox-only banking malware. It installs itself as a Firefox plugin (= it installs a native binary) and as a javascript file in the Chrome folder (= it modifies the source code of Firefox):
- %ProgramFiles%\Mozilla Firefox\plugins\npbasic.dll
- %ProgramFiles%\Mozilla Firefox\chrome\chrome\content\browser.js
If anyone has a sample, I’d like to have a look at it. It’s not technically a Firefox extension, but its payload could also be delivered as an extension (with no native code at all). If anybody wonders why there is not more Firefox crapware, there are two reasons for it:
- the browser market is still dominated by IE
- malware authors have not realised how easy it was to write malware for Firefox
Links:
- Malicious Firefox Extensions on this blog
- ‘Greasemonkey’ Malware Targets Firefox on Slashdot
- Firefox users targeted by rare piece of malware on Infoworld
- Trojan.PWS.ChromeInject.B on BitDefender
World of Warcraft – Scan.dll is not a Virus
Howdy,
As I logged on today on WoW, my antivirus raised a virus alert for the file Scan.dll in WoW’s installation folder. I am using Avast 4.8 Home Edition (Virus Database 081028-0 dating from today). It is probably a false positive, but it is also possible that my WoW install has been infected with something (since some malware specifically targets WoW accounts). Since I don’t want to lose all my precious gold, I preferred checking a few things before logging in.
Here is a VirusTotal scan of the suspicious file Scan.dll: http://www.virustotal.com/fr/analisis/2b23aa39412bdfffdc8e8f34215de118. Here is what we can learn from this:
- only 3/36 antiviruses flag scan.dll as suspicious (which is low but still more than one)
- all raise generic alerts such as “suspicious file” and “Win32:Trojan-gen {Other}”. In plain English, this means “I have no idea what this file does, however it is protected against analysis and I am better off raising a scary alert”.
- according to multiple tools the file is packed with UPX.
Hopefully, UPX is a really weak protection and the official release can unpack the file for us. So, all we have to do is download the latest UPX release, install it and unpack Scan.dll:
D:\test\upx303w>upx -d Scan.dll -o UnpackedScan.dll Ultimate Packer for eXecutables Copyright (C) 1996 - 2008UPX 3.03w Markus Oberhumer, Laszlo Molnar & John Reiser Apr 27th 2008 File size Ratio Format Name -------------------- ------ ----------- ----------- 90372 <- 39684 43.91% win32/pe UnpackedScan.dll Unpacked 1 file.
Now that we have an unpacked Scan.dll, let’s check again what Virus Total has to tell us: http://www.virustotal.com/fr/analisis/fdfe09829e1dd865144d6f80313209c4
This time, we have 0/36 detection rate, and the file does not seem to include additional layers of protection (which means it can now be fully analysed by antivirus engines).
Conclusion: it was the UPX protection layer that caused the new Avast and eSafe alert. Therefore this alert is a false positive, Scan.dll is not infected and you can go back to murloc farming. What is strange however is that UPX is a really common packer, I’m wondering exactly what part of it is confusing Avast’s heuristics.
Ok I didn’t understand anything above, what should I do/know ?
- at the time of writing, Scan.dll is not a virus
- the alert only comes from Avast and related antiviruses
- when Avast asks you what to do, choose Nothing. On the next virus database update, the alert will probably be removed.
- if you chose Quarantine, try to restore the file since WoW will probably complain that you messed with one of its DLLs.
Here is the post dealing with this issue on the official forums.
A Quick Survey on Automatic Unpacking Techniques
This is a non-comprehensive list of papers and tools dealing with automated unpacking. Please let me know if I’ve missed another technique or if I misunderstood any of the techniques below.
Ring0/Ring3 components, using manual unpacking and heuristics
OllyBonE:
OllyBonE (Break on Execution) uses a Windows driver to prevent memory pages from being executed, and an OllyDbg plugin communicating with the driver. As such it is not an automatic unpacker and requires manual tagging of the pages in which the unpacked code is expected to be found.
Technology used: Windows driver to prevent memory page execution, debugger plugin
Handles unknown packers: no.
Drawbacks: requires a priori knowledge of the memory location of the unpacked code, vulnerable to anti-debugging techniques, modification of the integrity of the host operating system due to the driver.
Code Available: yes, http://www.joestewart.org/ollybone/.
(Updated) Dream of Every Reverser / Generic Unpacker:
It is a Windows driver used to hook ring 3 memory accesses. It is used in a project called Generic Unpacker by the same author to find the original entrypoint. The tool then tries to find all import references, dumps the file and fixes the imports. It is reported to work against UPX, FSG and AsPack, but not against more complex packers.
Technology used: Windows driver to hook userland memory access
Handles unknown packers: no.
Drawbacks: requires a priori knowledge of the memory location of the unpacked code, modification of the integrity of the host operating system due to the driver.
Code Available: yes, http://deroko.phearless.org/GenericUnpacker.rar.
(updated) RL!Depacker
No description for this one, however it looks similar to Dream of Every Reverser / Generic Unpacker.
Code Available: yes, http://ap0x.jezgra.net/RL!dePacker.rar.
(updated) QuickUnpack
Again, no real description, but it looks similar to RL!Depacker and DOER / Generic Unpacker. It is a scriptable engine using a debugging API. It is reported to work against 60+ simple packers.
Code Available: yes, http://www.team-x.ru/guru-exe/?path=Tools/Unpackers/QuickUnpack/
Original Site (in Russian)
Universal PE Unpacker:
This is an IDA Pro plugin, using the IDA Pro Debugger interface. It waits for the packer to call GetProcAddress and then activates single-stepping mode until EIP is in a predefined range (an estimate for the OEP). It only works well against UPX, Morphine, Aspack, FSG and MEW (according to the authors of Renovo).
Technology used: Debugging and heuristics.
Handles unknown packers: no, needs an approximation of the OEP and assumes that the unpacker will call GetProcAddress before calling the original code.
Drawbacks: not fully automatic, very vulnerable to debugger detection, does not necessarily work against all packers or self-modifying code.
Code Available: yes, since IDA Pro 4.9
Instruction-level analysis, comparison between written addresses and executed addresses
Renovo:
Built on TEMU (BitBlaze), it uses full system emulation to record memory writes (and mark those memory locations as dirty). Each time a new basic block is executed, if it contains a dirty memory location a hidden layer has been found. Cost: 8 times slower than normal execution. It seems to unpack everything correctly except Armadillon and Obsidium (due to incorrect system emulation ?). It seems to only obtain partial results against Themida with the VM option on.
Technology used: Full system emulation.
Handles unknown packers: yes.
Drawbacks: order of magnitude slowdown, detection of the emulation stage
Code Available: I couldn’t find it.
Azure:
Paul Royal’s solution, named after BluePill because it is based on KVM, a Linux-based hypervisor. It uses Intel’s VT extension to trace the target process (at the instruction-level), by setting the trap flag and intercepting the resulting exception. The memory writes are then recorded and compared to the address of the current instruction. According to the paper, it handles every packer correctly (including Armadillo, Obsidium and Themida VM).
Technology used: Hardware assisted virtualization and virtual machine introspection.
Handles unknown packers: yes.
Drawbacks: detection of the hypervisor. Slowdown ?
Code Available: yes, http://blackhat.com/presentations/bh-usa-08/Royal/Royal_Extras.zip.
Saffron:
Developed by Danny Quist and Valsmith, a first version uses Intel PIN to dynamically instrument the analyzed code. It actually inserts instructions in the code flow, allowing lightweight fine-grained control (no need for emulation or virtualization), but it modifies the integrity of the packer. A second version modifies the page fault handler of Windows and traps when a written memory page is executed. It has mixed results with Molebox, Themida, Obsidium, and doesn’t handle Armadillo correctly (according to Paul Royal).
Technology used: Dynamic instrumentation, Pagefault handling (with a kernel component in the host operating system).
Handles unknown packers: yes.
Drawbacks: modifies the integrity of the code (with DI) and of the host operating system. It must not work in a virtual machine. The dynamic instrumentation is very slow. The memory monitoring of the pagefault handler is coarse-grained (pages are aligned on a 4k boundary), and therefore some memory access can go unnoticed.
Code Available: dynamic instrumentation available, what about the driver ?
(updated) OmniUnpack:
Uses a technique similar to the second version of Saffron: a Windows driver to enforce a W^X policy on memory pages.
Technology used: Pagefault handling and system call tracing (with a kernel component in the host operating system)
Handles unknown packers: yes.
Drawbacks: modifies the integrity of the host operating system. It must not work in a virtual machine. The memory monitoring of the pagefault handler is coarse-grained, leading to spurious unpacking stages.
Code Available: ?
Pandora’s Bochs:
Developed by Lutz Böhne, it is based on Bochs which is used to monitor memory writes and compare them with branch targets. Interestingly, the assumptions about the program are stated explicitly (which is a GOOD thing) : the unpacking does not involve multiple processes, it does not happen in kernel mode, the unpacked code is reached through a branch instruction (not a fall-through edge), etc… Another interesting point in this approach is that it uses no component in the guest OS (as opposed to Renovo for example), all the information is retrieved from outside the matrix (as with Azure).
Technology used: Full system emulation based on Bochs.
Handles unknown packers: yes.
Drawbacks: As stated in the paper the limitations are speed, compatibility (not all packed samples seemed to run under Bochs), detection of OEP and reconstruction of imports sometimes failed.
Code Available: http://damogran.de/blog/archives/21-To-release,-or-not-to-release-….html
Other techniques (comparison with static disassembly or disk image)
Secure and Avanced Unpacking by Sebastien Josse:
The idea developed by Sebastien Josse is to use full system emulation (based on QEMU ?) and to compare the basic blocks that are going to be executed by the virtual CPU with the equivalent address in the file image of the executable. If the memory and the disk version differ, it means that the code has been generated on the fly and therefore a hidden layer has been found. Josse then proposes techniques to rebuild a fully functional executable based on the memory dump. This technique seems to work well (but sometimes requires human intervention) against several packers, including Armadillo, ASProtect, PEtite, UPX, yC…
Technology used:Full system emulation, comparison between memory images and disk images.
Handles unknown packers: yes, manual intervention might be required in some cases.
Drawbacks: slowdown due to the full system emulation, full reconstruction of the unpacked program is not always possible.
Code Available: ?
PolyUnpack:
The idea behind PolyUnpack is to address the fundamental nature of unpacking, which is runtime code generation. To identifiy code that has been generated at runtime, PolyUnpack uses a conceptually elegant technique: it first statically analyses the program to build a map of statically accessible code, and then traces the execution of the program. The dynamically intercepted instructions are compared with the static disassembly, if they do not appear in the static disassembly then they have been generated at runtime.
Technology used: comparison between static disassembly and dynamic tracing. The dynamic trace is extracted with single-step debugging APIs.
Handles unknown packers: yes.
Drawbacks: vulnerable to debugger detection. Note that this is a limitation of the implementation, not of the concept.
Code Available: http://polyunpack.cc.gt.atl.ga.us/polyunpack.zip (updated 26/06/2009)