Uncategorized

While the current version of OllyDbg (2.01e as of writing) is still in development, it now has full plugin API support.
Compiling a plugin requires linking with an ollydbg.lib file describing the imports defined in the plugin.h header file.
For Borland compilers this is a straight forward procedure and the LIB file should be fully compatible with the plugin declarations.
For MSVC compilers (Visual Studio, Visual C++) the situation is a bit more complicated.
I will describe what worked for me and how to go about producing an MSVC compatible ollydbg.lib file.

For MSVC you first need to create a DEF file in the proper format and then create the LIB file with the LIB /DEF: command. In OllyDbg 2.x the plugin exports are a mixture of _cdecl and _stdcall.

Cdecl functions can be declared in the DEF file as FunctionName @Ordinal.

Stdcall functions must be declared in the DEF file in the decorated format FunctionName@<number of parameter bytes> @Ordinal.

The attachment contains an MSVC suitable ollydbg.lib file (tested in VC6++ and VS 2010), the DEF file, and the python script I wrote to extract the number of parameter bytes from each _stdcall function in plugin.h and create the required DEF file format. Since the plugin.h function declarations may change in future versions as OllyDbg 2.x is developed, the python script may come in useful to create an updated ollydbg.lib file.

Stop reading now if you don't need the details.

For Borland compilers one can simply use IMPLIB.exe to create a LIB file in the following manner, and you're ready to go.

implib ollydbg.lib ollydbg.exe

For MSVC, well, here's the MS definition of the steps:

Given a .DLL (in our case OLLYDBG.EXE) with functions exported via a C interface, you can create an import library by following these steps:

Use DUMPBIN /EXPORTS <.DLL file name> to obtain the list of exported symbols for the .DLL in question. The symbols appear in the "name" column of the table whose headings are "ordinal hint name."

Create a .DEF file that contains an EXPORTS section with the names of the functions listed in the "name" column of the DUMPBIN output.

For _cdecl functions, the symbol appears just as it would when used in the calling program. Just place this symbol in the EXPORTS section of the .DEF file.

Use LIB /DEF:<.DEF file name> to generate the import library and exports file. The base name of the import library will be the base name of the .DEF file. Use /OUT: to control the output library name.

OllyDbg 2.x now has a mixture of _stdcall and _cdecl exported functions in plugin.h. If you try to link with an ollydbg.lib produced in the previous way you'll get compiler errors such as this when trying to compile the bookmark.c plugin example:

I guess the rest of the details are in the python script below. For those who are interested in that kind of thing you might read it easier from the zip file copy with syntax highlighting.

There is one further modification of plugin.h for MSVC, two of the exported functions (Heapsort/Heapsortex) use the Borland _USERENTRY type in their parameter list which generates a compiler error. The solution is provided by the Borland defs.h file:

32-bit Windows Vista and later use a feature known as Dynamic Kernel Address Space. To quote from a technical article - the Memory Manager dynamically manages the kernel's address space, allocating and deallocating space to various uses to meet the needs of the system. As a result, the amount of virtual memory being used for internal components, device drivers, the file system cache, kernel stacks, system PTE's, per-session code data structures as well as paged and nonpaged pool memory will grow and shrink based on system activity.

The key to keeping track of all this dynamic memory lies in the unexported pointer nt!_MiSystemVaType, a mapped array of byte values that describes both the type of memory allocation, and by virtue of the indexed position within the array, the location and size of the memory block. Each time there is a new memory allocation, the MiSystemVaType array is updated.

In this code project I will try to show how to use MiSystemVaType to navigate the dynamic kernel address space to get a complete mapping of the various allocation types. In addition, I'll give an example of how to use it to find and identify loaded drivers, as well as discuss how it might be used to conduct efficient memory pool searches.

nt!_MiSystemVaType is a pointer to an array of byte values of enum type MI_SYSTEM_VA_TYPE. Each byte in the array describes a single Large Page and maps, in sequential order, the entire upper 2GB of logical address space from 0x80000000 (MmSystemRangeStart) - 0xFFFFFFFF. The size of the byte array is either 0x400 when PAE is enabled, where the default size of a Large Page is 2MB, or 0x200 in non-PAE mode, which uses a Large Page size of 4MB.

The Physical Address Extension (PAE) processor feature enables use of 64-bit page table entries for physical addresses that are wider than 32 bits. If PAE is enabled, the size of page table entries (PTEs) are increased from 32 to 64 bits (4 to 8 bytes). Consequently, the size of a Large Page is reduced from 4MB to 2MB in PAE mode. One can determine the size of the PTE data structure, nt!_MMPTE, (and hence if PAE is enabled or not) with the command:

Code:

kd> dt -v nt!_MMPTE
struct _MMPTE, 1 elements, 0x8 bytes

To determine if PAE is enabled programmatically we can read the ProcessorFeatures field of KUSER_SHARED_DATA, a shared memory structure mapped to all processes and located at 0x7FFE0000 in usermode. This is equivalent to what the IsProcessorFeaturePresent API does.

KUSER_SHARED_DATA is duplicated at 0xFFDF0000 in kernelmode. Fortunately ntddk.h gives us a handy macro with which to work with it. The snippet below will give us (by inference) the size of nt!_MMPTE, from which we can derive the size of a large page and the size of the MiSystemVaType array.

We need to programmatically find the offset to nt!_MiSystemVaType. Since this is an unexported pointer we'll have to parse a known kernel function which makes use of the variable. Uh Oh. Production code need not apply . Oh well, this is an RCE forum, right? At least that's better than using a hard-coded value, not as good as using symbols.

Rather than using a classic byte-pattern search that is often used to find unexported variables, I made use of a clever idea Zairon mentioned to me, that of looking for cross references between code and data sections in order to pick up instances of data variable usage. In essence, derive XREFS similar to IDA.

I really like Zairon's idea of using XREF analysis over a byte-pattern search method because it's simple, highly adaptable, and is less susceptible to changing byte patterns between different OS kernel versions.

The function I chose to parse for the offset of MiSystemVaType was the exported MmIsNonPagedSystemAddressValidprocedure. The simple algorithm logic I used was: "Scan for the first data XREF to the section called '.data''"

See the source code for the specific algorithm I implemented, plus a few suggestions for creating a more rigorous algorithm if desired, such as using a length disassembly engine (LDE) to avoid the possibility a false XREF could occur across instructions.

The simple logic above should be valid for all current 32-bit nt* kernel versions in Windows 7 / Vista / Server 2008. Even better, MmIsNonPagedSystemAddressValid has been deemed to be obsolete and is exported to support existing drivers only, so it's more unlikely to change anytime soon.

IceProbe is a utility that allows live tracing and analysis of SoftIce commands using the full capability of SoftIce itself. It is a tool strictly for code exploration, designed to be able to trace running Softice code in order to augment IDA analysis. It is debugging a debugger, in order to answer the question "How does Softice work?"

There is much that can be learned about system internals by studying Softice code. This utility will give a live hands-on method of tracing and exploring the code for the first time. It can also act as a GUI front-end for Softice, as bizarre as that might sound.

Concept:

Any SoftIce command typed into the command line window is stored in a global string buffer. The command string consists of the command name and any arguments. The buffer is passed to the individual function where it is parsed, and the command is executed.

We can selectively replace instances of this global buffer pointer with one of our own and call Softice commands directly from a GUI interface. An (optional) embedded breakpoint which will pop-up Softice is written into our driver code immediately before calling the command, which allows us to start tracing the Softice command.

While live tracing you have full use of all other Softice commands at your disposal, including the ability to set breakpoints in Softice code itself. There is an additional modification which will force the "Idt" command to expose the addresses of the Softice IDT hooks so you can also locate and analyse those various handlers as well.

Usage:

Iceprobe is simple to use, select Initialize/Reinitialize from the menu and the driver will return a listview listing of all the Softice commands and their addresses. A log window will monitor the driver. Double click on one of the entries and you will be presented with a dialog box to add any usual arguments to the command. When you select OK, Softice will popup at the start of the command, and you're ready to start tracing with F8.

Options:

Disable Manual Tracing Mode
We embed an INT 3 in our code and programatically enable "I3HERE DRV" in order to make Softice popup at the start of each command. Set this option if you don't want Softice to popup. The command will still be executed and output to the Softice window as normal.

Make "Idt" show real addresses
Expose the addresses of the Softice IDT hooks in the listing from the "Idt" command.

Disable extra Softice self address space checks
These are somewhat experimental patching of locations where Softice tests if an offset is within its own address space. Specifically, they occur in the "Search" command, in a portion of breakpoint handling code where MSR LastBranch and MSR LastException information is printed, and in the Int0D handler. You may or may not see any effect.

Include Undocumented Commands
There is only one command here, BPTE - Breakpoint on Thread Execution was its probable purpose. Code exists to be traced, but the command appears non-functional and was never documented. If selected, the BPTE command will be added to the listview where you can run it with test arguments.

Increase Recursive Disassembly Level (Calls nested 4 deep)
We must find every occurence of the Softice global command buffer used in each command, in order to replace them with a pointer to our own buffer. A recursive disassembly is therefore needed in order to trace through all nested subcalls within a command.

A simple recursive method is used - trace each call until a RET/RETN is reached. It was found that this was sufficient with a default value of 3 nested levels of disassembly to find all instances of the global buffer for each command. A value of 4 will find further instances, but most seem to be false positives and not part of the command execution path. This is due to how Softice code is laid out (code chunks, use of jmps, etc), and the simplified method of recursive disassembly.

Output Recursive Call Pattern for Xref with IDA (DbgPrint) - Shows the nested recursive disassembly of all Calls and SubCalls for a command, as determined by the Increase Recursive Disassembly Level option. The pattern can be matched to what you find during the IDA analysis. It makes it easier to keep track of where you are while jumping back and forth between IDA and the Softice/Softice tracing of a command.

Output Developmental Notes (DbgPrint)
Prints a bunch of output about the Softice driver and internal offsets, mostly used during development.

All these options can be "toggled" on or off by setting them and selecting Initialize/Reinitialize from the menu again.

IDA Analysis:

This tool is meant to work side by side with an IDA analysis of the ntice.sys driver. Iceprobe should run without problem with Driver Studio 2.7, 3.1 or 3.2. It is designed to work with the final official DS3.2.1 patch version of the Softice driver which was publically available on their ftp site. This offical patch is available here:

Briefly, Softice keeps its command names and offsets in indexed tables. The very first step is to run the following idc script. The CmdTable offsets are for the DS3.2.1 patch version. If you happen to be using a different version change the offsets accordingly, the above thread describes how to find them.

Your IDA disassembly will now identify all of the Softice commands by name. I would then strongly suggest to look at the IDB analysis and Softice headers produced by The Owl while developing Icedump, and use them to start naming some of the internal variables already defined. The article by +Spath is old but indispensable as well.

Now you can start filling in the blanks in your IDA analysis with live tracing of any command using Iceprobe. The ideal situation is to have Softice running under VMWare and have IDA on your desktop. Iceprobe is stable, but you ARE live tracing Softice, so running under VMWare, etc. is desirable.

To further enhance the experience, you can create progressive NMS symbol files of your IDA analysis and have Softice load its own symbol file into itself using its Symbol Loader. Produce the symbol file with Mostek's Ida2Sice

Any command can be traced, while at the same time being able to issue any other Softice command. However, if you execute the same command as you are tracing it will only rerun it with the same parameters that were initially set in the GUI, since we've overwritten the global buffer for that command with our own pointer.

For tracing the BPX command, you can set a breakpoint with a double click, or use BPM. You can even trace the HBOOT command and watch your VM reboot! (I put a protection in the GUI so you can't inadvertently click the HBOOT command).

WTF is this?:

I wrote this a few years back, partly as a way of tracing Softice code, but mostly as a way of exploring system internals and how Softice made use of various system structures, variables, hardware and registers. Sort of kernel spelunking through the eyes of a ring 0 debugger.

IceProbe was first integrated as a KDExtension driver to take advantage of the internal Softice disassembly engine available through the WINDBG_EXTENSION_APIS interface. Further Softice internal details can be found in the thread

This version uses a standalone driver and the disassembler is an integration of a module I created from the Ndisasm NASM disassembler for use in drivers. The disasm module is also available separately here:

There is a small RDTSC timing check at the start. If the difference between 2 RDTSC calls is greater than 0x100 then it's determined that you're in a VM or single step tracing the code (or have only 4K of memory maybe!). In that case the virus doesn't infect but instead returns to the default address of 0x40101D and only tries to connect to the irc server.

Doing a little test I found that 2 RDTSC calls would return a difference of between 1000-1500 ticks under VMWare, but only 50 ticks under a real system. Not a sophisticated VM test, but it seems to work.

There is also apparently a Redpill type VM check which I found crashes under VMWare.

The virus is Win9x/Me compatible. If the OS is Win2K or greater it hooks NtCreateFile / NtOpenFile. If Win9x, it hooks VWIN32_Int21Dispatch and monitors for the MS-DOS LFN (long file name) service 0x716C, which is used for open/create. The Win9x related stuff is "old school" and has been documented before. If interested see:

The virus copies itself to the last section of a file it infects, but the initial seed value for the Xor decryption routine is different for every infected file (uses an RDTSC randomization), showing its polymorphic nature. After copying itself from running memory into the file, the main Decrypt function is called once again, but this time it's encrypting itself in the file, the XOR routine being reversible.

After determining the system offsets for the Nt calls it hooks, the virus creates a section, maps itself into it, then immediately continues execution from this high memory mapping address. All further execution is run from this memory mapped image. This makes it a little more difficult when live tracing and comparing addresses to the disassembly, but the jump to high memory makes a great breakpoint when you get deeper into the live analysis.

One interesting (though not original) thing the virus does is how it disables Windows File Protection (WFP) so it can infect any file in any directory without being subverted.

Immediately after beginning execution in the high memory mapped section that I just mentioned, it calls NtAdjustPrivilegesToken with SeDebugPrivilege privilege so it is able to call CreateRemoteThread() in the Winlogon process.

Using a CreateToolhelp32Snapshot/Process32Next loop, it scans for the 4th process returned, or Winlogon. Then it hooks the 4 Nt calls within the winlogon address context by overwriting the Service Id of the relevant Zw ntdll KiFastSystemCall with a detour to its own hook routine (which is mapped into winlogon memory space).

Once this is done it calls CreateRemoteThread followed by a short Sleep routine to let the remote thread execute within winlogon context. After the Sleep nap, the CreateToolhelp32Snapshot loop continues and hooks the 4 Nt calls in every other active process.

The aforementioned CreateRemoteThread winlogon thread does a number of things besides disabling WFP. It sets up the Win9x/Me VxDCall file hook if applicable, writes the irc sites IP address to the Windows hosts file, accesses the registry, creates some Events, creates a couple more threads, one of which maps out ntoskrnl and the KeServiceDescriptorTable, tries to connect to the internet, etc.

WFP is disabled by patching in a call to ExitThread immediately after a NtWaitForMultipleObjects call in an sfc.os.dll function called SfcWatchProtectedDirectoriesWorkerThread. The function name pretty much describes what it does, it's a worker thread which continually monitors for file changes from a protected dll list. After patching SfcWatchProtectedDirectoriesWorkerThread the virus calls SfcTerminateWatcherThread. (IDA + symbols is a wonderful thing )

And finally, and congratulations if you're still reading this, there is a mysterious Int 0x2C interrupt call at the start of the program. This call is the main reason this virus held my interest for so long and is ultimately the genesis of this article. I was hoping a full analysis of the program would point out its purpose. Alas, I'm still as clueless as when I started.

Therefore I open up to speculation (or reason or knowledge), the true function of this Int2C call, if there is one, to you Gentle Reader (may Asimov forgive me).

Here is the start of the code once again showing the important bits. I've tried to outline the "normal" path of infection by highlighting in Blue. Under any condition that I can see, Int2C will always return STATUS_NO_EVENT_PAIR, so that particular path of execution is fixed.

The question is then. is this just quirky code to confuse us, or can an Int2C call have a real purpose in Ring3 under certain conditions?

I had started live tracing this piece of malware when I realized it was a prime candidate for some IDA idc scripting. There are a few things about the program which makes static analysis difficult.

It's encrypted. It's a rather simple encryption however which is easily scripted out.

The code is primarily executed in the .data section and there are many inline character strings and non-standard code instructions which prevent IDA from getting an accurate disassembly.

Variable pointers and import calls are referenced as EBP offsets, so IDA can't recognize absolute addresses to create the proper Xrefs, autogeneration and all the other wonderful analysis it normally performs.

Imports are determined dynamically through GetProcAddress, so until we define them IDA can't recognize them.

Let's address each of these problems through a bit of idc scripting and static analysis to supplement our live tracing. In Part 2 I'll mention a few points about the viral code itself.

I've included several files in the attachment, the idc scripts, a header file prototyping some functions and structures not included in the internal IDA definitions. As well there is as an IDB file in IDA 4.9 freeware version format which is fully commented with all functions and variables defined (or at least named, this is meant to be a "working" disassembly for further analysis, not necessarily a definitive treatise). The IDB file can be opened in any IDA version 4.9 and above.

Also included is of course the virus, or else what fun would this be? The win32_virut.exe file has been renamed with a .VXE extension and zip password protected with the password malware. It is quite an infectious file, but it readily detects a normal virtual machine sandbox and won't infect under those conditions. You actually have to force the code to decrypt its payload under a VM. Also, the remote site it tries to connect to has been closed for Terms Of Agreement violations (ya think?), so no live connection is ever made.

You may find it easier to read the idc scripts by downloading the originals or by reading this post in the Blogs Forum (follow the link under 'Post or View Comments' at the bottom of this blog).

This virus is a polymorphic, memory-resident file-infector, with backdoor behaviour. Once executed, it injects itself into WINLOGON, creates a new thread in that process, and passes the execution control to the host file.

It also hooks the following functions in each running process (in NTDLL module):
NtCreateFile, NtOpenFile, NtCreateProcess, NtCreateProcessEx
so that every time an infected process calls one of these functions, the execution is passed to the virus, which infects the accessed file, and then returns the control to the original function.

It infects EXE and SCR files, using different infection techniques:
Appending to the last section of the victim, and setting the Entry Point directly to the viral code. (our variant)

The virus is able to avoid emulators and virtual machines. To ensure there's only one instance of it running in the system, it creates an event with one of the following names:
VT_3, VT_4, VevT, Vx_4

It tries to connect to some IRC server, and join a certain channel. Once it joins the channel, it waits for commands that instruct it to download several files from Internet, and then execute them. The IRC server can be:
proxim.ntkrnlpa.info (our variant, site no longer active)

Much of what is written above can be figured out by live tracing the malware and eventually letting it infect our sandbox. But let's see what damage we can do to it before it does damage to us..

Step 1: Decrypt

Here is the Entry Point of the virus, which is in the .data section:

Code:

:00403200 cld
:00403201 call loc_40322E
:00403206 push ebx

Take note that the return address of the Call pushed onto the stack will be 0x403206. Trace into the Call and after a bit of preliminary code we reach here:

A simple XOR loop decryption where the xor value is modified on each iteration by the XCHG instruction. ECX is a counter decremented by the LOOP opcode. The initial decryption seed value is the db 8Eh, 0C8h we discovered above.

Armed with this small bit of analysis we can create the following idc script for decrypting.

After running this script you MUST go through the decrypted section and manually resolve the embedded string pointers with the IDA A(scii) command and any unrecognized or incorrect disassembly with the C(ode) command. This is a necessary step for the subsequent IDC scripts to work properly!

You will find a lot of things like the following, which you need to make sure is correctly resolved. By itself IDA won't properly disassemble the code.

Notice the neat little trick in the above code of how the second parameter of GetProcAddress is automatically pushed onto the stack by effectively being the return address of Call loc_4032EA, which jumps over the string. This type of thing is repeated throughout the program.

Chances are you won't get every bit of disassembly and ascii string identified correctly the first time through a manual fixup, but after applying
...