; Reto: La Boveda en Ensamblador
; Saul Castro Treviño A00844534
global _start
; Numeros de las syscalls de Linux
SYS_READ equ 0
SYS_WRITE equ 1
SYS_EXIT equ 60
; Limites del programa
MAX_MSG equ 255 ; el mensaje puede tener hasta 255 caracteres
MAX_KEY equ 15 ; una clave mas larga que esto ya es invalida
section .data
titulo db "=== BOVEDA ===", 10
titulo_len equ $ - titulo
p_clave db "Clave (1-25): "
p_clave_len equ $ - p_clave
p_msg db "Mensaje: "
p_msg_len equ $ - p_msg
txt_error db "Error: clave invalida", 10
error_len equ $ - txt_error
n1_a db "[N1] Letras: "
n1_a_len equ $ - n1_a
n1_b db " | Digitos: "
n1_b_len equ $ - n1_b
n1_c db " | Otros: "
n1_c_len equ $ - n1_c
n2_txt db "[N2] Cifrado: "
n2_len equ $ - n2_txt
n3_txt db "[N3] Checksum: 0x"
n3_len equ $ - n3_txt
n4_a db "[N4] Descifrado: "
n4_a_len equ $ - n4_a
n4_ok db "[N4] Verificacion: OK", 10
n4_ok_len equ $ - n4_ok
n4_fallo db "[N4] Verificacion: FALLO", 10
n4_fallo_len equ $ - n4_fallo
salto db 10
section .bss
keybuf resb 16 ; aqui se guarda lo que el usuario escribe como clave
msgbuf resb 256 ; mensaje original (no se modifica nunca)
cipherbuf resb 256 ; mensaje cifrado
plainbuf resb 256 ; mensaje descifrado
hexbuf resb 2 ; los dos digitos hexadecimales del checksum
charbuf resb 1 ; un solo caracter, para leer y para imprimir numeros
clave resq 1 ; la clave ya convertida a numero
msglen resq 1 ; cuantos caracteres tiene el mensaje
letras resq 1 ; contador de letras
digitos resq 1 ; contador de digitos
otros resq 1 ; contador de otros caracteres
desp_letra resq 1 ; cuanto se desplazan las letras
desp_digito resq 1 ; cuanto se desplazan los digitos
section .text
; Programa principal: ejecuta los cuatro niveles en orden
_start:
call nivel1
call nivel2
call nivel3
call nivel4
mov rax, SYS_EXIT
mov rdi, 0 ; codigo 0 = todo salio bien si sale 1 salio mal
syscall
; NIVEL 1: pedir la clave y el mensaje, validar la clave y contar caracteres
nivel1:
push rax
push rcx
push rdx
push rsi
push rdi
mov rsi, titulo
mov rdx, titulo_len
call imprimir
mov rsi, p_clave
mov rdx, p_clave_len
call imprimir
mov rdi, keybuf
mov rsi, MAX_KEY
call leer_linea ; rax = cuantos caracteres escribio el usuario
cmp rax, MAX_KEY
jg clave_mala ; si escribio mucho, no puede ser una clave valida
mov rcx, rax ; rcx = largo de la clave
mov rsi, keybuf
call convertir_clave ; rax = clave, o -1 si no es valida
cmp rax, -1
je clave_mala
mov [clave], rax
mov rsi, p_msg
mov rdx, p_msg_len
call imprimir
mov rdi, msgbuf
mov rsi, MAX_MSG
call leer_linea
cmp rax, MAX_MSG
jle largo_ok
mov rax, MAX_MSG ; si escribio mas de 255, solo se usan los primeros 255
largo_ok:
mov [msglen], rax
mov rsi, msgbuf
mov rcx, [msglen]
call contar ; deja los resultados en letras, digitos y otros
; Se imprime un salto de linea antes de [N1] porque
; no hay eco del Enter y la linea quedaria pegada al aviso.
mov rsi, salto
mov rdx, 1
call imprimir
mov rsi, n1_a
mov rdx, n1_a_len
call imprimir
mov rax, [letras]
call imprimir_numero
mov rsi, n1_b
mov rdx, n1_b_len
call imprimir
mov rax, [digitos]
call imprimir_numero
mov rsi, n1_c
mov rdx, n1_c_len
call imprimir
mov rax, [otros]
call imprimir_numero
mov rsi, salto
mov rdx, 1
call imprimir
pop rdi
pop rsi
pop rdx
pop rcx
pop rax
ret
clave_mala:
mov rsi, salto ; el error debe quedar en su propia linea
mov rdx, 1
call imprimir
mov rsi, txt_error
mov rdx, error_len
call imprimir
mov rax, SYS_EXIT
mov rdi, 1 ; codigo 1 = clave invalida
syscall
; NIVEL 2: cifrar el mensaje con la clave
nivel2:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov rax, [clave]
mov [desp_letra], rax ; las letras se mueven k posiciones
mov rdx, 0
mov rbx, 10
div rbx ; el resto de dividir k entre 10 queda en rdx
mov [desp_digito], rdx ; los digitos se mueven k mod 10 posiciones
mov rsi, msgbuf
mov rdi, cipherbuf
mov rcx, [msglen]
call desplazar
mov rsi, n2_txt
mov rdx, n2_len
call imprimir
mov rsi, cipherbuf
mov rdx, [msglen]
call imprimir
mov rsi, salto
mov rdx, 1
call imprimir
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret
; NIVEL 3: suma de control del texto cifrado
nivel3:
push rax
push rcx
push rdx
push rsi
mov rsi, cipherbuf
mov rcx, [msglen]
call checksum ; el resultado queda en al
call a_hexadecimal ; escribe los dos digitos en hexbuf
mov rsi, n3_txt
mov rdx, n3_len
call imprimir
mov rsi, hexbuf
mov rdx, 2
call imprimir
mov rsi, salto
mov rdx, 1
call imprimir
pop rsi
pop rdx
pop rcx
pop rax
ret
; NIVEL 4: descifrar y comprobar que se recupero el mensaje original
nivel4:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
; Retroceder k letras es lo mismo que avanzar 26 - k
mov rax, 26
sub rax, [clave]
mov [desp_letra], rax
; Para los digitos es avanzar 10 - (k mod 10)
mov rax, [clave]
mov rdx, 0
mov rbx, 10
div rbx ; rdx = k mod 10
mov rax, 10
sub rax, rdx
cmp rax, 10
jl desp_ok
mov rax, 0 ; si k mod 10 es 0 no hay que mover nada
desp_ok:
mov [desp_digito], rax
;llevo 5 horas haciendo esto ayuda
mov rsi, cipherbuf
mov rdi, plainbuf
mov rcx, [msglen]
call desplazar
mov rsi, n4_a
mov rdx, n4_a_len
call imprimir
mov rsi, plainbuf
mov rdx, [msglen]
call imprimir
mov rsi, salto
mov rdx, 1
call imprimir
mov rsi, msgbuf
mov rdi, plainbuf
mov rcx, [msglen]
call comparar ; rax = 1 si son iguales, 0 si no
cmp rax, 1
jne hay_fallo
mov rsi, n4_ok
mov rdx, n4_ok_len
call imprimir
jmp fin_nivel4
hay_fallo:
mov rsi, n4_fallo
mov rdx, n4_fallo_len
call imprimir
fin_nivel4:
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret
; SUBRUTINAS AUXILIARES
; imprimir: escribe en pantalla rdx caracteres que empiezan en [rsi]
imprimir:
push rax
push rdi
push rcx
push r11 ; syscall modifica rcx y r11, hay que guardarlos
mov rax, SYS_WRITE
mov rdi, 1
syscall
pop r11
pop rcx
pop rdi
pop rax
ret
; leer_linea: lee una linea del teclado en [rdi], guardando maximo rsi
; caracteres. Devuelve en rax cuantos caracteres tenia la linea.
; Se lee de un caracter en un caracter para no pasarnos al leer la
; siguiente linea.
leer_linea:
push rbx
push rcx
push rdx
push rsi
push rdi
push r11
mov rbx, rdi ; rbx = inicio del buffer
mov rdx, rsi ; rdx = maximo que se puede guardar
mov rcx, 0 ; rcx = caracteres leidos hasta ahora
leer_otro:
push rcx ; syscall modifica rcx, se guarda el contador
push rdx
mov rax, SYS_READ
mov rdi, 0
mov rsi, charbuf
mov rdx, 1
syscall
pop rdx
pop rcx
cmp rax, 1
jne fin_lectura ; no llego nada: se acabo la entrada
mov al, [charbuf]
cmp al, 10
je fin_lectura ; llego el Enter
cmp al, 13
je leer_otro ; se ignora el retorno de carro de Windows
cmp rcx, rdx
jge no_guardar ; ya no cabe: se cuenta pero no se guarda
mov [rbx + rcx], al
no_guardar:
inc rcx
jmp leer_otro
fin_lectura:
mov rax, rcx
pop r11
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
ret
; convertir_clave: pasa el texto de la clave a numero.
; Entrada: rsi = texto, rcx = largo. Salida: rax = clave, o -1 si es invalida
convertir_clave:
push rbx
push rcx
push rdx
push rsi
cmp rcx, 0
je clave_invalida ; no escribio nada
mov rax, 0 ; aqui se va armando el numero
siguiente_digito:
mov bl, [rsi]
cmp bl, '0'
jl clave_invalida ; es menor que '0', no es digito
cmp bl, '9'
jg clave_invalida ; es mayor que '9', no es digito
sub bl, '0' ; de caracter ASCII a su valor (0-9)
movzx rbx, bl
mov rdx, 10
mul rdx ; numero = numero * 10
add rax, rbx ; y se le suma el digito nuevo
inc rsi
dec rcx
jnz siguiente_digito
cmp rax, 1
jl clave_invalida ; debe ser al menos 1
cmp rax, 25
jg clave_invalida ; y como maximo 25
jmp fin_convertir
clave_invalida:
mov rax, -1
fin_convertir:
pop rsi
pop rdx
pop rcx
pop rbx
ret
; contar: cuenta letras, digitos y otros en los rcx caracteres de [rsi]
contar:
push rax
push rcx
push rsi
mov qword [letras], 0
mov qword [digitos], 0
mov qword [otros], 0
ciclo_contar:
cmp rcx, 0
je fin_contar
mov al, [rsi]
cmp al, 'A'
jl revisar_digito
cmp al, 'Z'
jle es_letra ; esta entre 'A' y 'Z'
cmp al, 'a'
jl es_otro ; entre 'Z' y 'a' no hay letras ni digitos
cmp al, 'z'
jle es_letra ; esta entre 'a' y 'z'
jmp es_otro
revisar_digito:
cmp al, '0'
jl es_otro
cmp al, '9'
jg es_otro
inc qword [digitos]
jmp siguiente_contar
es_letra:
inc qword [letras]
jmp siguiente_contar
es_otro:
inc qword [otros]
siguiente_contar:
inc rsi
dec rcx
jmp ciclo_contar
fin_contar:
pop rsi
pop rcx
pop rax
ret
; desplazar: copia rcx caracteres de [rsi] a [rdi] moviendo las letras
; desp_letra posiciones y los digitos desp_digito posiciones.
; Cuando se pasa de 'Z' (o 'z', o '9') se da la vuelta restando el tamano
; del grupo (26 letras o 10 digitos).
desplazar:
push rax
push rcx
push rsi
push rdi
ciclo_desplazar:
cmp rcx, 0
je fin_desplazar
mov al, [rsi]
cmp al, 'A'
jl no_mayuscula
cmp al, 'Z'
jg no_mayuscula
sub al, 'A' ; posicion dentro del abecedario (0-25)
add al, [desp_letra]
cmp al, 26
jl mayuscula_lista
sub al, 26 ; se paso de la Z, se regresa a la A
mayuscula_lista:
add al, 'A'
jmp guardar_caracter
no_mayuscula:
cmp al, 'a'
jl no_minuscula
cmp al, 'z'
jg no_minuscula
sub al, 'a'
add al, [desp_letra]
cmp al, 26
jl minuscula_lista
sub al, 26
minuscula_lista:
add al, 'a'
jmp guardar_caracter
no_minuscula:
cmp al, '0'
jl guardar_caracter ; no es digito, se copia igual
cmp al, '9'
jg guardar_caracter
sub al, '0'
add al, [desp_digito]
cmp al, 10
jl digito_listo
sub al, 10 ; se paso del 9, se regresa al 0
digito_listo:
add al, '0'
guardar_caracter:
mov [rdi], al
inc rsi
inc rdi
dec rcx
jmp ciclo_desplazar
fin_desplazar:
pop rdi
pop rsi
pop rcx
pop rax
ret
; checksum: suma de control de los rcx caracteres de [rsi].
; Para cada caracter: c = rotar_izquierda(c, 1) XOR caracter. Resultado en al.
checksum:
push rcx
push rsi
mov rax, 0 ; el acumulador empieza en 0
ciclo_checksum:
cmp rcx, 0
je fin_checksum
rol al, 1 ; rotar 8 bits: el bit que sale por la izquierda entra por la derecha
xor al, [rsi]
inc rsi
dec rcx
jmp ciclo_checksum
fin_checksum:
pop rsi
pop rcx
ret
; a_hexadecimal: convierte el numero en al a dos caracteres hexadecimales
; (mayusculas) y los guarda en hexbuf.
a_hexadecimal:
push rax
push rbx
mov bl, al ; copia del numero para sacar el digito bajo
shr al, 4 ; con el desplazamiento queda solo el digito alto
cmp al, 10
jl alto_numero
add al, 'A' - 10 ; 10 a 15 se escriben A a F
jmp alto_listo
alto_numero:
add al, '0'
alto_listo:
mov [hexbuf], al
mov al, bl
and al, 0Fh ; la mascara deja solo los 4 bits de abajo
cmp al, 10
jl bajo_numero
add al, 'A' - 10
jmp bajo_listo
bajo_numero:
add al, '0'
bajo_listo:
mov [hexbuf + 1], al
pop rbx
pop rax
ret
; comparar: compara rcx caracteres de [rsi] con los de [rdi].
; Devuelve rax = 1 si son iguales, rax = 0 si hay alguna diferencia.
comparar:
push rbx
push rcx
push rsi
push rdi
mov rax, 1 ; se supone que son iguales hasta encontrar diferencia
ciclo_comparar:
cmp rcx, 0
je fin_comparar
mov bl, [rsi]
cmp bl, [rdi]
jne son_distintos
inc rsi
inc rdi
dec rcx
jmp ciclo_comparar
son_distintos:
mov rax, 0
fin_comparar:
pop rdi
pop rsi
pop rcx
pop rbx
ret
; imprimir_numero: imprime el numero que esta en rax en decimal.
; Se divide entre 10 varias veces; los restos salen de derecha a izquierda,
; asi que se guardan en la pila y al sacarlos quedan en el orden correcto.
imprimir_numero:
push rax
push rbx
push rcx
push rdx
push rsi
mov rcx, 0 ; cuantos digitos llevamos
mov rbx, 10
dividir:
mov rdx, 0
div rbx ; rax = cociente, rdx = resto (un digito)
add rdx, '0'
push rdx
inc rcx
cmp rax, 0
jne dividir ; seguir hasta que el cociente sea 0
sacar_digito:
pop rdx
mov [charbuf], dl
mov rsi, charbuf
mov rdx, 1
call imprimir
dec rcx
jnz sacar_digito
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret
このプロジェクトをあなたのウェブサイトに埋め込むには、以下のコードをコピーしてウェブサイトのHTMLに貼り付けてください。