Semente de números pseudoaleatórios
O computador não sorteia de verdade. A partir da mesma semente, o gerador repete sempre a mesma sequência.
Um computador não joga dados. O que as linguagens chamam de "aleatório" é um gerador pseudoaleatório: uma conta que, a partir de um número inicial, a semente (seed), produz uma sequência que parece sorteada. Mesma semente, mesma sequência, sempre.
Em Python:
>>> import random
>>> random.seed(10); [random.randint(1, 13) for _ in range(5)]
[10, 1, 7, 8, 10]
>>> random.seed(10); [random.randint(1, 13) for _ in range(5)]
[10, 1, 7, 8, 10]
Em C, no Linux, é igual com srand(semente) e rand(). Depois de srand(1), o primeiro rand() da biblioteca C do Linux (glibc) é sempre 1804289383. Para repetir esses números sem escrever C, o Python chama a própria biblioteca do sistema:
import ctypes
libc = ctypes.CDLL("libc.so.6") # só no Linux (ou WSL)
libc.srand(1)
print(libc.rand()) # 1804289383
Isso vira falha de segurança quando o número sorteado protege alguma coisa (uma senha temporária, um token, uma chave) e a semente pode ser descoberta: um valor fixo no código, o horário em que o programa começou, o número do processo. Quem acha a semente recalcula todos os "sorteios". Para segredos, use um gerador criptográfico: em Python, o módulo secrets.
Em engenharia reversa, a pergunta é sempre a mesma: qual semente vale na hora do sorteio? Procure as chamadas a seed/srand, veja com que valor e em que ordem acontecem, e reproduza a sequência.