在编写本材料时,我们并不打算教读者如何使用在这条道路上使用的各种软件和硬件。因此,让我们不要用所有这些工具的复杂性来描述文章,把它放在幕后。但如果有人对细节感兴趣,我们很乐意对此发表评论。
不过,还是找到了一些关于这个芯片的信息。在Xeltek的FAQ中,有一个问题被问到:“Superpro6000和6100有什么区别?“CX系列适配器(用于6000程序员)和DX系列适配器(用于6100程序员)是完全相同的,除了ID芯片,该芯片在DX系列适配器中使用高级保护。这是关于什么保护:适配器应该保护谁或什么?当然是你和我!
这种保护类似于现在在打印机墨盒中普遍使用的保护,以避免重新填充,而是购买新的墨盒。但这里的情况有点不同。你用你的血买了一个廉价的适配器,希望你能永远使用它。不是在这里!制造商对此非常谨慎,就像创造者一样,决定人为地限制适配器的寿命。为了证明这一点,软件中检测到以下重要消息:
— «Socket adapter end-of-life is near. Please prepare to replace with anew one.»
但是,不管怎样,读/写Flash内存芯片的需要(我必须说,我们已经忘记了这一点,专注于其他一切)和不可抗拒的愿望,以任何方式减轻不幸的,注定不可避免的死亡的“病”适配器的命运,使我们继续前进。
然而,在日志中无法理解额头,很明显,现在必须移动到程序员本身。它的电子填充物看起来像一个薄片蛋糕,由许多独立的电路板连接在一起,支架和连接器。
其中一个主板(我们称之为“主板”)包含Atmel AT91SAM9G20ARM微控制器、SDRAM、带有固件的SPI闪存、带有编码器型号及其序列号的AE801 ID芯片、控制器USB芯片和许多其他芯片。
另一个电路板上安装了Xilinx芯片组,该芯片组与其他晶体管芯片组一起在读/写过程中控制连接到程序员的适配器上的芯片脚。
微控制器的所有ARM固件都包含在外部SPI闪存芯片中,这当然也无助于增强保护。加载程序通常隐藏在微控制器的内部内存中,无法读取,外部固件或更新被加密。但Xeltek的开发人员决定走另一条路,用专有的A801芯片,让ARM固件一团糟。这是我们接下来做的事情。
这个想法是对程序员的“USB”算法文件进行反汇编,然后在一个ARM IDE中构建一个项目,然后通过jTag,在汇编程序上有一个源代码,学习程序员在与AE801芯片通信方面的工作。
在主板上,JTAG连接器下面有一个空间。为了便于调试,JTAG连接器安装在软件的塑料外壳上,并以扁平的短线连接到主板。
这是一个开发工具包。
由于我们有一个JTAG OLIMEXARM-USB-OCD,从一些旧项目中幸存下来,ARM IDE的选择落在了IAR嵌入式工作台上,因为它支持通过GDB服务器进行调试。然而,配置这个GDB服务器是一个非常复杂的任务。从汇编源代码中构建的项目开始加载到程序员的SDRAM中,并有可能对其进行调试,这花了一些时间。
一旦一切都从调试器下运行,一些最“有趣”的算法方法被重写为C,以便更容易理解。因此,逐渐积累的信息使我们能够理解程序员的工作机制,下面将以稍微简化的形式描述。
当程序启动电源时,ARM微控制器启动其内置引导加载程序(条件为零),该引导加载程序将SPI Flash中的另一个引导加载程序(称为第一个引导加载程序)加载到其内部RAM中,将RAM重置到0地址,并将控制权交给第一个引导加载程序。启动后,第一个引导加载程序在ARM SDRAM中配置控制器,从而将外部RAM连接到0x20000000地址,并从相同的SPI闪存重写另一个引导加载程序(称为第二个引导加载程序),并将控制权移交给它。第二个引导加载程序配置控制器的外部USB芯片以与主机通信,在LCD上显示带有程序员名称和固件版本的字符串屏幕,与自己的AE801 ID芯片通信,最终进入主机命令处理循环。
为了处理每种类型的内存芯片,为ARM微控制器创建了一个固件,称为算法。算法文件具有USB扩展名,并存储在安装程序的“algox”目录中的主机上。“lib”目录包含Xilinx芯片的固件和扩展名为“wls”的文件,其中包含有关所需算法是否与每个支持的芯片匹配的信息,该芯片所需的适配器类型,以及每个芯片的一些附加参数。例如,电源电压、编程电压等。
在软件中选择任何芯片后,必须确保程序员安装了相应的适配器。为了做到这一点,主机向程序员发送一个请求命令,请求其中安装的适配器类型。程序员从适配器中请求AE801芯片类型,并将特殊生成的响应发送回主机。主机上的软件将“WLS”文件中的适配器类型与响应中的适配器类型进行比较,如果它们匹配,则将另一个引导加载程序(称为第三个引导加载程序)发送到地址0x20020000的程序员,该文件位于“bin”目录中。在将控制权移交给第三个引导加载程序后,主机使用它在地址0x20000000处下载相应的算法(USB文件),这是Xilinx固件,并最终将控制权移交给下载的算法。在对硬件进行了一些调整后,算法再次进入主机命令处理循环。中间的第三个加载程序只需要每次将工作算法加载到同一地址0x20000000。
当您在软件中选择不同的芯片时,从主机请求适配器类型开始,重复上述整个过程。
一旦加载到编程程序中的算法进入主机命令处理循环,所有进一步的操作都只由这些命令激活。换句话说,主机成为主机,程序员成为从属设备。适配器中的AE801芯片在向其7(CS)脚进料后,从适配器中导出,并在向其6脚进料后,从适配器中导出4 MHz的指令。AE801芯片的3支脚最初配置为接收来自程序员的命令。在发送下一个命令后,程序员将数据线配置为接收,AE801将其脚3分别发送给程序员并将数据发送给程序员。因此,适配器中的AE801和程序员之间交换数据。主板上的内部AE801和程序员之间的数据交换以相同的方式进行。
治疗
因此,我们直接讨论了DX系列适配器的保护机制。
因此,我们直接讨论了DX系列适配器的保护机制。
如前所述,为了避免文章过于详细,我们只提供了所走过的道路的精髓-程序员和AE801 ID芯片之间的数据交换过程的描述。
本描述中使用的术语:
•术语“支腿”指AE801芯片的相应结论。
•字节计数-从1开始。
•每一次,程序员在向芯片发送一定数量的字节后,将数据线翻译为接收,而芯片在接收到数据后,将脚3切换到传输等。因此,这些切换的描述和脚3的提及将不会出现。
·故事以AE801芯片为代表。
RSA表
{
0x0000, 0x0001, 0x03d0, 0x0640, 0x059d, 0x045f, 0x0191, 0x020b, 0x0863, 0x0209, 0x0933, 0x0489, 0x017f, 0x0168, 0x00d8, 0x0717,
0x0741, 0x0315, 0x040d, 0x0368, 0x0984, 0x084f, 0x06d8, 0x0832, 0x08b2, 0x0602, 0x07aa, 0x06a4, 0x0044, 0x086f, 0x08da, 0x07cd,
0x067f, 0x099f, 0x0797, 0x0086, 0x0ab4, 0x0088, 0x09e6, 0x07c5, 0x0433, 0x082a, 0x06d4, 0x00d0, 0x06f8, 0x029d, 0x0496, 0x04f5,
0x0525, 0x06ef, 0x037f, 0x02ad, 0x0606, 0x0133, 0x03b2, 0x0573, 0x0a1d, 0x017d, 0x09ad, 0x0674, 0x05ff, 0x0745, 0x097a, 0x02d9,
0x0373, 0x02f3, 0x09a6, 0x0402, 0x0969, 0x05be, 0x01c5, 0x0ab7, 0x042c, 0x074c, 0x0965, 0x047d, 0x0989, 0x0a81, 0x00a4, 0x00fd,
0x03ee, 0x099c, 0x0695, 0x05ee, 0x028d, 0x0435, 0x0243, 0x07df, 0x09d1, 0x07ee, 0x0509, 0x09b9, 0x023f, 0x02c0, 0x06fa, 0x098d,
0x05d5, 0x0499, 0x0800, 0x016f, 0x0019, 0x0410, 0x010b, 0x01ab, 0x07ea, 0x036f, 0x0094, 0x004e, 0x0a64, 0x06b9, 0x0abe, 0x051a,
0x0295, 0x0078, 0x0112, 0x06a8, 0x08dd, 0x06e9, 0x04d7, 0x08c3, 0x02e4, 0x00f3, 0x0015, 0x09e7, 0x0967, 0x06e2, 0x0650, 0x0508,
0x0882, 0x0028, 0x07f3, 0x00b7, 0x03d7, 0x0504, 0x0143, 0x0016, 0x0995, 0x08b5, 0x0437, 0x0763, 0x04c5, 0x0234, 0x04c7, 0x07da,
0x09bd, 0x027e, 0x051b, 0x01c0, 0x052a, 0x0544, 0x046c, 0x078c, 0x0199, 0x0299, 0x04b6, 0x094a, 0x07d3, 0x053c, 0x0083, 0x017b,
0x00d6, 0x077f, 0x090b, 0x0475, 0x00ab, 0x09cc, 0x0312, 0x0603, 0x0907, 0x07fa, 0x00b9, 0x0909, 0x0889, 0x0360, 0x0247, 0x00cc,
0x054c, 0x0213, 0x054e, 0x0a44, 0x0767, 0x07b0, 0x0074, 0x087b, 0x041e, 0x098a, 0x087d, 0x03ab, 0x069c, 0x06cc, 0x0604, 0x095f,
0x053f, 0x0954, 0x02da, 0x06d1, 0x08f0, 0x09bf, 0x01db, 0x039e, 0x08a8, 0x0ac5, 0x007a, 0x0222, 0x0a8f, 0x042f, 0x0322, 0x01f0,
0x00e3, 0x00f7, 0x0417, 0x09aa, 0x00fc, 0x077a, 0x04e9, 0x0a21, 0x0278, 0x06f7, 0x07ef, 0x08e7, 0x09cd, 0x04aa, 0x0739, 0x09e3,
0x0708, 0x0a72, 0x028e, 0x04a6, 0x04c0, 0x021b, 0x081d, 0x05c5, 0x069a, 0x05bc, 0x06ca, 0x00eb, 0x00ec, 0x0a9b, 0x04f7, 0x07a2,
0x04ec, 0x0338, 0x05b7, 0x0459, 0x043d, 0x02f5, 0x0284, 0x023b, 0x01f5, 0x0979, 0x01c4, 0x027b, 0x0868, 0x043c, 0x0397, 0x048f
};
5.从程序员那里得到1个字节,并将其作为标记存储。然后将随机的4字节发送到程序员。程序员得到这4个字节后,就像我们在P.4中一样,使用相同的RSA算法对它们进行加密,并以相同的顺序将生成的8个字节发送给我们(首先是高级字节,然后是每个单词的初级字节)。在获得这8个字节后,我们必须检查程序员是否完成了任务,如果是,我们将向他发送之前收到的令牌。
6.从程序员那里得到1字节,并将其作为标记再次记住。然后在数组中形成buf11-11随机字节,并将其保存以供后续操作。然后巧妙地将这11字节发送到程序员。诀窍是,我们必须向程序员发送88个字节,其中包含我们生成的11个字节,而最小的令牌位用于转换传输的11个字节。将88字节发送到程序员后,将接收到的令牌发送到程序员。
C的超控代码。
byte _mark; // Полученный из программатора байт.
byte _buf11[11]; // Массив для 11-и случайных байт.
for( i=0; i<11; i++ )
{
byte bt = rand();
_buf11 = bt;
if( _mark & 1 )
bt = ~bt;
// Упаковываем один байт в 8.
//
for( j=0; j<8; j++ )
{
byte rnd = rand();
byte msk = ( 1 << j );
if( bt & msk )
rnd |= msk;
else
rnd &= ~msk;
// Отправляем байт rnd с упакованным в негоочередным битом в программатор.
//
ОтправкаБайта( rnd );
}
}
// Отправляем впрограмматор маркер.
//
ОтправкаБайта( _mark );
7.从程序员那里得到1个字节,并将133个字节的数组发送到程序员,该数组由四个部分组成。
第一部分是48字节,其中包含适配器类型及其“年龄”的信息,取自本机适配器“DX0001”,其中文本“DX0001”被替换为“DX1011”。
且会有一个认证ID号
{
'D', 'X', '1', '0', '1', '1', 0xff, 0xff, 0xff, 0xff, '1', '6', '0', '6', '2', '4',
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x50, 0x00, 0xff, 0xff, 0x00, 0x00, 0x0c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
第二部分是16个随机字节。
第三部分是22个字节,由Buf11中保存的11个字节组成。
C上的22字节生成码
byte _buf22[22];
const byte _hash[] =
{
0x58,0x3E,0x09,0x2D,0xFD,0x45,0x68,0x10,0xD5,0x25,0xC7,0xB8,0x28,0x93,0x75,0xDE,
0x57,0x02,0x1E,0x24,0x36,0xB6,0x3A,0x59,0x11,0x6E,0x58,0x91,0xAE,0x53,0x69,0xDF,
0x44,0xA4,0x8B,0xFB,0x76,0x91,0x59,0x3C,0x30,0xB9,0xDA,0x21,0xD8,0x05,0xB4,0x16,
0x4C,0x05,0x78,0x8D,0xB5,0x1C,0x41,0x63,0x4C,0xBE,0xA6,0xCC,0x65,0xB8,0x38,0x1D,
0xE7,0xC6,0xC9,0x19,0xB7,0x73,0xB2,0x7D,0xCD,0x54,0xDC,0xFE,0x67,0x5E,0x79,0x68,
0xB8,0x77,0x73,0x37,0xC8,0x56,0xA2,0x4D,0x9B,0x86,0x56,0x3F,0x26,0x39,0xDE,0xF6,
0xA8,0x13,0xB4,0xBA,0x19,0xDE,0xDF,0x08,0x64,0x2A,0x9F,0xA4,0x3E,0xEE,0x90,0x5B,
0xF0,0xF3,0xC6,0x5F,0x1F,0x84,0x87,0xA3,0x94,0x0D,0x04,0x92,0xDC,0x3C,0xD0,0x6A,
0xD6,0x9B,0xA9,0xED,0x02,0xB0,0xB3,0xBB,0xF3,0x17,0x04,0x93,0x8F,0x18,0x22,0x9B,
0x33,0x0F,0x2A,0x4C,0x72,0x1A,0x0F,0xC2,0x3E,0x4C,0x77,0xAA,0xF2,0x04,0xDC,0x60,
0x68,0x81,0x7B,0x7C,0x60,0xE7,0xD3,0x61,0x3A,0xDA,0x69,0x4A,0x14,0x5A,0xB7,0x31,
0x9F,0xB5,0x60,0x61,0xB4,0x2D,0x80,0x10,0xCF,0x16,0x6B,0xF1,0x08,0x81,0xDA,0x12,
0xA6,0x46,0xF2,0xA2,0x14,0x68,0xAA,0x48,0x94,0x8B,0x9D,0xE3,0xD0,0xFB,0x84,0x74,
0x1C,0x3C,0x94,0x5A,0x3F,0xF0,0x37,0x8C,0xD9,0x7E,0xA7,0x38,0xA4,0xB5,0xA7,0x25,
0x65,0x15,0x7F,0xE5,0x3B,0xD1,0x14,0x1E,0xD3,0xA8,0x47,0x2E,0xD8,0xEB,0xB0,0xAE,
0x4F,0x34,0xF4,0x52,0xC7,0x23,0x9D,0x60,0x98,0x1E,0x2C,0xFC,0xF2,0x96,0xB7,0x83
};
void PrepareBuf22()
{
byte i, j, bt, bt1, bt2, bt3;
for( i=0; i<4; i++ )
_buf22 = rand();
bt2 = (_buf22[1] ^ _buf22[2] ) & 0xEF;
bt3 = _buf22[1] ^ _buf22[3];
_buf22[4] = (byte)((_buf22[0] + _buf22[1] + _buf22[2]) ^ _buf22[3]);
_buf22[1] ^= ( 1 << (_buf22[2] & 7) );
bt = _buf22[2];
bt1 = _buf22[0];
i = 0;
while( i < 10 )
{
bt += _buf11[i++];
bt1 += _buf11[i++];
}
_buf22[5] = bt;
_buf22[6] = bt1;
//----------------------------------------------------------------
_buf22[7+11] = 0;
_buf22[7+12] = 0;
for( i=0; i<10; i++ )
{
_buf22[7+11] += _buf11;
_buf22[7+12] ^= _buf11;
}
_buf22[7+11] ^= _buf11[9];
_buf22[7+12] ^= _buf11[10];
_buf22[7+13] = (_buf22[7+11] + _buf11[10]) ^ _buf22[7+12];
_buf22[7+14] = (_buf22[7+13] + _buf11[9]) ^ _buf22[7+12];
for( i=0; i<15; i++ )
{
bt = (_buf22[7+i] ^ _hash[bt2+i] );
bt = (bt << 2) | (bt >> 6);
bt ^= bt3;
_buf22[7+i] = bt;
}
}
第四部分是47个随机字节。
8.在133字节阵列传输后,芯片必须进入睡眠状态,直到第7条腿上出现下一个活动高水平,否则如果第7条腿是复位信号,芯片将自动进入复位状态。从第2段开始,一切都在重复。
康复病人出院
最终,CX1011适配器被安全地安装起来,并在新的程序员中获得了第二次生命。