溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

硬件學(xué)習(xí)之通過(guò)樹莓派操控 jtag

發(fā)布時(shí)間:2020-08-07 23:30:06 來(lái)源:ITPUB博客 閱讀:166 作者:酷酷的曉得哥 欄目:網(wǎng)絡(luò)安全
作者:Hcamael@知道創(chuàng)宇404實(shí)驗(yàn)室
時(shí)間:2019年10月21日
原文鏈接: https://paper.seebug.org/1060/

最近在搞路由器的時(shí)候,不小心把CFE給刷掛了,然后發(fā)現(xiàn)能通過(guò)jtag進(jìn)行救磚,所以就對(duì)jtag進(jìn)行了一波研究。

最開(kāi)始只是想救磚,并沒(méi)有想深入研究的想法。

救磚嘗試

變磚的路由器型號(hào)為:LinkSys wrt54g v8

CPU 型號(hào)為:BCM5354

Flash型號(hào)為:K8D6316UBM

首先通過(guò)jtagulator得到了設(shè)備上jtag接口的順序。

正好公司有一個(gè)jlink,但是參試了一波失敗,識(shí)別不了設(shè)備。

隨后通過(guò)Google搜到發(fā)現(xiàn)了一個(gè)工具叫:   tjtag-pi

可以通樹莓派來(lái)控制jtag,隨后學(xué)習(xí)了一波樹莓派的操作。

樹莓派Pins

我使用的是rpi3,其接口編號(hào)圖如下:

硬件學(xué)習(xí)之通過(guò)樹莓派操控 jtag

或者在樹莓派3中可以使用 gpio readall查看各個(gè)接口的狀態(tài):

硬件學(xué)習(xí)之通過(guò)樹莓派操控 jtag

rpi3中的Python有一個(gè) RPi.GPIO模塊,可以控制這些接口。

舉個(gè)例子:

>>> from RPi import GPIO
>>> GPIO.setmode(GPIO.BCM)>>> GPIO.setup(2, GPIO.OUT)>>> GPIO.setup(3, GPIO.IN)

首先是需要進(jìn)行初始化GPIO的模式,BCM模式對(duì)應(yīng)的針腳排序是上面圖中橙色的部門。

然后可以對(duì)各個(gè)針腳進(jìn)行單獨(dú)設(shè)置,比如上圖中,把2號(hào)針腳設(shè)置為輸出,3號(hào)針腳設(shè)置為輸入。

>>> GPIO.output(2, 1)>>> GPIO.output(2, 0)

使用output函數(shù)進(jìn)行二進(jìn)制輸出

>>> GPIO.input(3)1

使用input函數(shù)獲取針腳的輸入。

我們可以用線把兩個(gè)針腳連起來(lái)測(cè)試上面的代碼。

將樹莓派對(duì)應(yīng)針腳和路由器的連起來(lái)以后,可以運(yùn)行tjtag-pi程序。但是在運(yùn)行的過(guò)程中卻遇到了問(wèn)題,經(jīng)常會(huì)卡在寫flash的時(shí)候。通過(guò)調(diào)整配置,有時(shí)是可以寫成功的,但是CFE并沒(méi)有被救回來(lái),備份flash的數(shù)據(jù),發(fā)現(xiàn)并沒(méi)有成功寫入數(shù)據(jù)。

因?yàn)槭褂幂喿邮?,所以我只能自己嘗試研究和造輪子了。

jtag

首先是針腳,我見(jiàn)過(guò)的設(shè)備給jtag一般是提供了5 * 2以上的引腳。其中有一般都是接地引腳,另一半只要知道4個(gè)最重要的引腳。

這四個(gè)引腳一般情況下的排序是:

TDI
TDO
TMS
TCK

TDI表示輸入,TDO表示輸出,TMS控制位,TCK時(shí)鐘輸入。

硬件學(xué)習(xí)之通過(guò)樹莓派操控 jtag

jtag大致架構(gòu)如上圖所示,其中TAP-Controller的架構(gòu)如下圖所示:

硬件學(xué)習(xí)之通過(guò)樹莓派操控 jtag

根據(jù)上面這兩個(gè)架構(gòu),對(duì)jtag的原理進(jìn)行講解。

jtag的核心是TAP-Controller,通過(guò)解析TMS數(shù)據(jù),來(lái)決定輸入和輸出的關(guān)系。所以我們先來(lái)看看TAP-Controller的架構(gòu)。

從上面的圖中我們可以發(fā)現(xiàn),在任何狀態(tài)下,輸出5次1,都會(huì)回到 TEST LOGIC RESET狀態(tài)下。所以在使用jtag前,我們先通過(guò)TMS端口,發(fā)送5次為1的數(shù)據(jù),jtag的狀態(tài)機(jī)將會(huì)進(jìn)入到RESET的復(fù)原狀態(tài)。

當(dāng)TAP進(jìn)入到 SHIFT-IR的狀態(tài)時(shí), Instruction Register將會(huì)開(kāi)始接收TDI傳入的數(shù)據(jù),當(dāng)輸入結(jié)束后,進(jìn)入到 UPDATE-IR狀態(tài)時(shí)將會(huì)解析指令寄存器的值,隨后決定輸出什么數(shù)據(jù)。

SHIFT-DR則是控制數(shù)據(jù)寄存器,一般是在讀寫數(shù)據(jù)的時(shí)候需要使用。

講到這里,就出現(xiàn)一個(gè)問(wèn)題了,TMS就一個(gè)端口,jtag如何知道TMS每次輸入的值是多少呢?這個(gè)時(shí)候就需要用到TCK端口了,該端口可以稱為時(shí)鐘指令。當(dāng)TCK從低頻變到高頻時(shí),獲取一比特TMS/TDI輸入,TDO輸出1比特。

比如我們讓TAP進(jìn)行一次復(fù)位操作:

for x in range(5):
    TCK 0
    TMS 1
    TCK 1

再比如,我們需要給指令寄存器傳入0b10:

1.復(fù)位

2.進(jìn)入RUN-TEST/IDLE狀態(tài)

TCK 0
TMS 0
TCK 1

3.進(jìn)入SELECT-DR-SCAN狀態(tài)

TCK 0
TMS 1
TCK 1

4.進(jìn)入SELECT-IR-SCAN狀態(tài)

TCK 0
TMS 1
TCK 1

5.進(jìn)入CAPTURE-IR狀態(tài)

TCK 0
TMS 0
TCK 1

6.進(jìn)入SHIFT-IR狀態(tài)

TCK 0
TMS 0 
TCK 1

7.輸入0b10

TCK 0
TMS 0
TDI 0
TCK 1
TCK 0
TMS 1
TDI 1
TCK 0

隨后就是進(jìn)入 EXIT-IR -> UPDATE-IR

根據(jù)上面的理論我們就可以通過(guò)寫一個(gè)設(shè)置IR的函數(shù):

def clock(tms, tdi):
    tms = 1 if tms else 0
    tdi = 1 if tdi else 0
    GPIO.output(TCK, 0)
    GPIO.output(TMS, tms)
    GPIO.output(TDI, tdi)
    GPIO.output(TCK, 1)
    return GPIO.input(TDO)def reset():
    clock(1, 0)
    clock(1, 0)
    clock(1, 0)
    clock(1, 0)
    clock(1, 0)
    clock(0, 0)def set_instr(instr):
    clock(1, 0)  
    clock(1, 0)
    clock(0, 0)
    clock(0, 0)
    for i in range(INSTR_LENGTH):
        clock(i==(INSTR_LENGTH - 1), (instr>>i)&1)
    clock(1, 0)
    clock(0, 0)

把上面的代碼理解清楚后,基本就理解了TAP的邏輯。接下來(lái)就是指令的問(wèn)題了,指令寄存器的長(zhǎng)度是多少?指令寄存器的值為多少時(shí)是有意義的?

不同的CPU對(duì)于上面的答案都不一樣,通過(guò)我在網(wǎng)上搜索的結(jié)果,每個(gè)CPU應(yīng)該都有一個(gè)bsd(boundary scan description)文件。本篇文章研究的CPU型號(hào)是 BCM5354,但是我并沒(méi)有在網(wǎng)上找到該型號(hào)CPU的bsd文件。我只能找了一個(gè)相同廠商不同型號(hào)的CPU的bsd文件進(jìn)行參考。

bcm53101m.bsd

在該文件中我們能看到j(luò)tag端口在cpu端口的位置:

"tck              : B46  , " &
"tdi              : A57  , " &
"tdo              : B47  , " &
"tms              : A58  , " &
"trst_b           : A59  , " &
attribute TAP_SCAN_RESET of trst_b                   : signal is true;
attribute TAP_SCAN_IN    of tdi                      : signal is true;
attribute TAP_SCAN_MODE  of tms                      : signal is true;
attribute TAP_SCAN_OUT   of tdo                      : signal is true;
attribute TAP_SCAN_CLOCK of tck                      : signal is (2.5000000000000000000e+07, BOTH);

能找到指令長(zhǎng)度的定義:

attribute INSTRUCTION_LENGTH of top: entity is 32;

能找到指令寄存器的有效值:

attribute INSTRUCTION_OPCODE of top: entity is
  "IDCODE       (11111111111111111111111111111110)," &
  "BYPASS       (00000000000000000000000000000000, 11111111111111111111111111111111)," &
  "EXTEST       (11111111111111111111111111101000)," &
  "SAMPLE       (11111111111111111111111111111000)," &
  "PRELOAD      (11111111111111111111111111111000)," &
  "HIGHZ        (11111111111111111111111111001111)," &
  "CLAMP        (11111111111111111111111111101111) " ;

當(dāng)指令寄存器的值為 IDCODE的時(shí)候,IDCODE寄存器的輸出通道開(kāi)啟,我們來(lái)看看IDCODE寄存器:

attribute IDCODE_REGISTER of top: entity is
  "0000"             & -- version
  "0000000011011111" & -- part number
  "00101111111"      & -- manufacturer's identity
  "1";                   -- required by 1149.1

從這里我們能看出IDCODE寄存器的固定輸出為:   0b00000000000011011111001011111111

那我們?cè)趺传@取TDO的輸出呢?這個(gè)時(shí)候數(shù)據(jù)寄存器DR就發(fā)揮作用了。

  1. TAP狀態(tài)機(jī)切換到SHIFT-IR
  2. 輸出IDCODE到IR中
  3. 切換到SHIFT-DR
  4. 獲取INSTRUCTION_LENGTH長(zhǎng)度的TDO輸出值
  5. 退出

用代碼形式的表示如下:

def ReadWriteData(data):
    out_data = 0
    clock(1, 0)
    clock(0, 0)
    clock(0, 0)
    for i in range(32):            
        out_bit  = clock((i == 31), ((data >> i) & 1))
        out_data = out_data | (out_bit << i)
    clock(1,0)
    clock(0,0)
    return out_datadef ReadData():
    return ReadWriteData(0)def WriteData(data):
    ReadWriteData(data)def idcode():
    set_instr(INSTR_IDCODE)
    print(hex(self.ReadData()))

因?yàn)槲乙彩莻€(gè)初學(xué)者,邊界掃描描述文件中的內(nèi)容并不是都能看得懂,比如在邊界掃描文件中并不能看出BYPASS指令是做什么的。但是在其他文檔中,得知BYPASS寄存器一般是用來(lái)做測(cè)試的,在該寄存器中,輸入和輸出是直連,可以通過(guò)比較輸入和輸出的值,來(lái)判斷端口是否連接正確。

另外還有邊界掃描寄存器一大堆數(shù)據(jù),也沒(méi)完全研究透,相關(guān)的資料少的可憐。而且也找不到對(duì)應(yīng)CPU的文檔。

當(dāng)研究到這里的時(shí)候,我只了解了jtag的基本原理,只會(huì)使用兩個(gè)基本的指令(IDCODE, BYPASS)。但是對(duì)我修磚沒(méi)任何幫助。

沒(méi)辦法,我又回頭來(lái)看tjtag的源碼,在tjtag中定義了幾個(gè)指令寄存器的OPCODE:

INSTR_ADDRESS = 0x08INSTR_DATA    = 0x09INSTR_CONTROL = 0x0A

照抄著tjtag中flash AMD的操作,可以成功對(duì)flash進(jìn)行擦除,寫入操作讀取操作。但是卻不知其原理。

這里分享下我的腳本: jtag.py

flash 文檔: https://www.dataman.com/media/datasheet/Samsung/K8D6x16UTM_K8D6x16UBM_rev16.pdf

接下來(lái)將會(huì)對(duì)該flash 文檔進(jìn)行研究,并在之后的文章中分享我后續(xù)的研究成果。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI