Parando o tempo

Um dos problemas em usar um sistema operacional proprietário, com o Mac OS X, é a abundância de software proprietário, já empacotado e (supostamente) fácil de usar. Isso não é uma coisa ruim per se, afinal se um programa faz o que você precisa, você vai lá e compra; é claro que eu prefiro quando tenho acesso ao código fonte. Ler código dos outros é muito instrutivo. Fora todos os quesitos de liberdade, que nem vem ao caso.

O problema maior, no entanto, é quando uma software house decide parar de suportar um produto. Ou simplesmente a empresa pede falência e não há meios de comprar o software. Felizmente, a maioria desses produtos (que são muito específicos), são daqueles softwares que funcionam por uma quantidade de dias e depois não abrem mais até que se compre a licença.

Por explorarem pequenos nichos de mercado (para uma plataforma não tão disseminada por aí), nem sempre é possível achar soluções paleativas prontas. E na falta (eu desconheço alguma coisa tão boa quanto o IDA Pro para OS X) de um disassembler ou um debugger (para software sem símbolos de debug) é que surgem as soluções mais bizarras.

Esses programas usam a biblioteca padrão C para obter do sistema a data e a hora atual, e fazer um cálculo para determinar se o programa deve continuar funcionando ou não. Geralmente, em UNIX, usa-se as chamadas time() e gettimeofday() para obter essa informação. Como geralmente a biblioteca C não é compilada estaticamente (isto é: soldada ao binário do programa), é possível fazer um truque legal e parar o tempo.

O truque é simples e consiste apenas em criar uma biblioteca compartilhada que substitua as chamadas da biblioteca C do sistema. As funções que queremos substituir são simples: apenas retornam (de alguma forma) o UNIX time, portanto podem ser escritas em pouquíssimas linhas de C. Por exemplo, a chamada time() pode ser escrita dessa forma (retornando o UNIX time de agora pouco):

int time(void *trash) { return 1213097388; }

No caso da chamada gettimeofday(), precisamos de uma estrutura timeval (copiada da página de manual, mas trocamos os tipos time_t e suseconds_t por um unsigned, pois não queremos fazer o #include de cabeçalhos do sistema para evitar que a estrutura e/ou a chamada seja definida e cause um erro de compilação):

struct timeval {
unsigned tv_sec;    /* seconds since Jan. 1, 1970 */
unsigned tv_usec;  /* and microseconds */
};

Com essa estrutura em mãos, é fácil fazer a função substituta:

int gettimeofday(struct timeval *tv, void *trash) {
tv->tv_sec = 1213097388;
tv->tv_usec = 0;

return 0;
}

Pronto. O artefato capaz de parar o tempo está quase construído. Basta compilar.

A parte de compilar uma biblioteca compartilhada depende muito do sistema operacional; consulte o manual do gcc para saber dos específicos. No caso do OS X, é só usar a flag “-dynamiclib” e gerar o binário com uma extensão “.dylib” (não é realmente necessário, mas é interessante):

gcc -dynamiclib -o hiro-nakamura.dylib hiro-nakamura.c

Para carregar essa biblioteca junto com o programa também é uma tarefa que depende do sistema operacional. No caso do OS X, usa-se uma combinação de variáveis de ambiente:

export DYLD_FORCE_FLAT_NAMESPACE=1
export DYLD_INSERT_LIBRARIES=hiro-nakamura.dylib
export DYLD_BIND_AT_LAUNCH=YES

Pronto. Assim que rodar qualquer programa neste shell (no caso, bash, adapte o export para sua concha preferida), o tempo estará congelado.

Yatta! :)

Leave a Comment