// 10 intermediário patch de timestamp / inversão de comparação

Licença
Expirada

O software sabe que dia é hoje. Mas você pode mudar o que ele acha que é "expirado".

Trial de 2024. Você está em 2026. A matemática não te favorece.
O software compara time(NULL) com um timestamp hardcoded.
Duas abordagens: mudar o que o binário considera como data de expiração, ou mudar o que ele considera como "expirado".

~/crackmes/10-licenca-data
$ ./challenge
╔══════════════════════════════════════╗
║ LICENSA PRO v4.2 — TRIAL ║
╚══════════════════════════════════════╝

Expiração: 2024-01-01
Hoje: 2026-06-02

✗ LICENÇA EXPIRADA
Contate o suporte para renovar.
conceitos
Unix timestamp
Segundos desde 1970-01-01 UTC. O que o binário compara internamente. time(NULL) retorna o timestamp atual; a expiração é um valor fixo calculado a partir de constantes de data.
struct tm + timegm
A data hardcoded (2024-01-01) é convertida pra timestamp via timegm(). Os campos tm_year, tm_mon, tm_mday aparecem como constantes no disassembly de get_expiry().
LD_PRELOAD
Técnica de interceptação de syscalls sem modificar o binário. Sobrescreve time() com uma implementação que retorna um timestamp falso — invisível ao binário original.

Abre o disassembly de get_expiry().
Os valores EXPIRY_YEAR - 1900, EXPIRY_MONTH - 1 e EXPIRY_DAY são atribuídos como constantes ao struct tm.
Onde você vê 0x7c (124 = 2024 - 1900) no disassembly?

abordagem 1 — patchear a data
// passo 01
Localiza a constante no disassembly
bash
objdump -d ./challenge | grep -A15 ""
Você vai ver a constante 0x7c (decimal 124 = 2024 - 1900) sendo carregada no struct tm:
asm
401196:  c7 45 d0 7c 00 00 00   mov DWORD PTR [rbp-0x30], 0x7c   ; tm_year = 124
40119d:  c7 45 d4 00 00 00 00   mov DWORD PTR [rbp-0x2c], 0x0    ; tm_mon = 0
4011a4:  c7 45 d8 01 00 00 00   mov DWORD PTR [rbp-0x28], 0x1    ; tm_mday = 1
// passo 02
Troca 2024 por 2099
2099 - 1900 = 199 = 0xC7. Substitui o byte 7c por c7 no offset correto.
r2
r2 -w ./challenge
s 0x401199           # 3 bytes após o início da instrução = início da constante
wx c7 00 00 00       # 199 em little-endian
q
abordagem 2 — inverter a comparação
// passo 03
Troca jge por jl no main
O main faz if (now >= expiry) — em asm isso é um jge (opcode 7d). Trocando por jl (7c), o bloco de "expirado" nunca é atingido.
bash
objdump -d ./challenge | grep "jge"
# anota o endereço
r2 -w ./challenge
s <endereço do jge>
wx 7c
q
abordagem 3 — LD_PRELOAD (sem modificar o binário)
// passo 04
Intercepta time() em runtime
c
// fake_time.c
#include <time.h>
time_t time(time_t *t) {
    time_t fake = 1000000000; // 2001 — antes da expiração
    if (t) *t = fake;
    return fake;
}
bash
gcc -shared -fPIC -o fake_time.so fake_time.c
LD_PRELOAD=./fake_time.so ./challenge
O binário não foi modificado. LD_PRELOAD injeta a implementação falsa de time() em runtime. O programa acredita que está em 2001.
// a matemática do timestamp
  2024-01-01 UTC  →  Unix timestamp: 1704067200
  2026-06-02 UTC  →  Unix timestamp: 1748822400

  1748822400 >= 1704067200  →  true  →  expirado

  patcheando para 2099-01-01:
  timestamp: 4070908800

  1748822400 < 4070908800   →  false  →  válido ✓
comparativo
AbordagemModifica o binárioPersisteRastro forense
patchear tm_yearSim — 1 byte em .textSimConstante alterada
inverter jge→jlSim — 1 byte em .textSimOpcode alterado
LD_PRELOADNãoNão (sessão)Nenhum no binário
faketimeNãoNãoNenhum no binário

Se o software enviasse o timestamp pro servidor e o servidor validasse a licença remotamente, qual dessas abordagens ainda funcionaria? Qual exigiria algo diferente — como interceptação de tráfego de rede?