*LEGAL DISCLAIMER: The content on this website, including tools are for educational purposes only.
This website makes no warranties express or implied. You use any tools at your own risk!
Welcome to my FREEWARE tools page. The Open-Source GNU GPL tools listed here may be freely used and altered
according to the enclosed license. The other tools may be used free of charge provided they are not altered
without written permission if redistributed to the public. All tools may be freely distributed as long as they
retain their original contents as available on this website.
These tools are a mix of mostly command line applications originally built for Windows XP although they should
run on Windows 7 and up. Some newer tools include a Linux version. While the Windows tools have a dependency on
the Visual C++ runtime (available as a separate download), I decided to make things simpler for the Linux
versions by linking the executables statically, although the resutling executables are larger than I'd like.
NOTE:For questions, comments or bug reports, please use the
Pelook is a comprehensive information tool for 32 and 64 bit Windows EXE, DLL, driver and OBJ files (PE/COFF images).
This tool is suitable for reverse engineers or anyone needing to delve into the internals of Windows PE files.
At first glance, pelook may seem a lot like Microsoft's dumpbin tool, however you'll find it has many additional features
and doesn't have the unnecessary verbosity of dumpbin. Rather than blindly dumping fields
from the various headers and sections that comprise a PE image, pelook creates an intelligent report of the most useful information
in the most compact form possible without sacrificing readibility. Why shouldn't you be able to see
the target architecture, dependency DLLs, section list, loader flags, supported .NET framework and version resource all within the
same screenful of information and all within the same tool?
Pelook was originally written for the virtual memory map display
and pointer to image-file-offset conversions; two features I always needed but never found in other PE dumping tools.
Pelook combines the most widely used features in these popular tools in addition to other useful features:
filever - Microsoft's version information resource dump tool
clrver - Microsoft's CLR version tool (determine .NET Framework version needed for an EXE)
Some of pelook's features are:
Support for both 32-bit and 64-bit Windows modules (PE32 and PE32+, a.k.a. PE64)
Dump COFF and section headers (Portable Executable/OBJ files)
Dump Data Directory table
Dump Debug Directory Summary (recognition of Microsoft and Borland debug)
Dump import and delay-import dependency DLLs and associated functions (or just a DLL name list in quiet mode)
Resolve full paths to dependency DLLs similar to the Windows loader search
Dump export table
Dump resource tree
Dump initial bytes of entry point or disassemble them; for disassembly feature, download disasmdump and place both executables in same directory
Dump version resource block with the standard entries (or just basic versioning in quiet mode)
Dump exception function table in PE64 images (.pdata section)
Dump TLS directory
Dump load config directory
Detect and display CLR (.NET) header with versioning information and framework dependencies without needing to run the program (the clrver utility requires this); pelook also has no .NET dependencies
Display module's virtual memory map (pointers and image-file offsets, invaluable for manual hex editing); NOTE: if you want to see RVAs instead of pointers, override the load base with zero (i.e. -b 0)
Easily see memory access attributes for each section (Helps to answer, "Why am I getting an AV when patching code to write to a particular section")
Convert between pointer addresses and image-file offsets right from the command line (useful alongside debugging and hex editing)
The ability to override the preferred load base, affecting the output of pointers, virtual memory layout and exported function entry-points; invaluable for ASLR debugging
Decoding of timestamps and composition of bit flags as well as other named constants using "trimmed" SDK identifiers (e.g. decoding OS, subsystem, characteristics, etc.)
Recognition of EOF data (data at the end of PE image file that is not loaded into virtual memory by the Windows loader)
Automatic calculation and verification of checksum
Import table shows IAT slot column where loader stores runtime address (helpful for setting debugger breakpoints or API hooking)
Display base relocations: pointers, file offsets and target section
What is the date 90 days from yesterday or 3 weeks before Christmas? How many days are between two dates, accounting for
days between months, leap years, and leap centiries? The datesum tool implements a julian-day
algorithm to calculate day arithmetic on arbitrary dates. The supported year range is Nov 24, 4713 (BC) thru 1,465,001 (AD).
Usage Examples: (output when run on 2020-10-28)
-display 90 days from yesterday:
> datesum yesterday -a 90
-display 3 weeks before Christmas of 2020:
> datesum 2020-12-25 -s 21
-display the number of days since the 2016 Winter Solstice:
Peupdate originally started as a tool to alter the PDB pathname string embedded within executable modules
by Microsoft's compilers and build tools. This tool now supports other useful modifications to
PE files, including removal of
Microsoft's undocumented "Rich" Header,
and altering the PE timestamp to a specific date/time or literal values now used by Windows 10 as unique
[hash] ids (making deterministic builds possible). Multiple files can be processed at once, and each
file processed can have multiple modifications (i.e. whatever actions currently supported),
making this tool useful as a post-build step.
Peupdate's original purpose was to clear the PDB path string in an executable module placed by
Microsoft's linker; assuming you don't want [or can't] re-link with the /PDBPATH:none option because you don't have access to the source code.
Options include clearing the entire PDB path string, stripping just the path, or setting to a custom value specifed on the command line. 32
and 64-bit PE images are supported as well as CLR executables making use of the .NET framework. Leaving the filename intact
but stripping the path (-s option) is officially recommended as debuggers can rely on existing facilities to locate the PDB
file when the path has been removed.
The changes made by peupdate should not not alter the functionality of the executable module in any way although if
the file was originally (or needs to be) digitally signed or some post-build equivalent, it goes without saying that
the digital signature or other post-build steps would need to be re-applied after peupdate is used.
Upon changes, peupdate will recalculate the PE checksum as needed (unless forcibly disabled with
the -n option). For non-driver files and DLLs not
loaded during boot, a checksum is not required. This is why Microsoft's default linker settings set a
module's checksum to zero. For drivers and other PE images with a nonzero checksum, peupdate will recalculate
and store the updated checksum automatically, if changes have been made to the file.
You can however force the checksum to be calculated and saved with the -f option for any
module. This can be used as a standalone feature without any other changes to the PE image if you need to
generate and write the correct checksum to a PE image.
PDB Background: By default, Microsoft's linker embeds the full path string of the program database file
(.PDB) into an executable module whenever a .PDB is built with the module. This results in disclosure of what
some developers may consider private path information - information they'd rather not have present in binaries
released to the public. It's not uncommon to generate release/retail PDB files for crash-dump analysis and
or other debugging purposes, so disabling the PDB from being generated doesn't address the problem. Changing
the name of the executable does not change the original path information that may have been embedded within
the file at link time. A PDB path like:
may represent private information not meant for public consumption.
Even if not, why release a path that will probably never exist on a machine other
than the developer's? The best solution is to add the /PDBPATH:none or /PDBALTPATH:<path> options to
your linker settings and re-link. If re-linking isn't an option, or the source is not available, peupdate
can fix this path string for you in your final executables.
The idea for peupdate originated while I was searching for a tool to clear the PDB path string from my own executables.
I never found such a tool, but I did find this
Although I subsequently wrote peupdate from scratch due to some bugs and limitations in the original project,
this code was extremely useful for proper interpretation of the debug directory structure.
The task of modifying some strategically located bytes to produce a modified (or "cracked") executable is a fairly
common task for reverse engineers, yet I wasn't able to find a simple command line tool to do this that was also
PE-address aware (i.e. supports pointer addresses) in addition to supporting plain file offsets. A common scenario is that
you've got a debugger open and are looking directly at the address of one or more instructions you want to
NOP-out or slightly alter. Typical hex editors won't convert this pointer address for you. The old HIEW hex editor is the only
one I know of with this support, but you have to perform a series of steps in an interface that doesn't support
the clipboard. While IDA isn't useful for patching, you can indeed patch using OllyDbg, but not without
clicking through some odd menus and dialog "hoops".
Enter the bytepatch tool. Open a file, seek to a specified offset, write some bytes. Leave everything
else alone. Bytepatch is designed to apply a sequence of bytes to any position in a file, binary or otherwise,
retaining the size. The data can be specified directly on the command line using hex digits (spaces being optional) or by file.
Bytepatch supports pointer addresses (with a rebase option) or plain file offsets. At the moment, this tool only
supports patching one location (with any number of replacement bytes) at a time, so you need to run the tool
successive times to patch multiple locations in the same file.
To help reduce mistakes, bytepatch will display the bytes you are replacing under the default logging level and
also features a trial-run mode to simulate a patch without saving changes. Bytepatch also supports a quick
disassembly of the "before" and "after" patch bytes for further verification that you are patching the right
spot. Thanks to the author of OllyDbg for releasing his x86 32-bit disassembly engine to the public, bytepatch
leverages this to add "visual" verification. The engine was originally incorporated statically
into the pre-release version, but I found a command-line disassembler useful enough in its own right, such
as a quick way to decode a handful of byte values. It is also detected and called by multiple tools, so keeping it separate
helps keep the size of those tools down to a minimum.
If you download this tool, don't forget to also download disasmdump and place both
executables in the same directory to get the extended disassembly features.
NOTE: Bytepatch can be used on any binary file, not just Win32 executables. You just have to specify file offsets rather
than using the PE pointer features (-p option). Bytepatch currently lacks pointer support for PE64 (Win64) files; if you want this, let me know and I'll
make the changes. In the meantime, you can just patch using raw file offsets.
Disasmdump is a command-line tool that wraps an interface around the x86 32-bit OllyDbg Disassembling
Engine with pointer address support, syntax hilighting and other customizable formatting
features. While designed to disassemble hex values specified directly on the command-line, it will also
disassemble entire files or portions thereof, sending the results to standard output.
If you want to disassemble a snippet of bytes (such as shellcode or a small section of a PE file), common
practice is to use an interpreted script or a hex editor to convert the codes into binary form, saving the
result as a file, then opening it up in your favorite GUI disassembler such as IDA and click through some
dialogs before you can see if you are dealing with valid instructions. Microsoft's dumpbin is nice if you want
a giant dump of the .text section of a given PE file, but it's not well suited for working with files that contain
pure binary code. Even today, GUIs can still get in the way of simple tasks such as the
one just described.
With disasmdump, you can just paste your hex codes on the command line to see how they disassemble.
Because disasmdump makes use of the speed and accuracy of the Open-Source OllyDbg Disassembling Engine
(a scaled-down version of the one available in the Olly Debugger), it is perfect for quickly analyzing
byte sequences. Some of the features include showing or hiding the address and
opcode columns, disassembling at specified offsets and up to the nearest whole instruction as well as altering
other visual charateristics of the disassembly made available by Olly's Engine. See the
command-line link for details on all available options.
This tool represents my first Open-Source release. The download includes the EXE as well as the complete
source code in addition to the original OllyDbg Disassembler Engine 2.01 source from which the disassembling
functionality was based. Besides the C-Runtime library, I made sure the code has no other dependencies. Feel
free to build your own modified copy such as altering the color scheme. A fun feature to add might be a
customizable syntax color config file. The source was built with the VS.NET 7.1 compiler, but you shouldn't
have many problems porting to another version or other C++ compiler. I normally write my Windows code with Linux platform
compatibility in mind, but I didn't want to worry about making Olly's engine GCC compatible for the initial
release. The tool runs fine in Linux under Wine, but the syntax coloring does not work.
Evl is a command line programmer's calculator. It evaluates C-style mathematical expressions from the command line, working with
values up to 64-bits, but with the capability to constrain results to smaller standard int sizes. Like C-style
expressions, base-10 numbers are the default and hex values may be entered when the numbers are prefixed with 0x
(zero-x) such as 0xDB. Unlike C-style expressions, Evl has additional features such as support for bitwise rotation
(ROR, ROL) and input values being specified in binary (prefix numbers with 0b, such as 0b10001001.
Evl performs all standard arithmetic operations in addition to modulus, square roots, powers, string comparison,
boolean logic and bitwise operations. The command line switches also let you change the floating point
precision and add variable substitutions. To convert a number (or otherwise the result of the expression) to a
common base, simply use any combination of the output flags: /o: (b=binary, o=octal, h=hex, s=signed int,
u=unsigned int, d=double float, c=char). Like C-style expressions, when a floating point number is encountered,
the type of the expression result is promoted to a double.
The base conversion facilities between different int types may be more useful to some than the expression capabilities.
I.e.: simply input a number in any standard base and specify one or more output flags to see a representation in another.
Evl integer arithmetic and bitwise operations work natively with a 64-bit int QWORD type, however the output of
an expression can be forced into any of the standard int sizes (signed or unsigned) in BYTE, WORD, DWORD types.
For example, the smallest type to contain the value is how the value is handled and displayed by default. This is
especially useful to properly display negative integers, as Evl intelligently trims off sign extension
bits so a signed byte that happens to be negative can be interpreted properly when the same value in a larger
int type would be positive. For example, -1 can be 0xFF when decoding a signed BYTE but when displayed in a
signed WORD, becomes positive 255 (0x00FF).
• add 171 to 196 (notice that input numbers can use and mix any base provided you specify the proper
prefix) and see the result in both hex and unsigned int:
evl /o:hu 0xAB+0b11000100
• calculate the total price based on 8.325% sales tax, only keeping 2 decimal points of precision in the
evl "(29.95+14.95)*1.08325" /f:2
• convert 75 degrees Fahrenheit to Celsius using formula (F-32*5/9):
• use variable substitution to calculate area of a circle using pi*radius2 formula:
evl "pi*r pow 2" /v:pi=3.14159 /v:r=5
• determine the smallest negative value that can fit in a 16-bit signed WORD by
applying a bitwise NOT to zero, dividing the result half and adding one when specifying the int size as a WORD;
Note, you can use the same expression to see the smallest negative value of any int size type by simply
changing the /t argument:
str is a silly little tool to perform rudimentary string operations and dump the result to the
console. str allows standard string operations to be accessible from the command
line using a concise syntax. If writing a test program, script or even pasting into a text editor for
purposes of achieving a quick string result seems like overkill, that was my opinion too!
If you've ever needed to run strlen() against the data on the clipboard, maybe even lowercase all the
characters, strip-out the non-printables, and place the result back on the clipboard, this tool may be for you.
stripping, search and replace to ASCII hex dumps. Str currently supports C-style unescaping from
the command-line, get and retrieval from the Windows clipboard, reversing characters, strcmp, etc.
While single actions can be performed on single strings, the power of str is that actions can be chained
together allowing the result of one action to be the input into the next. In practice however, you may
only ever chain at most two actions if at all! With that said, let me explain the action-chaining:
The first string specified
occupies the first slot and identifies the target string. When an action is performed on the target,
the result overwrites the first. The next action operates on that result and overwrites it with
the new result. When multiple arguments are required, such as for the -sr (search and replace action),
the search and replace strings are additionally specified in order followed by the -sr action.
The resulting string is then written to slot #1 and slot's #2 and #3 are cleared for the next operation.
It then makes sense that the arguments are order-specific, so options that you want to take effect at the time
the action is taken must be specified prior to the action. This is why all options can be turned on and off
since you might want some options enabled only at certain points in the chain.
The final string is written upon termination of the program in verbose mode (default) or the string may
be manually output at any step in the chain of actions. In this example, we want to uppercase an arbitrary string,
search and replace all of the spaces to "_" underscore characters, then base64 encode the result. We'll
use the command-line sequence below and specify -out actions in between so you can see the result of each stage of the chain:
str "the wave of the future is now" -uc -out " " _ -sr -out -b64e
"THE WAVE OF THE FUTURE IS NOW" (strlen=29)
We can replace the clipboard's contents in-place - converting backslash-escaped quotes to normal quotes:
NOTE: In accordance with cmd.exe's escapement rules, literal backslashes and quotes must be properly escaped so they are properly passed to the tool
str -g \\\" \" -sr -s
In another example we can see the hex representation of a string with:
IMPORTANT: To specify a string that is or begins with the switch character "-", you must escape the command line argument
with a hyphen-frontslash sequence "-/" (without quotes) prior to the string parameter. This lets the program know the
argument that follows IS NOT a switch. For example, to pull a
string from the Windows clipboard, remove all hyphens, then store the result back on the clipboard, you'd use the -sr (search and replace) action like this:
str -g -/ - "" -sr -s //if the clipboard started with "one-two-three-four-five---->ten"
// it would end up with "onetwothreefourfive>ten"
Do you have a tool/command that only operates on one file at a time but you need to execute the command on multiple files?
The forevery tool searches for all files and/or directories from an input mask and executes your command
for each entry matched. This program is basically a Windows equivalent to the linux find | xargs
combination but with extra features. Features include recursive directory searching, output to stderr while command-output
goes to stdout, optional quoting when building final command-lines, optional arguments to custom commands and
multiple verbosity levels.
Admittedly, you can perform this tool's basic functionality using the Windows cmd.exe Shell via the for command,
but the for syntax is so convoluted I could never memorize it!
(whether or not to use parens, the mask being separate from the start path, etc.)
NOTE: Shell commands (e.g. dir, echo, etc.) won't work directly with the command (-c) option since they aren't executable files.
As a workaround, you may place these commands in a batch file and the batch file may be referenced with -c; just make sure you include the .bat or .cmd extension.
Back in the day before I had more advanced tools, I would use Explorer's properties dialog to look at version
resource members embedded within executables since it was quick an dirty. However this was a pain from the
command line because I had to CD to the directory, run "explorer .", find and select the file from within the listing and
finally right-click, selecting "Properties". This is one of those things that seemed like it should be possible
to launch from the command line, but Microsoft never exposed an Explorer command-line option to do this that I
was aware of.
This tiny 2048-byte executable implements the COM interface necessary to launch Explorer's property-sheet dialog
from the convenience of the command line. Because one of the goals of this project was to make the tiniest EXE possible
while still being useful, you won't find any anything wasting space like a version resource block or
command-line help! :)
Shelljmp is a Win32 x86 host process to run a fragment of raw binary code. In the security industry, raw binary
code originating from malware (commonly refered to as “shellcode” or otherwise a
“payload”) needs to be studied, dissected, and sometimes executed. To save time in writing a custom
harness for a chunk of code you may want to run, or if you have the binary representation of a complete function
and you'd like to test it from the command line or batch script with various different arguments, shelljmp can
The code you execute should generally consist of instructions that are position-independent. Malware payloads
generally contain position independent code as it is hard to guarantee anything at a particular offset on
different machines running different operating systems, especially with technologies such as ASLR (Address Space
Layout Randomization) present in Windows Vista and greater.
Shelljmp additionally allows the passing custom arguments on the stack to your code or function if desired.
Supported stack arguments include DWORD values, DWORD pointers (to initialized values), file-initialized buffers,
empty buffers, and zero-terminated strings (wide and ansi) - all of which can be specified on the command line.
Your code doesn't need to return back (RET) to the hosting process, but if it does and the stack isn't trashed
(too bad), shelljmp can provide register details and the contents of any buffers passed to the code that were
subsequently modified and return the contents of the EAX register through the process' exit code (accessible
through ERRORLEVEL for scripting purposes). Shelljmp can also correctly handle code that cleans up the stack
itself or relies on the caller, without needing to know the calling convention (if any) by means of backing up
ESP. If you want an automatic breakpoint at the beginning of your code when using a debugger, shelljmp can
insert an "INT 3" instruction for you. A structured exception handling frame is established just prior to the
execution of the code, and will report the details of any unhandled exceptions that occur.
The idea for this tool came from studying the
2015 Flare-On Challenge #2. I wanted the ability to execute a function in
isolation from the command line to facilitate a brute-force attack.
As there were no built-in hashing command line utilities in Windows at the time this was written, I found having
such a utility was useful. This utility can calculate an MD5, SHA1, SHA256 or CRC32 hash from a file, standard
input or string specified directly from the command line. I'm also making the linux version of this utility
available, although you might wonder why since most Linux distros come with the md5sum, sha1sum and sha256sum
utilities. For starters, this was one of my first projects I started with my new cross platform library and,
ideally any non-Windows specific applications will have a linux counterpart. Secondly, for hash calculations
where no default linux program is usually installed such as CRC32, or newly-added algorithms in the future.
Since the XOR operation is commonly used as an encryption/obfuscation technique in many applications, I found
having a utility that XOR'ed a repeating (or non-repeating) key to a chunk of data was useful enough to warrant
a dedicated tool. If you have a high-entropy key containing truly random data such as samples of radioactive
decay, XOR can be used as encryption that is theoretically unbreakable, when applied properly. Known as the
one-time pad, this type of encryption is generally not used due to the impractical nature of protecting and
destroying such a key. That does not stop XOR from being a common obfuscation/deobfuscation operation by itself or
coexisting with additional encryption. XOR is popular since it is a fast, simple bitwise operation supported by
all processors as it is one of the base logic gates in electrionic components.
In reverse engineering, it is not uncommon to come across a chunk of data that has been
obfuscated with a single repeating byte or series of bytes that you must XOR. Rather than writing a custom
program to perform such a simple operation, this tool might prove useful in your toolset.
InjectDLL is a Windows command line tool to inject DLLs into other processes. I wrote this back in the day when I
wanted to force a process (cmd.exe in my case) to update its environment variables. However, DLL injection has many other fun
uses for those in the security industry.
NOTE: Included with the
download is updateenv.dll, a DLL I also wrote that causes a process to update its environment.
It is not a dependency for InjectDLL, so you can delete it if you don't need it.
Otherwise, simply inject it into any running process and its DllMain() does all the work.
This tool supports DLL injection, DLL ejection (unload a DLL), and a method to invoke DLL exports (functions) in a
process (however I never added support for passing anything other than 1 argument). Any of these 3 actions can be combined or run individually. Each action can be programmed with delays,
and elevation to the SE_DEBUG_NAME privilege (/elevate). Processes may be specified by system id, name or may be launched directly
in suspended or running mode.
There are many methods of DLL injection, but the type chosen for this utility was believed to be the safest,
most robust method for a general purpose tool. InjectDLL uses a CreateRemoteThread/LoadLibrary technique
relying on the fact that all windows processes load kernel32.dll at the same linear address as every other
process. This is even the case with ASLR enabled because the system DLL addresses are randomized at boot-time
and not between the injector and injectee processes. Because LoadLibrary (also a kernel32 function with a predictable linear address) has the same calling
convention and argument count as that required by CreateRemoteThread, you can simply invoke LoadLibrary on behalf
of any target process you wish.
For DLLs you will be injecting, you should follow some basic guidelines provided here.
When a DLL executes, DllMain is where most people would put their code to be executed in the target process and
many times you can get away with it depending on the API calls you need to make. However DllMain is not always
the best place for your code as you may end up DeadLocking the process you are injecting. Once your DLL is
injected, you may need to call a separate function in your DLL after LoadLibrary is finished to do the actual
work you need. You can't rely 100% on your DLL being loaded at the same linear address as the preferred base
address specified in the DLL image in all processes. Windows may rebase your DLL depending on what other
DLL modules have been loaded. InjectDLL employs another technique to call your custom DLL exported functions
once loaded in the target process' address space. Using the /invoke and /invokeparam options, InjectDLL will copy
the binary instructions needed to dynamically locate the real address of the custom function (via GetProcAddress)
into the remote process and execute this code with CreateRemoteThread. InjectDLL will also report return value of
the custom function.
GetHTTP is a command line tool that is useful for debugging HTTP and/or HTTPS server responses, similar to the linux tool "curl".
The tool was rewritten to use OpenSSL for HTTPS support and certificate debugging such as showing the complete certificate chain.
Request headers can be fully customized and all HTTP transaction headers can be displayed. An URL download option is also available.
Base64 is a simple tool used to encode or decode data in the Base64 scheme. Output may be send to
the console or to a file. Input may be speicifed on the command line or as an input file.
64 is the smallest power of two base that can represent printable ASCII characters including the digits,
uppercase and lowercase letters. This scheme was invented in the early days of the internet as a way to encode
and transport binary data (like file attachments) over text-only protocols such as POP3 and SMTP for e-mail.
This utility kills processes by name or by PID, like many other similar utilities, but this tool has a unique
list-mode feature. The /l option will list active processes by matched substring (matching not
only the name, but the full command line used to start the process). More importantly, the processes in list
mode are sorted descending by aggregate time running in system. This means the last processes you see listed will be the
most recently run. This makes malware or other recently-run programs easy to spot quickly.
Supports 32 and 64 bit processes; when run under 64-bit versions of Windows, the "bitness" of each process is indicated.
NOTE: This program requires administrator rights to kill or retrieve information for system processes.
This tool wipes files in accordance to DoD 5220.22-M, which may not the most forensically secure method in modern days
but certainly offers some security beyond a simple file-delete.
Note the security of this tool relies on the underlying filesystem mapping the overwrite-data to the same physical location as the original.
This is often the case unless the file is on a journalling or transactional filesystem. The method used is:
-overwrite all data with bit pattern 10's (byte pattern 0xAA)
-overwrite all data with bit pattern 01's (byte pattern 0x55)
-overwrite all data with random bits and verify
Finally, the file is renamed, then deleted.
Somwhat of a sample GUI app to parse and display hierarchical tag files (i.e. HTML, XML) in a SysTreeView32 control.
This app uses a homegrown parser allowing the correct interpretation of the badly-formatted HTML (less strict about formation of
attributes and closing tags) and the more strict XML.