通过单个GPIO引脚转储闪存

时间:2014-06-01 10:03:39

标签: c embedded reverse-engineering gpio firmware

我正在使用英飞凌的XMC4500 Relax Kit,我正试图通过一个GPIO引脚提取固件。

我非常天真的想法是通过GPIO引脚一次转储一位,并用逻辑分析仪以某种方式“嗅探”数据。

伪代码:

while(word by word memory copy hasn't finished)
  ...
  register = value;
  temp_value = value AND 0x1;
  pin = temp_value;
  value = value >> 1;
  ...

我是否在正确的轨道上?有没有人有更好/更好的想法如何存档?

###编辑###

实际上我的(shell)代码的要求是它需要非常小。我找到了this如何做的好玩法 通过闪烁LED来转储固件。

然而,我很难通过Saleae Logic Analyzer获得正确的数值。

基本上我正在做的是:

  1. 设置GPIO引脚方向以输出
  2. 使用时钟(SPI串行时钟)闪烁LED1(引脚1.1)
  3. 使用数据位(SPI MOSI)闪烁LED2(引脚1.0)
  4. 使用逻辑分析仪嗅探引脚
  5. 这是我的C代码:

    #include "XMC4500.h"
    
    #define DEL 1260
    
    void init() 
    {
      // P1.0 output, push pull
      PORT1->IOCR0 = 0x80UL << 0;
      // P1.1 output, push pull
      PORT1->IOCR0 |= 0x80UL << 8;
    }
    
    void delay(int i) { 
      while(--i) { 
        asm("nop\n"); 
        asm("nop\n"); 
      } 
    }
    
    // Sets a pin to high
    // P1.0 = SPI MOSI
    // P1.1 = SPI CLOCK
    void output_high(int i) {
      // P1.0 high
      if(i == 0) {
        PORT1->OUT |= 0x1UL;  
      }
    
      // P1.1 high
      if(i == 1) {
        PORT1->OUT |= 0x2UL;
      } 
    }
    
    // Sets a pin to low
    // P1.0 = SPI MOSI
    // P1.1 = SPI CLOCK
    void output_low(int i) {
      // P1.0 low
      if(i == 0) {
        PORT1->OUT &= (~0x1UL);
      }
    
      // P1.1 low
      if(i == 1) {
        PORT1->OUT &= (~0x2UL);
      }
    }
    
    // SPI bit banging
    void spi_send_byte(unsigned char data)
    {
      int i;
    
      // Send bits 7..0
      for (i = 0; i < 8; i++)
      {
        // Sets P1.1 to low (serial clock)
        output_low(1);
    
        // Consider leftmost bit
        // Set line high if bit is 1, low if bit is 0
        if (data & 0x80)
          // Sets P1.0 to high (MOSI)
          output_high(0);
        else
          // Sets P1.0 to low (MOSI)
          output_low(0);
    
        delay(DEL);
    
        // Sets P1.1 to high (Serial Clock)
        output_high(1);
    
        // Shift byte left so next bit will be leftmost
        data <<= 1;
      }
    }
    
    int main() {
      init();
    
      while(1) {
        spi_send_byte('t');
        spi_send_byte('e');
        spi_send_byte('s');
        spi_send_byte('t');
      }
    
      return 0;
    }
    

    ### 2nd EDIT ###

    使用以下代码转储闪存工作正常:

    #include "XMC4500.h"
    
    // SPI bit banging
    void spi_send_word(uint32_t data)
    {
      int i;
    
      // LSB first, 32 bits per transfer
      for (i = 0; i < 32; i++)
      {
        // set pin 1.1 to low (SPI clock)
        PORT1->OUT &= (~0x2UL);
    
        // set line high if bit is 1, low if bit is 0
        if (data & 0x1) {
          // set pin 1.0 to high (SPI MOSI)
          PORT1->OUT |= 0x1UL;
        }
        else {
          // set pin 1.0 to low (SPI MOSI)
          PORT1->OUT &= (~0x1UL);   
        }
    
        // set pin 1.1 to high (SPI clock)
        PORT1->OUT |= 0x2UL;
    
        data >>= 1;
      }
    }
    
    int main() {
      // start dumping at memory address 0x08000000
      unsigned int *p;
      p = (uint32_t *)(0x08000000u);
    
      // configure pin 1.0 and pin 1.1 as output (push-pull)
      PORT1->IOCR0 = 0x8080UL;
    
      while(1) {
        spi_send_word(*p);
        p++;
      }
    }
    

2 个答案:

答案 0 :(得分:4)

您的解决方案的最大问题是恢复时间信息 - 知道一个单词的开始位置和另一个单词的结束位置。在UART tx引脚上输出数据会更简单 - UART会为您添加启动和停止位并管理时序,并且可以通过常规PC串行端口直接读取输出。

如果你不能使用UART,那么通过用UART定时对GPIO进行位冲击来模拟UART仍然可以使用传统的串行端口来直接接收数据。

可以找到示例软件UART 实现here。在您的情况下,您当然只需要传输功能。

答案 1 :(得分:2)

根据您的要求,这可能会有所体现。需要考虑的是,如果在循环读取数据时存在任何时序变化,例如闪存读取时间变化,缓存等等,您将遇到一个问题,找出字节开始和停止的位置。您可能想看看1-Wire协议:

http://en.wikipedia.org/wiki/1-Wire

您不必将其实现为规范或任何内容,只需看一下即可。如果你实现类似的东西,那么你的逻辑就是这么简单:

while(word by word memory copy hasn't finished)
  ...
  register = value;
  temp_value = value AND 0x1;
  one_wire_send(temp_value);
  value = value >> 1;
  ...