Sunday, July 15, 2012

Scripts, Tools and the IDT.

So from my last post I had a few people reach out to me about fixing up dumped modules. Unfortunately, I subscribe heavily to the NIH attitude and ended up writing my own quick python module using the pefile module (note you can pip install pefile as well). All my script really does is set the PointerToRawData to the VirutalAddress value and writes out the changes to a new file (prefixed by new_).


import os
import sys
import glob
import argparse
import pefile


def dump_directory(path):
    for filename in glob.glob(path+os.sep+"*.sys"):
        dump_file(filename)

def dump_file(filename):
        print "Fixing up: %s"%filename
        try:
            pe = pefile.PE(filename)
            for section in pe.sections:
                print "Updating: %s PointerToRawData 0x%x to"\
                      " VirtualAddress: 0x%x"%(section.Name,
                                               section.VirtualAddress,
                                               section.PointerToRawData)
                # Update the section.PointerToRawData to be equal to
                # the VirtualAddress/
                section.PointerToRawData = section.VirtualAddress
            # write the changes
            pe.write(filename='new_'+filename[filename.rindex(os.sep)+1:]) 
            print "new_%s written to disk."%filename
        except pefile.PEFormatError, msg:
            print "Error %s file is not a PE file? msg: %s"%(filename, msg)
            
def main():
    parser = argparse.ArgumentParser(
        description='Fixes up the VirtualAddress of drivers dumped from memory.')
    parser.add_argument('--directory',
                        '-d',
                        action='store',
                        help='Directory with *.sys driver files.')
    parser.add_argument('--file',
                        '-f',
                        action='store',
                        help='Single file to fix up.')
    args = parser.parse_args()
    
    if args.directory is not None:
        dump_directory(args.directory)
    elif args.file is not None:
        dump_file(args.file)
    else:
        parser.print_help()
        
if __name__ == '__main__':
    main()

If you are curious about the recommendations I got. @skier_t recommended his tool rreat. The tool from @iMHLv2 was a pretty interesting looking framework/toolset for memory analysis of malware called volatility. I'll definitely play around with their tools more, but for now i'mma write my own junk :>.

So besides fixing up the image once dumped, I've also been working on looking at the various functions of the driver after it's loaded. I came across two very curious blocks of code. At first, IDA didn't flag them as being functions.
IDA listing just the code as is
But by selecting the start of the function and hitting P, IDA will define it for us.
woo, we have functions! :>
You'll notice in the above code the two comments I added. If you are not familiar with the SIDT and LIDT x86 operands, well they are for storing and loading the Interrupt Descriptor Table. I suggest reading materials (both from phrack) if you want to learn more about the IDT and how they are used for hooking. "Handling Interrupt Descriptor Table for fun and profit" article by kad for a deep technical dive into the IDT and the IDT hooking article by mxatone and ivanlef0u for a more 'windowsy' look.

Anyways, it appears that the above disassembly stores the IDT values in memory, does a modification (*notice the mov eax, dword_EE01033C...) then reloads the modified version back into the idt register. When doing run-time analysis I didn't see anything at that address except nulls, so i'm not really sure what the point of it is yet. Keep in mind i'm pretty new to this whole IDT business as well. I tried setting a breakpoint on the two functions which modify the IDT and I can't seem them being called at any point yet. I think I will need to do more work in this area to get a better understanding of it all.

One thing I did notice however is that I'd like an automated way of inspecting the various interrupt entries. If you love python and you use windbg, you should really take a look at pykd, it's pretty damn awesome. After a few minutes of poking through it's samples I found an old (non-working) script which read the IDT entries. I had to rewrite most of the sample script to run in the latest version, but it works now. It's pretty simple in that it just loops through the IDT entries, extracts the dispatch address, dispatch code and the symbol name and displays it. Here's the code:

from pykd import *
import sys

if __name__ == "__main__":
    if isKernelDebugging():
        dprintln( "check interrupt handlers...\n" ) 
        idtr = reg( "idtr" )
        nt = loadModule( "nt" )
        ErrorCount = 0
        dprintln("idtr is: %08x"%idtr)
        for i in xrange(0, 255):
            idtEntry = nt.typedVar("_KIDTENTRY", idtr+i*8)
            if idtEntry.Selector == 8:
                offset = ( idtEntry.ExtendedOffset * 0x10000 ) + idtEntry.Offset
                InterruptHandler = offset
                kinterrupt = nt.typedVar("_KINTERRUPT",InterruptHandler)
                if InterruptHandler != 0x00:
                    try:
                        dprintln("IDT [%02x] InterruptHandler: 0x%08x "\
                                 "DispatchAddress: 0x%08x "\
                                 "KINTERRUPT.DispatchCode 0x%08x"\
                                 " (symbol: %s)"%(i,InterruptHandler,
                                                  kinterrupt.DispatchAddress,
                                                  kinterrupt.DispatchCode,
                                                  findSymbol(InterruptHandler)))
                    except Exception, msg:
                        dprintln("IDT [%02x] empty"%i)
    else:
        dprintln( "we are not debugging the kernel..." )

And here's some output from it being run from WinDBG:
idt_dump.py pykd script, dumpin' some interrupt tables baby!
I think I'm going to become very well acquainted with pykd, because well, doing this kind of stuff manually kinda sucks.

1 comment: