Saturday, April 21, 2012

MBR Parser

With the increase in MBR infectors, I've decided to release a script I wrote that parses the MBR as well as hashes and disassembles the bootcode. I've found that MBR bootcode is pretty stable across systems of the same OS, so this script should allow you to quickly check for any discrepancies on a system.

You of course need Python and Distorm to use this script.

A shortened example output can be seen below:

$ python mbr_parser.py -f mbr.bin Disk signature: 96-80-96-80 Bootcode md5: 4ad444d4e7efce9485a94186c3f4b157 Bootcode Disassembly: 00000000: 33c0 XOR AX, AX 00000002: 8ed0 MOV SS, AX 00000004: bc007c MOV SP, 0x7c00 00000007: fb STI 00000008: 50 PUSH AX 00000009: 07 POP ES 0000000a: 50 PUSH AX 0000000b: 1f POP DS 0000000c: fc CLD 0000000d: 50 PUSH AX 0000000e: be007c MOV SI, 0x7c00 00000011: bf0006 MOV DI, 0x600 00000014: b90002 MOV CX, 0x200 00000017: f3a4 REP MOVSB 00000019: bf1e06 MOV DI, 0x61e 0000001c: 57 PUSH DI 0000001d: cb RETF 0000001e: b441 MOV AH, 0x41 00000020: b280 MOV DL, 0x80 00000022: bbaa55 MOV BX, 0x55aa 00000025: cd13 INT 0x13 00000027: 81fb55aa CMP BX, 0xaa55 0000002b: 7530 JNZ 0x5d 0000002d: f6c101 TEST CL, 0x1 00000030: 742b JZ 0x5d 00000032: be0008 MOV SI, 0x800 00000035: c7041000 MOV WORD [SI], 0x10 00000039: c744020600 MOV WORD [SI+0x2], 0x6 [snip] 000001b2: 0000 ADD [BX+SI], AL 000001b4: 002c ADD [SI], CH 000001b6: 44 INC SP 000001b7: 63 DB 0x63 ===== Partition Table #1 ===== Boot flag: 0x80 (Bootable) Partition type: 0x7 (NTFS) Starting Sector (LBA): 0x3f (63) Starting CHS: Cylinder: 0 Head: 1 Sector: 1 Ending CHS: Cylinder: 520 Head: 254 Sector: 63 Size in sectors: 0x7fb68a (8369802) ===== Partition Table #2 ===== Boot flag: 0x0 Partition type: 0x0 (Empty) Starting Sector (LBA): 0x0 (0) Starting CHS: Cylinder: 0 Head: 0 Sector: 0 Ending CHS: Cylinder: 0 Head: 0 Sector: 0 Size in sectors: 0x0 (0) ===== Partition Table #3 ===== Boot flag: 0x0 Partition type: 0x0 (Empty) Starting Sector (LBA): 0x0 (0) Starting CHS: Cylinder: 0 Head: 0 Sector: 0 Ending CHS: Cylinder: 0 Head: 0 Sector: 0 Size in sectors: 0x0 (0) ===== Partition Table #4 ===== Boot flag: 0x0 Partition type: 0x0 (Empty) Starting Sector (LBA): 0x0 (0) Starting CHS: Cylinder: 0 Head: 0 Sector: 0 Ending CHS: Cylinder: 0 Head: 0 Sector: 0 Size in sectors: 0x0 (0)

Update: Fixed output to 16bit assembly. Thanks for the feedback!

The script can be found here.

19 comments:

cdtdelta said...

Do you have any examples of what you might see in the output if the MBR is infected?

Anonymous said...

cylinder

Mario said...

Found this on Twitter. I have a question: why is the disassembly you're showing a 32 bit one? Boot sectors carry 16 bit code.

JL said...

@Mario: Thanks, I just updated the script to disassemble in 16 bits.

@Anonymous: Thanks, missed that typo.

@Cdtdelta: The output looks the same :-P I can provide clean and infected mbrs if that would help, but you could just easily take one from a clean VM and then infect the VM with a sample from offensive computing (like Mebromi) and see for yourself. I'm really leaving the analysis to the user.

Ronny Vasquez said...

Nice script! Combined with diff can find infected drives or use like a monitor for infections with syslog.

Jim said...
This comment has been removed by the author.
Jim said...

Take 2:

I like having the disassembly, but the disconcerting thing was it attempting to disassemble the ASCII strings

288: 32 E4 8A 56 00 CD 13 EB D6 61 F9 C3 49 6E 76 61 2äŠV.Í.ëÖaùÃInva
304: 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61 lid partition ta
320: 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E ble.Error loadin
336: 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 g operating syst
352: 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 em.Missing opera
368: 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 00 00 ting system.....

JL said...

@Jim: true... I might fix later to skip over that, until then, feel like submitting a patch?

The beauty of open source is anyone can modify the code as needed ;-)

Jim said...

I might whip something up. :) My Python isn't as good as my Perl, though.

JL said...

@Jim: now's your chance to improve it ;-)

Lakshmi N said...

Jamie,

Thanks for the script. It worked great.

Thanks,

Lakshmi N

Ange said...

my contribution to understand MBRs:
I re-implemented Starman's MBRs in ASM at http://code.google.com/p/corkami/source/browse/trunk/misc/mbr

JL said...

@Ange:

cool! Thanks for sharing :-)

JL said...

@jim:

lemme know if this works for you (update already in repository) ;-):

[snip]

0x00000129: 61 POPA
0x0000012a: f9 STC
0x0000012b: c3 RET

0x0000012c: 49 6e 76 61 6c 69 64 20 70 61 72 74 69 74 69 6f Invalid.partitio
0x0000013c: 6e 20 74 61 62 6c 65 00 45 72 72 6f 72 20 6c 6f n.table.Error.lo
0x0000014c: 61 64 69 6e 67 20 6f 70 65 72 61 74 69 6e 67 20 ading.operating.
0x0000015c: 73 79 73 74 65 6d 00 4d 69 73 73 69 6e 67 20 6f system.Missing.o
0x0000016c: 70 65 72 61 74 69 6e 67 20 73 79 73 74 65 6d 00 perating.system.
0x0000017c: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0000018c: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0000019c: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x000001ac: 00 00 00 00 00 00 00 00 00 2c 44 63 .........,Dc

===== Partition Table #1 =====
Boot flag: 0x80 (Bootable)
Partition type: 0x7 (NTFS)

[snip]

vl12 said...

Which tool did you use for dump the MBR? I've tried everything from the "http://thestarman.pcministry.com/asm/mbr/BootToolsRefs.htm#FREE"

The script send me the following result:
C:\temp>python mbr_parser.py -f mbr.bin
Traceback (most recent call last):
File "mbr_parser.py", line 219, in
main()
File "mbr_parser.py", line 213, in main
myMBR = MBRParser(data)
File "mbr_parser.py", line 110, in __init__
self.PartitionTable = PartitionTable(data[440:])
File "mbr_parser.py", line 97, in __init__
self.DiskSignature0 = struct.unpack("<B", data[:1])[0]
struct.error: unpack requires a string argument of length 1


vl

JL said...

@vl give me a bit and I'll figure out what the problem is. Someone else reported that they had a problem running the script on windows and I had forgotten to test on different windows systems. I'll post an update when I figure out what the issue is.

vl12 said...

I'll appreciate, thank you

Nacho said...

Thanks for sharing this little gem. Regarding the problem with running it on windows systems the original script always fail (in my tests) with the message "MBR file too small". The problem was that Windows makes a distinction between text and binary files and the solution is to change the line 216:
Before: file = open(a,'r')
After: file = open(a,'rb')
Now it works perfect.
Many thanks Gleeda.

Jamie Levy said...

Thanks, Nacho! I just updated it. I can't believe I missed that :-)