Hex Blog
September 10th, 2009, 15:19
Writing boot code is useful for many reasons, whether you are:
In the beginning, we were using bochsdbg.exe to debug our code and little by little once we coded the "Bochs Disk Image loader" part we could debug the MBR with IDA and Bochs plugin.
Now you may be wondering: How can I use IDA Bochs plugin to debug my MBR?
For a quick answer, here are the needed steps:
In the following example, bochsrc loader will detect "c.img".
After finding the disk image file, the loader will simply create a new segment at 0x7C00 containing the first sector of that file only and then it selects the Bochs debugger (in Disk Image loader mode). Once the loader is finished you can press F9 and start debugging.
As simple as this sounds, this process is really limited:
If you don't have a Bochs image ready, please use the bximage.exe tool to create a disk image.
http://hexblog.com/ida_pro/pix/mbr_dskimg.jpg Preparing bochsrc file
Edit your bochsrc file and add the ata0 (generated by bximage tool) line to it, and finally run bochsdbg.exe to verify that you can run Bochs properly (outside of IDA).
http://hexblog.com/ida_pro/pix/mbr_testbochs.jpg
If you see the Bochs debugger prompt, you can press "c" to continue execution but Bochs will complain because our disk image is not bootable. (As a new disk image, It lacks the 55AA signature at the end of the first sector) Inserting the MBR into the disk image
For your convenience, we included a sample mbr.asm file ready for you to compile.
Loading bochsrc with IDA
As discussed previously, loading the bochsrc file into IDA is not enough (see above) so we need to write another script that acts like a loader:
What we did is simply extend the segment from 512 to 1024 (our sample MBR is 1024 bytes long) and load into IDA the rest of the MBR code from the compiled mbr.asm binary. Importing symbols into IDA
When we assemble mbr.asm, a map file will also be generated. We will write a simple parser to extract the addresses and names from the map file and copy them to IDA:
Now that we addressed all of the issues previously mentioned, let us glue everything with a batch file: [CODE]
The second time we run IDA with the "-rbochs" switch telling IDA to open the database and directly run the debugger.
You can still run IDA just once: "start idag -c -A -OIDAPython:mbr.py bochsrc" however you do not call Exit() and you turn off batch mode (with Batch()). http://hexblog.com/ida_pro/pix/mbr_final.jpg
And last but not least, how do you debug your MBR code? Please download the files from here ("http://hexblog.com/ida_pro/files/mbr_bochs.zip"). Comments and suggestions are welcome.
http://hexblog.com/2009/09/develop_your_master_boot_recor.html
While developing the IDA Bochs plugin at Hex-Rays, we had to write a small MBR and we needed a nice and fast way to compile and debug our code.
Developing your own operating system
Developing disk encryption systems
Experimenting and researchingOr even writing a bootkit
In the beginning, we were using bochsdbg.exe to debug our code and little by little once we coded the "Bochs Disk Image loader" part we could debug the MBR with IDA and Bochs plugin.
Now you may be wondering: How can I use IDA Bochs plugin to debug my MBR?
For a quick answer, here are the needed steps:
In case you did not know, bochsrc files (though they are text files) are handled by the bochsrc.ldw (IDA Loader). The loader parses the bochsrc file looking for the first "ata" keyword then it locates its "path" attribute.
Prepare a Bochs disk image
Prepare a bochsrc file
Insert your MBR into the disk image
Open bochsrc file with IDAStart debugging
In the following example, bochsrc loader will detect "c.img".
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
megs: 16
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="c.img", mode=flat, cylinders=20, heads=16, spt=63
boot: disk
...
After finding the disk image file, the loader will simply create a new segment at 0x7C00 containing the first sector of that file only and then it selects the Bochs debugger (in Disk Image loader mode). Once the loader is finished you can press F9 and start debugging.
As simple as this sounds, this process is really limited:
Fortunately, IDA Pro provides a rich API (with the SDK or scripting) that will allow us to tackle all these issues. Preparing a Bochs disk image
What if the MBR loads more code from different sectors? (MBR with 2 or more sectors of code)
What about symbol names?What if we want to customize and control the MBR loading process?
If you don't have a Bochs image ready, please use the bximage.exe tool to create a disk image.
http://hexblog.com/ida_pro/pix/mbr_dskimg.jpg Preparing bochsrc file
Edit your bochsrc file and add the ata0 (generated by bximage tool) line to it, and finally run bochsdbg.exe to verify that you can run Bochs properly (outside of IDA).
http://hexblog.com/ida_pro/pix/mbr_testbochs.jpg
If you see the Bochs debugger prompt, you can press "c" to continue execution but Bochs will complain because our disk image is not bootable. (As a new disk image, It lacks the 55AA signature at the end of the first sector) Inserting the MBR into the disk image
For your convenience, we included a sample mbr.asm file ready for you to compile.
nasmw -f bin mbr.asmTo insert the mbr into the disk image, we can write a small Python function:
def UpdateImage(imgfile, mbrfile):
"""
Write the MBR code into the disk image
"""
# open image file
f = open(imgfile, "r+b"
if not f:
print "Could not open image file!"
return False
# open MBR file
f2 = open(mbrfile, "rb"
if not f2:
print "Could not open mbr file!"
return False
# read whole MBR file
mbr = f2.read()
f2.close()
# update image file
f.write(mbr)
f.close()
return True
Loading bochsrc with IDA
As discussed previously, loading the bochsrc file into IDA is not enough (see above) so we need to write another script that acts like a loader:
def MbrLoader():
"""
This small routine loads the MBR into IDA
It acts as a custom file loader (written with a script)
"""
import idaapi;
import idc;
global SECTOR_SIZE, BOOT_START, BOOT_SIZE, BOOT_END, SECTOR2, MBRNAME
# wait till end of analysis
idc.Wait()
# adjust segment
idc.SetSegBounds(BOOT_START, BOOT_START, BOOT_START + BOOT_SIZE, idaapi.SEGMOD_KEEP)
# load the rest of the MBR
idc.loadfile(MBRNAME, SECTOR_SIZE, SECTOR2, SECTOR_SIZE)
# Make code
idc.AnalyzeArea(BOOT_START, BOOT_END)
What we did is simply extend the segment from 512 to 1024 (our sample MBR is 1024 bytes long) and load into IDA the rest of the MBR code from the compiled mbr.asm binary. Importing symbols into IDA
When we assemble mbr.asm, a map file will also be generated. We will write a simple parser to extract the addresses and names from the map file and copy them to IDA:
Putting it all together
def ParseMap(map_file):
"""
Opens and parses a map file
Returns a list of tuples (addr, addr_name) or an empty list on failure
"""
ret = []
f = open(map_file)
if not f:
return ret
# look for the beginning of symbols
for line in f:
if line.startswith("Real":
break
else:
return ret
# Prepare RE for the line of the following form:
# 7C1F 7C1F io_error
r = re.compile('\s*(\w+)\s*(\w+)\s*(\w*)')
for line in f:
m = r.match(line.strip())
if not m:
continue
ret.append((int(m.group(2), 16), m.group(3)))
return ret
def ApplySymbols():
"""
This function tries to apply the symbol names in the database
If it succeeds it prints how many symbol names were applied
"""
global MBRNAME
map_file = MBRNAME + ".map"
if not os.path.exists(map_file):
return
syms = ParseMap(map_file)
if not len(syms):
return
for sym in syms:
MakeNameEx(sym[0], sym[1], SN_CHECK|SN_NOWARN)
print "Applied %d symbol(s)" % len(syms)
Now that we addressed all of the issues previously mentioned, let us glue everything with a batch file: [CODE]
rem Assemble the MBR if exist mbr del mbr nasmw -f bin mbr.asm if not exist mbr goto end rem Update the image file python mbr.py update if not errorlevel 0 goto end rem Run IDA to load the file idaw -c -A -OIDAPython:mbr.py bochsrc rem database was not created if not exist bochsrc.idb goto end if exist mbr del mbr if exist mbr.map del mbr.map rem delete old database if exist mbr.idb del mbr.idb rem rename to mbr ren bochsrc.idb mbr.idb rem Start idag (without debugger) rem start idag mbr rem Start IDAG with debugger directly start idag -rbochs mbr echo Ready to debug with IDA Bochs :endIf you noticed, we run IDA twice: the first time we run it and pass our script name to IDAPython; the script will continue the custom loading process and symbol propagation for us.
The second time we run IDA with the "-rbochs" switch telling IDA to open the database and directly run the debugger.
You can still run IDA just once: "start idag -c -A -OIDAPython:mbr.py bochsrc" however you do not call Exit() and you turn off batch mode (with Batch()). http://hexblog.com/ida_pro/pix/mbr_final.jpg
And last but not least, how do you debug your MBR code? Please download the files from here ("http://hexblog.com/ida_pro/files/mbr_bochs.zip"). Comments and suggestions are welcome.
http://hexblog.com/2009/09/develop_your_master_boot_recor.html