Descargar

Debug (página 2)


Partes: 1, 2

Una vez que el programa está en ejecución, el indicador ("prompt") es un guión "-", indicando que el "Shell" (http://www.zator.com/Hardware/images/Ico_hoja.gif ) 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 (http://www.zator.com/Hardware/images/Ico_hoja.gif 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 (http://www.zator.com/Hardware/images/Ico_hoja.gif 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 (http://www.zator.com/Hardware/images/Ico_hoja.gif 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 http://www.zator.com/Hardware/images/Ico_hojaFup.gif.

Hemos visto un ejemplo al tratar de los servicios de interrupciones del PC (http://www.zator.com/Hardware/images/Ico_hoja.gif Servicios-BIOS), aquí mostraremos otro.  Sabemos que el punto de inicio de la BIOS es la dirección F000:FFF0 (http://www.zator.com/Hardware/images/Ico_hoja.gif 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:windowscommandmore.com

C:WINDOWS>debug – N c:windowscommandmore.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.  http://www.zator.com/Hardware/images/Bombilla_.gifAtenció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:buzonscript.txt > D:buzonresultad.txt

 

 

 

 

Autor:

José Luis Peñaloza Gallegos

México

11 de Septiembre de 2008

Partes: 1, 2
 Página anterior Volver al principio del trabajoPágina siguiente