// estudo de caso

Como o FanPilot foi feito

Um estudo de caso: engenharia reversa do controle de fans de um fabricante, um celular na frente dele e o cuidado para nada estragar o hardware no caminho.

Do painel até o fan: A biblioteca cliente do Gigabyte Control Center conversa por .NET Remoting (IPC) com o EasyTune Engine Service, que grava a curva no chip ITE IT8689E. O FanPilot usa a mesma biblioteca cliente e serve o painel por HTTP e SSE ao celular pareado.
  1. Celular / tabletpareado por QR · HTTP + SSE
  2. FanPilotserviço · painel web · regras de segurança
  3. Gigabyte.Engine.EnvironmentControl.dllSDK cliente do GCC, carregado por reflection
  4. EasyTuneEngineService.NET Remoting · ipc://GigabyteEngine · SYSTEM, só administradores
  5. ITE IT8689E Super I/OSmartGuardian · a curva roda no hardware

O problema

Nas placas Gigabyte, o único jeito de mudar as curvas dos fans pelo Windows é o Gigabyte Control Center: um programa de desktop, no próprio PC. O FanPilot nasceu para fazer o mesmo pelo celular, na mesma rede Wi-Fi, sem substituir o serviço do fabricante e sem distribuir nenhum arquivo dele.

Achando a entrada

A análise partiu dos arquivos instalados: metadados e IL dos assemblies .NET, dumps de API só por reflection e decompilação pontual, confirmados depois em execução com pequenos probes (tools/Probe*.cs). A máquina de referência foi uma B760M GAMING X AX com um i5-13400.

A arquitetura se revelou um cliente e um serviço conversando por .NET Remoting:

  • O GCC.exe é só a interface. Ele carrega a Gigabyte.Engine.EnvironmentControl.dll, um SDK cliente de classes *Module.
  • Esses módulos se conectam a ipc://GigabyteEngine/EnvironmentControl/<Objeto>, publicados pelo EasyTuneEngineService, um serviço do Windows que roda como SYSTEM.
  • O serviço faz o trabalho de verdade, até a Gigabyte.EnvironmentControl.LowPinCount.dll, que conversa com o chip Super I/O ITE IT8689E da placa pelas portas LPC.

Dois detalhes pesaram logo de cara:

  • A ACL da porta IPC só admite Administradores: um processo sem elevação recebe “acesso negado”. Por isso o FanPilot pede elevação.
  • A primeira chamada numa conexão nova falha com “o pipe está sendo fechado”; tentar de novo funciona. O FanPilot tenta de novo uma vez.

Lendo os sensores

HardwareMonitorControlModule.GetCurrentMonitoredData devolve cada leitura como {Type, Title, DeviceUUID, Value, Unit}: na placa de teste, 23 itens, das temperaturas da CPU e do VRM ao RPM dos fans, tensões, clocks e potência da CPU. A GPU não está ali, então o FanPilot lê as placas NVIDIA pelo nvml.dll do próprio driver.

Gravando uma curva

O Control Center escolhe uma “geração” de controle de fans pelo número de grupo da placa. Nesta placa é a terceira, e o módulo que funciona é o SmartGuardianFanControlCoordinateSystemModule: Get e Set de uma curva por header, escolha do sensor que comanda cada um, temperaturas e RPM lado a lado.

Uma curva é um SmartFanDataPointSet: um modo e uma lista de pontos, X em °C e Y como PWM de 0 a 255. Dentro do serviço, o Set passa por um conversor que a transforma nos parâmetros nativos SmartGuardian do chip. Isso tem uma consequência grande: a curva roda no hardware, e continua valendo com o FanPilot, e até o Windows, fechado.

O ponto que precisava ser 255

O primeiro teste de escrita “não funcionou”: todos os fans foram para a velocidade máxima. O conversor, lido em SmartGuardianFanConfigConverter, explicou o porquê. Ele mapeia os pontos assim:

Ponto Vira
P[0].X a temperatura em que o fan desliga
P[1] temperatura e PWM de partida
P[2..n-2] “extra vectors”: início, PWM e inclinação de cada trecho
P[n-1] a temperatura de velocidade máxima, e o Y precisa ser 255

Com menos de três pontos, ou com o último ponto abaixo de 255, a conversão aborta em silêncio e o serviço grava uma configuração zerada, que o chip trata como velocidade máxima. Por isso o editor do FanPilot sempre termina a curva em 100%, e a validação recusa tudo o que o chip leria errado.

A correção foi confirmada na placa real: o System 1, com 40% até 70 °C, foi de 2352 para 1573 RPM em 8 segundos, e voltou a subir quando a curva original foi restaurada.

Sem os arquivos do fabricante

O FanPilot não redistribui nem compila contra nenhum arquivo da Gigabyte. Uma camada fina, a GigabyteApi, resolve por reflection os tipos e membros do Control Center já instalado no PC, confere o contrato inteiro na partida e guarda as referências. Se uma atualização do Control Center mudar algo, o FanPilot diz no painel exatamente o que falta e segue pelo LibreHardwareMonitor. Um único build atende todas as placas, e a CI consegue gerá-lo.

Todas as outras placas

Placas sem o Control Center passam pelo LibreHardwareMonitor e pelo driver PawnIO, que o FanPilot oferece instalar pelo winget. Nelas, o chip só aceita uma porcentagem e não guarda curva, então o FanPilot executa as curvas, uma vez por segundo. Cada curva tem uma resposta (Rápida, Equilibrada ou Suave): a temperatura é suavizada, o fan só desacelera depois de o PC esfriar alguns graus e a velocidade muda aos poucos. Fechar o FanPilot, um sensor sem leitura ou falhas seguidas devolvem todos os fans à BIOS.

No Linux (beta), o FanPilot chega à placa pelos drivers hwmon do kernel (nct6775, it87, asus-ec-sensors…) e à GPU pelo NVML ou pelo amdgpu, como um serviço do systemd.

Os dois caminhos ficam atrás de uma interface só, a IBoard, e o resto do FanPilot não precisa saber com qual está falando.

O programa em volta

  • Um serviço, que sobe com o Windows antes do logon e é reiniciado pelo Windows se cair, mais um ícone na bandeja e uma interface no terminal.
  • Um painel web servido pelo próprio FanPilot por HTTP, com as leituras ao vivo empurradas por Server-Sent Events.
  • Pareamento por QR: um código de 144 bits, de uso único, mostrado no PC, e depois um cookie de sessão do qual só o hash fica guardado. O servidor só atende pedidos endereçados ao PC (contra DNS rebinding) e confere a origem e um cabeçalho próprio em toda escrita (contra CSRF).
  • Regras de segurança num lugar só: pelo menos 20% em toda curva, 100% no último ponto até 90 °C, bombas a 100% ou num mínimo escolhido e um watchdog de 15 segundos que reaplica a sua curva se algo a sobrescrever.

Como é testado

  • 308 casos de teste em C# (xUnit), sem hardware e sem Administrador, contra uma placa falsa com cenários: a placa de demonstração, um pior caso com oito fans e nomes longos, uma placa sem suporte, um serviço fora do ar, um lento.
  • Testes de unidade do JavaScript do painel (o test runner do Node): matemática das curvas, i18n, sensores.
  • 70 testes de ponta a ponta em 15 arquivos do Playwright, rodando no Edge: o editor de curvas por mouse, teclado e toque, pareamento, alertas, histórico, modo painel, a integração MQTT, reconexão depois de reiniciar, acessibilidade com o axe-core e screenshots em 320, 390 e 1024 px nos dois temas.
  • --hardware-test na placa real: velocidade fixa, uma curva vigiada, Turbo, o fan da GPU e um reinício, nunca abaixo de 40%, recusando o fan da CPU e as bombas, parando se a CPU ou a GPU passarem de 80 °C e devolvendo tudo como estava.
  • CI a cada push (GitHub Actions): build, tudo o que está acima como Administrador e um smoke test do zip da release.
Curvas que você arrasta

Fontes: docs/REVERSE_ENGINEERING.md e as specs de design no repositório. REVERSE_ENGINEERING.md