Comparando strings no WinDbg
Caloni, 2011-05-22 computer blogO WinDbg fornece aos programadores diversos meios (muitos redundantes) de comparar valores inteiros em quaquer lugar da memória, em qualquer tamanho (8, 16, 32, 64 bits). Porém, menos tamanhos arbitrários, que é o que acontece quando precisamos comparar strings, em geral seguindo o padrão em C, com o zero terminador mas sem um contador de caracteres. De qualquer forma, não é possível comparar streams ilimitadas de bytes. Estamos presos aos 64 bits máximos.
Uma solução simples e rápida é comparar os 4 primeiros bytes de uma string, ou os 4 primeiros bytes que diferem de uma lista grande.
Por exemplo, imagine o seguinte código que abre todos os arquivos da pasta de sistema:
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <stdio.h>
int main()
{
CHAR sysPath[MAX_PATH];
CHAR findPath[MAX_PATH];
GetSystemDirectory(sysPath, MAX_PATH);
sprintf(findPath, "%s\\*.*", sysPath);
WIN32_FIND_DATA findData;
HANDLE findH = FindFirstFile(findPath, &findData);
if( findH != INVALID_HANDLE_VALUE )
{
do
{
CHAR filePath[MAX_PATH];
sprintf(filePath, "%s\\%s", sysPath, findData.cFileName);
HANDLE fileH = CreateFile(filePath, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if( fileH )
{
CHAR firstBytes[4];
DWORD wasRead = 0;
if( ReadFile(fileH, firstBytes, 4, &wasRead, NULL)
&& wasRead == 4 )
{
printf("%s: %02X %02X %02X %02X\n", findData.cFileName,
(int) firstBytes[0], (int) firstBytes[1],
(int) firstBytes[2], (int) firstBytes[3]);
}
CloseHandle(fileH);
}
}
while( FindNextFile(findH, &findData) );
FindClose(findH);
}
}
Queremos colocar um breakpoint no momento em que o arquivo shell32.dll estiver sendo aberto. Para isso, devemos nos atentar para os parâmetros passados para a função CreateFile.
windbg strcmpwindbg1.exe 0:000> bp kernel32!CreateFileA Breakpoint 0 hit eax=001bf918 ebx=7efde000 ecx=001bf7e0 edx=001bf7e0 esi=001bf824 edi=001bfd90 eip=7663ca6e esp=001bf804 ebp=001bfd90 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 kernel32!CreateFileA: 7663ca6e 8bff mov edi,edi 0:000> da poi(esp+4) 001bf918 "C:\Windows\system32\accessibilit" 001bf938 "ycpl.dll" 0:000> g Breakpoint 0 hit eax=001bf918 ebx=7efde000 ecx=001bf7e0 edx=001bf7e0 esi=001bf824 edi=001bfd90 eip=7663ca6e esp=001bf804 ebp=001bfd90 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 kernel32!CreateFileA: 7663ca6e 8bff mov edi,edi 0:000> da poi(esp+4) 001bf918 "C:\Windows\system32\ACCTRES.dll"
O padrão aqui é que todo path passado para o CreateFile vai começar com c:\windows\system32, o que não é uma informação que podemos usar para buscar um arquivo específico.
Temos que nos atentar para o padrão de bits após esse path. Vamos dar uma olhada por dentro da string.
0:000> db 001bf918 001bf918 43 3a 5c 57 69 6e 64 6f-77 73 5c 73 79 73 74 65 C:\Windows\syste 001bf928 6d 33 32 5c 41 43 43 54-52 45 53 2e 64 6c 6c 00 m32\ACCTRES.dll. 001bf938 79 63 70 6c 2e 64 6c 6c-00 cc cc cc cc cc cc cc ycpl.dll........
O nome do arquivo começa no offset 16+4 = 20, ou 14 em hexa. Dessa forma, podemos capturar o padrão de bits da seguinte maneira:
0:000> dd poi(esp+4)+14 l1 001bf92c 54434341
Para nos certificarmos que é realmente esse o padrão, e para já montarmos nosso próprio padrão para o shell32.dll, vamos alocar um pedaço de memória e verificar se a sequência de bits está correta.
0:000> dd poi(esp+4)+14 l1 001bf92c 54434341 0:000> .dvalloc 100 Allocated 1000 bytes starting at 00030000 0:000> ea 00030000 "ACCTRES.dll" 0:000> dd 00030000 l1 00030000 54434341
Ótimo. Os padrões bateram, então podemos colocar um breakpoint condicional partindo do padrão de bits do nome do arquivo que precisamos.
0:000> bp kernel32!CreateFileA "j (poi(poi(esp+4)+14)=6c656873) ''; 'g'" breakpoint 0 redefined 0:000> g eax=0021f48c ebx=7efde000 ecx=0021f354 edx=0021f354 esi=0021f398 edi=0021f904 eip=7663ca6e esp=0021f378 ebp=0021f904 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 kernel32!CreateFileA: 7663ca6e 8bff mov edi,edi 0:000> da poi(esp+4) 0021f48c "C:\Windows\system32\shell32.dll"
Com isso, economizamos alguns minutos de puro tédio, verificando os nomes um a um conforme eles são abertos. Ou, dependendo da massa de dados, algumas décadas. Quem sabe. Pode ser muito mais útil um outro dia.