对jtag调试器进行逆向工程,分析mcu的jtag协议,这是个巨大的工程,方法不对,根本做不出来。但是借助逻辑分析仪进行数据采集,用python进行数据处理,直接生产程序代码,看起来能很容易的取得调试器的协议。
msp430的jtag,官方只开源了烧写flash部分的源码和协议,至于单步调试,读写寄存器,断点设置等,都没有公开,只有几家商业公司掌握着,msp430开源社区里没这方面的任何进展。因此,我下定决心对fet430uif进行“逆向工程",完成arm板的jtag调试器,实现对msp430的gdb调试。
msp430的jtag使用的是4pin的jtag接口,TCK,TMS,TDI, TDO。使用agient logic analyzer采集jta个的时序,分别多次采集mspdebug的基本命令:读寄存器,写寄存器,单步,断点设置,运行,运行到断点等。
把采集到的数据保存好,再选着list数据(并非waveform数据)导出成cvs格式的数据格式,方便python读取分析。采集到的jtag数据量非常大,50ns的采样间隔,500ms就有160mb的数据。
msp430的jtag的基本操作只有shiftIR,shiftDR8,shiftDR16等几个,不多,用python写一个状态机,即可对这些数据进行处理。使用python对数据进行初步处理生成对应的jtag基本操作,生成一个只有几十kb的txt文本。
从采集的时序数据中初步得出了上面这些jtag基本操作的数据,这样进行分析手工编写代码,具有一定难度,工作量巨大。我再次使用python进行处理,生成C语言代码,为了方便阅读,还会自动生成一些简单的注释,生成的代码包含了宏定义。
这样,fet430uif调试器产生的时序,我都可以很方便的解释成C语言代码,分析出jtag协议,易如反掌。本来一个非常有难度的逆向工程,借助了逻辑分析仪与python就变得异常简单容易,大大减少了工作量。作为一个嵌入式工程师,学习掌握多种编程语言,对工作的帮助是不可估量。当初我学习python语言也是因为网上看到许多大牛对python的推崇,没想到却短期内给我的工作带来如此巨大的帮助。所以,尽量多学习掌握不同的语言,你永远也不知道它有多么重要,但它可能某一天帮了你一把。
附录python源码:
decode-msp430-jtag.py
import sys
import os
# TDO - data[0]
# TDI - data[1]
# TMS - data[2]
# TCK - data[3]
class DeShiftData:
def __init__(self):
self.data = "";
def finish(self):
data = ""
if len(self.data) == 8:
data = "0x%0.2X" % int(self.data, 2)
else:
data = "0x%0.4X" % int(self.data, 2)
self.data = ""
return data
def push(self, data):
self.data = self.data + data
class TAPState:
def __init__(self):
self.state="IDLE"
def change(self, tms):
lastState = self.state
if self.state == "IDLE":
if tms == "0":
pass
else:
self.state = "selectDRScan"
elif self.state == "selectDRScan":
if tms == "0":
self.state = "captureDR"
else:
self.state = "selectRIScan"
elif self.state == "captureDR":
if tms == "0":
self.state = "shiftDR"
else:
self.state = "exit1DR"
elif self.state == "shiftDR":
if tms == "0":
#loop
pass
else:
self.state = "exit1DR"
elif self.state == "exit1DR":
if tms == "0":
self.state = "pauseDR"
else:
self.state = "updateDR"
elif self.state == "pauseDR":
if tms == "0":
#loop
pass
else:
self.state = "exit2DR"
elif self.state == "exit2DR":
if tms == "0":
self.state = "shiftDR"
else:
self.state = "updateDR"
elif self.state == "updateDR":
if tms == "0":
self.state = "IDLE"
else:
self.state = "selectDRScan"
#IR
elif self.state == "selectRIScan":
if tms == "0":
self.state = "captureIR"
else:
self.state = "fuseCheck"
elif self.state == "captureIR":
if tms == "0":
self.state = "shiftIR"
else:
self.state = "exit1IR"
elif self.state == "shiftIR":
if tms == "0":
#loop
pass
else:
self.state = "exit1IR"
elif self.state == "exit1IR":
if tms == "0":
self.state = "pauseIR"
else:
self.state = "updateIR"
elif self.state == "pauseIR":
if tms == "0":
#loop
pass
else:
self.state = "exit2IR"
elif self.state == "exit2IR":
if tms == "0":
self.state = "shiftIR"
else:
self.state = "updateIR"
elif self.state == "updateIR":
if tms == "0":
self.state = "IDLE"
else:
self.state = "selectIRScan"
return (lastState,self.state)
#travel all lines in file
datafile = sys.argv[1]
last_data = "0110"
lastTime = "0 ns"
count=0
tap = TAPState()
dataIR = DeShiftData()
dataDR = DeShiftData()
dataOUT = DeShiftData()
count=0
for line in open(datafile):
number,data,myTime = line.split(",")
#skip first line
try:
data = bin(int(data, 16))[2:].zfill(4)
except:
print "#convererror line is: %s" % line
continue
#skip
if last_data == data or data[0] == "\"":
continue
lastTime = myTime
TDO = data[0]
TDI = data[1]
TMS = data[2]
TCK = data[3]
#pos edge
if TCK == "1" and last_data[3] == "0":
#tap
lastTapState,tapState = tap.change(TMS)
if lastTapState == "captureIR" or lastTapState == "captureDR":
#start a shitf
pass
elif tapState == "shiftIR" or lastTapState == "shiftIR":
#print "shiftIR " + TDI
dataIR.push(TDI)
if tapState == "exit1IR":
irdata = dataIR.finish()
print myTime.replace("\n", "") + " ShiftIR " + irdata
elif tapState == "shiftDR" or lastTapState == "shiftDR":
#print "shiftDR " + TDI + " " + TDO
dataDR.push(TDI)
dataOUT.push(TDO)
if tapState == "exit1DR":
drdata = dataDR.finish()
outdata = dataOUT.finish()
print myTime.replace("\n", "") + " ShiftDR " + drdata
print myTime.replace("\n", "") + " = " + outdata
if TDI != last_data[1] and tapState == "IDLE":
if TDI == "0":
print myTime.replace("\n", "") + " clrTCLK 0"
else:
print myTime.replace("\n", "") + " setTCLK 1"
last_data = data
jtag-to-c.py
import sys
#travel all lines in file
datafile = sys.argv[1]
print """/*
* %s
*/
""" % sys.argv[1]
codeDict = {"0xC8":"IR_CNTRL_SIG_16BIT", "0xc8":"IR_CNTRL_SIG_16BIT",
"0x88":"IR_CNTRL_SIG_M8BIT",
"0x48":"IR_CNTRL_SIG_L8BIT",
"0x28":"IR_CNTRL_SIG_CAPTURE",
"0xA8":"IR_CNTRL_SIG_RELEASE", "0xa8":"IR_CNTRL_SIG_RELEASE",
"0x44":"IR_PREPARE_BLOW",
"0x24":"IR_EX_BLOW",
"0x82":"IR_DATA_16BIT",
"0xC2":"IR_DATA_QUICK", "0xc2":"IR_DATA_QUICK",
"0x22":"IR_DATA_PSA",
"0x62":"IR_SHIFT_OUT_PSA",
"0xC1":"IR_ADDR_16BIT", "0xc1":"IR_ADDR_16BIT",
"0x21":"IR_ADDR_CAPTURE",
"0xA1":"IR_DATA_TO_ADDR", "0xa1":"IR_DATA_TO_ADDR",
"0xFF":"IR_BYPASS", "0xff":"IR_BYPASS",
"0x90":"IR_CTRL_SETTING",
"0x30":"IR_30_SETTING",
"0x42":"IR_DATA_CAPTURE",
"0xD0": "IR_D0_CHECK", "0xd0": "IR_D0_CHECK"
}
lastTime = "0"
for line in open(datafile):
#print line.strip("\n")
try:
myTime, sym, operation, code = line.split()
except:
print "//FIXME: error - line.split() %s" % line
continue
code = code.strip("\n")
if sym != "us":
interval = (float(myTime) - float(lastTime))
if interval > 0.1:
print "\n//Interval %F ms" % interval
print "MsDelay(%d);" % int(interval)
lastTime = myTime
if operation == "ShiftIR":
if codeDict[code] == "IR_CNTRL_SIG_CAPTURE":
print "\n//fetch"
elif codeDict[code] == "IR_CTRL_SETTING":
print "\n//setting"
elif codeDict[code] == "IR_D0_CHECK":
print "\n//check"
elif codeDict[code] == "IR_CNTRL_SIG_RELEASE":
print "\n//run"
print "IR_Shift(" + codeDict[code] + ");"
elif operation == "ShiftDR":
if len(code) == 4:
print "DR_Shift8(" + code + ");",
else:
print "DR_Shift16(" + code + ");",
elif operation == "=":
print "\t\t//=" + code
elif operation == "clrTCLK":
print "ClrTCLK();"
elif operation == "setTCLK":
print "SetTCLK();"