Engenharia reversa industrial

A história do processo de empréstimo no desenvolvimento de eletrônicos em um bom exemplo.



Gravando o registro do elevador do sniffer caseiro


Uma vez eu precisei copiar um dispositivo bastante simples. A empresa de fabricação deixou de existir, mas ainda havia uma demanda em todo o país pela substituição de dispositivos quebrados ou usados.


O dispositivo em si é o botão de chamada do elevador na foto à esquerda. Para experimentos, eles me deram duas cópias, uma das quais poderia ser completamente desmontada.


O plano de trabalho geral era mais ou menos assim:


  1. Estudando a placa de circuito;
  2. Estudar a base do elemento do próprio quadro;
  3. Esboçando seu circuito elétrico;
  4. Tente ler o arquivo de firmware do microcontrolador;
  5. Desmontagem de firmware;
  6. Extraindo o algoritmo de operação;
  7. Desenvolvimento de um novo conselho;
  8. Escrevendo um novo firmware.

Se o parágrafo 4 falhar, o plano adicional teria sido mais complicado, mas tive sorte.


Estudamos o experimental



Microcontrolador principal



Uma parte do circuito elétrico do elevador, no qual nossas placas estão circuladas em vermelho


A placa é montada em um microcontrolador de 1997, o AT89C2051 , que é baseado na arquitetura Intel MCS-51 . Em 2020, ela comemora 40 anos no mercado de sistemas embarcados.


Uma pequena explicação: um microcontrolador é um microcircuito que contém um núcleo de computação e um conjunto de periféricos para controlar dispositivos externos. Por exemplo, em uma máquina de lavar moderna, o microcontrolador consulta os botões de controle, sensores, exibe informações na tela e controla as bombas, aquecedor, válvulas e acionamento de tambor. Para a maioria dessas funções, ele não requer dispositivos intermediários, apenas um conjunto de componentes eletrônicos passivos.


Desmontamos a placa de circuito

Esboçar o diagrama de circuito original da placa no futuro ajudará a descobrir a finalidade dos pinos do microcontrolador, que é necessário para analisar o código do firmware.


O dispositivo original foi desenvolvido por uma empresa chinesa e, portanto, seu circuito é extremamente confuso e com muitos componentes desnecessários. Por exemplo, o relé foi ativado através de uma cascata tripla de um transistor bipolar, acoplador óptico e dispositivo de campo (nessa ordem).


Um conhecido que trabalha com fabricantes chineses me disse que os chineses estão envolvidos em complicações semelhantes de esquemas para aumentar o custo de desenvolvimento e produção, se apenas uma pessoa o fizer. Depois disso, estou inclinado a acreditar nele:



O mesmo lugar no painel de duas camadas chinês dos dois lados. Três resistores enormes não estão conectados a nada. Eu até brilhava no quadro com uma lanterna poderosa para ter certeza.


O esquema é copiado, lugares misteriosos são modelados em um multisim , assumimos o firmware.


Tentando ler o firmware


Tive muita sorte de a proteção de leitura não estar ativada nas duas placas nos controladores, portanto, mesclei com êxito as duas opções de firmware com pornografia semelhante:



Foto do blog pessoal de um entusiasta americano


Desmontando o firmware


A próxima etapa, precisamos converter esse código de máquina em algo mais legível:



Pegamos a conhecida ferramenta IDA Pro , que já possui nosso controlador com todos os registros periféricos, e abrimos o arquivo de firmware HEX:



Processando dados recebidos pelo conselho em linguagem assembly


Depois disso, há um processo bastante tedioso de estudar o conjunto de instruções do nosso kernel de computação, comentando e decodificando o código do assembler.


Os próprios manipuladores de interrupção foram encontrados nos endereços da tabela de vetores de interrupção; as entradas nos registros periféricos forneceram informações sobre a configuração da interface de comunicação. Passo a passo, o código do assembler sem nome se transformou em algo que pode ser lido.


Extração do algoritmo de trabalho


Como eu precisava desenvolver um novo dispositivo em uma base de elementos diferente, foi necessário extrair um algoritmo do código. Algum tempo depois, nasceu um pseudo código:


void UartISR (void) { counter500ms = 0; //ClearFlag(isrFlags, ISR_FLAG_3); ProcessUart(recievedByte); } void ProcessUart(uint8_t recievedData) { static uint8_t uartPacketsToRxLeft, uartRecievedCmd, uartCurrPacketCRC; static uint8_t i, carryFlag; static uint16_t uartIsrPointer; static uint8_t uartBuffer1[8], uartBuffer2[6]; static uint8_t uartBuffer1Pos, uartBuffer2Pos; // 0 - // 1 - // 2 - // 3 - led state, 0x0F // 4 - // 5 - // 6 - // 7 - // 8 - buttons time static uint8_t dataRegisters[9]; // RAM:0050 uint8_t tmpVal, i; uint8_t dataToSend; if (GetFlag(UartISRFlags, UART_RECIEVED_FLAG)) { ClearFlag(UartISRFlags, UART_RECIEVED_FLAG); if (recieved9thBit) { switch (recievedData) { case 0xC1: uartPacketsToRxLeft = 8; uartRecievedCmd = 1; uartBuffer1Pos = 0; uartBuffer1[uartBuffer1Pos] = recievedData; //uartIsrPointer = 0x0037; //tmpVal_0037 = recievedData; uartCurrPacketCRC = recievedData; UartRxOn(); return; break; case 0xC2: uartPacketsToRxLeft = 3; uartRecievedCmd = 2; 

O mesmo processamento de dados recebidos em C


Quem se importa com o protocolo de transferência:


A estação de controle do elevador se comunicava com os botões dos botões de chamada por meio de uma interface full-duplex de 24 volts. No modo normal, os cartões de botão ouviam a linha, aguardando um pacote de dados de 9 bits. Se o endereço da nossa placa veio neste pacote (foi definido pelo switch DIP na placa), a placa passou para o modo de recepção de 8 bits e todos os pacotes subseqüentes foram ignorados pelo restante das placas no hardware.

O primeiro após o endereço foi um pacote com um código de comando de controle. Especificamente, este conselho levou apenas 3 equipes:
  1. Escrevendo para registros de dados. Por exemplo, a frequência e a duração do botão piscando em uma chamada;
  2. Ligar a luz de fundo do botão;
  3. Consulte o estado dos botões (pressionados ou não).


O último byte foi a soma de verificação, que é um XOR simples de todos os bytes após o endereço.
Após a soma de verificação, a placa voltou ao modo de espera para o seu endereço.

Desenvolvimento de nova diretoria


Para o estágio de desenvolvimento de um novo diagrama de fiação e uma placa de circuito impresso, não tenho fotos, mas era algo assim:


A fiação e a fiação foram feitas no Altium Designer . A fabricação da placa de circuito impresso foi encomendada em Zelenograd " Resonite ".


Escrevendo um novo firmware


Enquanto nossa nova placa está em produção, vamos ao objeto em que esses botões de chamada estão instalados e verificamos a exatidão do protocolo de transferência desmontado usando o sniffer montado no arduino:

Um pedaço de circuito do transmissor que é eletricamente equivalente ao original. O receptor é apenas um acoplador óptico.


 //UART1 initialize // desired baud rate:19200 // actual baud rate:19231 (0,2%) // char size: 9 bit // parity: Disabled void uart1_init(void) { UCSR1B = 0x00; //disable while setting baud rate UCSR1A = 0x00; UCSR1C = 0x06; UBRR1L = 0x33; //set baud rate lo UBRR1H = 0x00; //set baud rate hi UCSR1B = 0x94; } #pragma interrupt_handler uart1_rx_isr:iv_USART1_RXC void uart1_rx_isr(void) { unsigned char tmp; unsigned int rcv = 0; if (UCSR1B & 0x02) { rcv = 0x100; } rcv |= UDR1; tmp = (rcv >> 4) & 0x0F; if (rcv & 0x100) { tmp |= 0xC0; } else { tmp |= 0x80; } txBuf12 = (rcv & 0x0F); txBuf11 = tmp; txState1 = 0; TX_ON(); msCounter0 = 5000; } 

Fale sobre o nosso sniffer no ICC AVR


Em seguida, foi necessário agir com muito cuidado para não queimar nada no elevador e impedir que ele parasse.



Entramos no botão de chamada. Fios amarelos grossos - interface de alimentação e transmissão da placa. Branco no conector de 4 pinos - conectando o botão e a luz de fundo.


Verificamos que tudo funciona como deveria, corrigimos os batentes e escrevemos um novo firmware para o nosso dispositivo:


 //ICC-AVR application builder : 11.02.2015 12:25:51 // Target : M328p // Crystal: 16.000Mhz #include <macros.h> #include <iccioavr.h> #include <avrdef.h> #include "types.h" #include "gpio.h" #define TX_OFF() UCSR0B &= 0b11011111; #define TX_ON() UCSR0B |= 0b00100000; #define TX_STATE() (UCSR0B & 0b00100000) #define MAX_TIMEOUT 3000 //#define SNIFFER_MODE 1 //#define MASTER_MODE 1 // #pragma avr_fuse (fuses0, fuses1, fuses2, fuses3, fuses4, fuses5) #pragma avr_fuse (0xFF, 0xD1, 0xFC) #pragma avr_lockbits (0xFC) // AVR signature is always three bytes. Signature0 is always the Atmel // manufacturer code of 0x1E. The other two bytes are device dependent. #pragma avr_signature (0x1E, 0x95, 0x0F) // atmega32 static GPIOx errorLed, rcvLed, butUp, butDn, ledUp, ledDn, butLedUp, butLedDn, ledButUp, ledButDn; static uint8_t msFlag = 0; static uint8_t ledState = 0, buttonsState = 0; static uint16_t rcvLedLitTime = 0, butMaskCalcTime = 0, timeoutTimer = 0; typedef struct { uint16_t buffer[10]; uint8_t dataLength; } UartPacket; static UartPacket txPacket, rxPacket; #ifdef SNIFFER_MODE static uint8_t txBuffer[64], txBufferLength = 0, bufferMutex = 0; #endif static uint8_t GetPacketCRC(UartPacket* packet); static void SendLedState(void); uint8_t GetAddress(void) { return (PINC & 0x3F); } 

Código C para a nova placa baseada no microcontrolador AVR ATmega328P


A simplicidade do dispositivo e do firmware pode ser estimada pela quantidade de código, ele contém apenas cerca de 600 linhas na linguagem C.


O processo de construção ficou assim:



A taxa é diferente, mas o princípio é o mesmo


Não consigo anexar uma foto do dispositivo acabado, basta acreditar que ele ainda está sendo produzido e vendido.


Conclusão lírica


Em relação aos botões do elevador "para cima" e "para baixo" no chão. Percebi que muitas pessoas não entendem completamente seu objetivo e abalam os dois ao mesmo tempo.



Daqui


O elevador possui dois conjuntos de botões: na cabine, há um painel de pedidos e, no piso, um painel de chamadas. Você já pode adivinhar pelo nome que o painel de pedidos tem uma prioridade de controle mais alta.


Todos os elevadores com painéis de chamada com botões para cima e para baixo trabalham com uma das opções do algoritmo de otimização de viagem, cujo objetivo é transportar o número máximo de passageiros no tempo mínimo e uma condição separada para o tempo de espera máximo no piso (regulado por padrão estadual).


Esse algoritmo geralmente envolve a seleção de passageiros nos andares, se eles estiverem viajando na mesma direção, conforme indicado, pressionando o botão de chamada "para cima" ou "para baixo".


Imagine uma situação em que um elevador com passageiros desça e receba uma chamada "inativa" de um andar abaixo. O elevador irá parar para pegar o passageiro (sim, ainda há o carregamento da cabine pelo sensor de peso, mas vamos abaixá-lo).


O elevador continua e recebe uma chamada "para cima" do andar de baixo. É lógico que o elevador não pare para pegar um passageiro, pois não mudará a direção da viagem (isso também é regulado pelo padrão), mas pegue um passageiro para descer e depois subir - consumo inútil de energia e espaço no elevador.


O elevador continua e recebe duas chamadas “para cima e para baixo” do andar de baixo, pressionadas por algum passageiro impaciente que precisa subir. É lógico que o elevador pare neste andar, mas o passageiro não entrará nele, mas as pessoas na cabine levarão tempo para desacelerar e parar o elevador, abrir as portas, esperar, fechar as portas e acelerar a velocidade nominal.


Se o elevador tiver apenas um botão no chão, em 99% dos casos, ele funcionará de acordo com o algoritmo de “coletiva descendente” e, se houver ordens na cabine, ele só será interrompido ao descer.


Se você possui habilidades de programação em JS, pode tentar implementar um algoritmo de controle semelhante no jogo online Elevator Saga . Ele tem todos os aspectos da otimização de viagens sem se aprofundar no hardcore, como a operação de circuitos de segurança de elevadores.



No meu canal de telegrama eu publico materiais semelhantes. Neste momento, você pode acompanhar o desenvolvimento do próximo dispositivo.

Source: https://habr.com/ru/post/pt459492/


All Articles