24 de maio de 2026

Reverse Engineering da Proteção de Captura de Tela de um Jogo

Começou com uma frustração simples: eu e meus amigos jogamos um jogo online juntos, e não conseguimos compartilhar tela no Discord. O jogo bloqueia. Tela preta, toda vez.

Eu poderia ter aceitado isso. Mas algo em saber que um software no meu computador estava deliberadamente me impedindo de fazer algo inofensivo me deixou curioso. Passei o dia inteiro nessa toca de coelho.

A primeira pista

Antes de tocar em qualquer ferramenta, notei algo estranho: semanas atrás, por alguns minutos, todo mundo no grupo conseguiu compartilhar tela ao mesmo tempo. Depois parou de novo. Pra todo mundo, simultaneamente.

Se a proteção fosse puramente client-side, cada computador decidiria isso de forma independente. O fato de ter quebrado e voltado em sincronia apontava pra algo server-side.

Um segundo detalhe apontava pro mesmo lado: streamer transmite esse jogo sem problema nenhum. Então o bloqueio não é incondicional: existe uma whitelist por conta em algum lugar, e o cliente pergunta ao servidor se deve aplicar. Quando esse serviço piscou, todo cliente caiu no estado de não bloquear, que é exatamente a janela sincronizada que a gente tinha visto.

Encontrando o mecanismo

Uma busca rápida confirmou a API usada: SetWindowDisplayAffinity, do user32.dll. Com a flag WDA_EXCLUDEFROMCAPTURE, qualquer captura de tela (Discord, OBS, navegador) enxerga a janela como preta.

As ferramentas óbvias, trancadas

O primeiro movimento natural é ver o programa rodando: anexar algo que registre a chamada na hora que ela acontece. Tentei o API Monitor, filtrei por SetWindowDisplayAffinity, e não apareceu nada. Tentei anexar um debugger, o x32dbg, até como administrador, e o processo se recusava a aparecer na lista pra anexar. O anti-cheat do jogo estava bloqueando ativamente qualquer coisa que tentasse observá-lo de fora.

Foi isso que me empurrou pra análise estática. Se eu não conseguia ver o processo enquanto ele rodava, eu ia ler o binário parado.

Abrindo a caixa preta com Ghidra

Nunca tinha feito engenharia reversa antes. Instalei o Ghidra, carreguei o executável do jogo, e busquei no import table por SetWindowDisplayAffinity. Um único resultado: um único lugar no código inteiro onde essa função era chamada.

void FUN_00be4080(int param_1, char param_2) {
    DWORD dwAffinity = 0x11;
    if (param_2 == '\0') 
    {
        dwAffinity = 0;
    }
    SetWindowDisplayAffinity(*(HWND *)(param_1 + 4), dwAffinity);
}

O código decompilado em volta era mais confuso do que uma função desse tamanho tinha o direito de ser, e os caminhos de arquivo embutidos no binário explicavam por quê: o cliente é escrito em Rust (a crate de janela tao apareceu nos símbolos). Incomum pra um jogo, e o motivo de uma função mais acima na cadeia ser grande demais pro Ghidra decompilar. E esse wrapper só executa a decisão; a escolha de verdade é uma flag numa estrutura por conta, preenchida com o que o servidor responde.

Patchei o executável diretamente, trocando a instrução que passava a flag de bloqueio por uma que sempre passava zero, e ver o decompilador atualizar em tempo real, refletindo a mudança, foi o momento em que entendi visceralmente o que é engenharia reversa. Só que nunca chegou a um jogo rodando: o launcher faz o hash do binário ao abrir, viu que o meu arquivo alterado não era o original, e baixou o certo de novo e rodou esse, calado. No decompilador o patch parecia certo; se funcionava de verdade eu nunca soube, porque a checagem de integridade desfez tudo antes de um único frame aparecer.

A camada de rede

Capturando tráfego com Wireshark, descobri que a decisão não vinha dos servidores do próprio jogo, mas de um SDK de terceiros (otimização de rede) embutido no cliente. Isso explicava a sincronização: quando o servidor deles caía, o sinal de bloqueio sumia pra todo mundo ao mesmo tempo.

O que ficou

Não resolvi o problema original: meus amigos ainda não conseguem ver minha stream do jogo. Mas saí entendendo, na prática, como o Windows controla captura de tela a nível de API, como um anti-cheat fecha a porta pra quem tenta observar ou modificar o processo de fora, e como um cliente defende o próprio binário justamente contra o tipo de patch que eu fiz.

A parte melhor não foi nenhuma descoberta específica. Foi tratar algo opaco e aparentemente fixo como um sistema que podia ser entendido.

← Voltar pro blog