Disponible la nueva versión "donationware" 7.3 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Tecnología del PC

[Home]  [Inicio]  [Índice]


DEBUG

Nota:  "Bug" significa fallo, defecto en un programa;  "debug" significa depurar (escrutar y eliminar fallos).  La palabra ha quedado como verbo (depurar), de la que han derivado otras.  Por ejemplo: "Debugger" (depurador).  Por extensión, todos los programas y utilidades que sirven para escudriñar los datos y el código a bajo nivel, se identifican genéricamente con esta denominación.

§1  Sinopsis

DEBUG.EXE es un ejecutable que, hasta 2001, acompañó a todas las versiones de MS-DOS, a partir de la  2.0, y de Windows [1].  Es sin duda un programa antiguo pero de enorme potencial didáctico para el principiante.  En adelante nos referiremos a él como debug simplemente.

Se trata de una utilidad interactiva de exploración de bajo nivel, pero que también puede utilizarse para ciertas funciones. Por ejemplo, como un tosco traductor de sentencias ensamblador a lenguaje máquina.  Como todos los programas de su tipo, debe utilizarse con cierto cuidado.  En especial si utiliza el comando W [4] (se puede borrar todo un disco duro con un simple comando erróneo).  Para utilizarlo basta invocarlo desde MS-DOS o desde una ventana DOS de Windows.  Aunque en este último caso algunas de sus funcionalidades más interesantes nos estarán vetadas.

Nota:  Es importante recordar que muchos usos de estas utilidades de bajo nivel, requieren un funcionamiento stand-alone.  Es decir, fuera de un Sistema Operativo multi usuario, ya que éstos encapsulan y ocultan (virtualizan) muchos aspectos del hardware.

§2  Opciones

Una vez que el programa está en ejecución, el indicador ("prompt") es un guión "-", indicando que el "Shell" ( H1.7.1w3) espera recibir órdenes.  Para salir basta pulsar una Q.  Como muchos programas de su género, sus comandos empiezan por una letra o combinación de ellas (pueden usarse indistintamente mayúsculas o minúsculas) y ciertos parámetros opcionales (no es imprescindible separar la letra de opción de los parámetros opcionales que siguen).  La opción más sencilla es la interrogación ?, cuyo resultado es una lista resumida de las opciones disponibles:

ensamblar     A [dirección]
comparar      C dirección de intervalo
volcar        D [intervalo]
escribir      E dirección [lista de valores]
llenar        F lista de intervalos
ir            G [=dirección] [direcciones]
hex           H valor1 valor2
entrada       I puerto
cargar        L [dirección] [unidad] [primer_sector] [número]
mover         M dirección de intervalo
nombre        N [nombre_ruta] [lista_argumentos]
salida        O byte de puerto
proceder      P [=dirección] [número]
salir         Q
registrar     R [registrar]
buscar        S lista de intervalos
seguimiento   T [=dirección] [valor]
desensamblar  U [intervalo]
escribir      W [dirección] [unidad] [primer_sector] [número]
asignar memoria expandida              XA [#páginas]
desasignar memoria expandida           XD [identificador]
asignar páginas de memoria expandida   XM [Lpágina] [Ppágina] [identificador]
mostrar estado de la memoria expandida XS

La mayoría de los comandos de debug ejecutan una acción y vuelven al indicador del shell, pero si es un comando largo, como puede ser mostrar un trozo grande de código, puede detenerse pulsando CTRL-Pausa o interrumpirse con CTRL-C para volver al shell.

Una característica poco conocida, es que debug puede aceptar entradas desde un fichero "Script", que puede ser un simple fichero de texto ASCII en el que cada comando esté separado del anterior por un INTRO.  Después del último, que debe ser una "Q" para salir de debug, es conveniente dejar una línea en blanco pulsando INTRO dos veces.  Las líneas pueden contener comentarios.  Cualquier cosa a partir del carácter punto y coma (;) hasta el final de la línea, será ignorado.

; esto es un comentario

D   ; aquí se mostrará algo...

Suponiendo que tengamos un fichero "Script" de nombre Ordenes.txt,  puede ser utilizado como entrada para debug mediante un comando de redirección en la siguiente forma:

DEBUG < Ordenes.txt

También puede conseguirse que el programa redireccione la salida hacia un fichero que puede ser inspeccionado más tarde.   Aunque tiene la dificultad de tener que trabajar "a ciegas", puede ser de utilidad en determinadas circunstancias.  Por ejemplo, cuando se desea un volcado de determinadas zonas de la memoria.  En el caso anterior podría obtenerse un fichero Result.txt con el siguiente comando:

DEBUG < Ordenes.txt > Result.txt

§3  Errores

Cuando debug no sabe interpretar un comando, muestra un mensaje de error y un indicador "^" debajo del sitio del comando donde está el error.

§4  Entradas & Salidas

DEBUG asume que los datos numéricos proporcionados son hexadecimales, y cuando se trate de direcciones de memoria, deben introducirse en forma segmentada ( H5.1).  A su vez, los resultados son mostrados también en formato hexadecimal cuando se refieren a direcciones de memoria.  Cuando se trata simplemente del contenido de ciertas posiciones de memoria, el resultado es mostrado en formato hexadecimal y en ASCII.  Por ejemplo, una salida puede presentar el siguiente aspecto:

177C:0180  01 21 10 03 41 10 05 61-10 07 81 10 09 A1 10 0B   .!..A..a........
177C:0190  C1 10 0D E1 10 0F 01 11-11 21 11 13 41 11 15 61   .........!..A..a
177C:01A0  11 17 81 11 19 A1 11 1B-C1 11 1D E1 11 1F 01 12   ................
177C:01B0  21 21 12 23 41 12 25 61-12 27 81 12 29 A1 12 2B   !!.#A.%a.'..)..+
177C:01C0  C1 12 2D E1 12 2F 01 13-31 21 13 33 41 13 35 61   ..-../..1!.3A.5a
177C:01D0  13 37 81 13 39 A1 13 3B-C1 13 3D E1 13 3F 01 14   .7..9..;..=..?..
177C:01E0  41 21 14 43 41 14 45 61-14 47 81 14 49 A1 14 4B   A!.CA.Ea.G..I..K
177C:01F0  C1 14 4D E1 14 4F 01 15-51 21 15 53 41 15 55 61   ..M..O..Q!.SA.Ua

Cada fila muestra 16 posiciones de memoria a partir de la posición señalada por las columnas de la izquierda, que las muestran como desplazamiento:segmento.  El bloque central muestra el contenido hexadecimal de cada byte, mientras que el bloque derecho contiene la representación ASCII.  Por ejemplo, la 5ª fila muestra el contenido de las posiciones 177C:01C0 a 177C:01CF (ambas inclusive).  Sus dos últimos bytes contienen respectivamente los caracteres 5 y a, que corresponden a las cantidades 35h y 61h del bloque central.  Que como sabemos ( E2.2.1a), equivalen a los decimales 53 y 97, que son precisamente los valores ASCII de los caracteres mencionados.

Nota:  Por razón de que éste tipo de salida pueda ser listado a impresora, el bloque derecho no contiene en realidad la representación ASCII de todos los caracteres (algunos ni siquiera tienen una representación imprimible).  En realidad solo se muestran los caracteres imprimibles del primer bloque (US-ASCII). El resto está representado por un punto.

No perder de vista que, a pesar de que algunas posiciones de memoria puedan contener valores cuya equivalencia ASCII sea un carácter imprimible.  Esto no significa que dichas posiciones representen efectivamente tales valores para la aplicación que las utiliza. Por ejemplo, puede que en realidad, las dos posiciones de memoria mencionadas (dos octetos), en vez de los caracteres 5 y a, representen una palabra de 16 bits en formato Little Endian ( E2.2.6a), que a su vez representan una cantidad entera (un número). 

§5  Comandos

Aunque su descripción completa llenaría todo un capítulo [2], a continuación comentamos algunas de las opciones más interesantes relativas a inspección del código y de contenidos de memoria.

§5.1  La opción D ("Dump") permite obtener un volcado del contenido de la memoria (de debug).  La sintaxis acepta dos formas:

D [dirección-inicial] [L posiciones-desde-dirección-inicial ]

D [direccion-inicial][direccion-final]

Naturalmente, para que exista algo en el bufer de memoria (al principio quizás solo contiene basura) es preciso indicarle que lea algo.  Este algo puede ser un rango de direcciones de memoria RAM; una dirección absoluta de disco (sector); un fichero, o el contenido de los registros del procesador.  Para empezar puede indicársele que cargue un fichero xxx.xyz, que tenemos en el directorio actual o en el PATH, cuyo contenido queremos ver.

C:\> debug xxx.xyz

-D

aquí se obtendría una salida análoga a la comentada .

Hemos visto un ejemplo al tratar de los servicios de interrupciones del PC ( Servicios-BIOS), aquí mostraremos otro.  Sabemos que el punto de inicio de la BIOS es la dirección F000:FFF0 ( H4.1), y que desde esta posición hasta el final de la memoria DOS hay 16 bytes.  Puede obtenerse un volcado de estas posiciones con el siguiente comando:

-D F000:FFF0

La salida en mi equipo es:

F000:FFF0  CD 19 E0 00 F0 30 36 2F-33 30 2F 39 37 00 FC 38   .....06/30/97..8

En este caso el comando solo muestra una línea porque ha alcanzado el final de la memoria.  Puede verse que la fecha de la BIOS del sistema ocupa las últimas posiciones.

Si se ordena el volcado de una dirección suficientemente alejada del final de la memoria, sin indicar ningún número de posiciones, por defecto se toma el valor 128 (8 filas de 16 posiciones).  Si desea una cantidad distinta, hay que añadir una L ("Long") y el número de posiciones, hasta un máximo de 64 KB (FFFF).  Todos los números deben ser expresados en hexadecimal.  Por consiguiente los dos comandos que siguen son equivalentes (recuerde que 80 es el equivalente hexadecimal de 128):

D FE00:0000

D FE00:0000 L 80

He aquí el resultado:

FE00:0000  41 77 61 72 64 20 53 6F-66 74 77 61 72 65 49 42    Award SoftwareIB
FE00:0010  4D 20 43 4F 4D 50 41 54-49 42 4C 45 20 34 38 36    M COMPATIBLE 486
FE00:0020  20 42 49 4F 53 20 43 4F-50 59 52 49 47 48 54 20    BIOS COPYRIGHT  .
FE00:0030  41 77 61 72 64 20 53 6F-66 74 77 61 72 65 20 49    Award Software I
FE00:0040  6E 63 2E 6F 66 74 77 61-72 65 20 49 6E 63 2E 20    nc.oftware Inc. .
FE00:0050  41 77 03 0C 04 01 01 6F-66 74 77 E9 12 14 20 43    Aw.....oftw... C
FE00:0060  1C 41 77 61 72 64 20 4D-6F 64 75 6C 61 72 20 42    .Award Modular B
FE00:0070  49 4F 53 20 76 34 2E 35-31 50 47 4D 00 8E 32 EC    IOS v4.51PGM..2.

Sabemos que esta zona corresponde al área de la BIOS del Sistema...

§5.2  Otra opción muy interesante nos permite buscar determinados contenidos a través de la memoria utilizando el prefijo S ("Search").  Existen dos sintaxis alternativas:

S direccion-de-inicio L longitud-a-explorar contenido-a-buscar

S direccion-de-inicio direccion-final L contenido-a-buscar

El contenido debe ser proporcionado en hexadecimal (tal como aparecería en la columna central del ejemplo anterior) o entre comillas si es un texto ASCII.  Por ejemplo, en un equipo IBM, deseo ver algunas características de la BIOS.  Se que estos datos se alojan a partir de la posición F000:0000 hasta el final de la memoria.  Es decir, una longitud de 64 Kbytes (FFFF) a partir de la posición de inicio, y que seguramente, los datos buscados estarán cerca de la identificación de la propia BIOS (la cadena "IBM").  Por lo que utilizo la siguiente sintaxis [3]:

-s f000:0000 L ffff "IBM"

y obtengo la siguiente salida:

F000:3A0C
F000:3A70
F000:3B13
F000:4839
F000:48BA
F000:492A
F000:499A
F000:4A0A

con las direcciones donde se ha encontrado la ocurrencia buscada.  A continuación puedo explorar las proximidades de cada posición.  Por ejemplo para la primera:

-D f000:3a00

F000:3A00  61 73 65 20 36 2E 30 20-66 6F 72 20 49 42 4D 20   ase 6.0 for IBM
F000:3A10  54 68 69 6E 6B 50 61 64-00 20 20 20 20 20 00 43   ThinkPad. .C
F000:3A20  6F 70 79 72 69 67 68 74-20 31 39 38 35 2D 31 39   opyright 1985-19
F000:3A30  39 39 20 50 68 6F 65 6E-69 78 20 54 65 63 68 6E   99 Phoenix Techn
F000:3A40  6F 6C 6F 67 69 65 73 20-4C 74 64 2E 0D 41 6C 6C   ologies Ltd..All
F000:3A50  20 52 69 67 68 74 73 20-52 65 73 65 72 76 65 64   Rights Reserved
F000:3A60  0D 0D 28 43 29 20 43 4F-50 59 52 49 47 48 54 20   ..(C) COPYRIGHT
F000:3A70  49 42 4D 20 43 4F 52 50-4F 52 41 54 49 4F 4E 20   IBM CORPORATION


§5.3
  Si lo que se busca está en un fichero, primero hay que cargarlo.  Lo que se puede hacer de dos formas:  en la propia invocación de debug, o mediante los comandos N y L.  Por ejemplo, si queremos inspeccionar el contenido del fichero more.com, podemos utilizar dos formas:

C:\WINDOWS>debug c:\windows\command\more.com

C:\WINDOWS>debug
- N c:\windows\command\more.com

- L

La sintaxis para el prefijo N es:

N [path-name]nombre-de-fichero

En ambos casos el contenido del fichero será cargado en memoria y podrá ser utilizado.  Atención: después de la orden L ("Load"), debug le avisará si no encuentra el fichero, pero seguirá su funcionamiento.  En tal caso simplemente piensa que N señala el nombre de un nuevo fichero que creará, con el contenido de la memoria -de debug-, cuando posteriormente seleccione la opción escribir W ("Write").

Nota:  Como ya habrá deducido, debug permite leer un fichero, realizar modificaciones en él (con la opción E) y escribir el fichero modificado bajo otro nombre con N/W.

A continuación podemos comprobar el tamaño del fichero cargado.  Para ello usamos la opción R (ver estado de los registros) y obtenemos el siguiente resultado:

-R
AX=0000  BX=0000  CX=2917  DX=0000  SP=FFFE  BP=0000  SI=0000  DI=0000
DS=17A7  ES=17A7  SS=17A7  CS=17A7  IP=0100   NV UP EI PL NZ NA PO NC
17A7:0100 E8CD0F        CALL 10D0
-

Aquí nos interesa especialmente el contenido del registro CX, cuyo valor, 2917h (1519d) en este caso, señala el tamaño del fichero en bytes. También podíamos haber utilizado directamente el comando R CX que nos proporciona el valor de dicho registro:

-R CX
CX 2917
:
-

Nota:  A continuación del valor correspondiente, debug muestra dos puntos (:), señalando que puede cambiar el valor del registro introduciendo un nuevo número.  Pulse INTRO para dejar las cosas como están y volver al "prompt" (-) de debug.

Al llegar a este punto, es importante señalar que, a excepción de los ficheros .exe, debug carga los ficheros a partir de la dirección 100h (256d) de "su" memoria.  De forma que el primer octeto del fichero se carga en la dirección 100; el segundo en 101; etc.  En nuestro caso, el fichero se extenderá desde la posición 100h hasta 2A17h (100h + 2917h).

A continuación, para buscar la cadena "Microsoft" en el fichero, utilizamos cualquiera de los siguientes comandos:

- S 100 L 2917 "Microsoft"

- S 100 2a17 "Microsoft"

El primer comando señala el punto de inicio y el tamaño de la zona a buscar;  el segundo utiliza los puntos inicial y final de la exploración.  Con ambos se obtiene la misma respuesta:

17A7:1083
17A7:10B2

 

Truco:  Debido a la forma especial en que se cargan los ejecutables .exe, si quiere inspeccionar la forma nativa de uno de estos ficheros (como es su imagen en el disco), debe hacer una copia del fichero; renombrarlo con cualquier otra terminación (o sin ella) y cargar esta copia.  En caso contrario, debug se preparará para funcionar como un verdadero depurador de tiempo de ejecución (run-time), para lo que cargará el fichero y lo acomodará en memoria de forma que pueda ejecutarse [5].

 

Como ejercicio, compruebe las distintas longitudes y aspecto (especialmente al principio) de un mismo fichero .exe cargándolo con debug de las dos formas (busque en su sistema un .exe lo más pequeño posible).


§5.4
  Además de la capacidad de leer ficheros antes mencionada, quizás una de las opciones más interesantes (y peligrosas) de debug es la posibilidad de leer/escribir el contenido de disco a bajo nivel mediante los prefijos L ("Load") y W ("Write").  Se utiliza la siguiente sintaxis:

L [Dirección] [Unidad] [Primer_sector] [Número]

W [Dirección] [Unidad] [Primer_sector] [Número]

El significado de los parámetros es como sigue:

  • Dirección:  Posición de la memoria de debug, a partir de la cual se instalarán los datos leídos del disco, o se tomarán para el proceso de escritura (aquí se utiliza generalmente el valor 100h).

  • Unidad:  Valor numérico que indica la lógica a utilizar.  0 = A:;  1 = B:;  2 = C:;  3 = D:, etc [6]

  • Primer sector:  A partir del que se realizará la lectura, o se comenzará a escribir.

  • Número:  Número de sectores que se desea cargar/escribir.  Como debug no puede leer/escribir más de 64 Kbytes (216 = 65536) y los sectores de disco son de 512 bytes, el máximo número de sectores es 128 (80h).  Así pues, el valor máximo aquí es 80. 

Ejemplo:  Para obtener el contenido del sector de arranque ("Master boot sector" MBR) de un disquete en A:, utilizaremos el comando:

C:\WINDOWS>debug
- L 100 0 0 1

- D 100 L 200

Como el resultado no cabe en una pantalla, ingeniaremos un procedimiento para conservarlo en un fichero (más adelante nos servirá para otro propósito).  Para ello creamos un fichero script.txt con el siguiente contenido (dos INTRO después de la "Q"):

L 100 0 0 1
D 100 L 200
 
Q

 

A continuación invocamos debug con el comando

C:\WINDOWS>debug < D:\buzon\script.txt > D:\buzon\resultad.txt

Después de un breve parpadeo del disco y del disquete, vuelve el "prompt" del DOS.  La operación ha concluido, y en el directorio correspondiente de la unidad D: tenemos el fichero resultad.txt con el volcado del sector de arranque del disquete, además de los "ecos" de los comandos introducidos (los hemos mantenido para orientación del lector).


§5.5  La opción R("Register") sirve para comprobar y modificar el contenido de los registros; se ha comentado en el apartado dedicado a la arquitectura del procesador ( H3.2).

  Inicio.


[1]  En las primeras versiones de MS-DOS era un modesto programita DEBUG.COM de apenas 16 KB.  La versión que acompaña a Windows98 es ya un ejecutable en formato .EXE de 21 KB.

[2]  En realidad debug es una utilidad de enorme potencia que requeriría todo un libro para ella sola. Puede encontrarse una amplia descripción de esta herramienta en el libro de Paul Somerson "PC Magazine: DOS Powers Tools".

[3]  Desgraciadamente no hay una convención estándar sobre la colocación de los datos en la BIOS, de forma que cada fabricante sigue sus propias reglas y es imposible por tanto, utilizarla para cualquier tipo de identificación del sistema.

[4]  En palabras del mencionado Paul Somerson: "Desgraciadamente, debido a que DEBUG es un programa tan increíblemente potente, también es increíblemente peligroso".... "Hay un dicho que afirma que 'El software no puede destruir el hardware'.  Desgraciadamente esto no es cierto. Además de enviar mecanismos sensibles de la unidad de disco a tierra de nadie, es posible hacer saltar monitores o transformadores.  Repito, si sigue al pie de la letra las instrucciones y hace caso a todos los avisos, todo irá bien."

[5]  Recuerde que la imagen de un ejecutable .exe cargado en memoria es distinta de su imagen en disco.  Solo en los ejecutables .com la imagen en disco es una imagen exacta de su disposición en memoria.

[6]  Es evidente que un pequeño error de número en este parámetro puede acarrear consecuencias nefastas.  Por ejemplo, si cree que está "jugando" con el disquete e inadvertidamente se dirige al disco duro!!  (relea la nota [4]).