#!/usr/bin/python

"""
   Computrace Option ROM Flash dumper
   Copyright (c) 2009 Core Security Technologies, Core SDI Inc.
   Developed by Anibal Sacco (Anibal.Sacco@coresecurity.com)
   and Alfredo Ortega (aortega@coresecurity.com)

   Implementation of the flash-dumping technique described in
   http://www.phrack.com/issues.html?issue=66&id=7   section 3.2.1

   

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version. 

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details. 

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

"""


import os,struct,sys

# Computrace dumper tool

# if you already know both tools path, please indicate below.
# Will be autodetected otherwise
flashRomPath=""
upxPath=""
dmidecodePath=""

# BIOS file name
BIOSFile="BIOSDUMP.bin"
# Computrace Option rom filename
OROMFile="computrace.rom"

temporalFile="TMPdumpTmp.tmp"

# Prints Option ROM Information
def dumpOptionRomInfo(bios,pos):
	if struct.unpack("H",bios[pos:pos+2])[0] == 0xAA55:
		print "[INFO] Option Rom header found at position %x" % pos
	else:	
		print "[ERROR] Option Rom header not found!"
		return -1
	ORlen=ord(bios[pos+2:pos+3])*512
	print "[INFO] Option Rom lenght: %s" % ORlen
	ORversion=bios[pos+5:pos+5+18]
	print "[INFO] Version: %s" % ORversion
	VID=struct.unpack("H",bios[pos+33:pos+35])[0]
	CID=struct.unpack("H",bios[pos+35:pos+37])[0]
	print "[INFO] PCI Vendor/CHIP ID: %x:%x" % (VID,CID)
	computrace=bios[pos:pos+ORlen]
	upxFound=0
	if computrace.find("UPX!")>0:
		print "[INFO] UPX signature found -> Not a stub, complete agent found."
		upxFound=1
	else:	print "[INFO] Computrace found but no UPX signature -> this may be the Stub Agent"
	print "[INFO] Dumping to '%s' file..." % OROMFile
	dumpfile=open(OROMFile,"wb")
	dumpfile.write(computrace)
	dumpfile.close()
	return upxFound

	

def dumpTheEasyWay():
	print "[INFO] Triying to dump the BIOS, the easy way."
	cmd="flashrom -r %s > /dev/null" % BIOSFile
	print "[CMD] Executing '%s'" % cmd
	os.system(cmd)
	try:
		BIOS=open(BIOSFile,"rb").read()
	except:
		print "[INFO] That didn't work."
		return ""
	print "[INFO] FlashRom recognized and dumped the Flash Chip in %s" % BIOSFile
	if BIOS.find("CompuTrace")>0:
		print ("[SUCCESS] Computrace Found!")
		return BIOS
	return ""

def dumpTheHardWay():
	#finding list of flashrom supported chips
	print "[INFO] Triying to dump the BIOS, the hard way."
	cmd="flashrom -L > %s" % temporalFile
	print "[CMD] Executing '%s'" % cmd
	os.system(cmd)
	FR=open(temporalFile,"rb")
	chips=[]
	# parsing the chip list provided by FlashRom
	for line in FR.readlines():
		if line.find("chipsets")>0:
			break
		line=line.split()
		if len(line)>0:
			if len(line[1])>0:
				chips.append(line[1])
	print "[INFO] About %d flash models supported by Flashrom, triying all of them..." % len(chips)
	for chip in chips:
		cmd="flashrom -f -r -c %s %s > /dev/null" % (chip,BIOSFile)
		print "[CMD] Executing '%s'" % cmd
		os.system(cmd)
		try:
			f=open(BIOSFile,"rb")
			BIOS=f.read()
			f.close()
			if BIOS.find("CompuTrace")>0:
				print ("[SUCCESS] Computrace Found!")
				return BIOS
		except: pass
	return ""

def unpackOptionRom(OROMFile):
	#The Option Rom inicialization is 1024 bytes in lenght.
	OROM=open(OROMFile,"rb").read()
	UPXOROMname="%s.unpacked" % OROMFile
	UPXOROM=open(UPXOROMname,"wb")
	pos=OROM.find("UPX!")
	if (pos==-1):
		print "[ERROR] UPX signature not found!"
		return ""
	UPXOROM.write(OROM[pos-4:])
	UPXOROM.close()
	cmd="upx -d %s" %  UPXOROMname
	print "[CMD] Executing '%s'" % cmd
	os.system(cmd)
	return UPXOROMname


#----------------------------------------------------

# Usage
if len(sys.argv)<2:
        print "Search and dumps computrace Agent in BIOS"
        print "Alfredo Ortega - Anibal Sacco - Core Security Technologies"
        print "If you really want to dump the bios, try '%s --dump' as root" % sys.argv[0]
	exit(0)

# search for required tools

#Flashrom
if flashRomPath=="":
	os.system("which flashrom > %s" % temporalFile)
	flashRomPath=open(temporalFile,"rb").read()
	if len(flashRomPath)==0:
		print "[ERROR] FlashROM tool not found! Exiting..."
		print "[ERROR] Try: 'sudo apt-get install flashrom' in debian-based linux distros"
		exit(-1)

#UPX
if upxPath=="":
	os.system("which upx > %s" % temporalFile)
	upxPath=open(temporalFile,"rb").read()
	if len(upxPath)==0:
		print "[ERROR] UPX tool not found! continuing but will not uncompress Option ROM if found..."
		print "[ERROR] Try: 'sudo apt-get install upx' in debian-based linux distros"

#DMIDecode
if dmidecodePath=="":
	os.system("which dmidecode > %s" % temporalFile)
	dmidecodePath=open(temporalFile,"rb").read()
	if len(dmidecodePath)==0:
		print "[ERROR] dmidecode tool not found! continuing but will not report BIOS and computer make."
		print "[ERROR] Try: 'sudo apt-get install dmidecode' in debian-based linux distros"
	else:   
		print "[INFO] System type:"
		os.system("dmidecode -t 1")

#cleanup
try:
	os.unlink(temporalFile)
except: pass

#Try the easy way, automatically recognize the Flash chip
BIOS=dumpTheEasyWay()

if len(BIOS)>0:
	if dumpOptionRomInfo(BIOS,BIOS.find("CompuTrace")-5) == 1:
		if upxPath!="":
			print "[INFO] We found UPX, triying to unpack Option ROM..."
			name = unpackOptionRom(OROMFile)
			print "[INFO] If upacking was successfull, the file %s can be run directly from DOS, is a .COM file" % name
			exit(0)
		print "[INFO] We found a UPX-compressed ROM, but no UPX present in path, couldn't decompress"
	exit(0)

#Try the hard way, bruteforcing all known chips
BIOS=dumpTheHardWay()
if len(BIOS)>0:
	if dumpOptionRomInfo(BIOS,BIOS.find("CompuTrace")-5) == 1:
		if upxPath!="":
			print "We found UPX, triying to unpack Option ROM..."
			name=unpackOptionRom(OROMFile)
			print "[INFO] If upacking was successfull, the file %s can be run directly from DOS, is a .COM file" % name
			exit(0)
		print "[INFO] We found a UPX-compressed ROM, but no UPX present in path, couldn't decompress"
	exit(0)



