Я пытался разработать модуль, который позволит мне изменять выбранные ответы подчиненного устройства на шине I2C. Вот исходная конфигурация шины (подтягивания и подключения питания не показаны для ясности:
На этой шине только 2 устройства, и это только 100 кГц. Контроллер MCU (ведущий I2C) и считыватель RFID-карт (ведомый I2C) NXP PN512. Я не могу изменить прошивку контроллера или изменить транзакции шины I2C. Хорошая часть заключается в том, что контроллер отправляет только 2 типа транзакций:
Master (Write Register) - <s><address+W><register number><data><p>
Master (Read Register) - <s><address+W><register number><p><s><address+R><data><p>
То, что я хочу сделать, это заменить выбранные байты данных во время чтения основного регистра моими собственными байтами. Я могу отправить регистрационные номера, которые MCU хочет прочитать, на мой компьютер через UART (921,6 КБод). Я могу обработать их в C / C ++ или Python там. Когда я получу номер регистра, значение которого необходимо заменить, я могу отправить фальшивый байт обратно на мое устройство, и он позаботится о его отправке обратно в контроллер, заменив исходный ответ карты.
Сначала я разделил шину I2C на две шины:
Я попробовал Arduino Nano и позже CPLD, используя растяжение часов. Аппаратный I2C ATmega328, обращенный к контроллеру MCU, не мог поддерживать, поскольку иногда последовательность запуска генерировалась раньше, чем через 5 мсек после предыдущего цикла остановки. Таким образом, время от времени AVR выполнял транзакцию чтения. CPLD мог справиться со скоростью остановки / запуска, оказалось, что в MCU отключено растяжение шины.
Мне пришла в голову мысль, что я могу «предсказать» чтение основного регистра, обнаружив запись одного байта, так как я уверен, что за ней следует чтение. Кажется, что во время следующей записи адреса цикла чтения у меня было достаточно времени, чтобы ввести байт от ведомого устройства. Это не совсем работает. Вначале операции с шиной казались нормальными (примерно первые 5 секунд), но затем контроллер прекратил все коммуникации на шине, как будто обнаружил, что он не напрямую говорит о считывании меток.
Устройство чтения карт также может генерировать прерывания для мастера. IRQ основаны на таймере или событии. Я связал проблему с задержкой, которую я изначально вводил в автобусе. Я мог ошибаться, но я придумал другой дизайн с «нулевой задержкой».
Идея состоит в том, что я могу только разорвать линию SDA и оставить линию SCL подключенной между ведущим и ведомым. Таким образом, я все еще могу заменить байты в строке данных в любом направлении. Проект оказался более сложным, так как я должен контролировать направление линии SDA на основе цикла шины. Вот код VHDL, который обрабатывает транзакции шины и отправляет шестнадцатеричные байты по UART на компьютер. Получение байтов с компьютера еще не реализовано:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity I2C_Sniffer is
port (
clk : in std_logic;
scl_master : in std_logic;
sda_master : inout std_logic;
sda_slave : inout std_logic;
tx : out std_logic
);
end entity I2C_Sniffer;
architecture arch of I2C_Sniffer is
signal clkDiv: std_logic_vector(7 downto 0) := (others => '0');
type I2C_STATE is (I2C_IDLE, I2C_MASTER_WRITE, I2C_SLAVE_ACK, I2C_MASTER_READ, I2C_MASTER_ACK);
signal i2cState: I2C_STATE := I2C_IDLE;
type I2C_BUS_DIR is (MASTER_TO_SLAVE, SLAVE_TO_MASTER);
signal i2cBusDir: I2C_BUS_DIR := MASTER_TO_SLAVE;
signal i2cRxData: std_logic_vector(7 downto 0);
signal i2cCntr: integer range 0 to 8 := 0;
signal i2cAddr: std_logic := '1';
signal i2cCmd: std_logic := '0';
signal scl_d: std_logic := '1';
signal scl: std_logic := '1';
signal sda_d: std_logic := '1';
signal sda: std_logic := '1';
--Strobes for SCL edges and Start/Stop bits
signal start_strobe : std_logic := '0';
signal stop_strobe : std_logic := '0';
signal scl_rising_strobe : std_logic := '0';
signal scl_falling_strobe : std_logic := '0';
type UART_STATE is (UART_IDLE, UART_START, UART_DATA, UART_STOP);
signal uartState: UART_STATE := UART_IDLE;
signal uartTxRdy: std_logic := '0';
signal uartTxData: std_logic_vector(7 downto 0);
signal uartCntr: integer range 0 to 8 := 0;
begin
CLK_DIV: process (clk)
begin
if rising_edge(clk) then
clkDiv <= std_logic_vector(unsigned(clkDiv) + 1);
end if;
end process;
I2C_STROBES: process (clk)
begin
if rising_edge(clk) then
--Pipelined SDA and SCL signals
scl_d <= scl_master;
scl <= scl_d;
scl_rising_strobe <= '0';
if scl = '0' and scl_d = '1' then
scl_rising_strobe <= '1';
end if;
scl_falling_strobe <= '0';
if scl = '1' and scl_d = '0' then
scl_falling_strobe <= '1';
end if;
if i2cBusDir = MASTER_TO_SLAVE then
sda_d <= sda_master;
sda <= sda_d;
else
sda_d <= sda_slave;
sda <= sda_d;
end if;
start_strobe <= '0';
if sda_d = '0' and sda = '1' and scl = '1' and scl_d = '1' then
start_strobe <= '1';
end if;
stop_strobe <= '0';
if sda_d = '1' and sda = '0' and scl = '1' and scl_d = '1' then
stop_strobe <= '1';
end if;
end if;
end process;
BUS_DIR: process(sda_master, sda_slave, i2cBusDir)
begin
if i2cBusDir = MASTER_TO_SLAVE then
sda_slave <= sda_master;
sda_master <= 'Z';
else
sda_master <= sda_slave;
sda_slave <= 'Z';
end if;
end process;
I2C: process(clk)
begin
if rising_edge(clk) then
uartTxRdy <= '0';
case i2cState is
when I2C_IDLE =>
i2cBusDir <= MASTER_TO_SLAVE;
if start_strobe = '1' then
i2cAddr <= '1';
i2cCntr <= 0;
i2cState <= I2C_MASTER_WRITE;
end if;
-- Master Write (Address/Data)
when I2C_MASTER_WRITE =>
i2cBusDir <= MASTER_TO_SLAVE;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010";
uartTxRdy <= '1';
end if;
if scl_rising_strobe = '1' then
if i2cCntr <= 7 then
i2cRxData(7 - i2cCntr) <= sda;
i2cCntr <= i2cCntr + 1;
end if;
end if;
if i2cCntr = 4 then
case i2cRxData(7 downto 4) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
case i2cRxData(3 downto 0) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
if scl_falling_strobe = '1' then
i2cState <= I2C_SLAVE_ACK;
if i2cAddr = '1' then
i2cCmd <= i2cRxData(0);
i2cAddr <= '0';
end if;
end if;
end if;
when I2C_SLAVE_ACK =>
i2cBusDir <= SLAVE_TO_MASTER;
if scl_falling_strobe = '1' then
i2cCntr <= 0;
if i2cCmd = '0' then
i2cState <= I2C_MASTER_WRITE;
else
i2cState <= I2C_MASTER_READ;
end if;
end if;
when I2C_MASTER_READ =>
i2cBusDir <= SLAVE_TO_MASTER;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010";
uartTxRdy <= '1';
end if;
if scl_rising_strobe = '1' then
if i2cCntr <= 7 then
i2cRxData(7 - i2cCntr) <= sda;
i2cCntr <= i2cCntr + 1;
end if;
end if;
if i2cCntr = 4 then
case i2cRxData(7 downto 4) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
case i2cRxData(3 downto 0) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 and scl_falling_strobe = '1' then
i2cState <= I2C_MASTER_ACK;
end if;
when I2C_MASTER_ACK =>
i2cBusDir <= MASTER_TO_SLAVE;
if scl_falling_strobe = '1' then
i2cCntr <= 0;
end if;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010"; -- \n
uartTxRdy <= '1';
end if;
end case;
end if;
end process;
UART: process (clk, clkDiv(1), uartTxRdy)
begin
if rising_edge(clk) then
case uartState is
when UART_IDLE =>
if uartTxRdy = '1' then
uartState <= UART_START;
end if;
when UART_START =>
if clkDiv(1 downto 0) = "00" then
tx <= '0';
uartState <= UART_DATA;
uartCntr <= 0;
end if;
when UART_DATA =>
if clkDiv(1 downto 0) = "00" then
if uartCntr <= 7 then
uartCntr <= uartCntr + 1;
tx <= uartTxData(uartCntr);
else
tx <= '1';
uartState <= UART_STOP;
end if;
end if;
when UART_STOP =>
if clkDiv(1 downto 0) = "00" then
tx <= '1';
uartState <= UART_IDLE;
end if;
end case;
end if;
end process;
end architecture arch;
Ниже приведены изменения шины, снятые с CPLD, контролирующим линию SDA.
Регистрация написать:
Зарегистрироваться читать:
Вы можете увидеть несколько сбоев при изменении направления движения автобусов. Это вызвано различиями во времени между CPLD, меняющим направление шины, и устройством чтения карт, генерирующим ACK. Уровень ACK, по-видимому, стабилен на переднем крае SCL. Насколько я знаю, это все, что вам нужно.
Когда эта вещь установлена, контроллер ведет себя так же, как и в случае разделенных шин, которые в течение нескольких секунд приостанавливают любую работу шины. Я также тестирую этот w Arduino, который имитирует этот MCU и генерирует для меня трафик шины, и похоже, что Arduino время от времени зависает. Так что я думаю, что у меня может быть какая-то проблема с конечным автоматом VHDL, когда при некоторых условиях я застреваю в одном состоянии без выхода. Любые идеи?
источник
There's only 2 devices on this bus running at 100kHz
а потомThe hardware I2C was a slave and a bit banged I2C was a master on the card reader bus at 1Mbps
. Почему там два автобуса? Зачем нужен скоростной автобус? Предоставьте эскиз вашего первоначального дизайна и постарайтесь уточнить ваш вопрос.Ответы:
Я думаю, что попытки взлома Кутси, как и вы, требуют неприятностей, с именно такими симптомами, с которыми вы сталкиваетесь. Вы в основном пытаетесь обмануть и надеетесь, что вас не поймают.
Согласно твоему описанию, ты не пробовал одну вещь - полную эмуляцию этого устройства для чтения карт. Вы действительно не объяснили, что именно это делает и насколько это сложно, но, судя по тому, что посылает мастер, все не так сложно.
Используйте микроконтроллер с поддержкой аппаратного IIC-слэйва. Это связано с мастером. Прошивка эмулирует кардридер. Поскольку единственное, что когда-либо читает мастер, - это последовательность регистров, другая часть микропрограммы полностью асинхронно связывается со считывателем карт для получения информации и управления ею. Это также означает, что линии сброса и IRQ тоже разделены.
Если все сделано правильно, это должно сработать, так как никакого мошенничества не происходит. Считыватель карт видит контроллер, который посылает ему команды и читает, как именно он должен был использоваться. Это включает в себя реагирование на события IRQ.
Мастер думает, что он напрямую общается с реальным кард-ридером, потому что вы эмулируете все его операции так же, как реальные, включая сброс и поведение IRQ.
Это может звучать как большая работа, чем быстрое и грязное защемление другого байта в хакерской шине, но, как вы обнаружили, это не так быстро и может всегда иметь проблемы с синхронизацией. При полной эмуляции все временные ограничения снимаются. Если ваша эмуляция еще не дошла до того, что сделал кард-ридер, то он действует для мастера, как будто этого еще не произошло. По сути, вы делаете вид, что ничего нового не произошло, пока ваша эмуляция не будет готова ответить на событие во всех аспектах.
Это означает, что у вас действительно есть две асинхронные части прошивки: эмуляция IIC считывателя, представленного мастеру, и полный драйвер кард-ридера, который позволяет вам сохранять все его состояние в вашей внутренней памяти.
Так как вы не обманываете, это должно работать, если все сделано правильно. Единственная проблема системного уровня заключается в том, что мастер будет видеть некоторую задержку и вызывать действия устройства чтения карт, чем в существующей системе. Для «кард-ридера» это не имеет большого значения, и, учитывая, что эта задержка в худшем случае, скорее всего, составит 10 секунд. Это, конечно, не должно быть заметно в человеческом масштабе времени.
Обратите внимание, что связь между вашим эмулятором и кард-ридером не ограничивается используемыми в настоящее время 100 кбит / с. Вы должны запустить это так быстро, как позволяет кард-ридер и ваше оборудование. В конце концов, по этой ссылке вы будете хозяином, поэтому у вас есть часы. Опять же, при правильной архитектуре прошивки и асинхронных задачах это не должно иметь значения. Фактически, ваш драйвер, вероятно, будет общаться чаще и получать больше данных от карт-ридера, чем мастер получает от вашего эмулятора.
источник
Я бы предположил, что вы были на правильном пути с Arduino Nano в качестве MITM, хотя я думаю, что было бы лучше с двумя.
NXP-PN512 будет работать на тактовой частоте 3,4 МГц, поэтому я рекомендую вам использовать что-то порядка 1,5 - 2 МГц для правого MCU, разговаривающего с Reader.
Так как левый MCU установлен на 100 кГц, после того, как вы опознали какие-либо байты транзакции (адрес / регистр-WR), вы можете скопировать его по 8-битной параллельной шине (или даже шире) между MCU и отправить команды читателю в меньше чем один такт на медленном канале I2C. Точно так же получение байта от считывателя достигается менее чем за такт на медленной шине, что дает достаточное время для настройки байта ответа.
Здесь я предполагаю, что вам может потребоваться перевести несколько байтов в качестве NFC-идентификатора, а не просто байтовое преобразование (что требует меньше времени).
Основная проблема, которую я вижу, состоит в том, что если вам нужно сериализовать несколько байтов на / с ПК для отображения ваших изменений, синхронизация становится еще более критичной. Если бы был способ встроить свой алгоритм / таблицу изменений отображения в левый MCU, это казалось бы лучшим подходом, хотя решение отображения многобайтовых идентификаторов все еще остается самой сложной задачей.
Если я ошибаюсь и вам просто нужно сопоставить, скажем, один байт идентификатора карты, то это может сработать.
На ранних испытаниях с Arduino вы гарантировали, что все прерывания были отключены (по крайней мере, используется только TWI)? Если вы этого не сделали, то это могло повлиять на ваше время.
источник