O BattlEye é predominantemente um anti-batota alemão de terceiros, desenvolvido principalmente por
Bastian Heiko Suter, 32 anos. Ele fornece (ou tenta fornecer) aos editores de jogos um sistema anti-fraude fácil de usar que usa mecanismos de proteção geral, bem como a detecção de truques para jogos específicos para otimizar a segurança. Conforme indicado no site do produto, ele sempre permanece no topo da tecnologia moderna e utiliza métodos inovadores de proteção e detecção; obviamente, isso é uma consequência da nacionalidade do desenvolvedor:
QUALITY MADE IN GERMANY
. O BattlEye consiste em muitos elementos que trabalham juntos para encontrar trapaceiros em jogos que pagaram pelo uso do produto. Os quatro elementos principais são:
- BEService
- Um serviço de sistema Windows que se comunica com o servidor BattlEye BEServer , que fornece comunicação cliente-servidor com BEDaisy e BEClient .
- BEDaisy
- Um driver de kernel do Windows que registra mecanismos preventivos de processamento de eventos e mini filtros para impedir que trapaceiros modifiquem ilegalmente o jogo
- Becient
- Uma biblioteca do Windows conectada dinamicamente, responsável pela maioria dos vetores de detecção, incluindo os descritos neste artigo. Após a inicialização, ele é anexado ao processo do jogo.
- Beserver
- Servidor de back-end proprietário, responsável por coletar informações e tomar medidas específicas contra trapaceiros.
Shellcode
Recentemente, uma cópia do código do shell BattlEye apareceu na Internet e decidimos escrever sobre o que exatamente a versão atual do BattlEye está procurando. Não analisamos o BattlEye por seis meses, portanto, nosso último despejo do código do shell provavelmente está desatualizado. Várias partes do código foram recuperadas apenas da memória deste último despejo, supondo que o BattlEye apenas completou o código do shell e não excluiu os procedimentos de detecção anteriores.
Como
O BattlEye supostamente transmite o código de shell do seu servidor para um serviço do Windows chamado BEService. Este serviço se comunica com o módulo BEClient localizado dentro do jogo. A troca de dados é realizada através do
\.namedpipeBattleye
e até 2018 não era criptografada. Agora, todos os dados transmitidos são criptografados com um xor-encryptor com chaves muito pequenas, o que facilita extremamente a execução de ataques de texto simples conhecidos. Quando o código do shell é transmitido ao cliente, ele é localizado e executado fora de todos os módulos conhecidos, o que facilita a determinação. Para criar um despejo de código de shell, você pode processar as funções padrão da API do Windows, como CreateFile, ReadFile etc., e despejar a área de memória correspondente de todos os módulos de chamada (solicitando informações de memória para o endereço retornado) que estão fora de todos os módulos conhecidos ou verifique periodicamente o espaço de memória virtual do jogo em busca de memória executável fora de todos os módulos conhecidos e despeje-o em disco. Ao mesmo tempo, você precisa acompanhar quais áreas já foram despejadas, para que, como resultado, você não obtenha muitos despejos idênticos.
Explicação
Os fragmentos do pseudocódigo apresentados no artigo são
fortemente modificados em prol da beleza. Você não poderá despejar o código do shell BattlEye e reconhecer imediatamente essas partes; o shellcode não contém chamadas de função e muitos algoritmos no artigo são implantados. Mas, na verdade, isso não é importante, porque quando você terminar de ler sobre essa terrível antiguidade, terá a oportunidade de contorná-la (:
Classificação de memória
O mecanismo de detecção mais comum em anti-fraudes é a enumeração de memória e a varredura de memória para procurar imagens de fraude
conhecidas . É fácil de implementar e, como o passado mostrou, com a abordagem correta, é bastante eficaz se você não esqueceu os conceitos básicos do assembler e colocou na lista negra o prólogo de uma função comum.
Battleye repete todo o espaço de endereço do processo do jogo (o processo atual neste contexto) e realiza várias verificações no desempenho da página e encontra o código do shell fora do espaço de memória correspondente.
Veja como isso é implementado em Battleye:
Anomalias de memória
O BattlEye marca todas as anomalias no espaço de endereço da memória, principalmente a memória dos módulos executáveis que não correspondem à imagem carregada:
void memory::anomaly_check(MEMORY_BASIC_INFORMATION memory_information) {
Digitalizando para padrões
Como mencionado acima, o BattlEye também verifica a memória dos processos locais quanto à presença de vários padrões claramente definidos, como pode ser visto na implementação mostrada abaixo.
Ao ler esse pseudo-código, você pode supor que
essas verificações possam ser contornadas reescrevendo a área de código de cada módulo carregado, pois elas não serão varridas em busca de padrões em imagens conhecidas. Para não cair nas verificações de integridade, é necessário fazer o download de todas as áreas compactadas e na lista de permissões e reescrever as áreas de código marcadas como
RWX , porque não podemos executar verificações de integridade sem emular o empacotador. Na versão atual do código de shell BattlEye, esses padrões de memória são codificados:
[05 18] ojectsPUBGChinese [05 17] BattleGroundsPrivate_CheatESP [05 17] [%.0fm] %s [05 3E] 0000Neck0000Chest0000000Mouse 10 [05 3F] PlayerESPColor [05 40] Aimbot: %d02D3E2041 [05 36] HackMachine [05 4A] VisualHacks.net [05 50] 3E232F653E31314E4E563D4276282A3A2E463F757523286752552E6F30584748 [05 4F] DLLInjection-master\x64\Release\ [05 52] NameESP [05 48] Skullhack [05 55] .rdata$zzzdbg [05 39] AimBot [05 39] EB4941803C123F755C623FEB388D41D0FBEC93C977583E930EB683E1DF [05 5F] 55E9 [05 5F] 57E9 [05 5F] 60E9 [05 68] D3D11Present initialised [05 6E] [ %.0fM ] [05 74] [hp:%d]%dm [05 36] 48836424380488D4C2458488B5424504C8BC848894C24304C8BC7488D4C2460 [05 36] 741FBA80000FF15607E0085C07510F2F1087801008B8788100EB [05 36] 40F2AA156F8D2894E9AB4489535D34F9CPOSITION0000COL [05 7A] FFE090 [05 79] %s00%d00POSITION0000COLOR0000000 [05 36] 8E85765DCDDA452E75BA12B4C7B94872116DB948A1DAA6B948A7676BB948902C [05 8A] n<assembly xmlsn='urn:schemas-mi
Esses padrões de memória também contêm um cabeçalho de dois bytes, ou seja, o valor estático desconhecido
05
e um identificador exclusivo.
O que não veremos é que o BattlEye também transmite dinamicamente padrões do
BEServer e os envia para o
BEClient , mas não discutiremos isso no artigo.
Eles são verificados iterativamente pelo seguinte algoritmo:
void memory::pattern_check(void* current_address, MEMORY_BASIC_INFORMATION memory_information) { const auto is_user32 = memory_information.allocation_base == GetModuleHandleA("user32.dll"
Validação de módulos específicos (Microsoft)
As verificações de módulo relatam a presença de módulos específicos carregados no jogo:
void memory::module_specific_check_microsoft(MEMORY_BASIC_INFORMATION memory_information) { auto executable = memory_information.protect == PAGE_EXECUTE || memory_information.protect == PAGE_EXECUTE_READ || memory_information.protect == PAGE_EXECUTE_READWRITE auto allocated = memory_information.state == MEM_COMMIT if (!allocated || !executable) continue auto mmres_handle = GetModuleHandleA("mmres.dll" auto mshtml_handle = GetModuleHandleA("mshtml.dll" if (mmres_handle && mmres_handle == memory_information.allocation_base) { battleye_module_anomaly_report module_anomaly_report module_anomaly_report.unknown = 0 module_anomaly_report.report_id = 0x5B module_anomaly_report.identifier = 0x3480 module_anomaly_report.region_size = memory_information.region_size battleye::report(&module_anomaly_report, sizeof(module_anomaly_report), 0 } else if (mshtml_handle && mshtml_handle == memory_information.allocation_base) { battleye_module_anomaly_report module_anomaly_report module_anomaly_report.unknown = 0 module_anomaly_report.report_id = 0x5B module_anomaly_report.identifier = 0xB480 module_anomaly_report.region_size = memory_information.region_size battleye::report(&module_anomaly_report, sizeof(module_anomaly_report), 0 } }
Verificando módulos específicos (desconhecido)
Uma verificação de módulos específicos foi adicionada ao sistema, que sinaliza ao servidor que você carregou os módulos que atendem a
qualquer um destes critérios:
void memory::module_specific_check_unknown(MEMORY_BASIC_INFORMATION memory_information) { const auto dos_header = (DOS_HEADER*)module_handle const auto pe_header = (PE_HEADER*)(module_handle + dos_header->e_lfanew const auto is_image = memory_information.state == MEM_COMMIT && memory_information.type == MEM_IMAGE if (!is_image) return const auto is_base = memory_information.base_address == memory_information.allocation_base if (!is_base) return const auto match_1 = time_date_stamp == 0x5B12C900 && *(__int8*)(memory_information.base_address + 0x1000) == 0x00 && *(__int32*)(memory_information.base_address + 0x501000) != 0x353E900 const auto match_2 = time_date_stamp == 0x5A180C35 && *(__int8*)(memory_information.base_address + 0x1000) != 0x00 const auto match_2 = time_date_stamp == 0xFC9B9325 && *(__int8*)(memory_information.base_address + 0x6D3000) != 0x00 if (!match_1 && !match_2 && !match_3) return const auto buffer_offset = 0x00
Não sabemos quais módulos atendem a esses critérios, mas suspeitamos que esta seja uma tentativa de detectar um conjunto muito limitado de módulos de truques específicos.
Adendo: @ how02 nos informou que o módulo
action_x64.dll
possui um
0x5B12C900
e
0x5B12C900
e contém uma área de código na qual você pode escrever; como mencionado anteriormente, isso pode ser usado para exploração.
Proteção de memória
O BattlEye também implementa um procedimento de detecção muito dúbio, que, em nossa opinião, procura memória com o sinalizador
PAGE_GUARD definido , sem realmente verificar se o sinalizador
PAGE_GUARD está
definido :
void memory::guard_check(void* current_address, MEMORY_BASIC_INFORMATION memory_information) { if (memory_information.protect != PAGE_NOACCESS) { auto bad_ptr = IsBadReadPtr(current_address, sizeof(temporary_buffer auto read = NtReadVirtualMemory( GetCurrentProcess(), current_address, temporary_buffer, sizeof(temporary_buffer), 0 if (read < 0 || bad_ptr) { auto query = NtQueryVirtualMemory( GetCurrentProcess(), current_address, 0, &new_memory_information, sizeof(new_memory_information), &return_length memory_guard_report.guard = query < 0 || new_memory_information.state != memory_information.state || new_memory_information.protect != memory_information.protect if (memory_guard_report.guard) { memory_guard_report.unknown = 0 memory_guard_report.report_id = 0x21 memory_guard_report.base_address = memory_information.base_address memory_guard_report.region_size = (int)memory_information.region_size memory_guard_report.memory_info = memory_information.type | memory_information.protect | memory_information.state battleye::report(&memory_guard_report, sizeof(memory_guard_report), 0 } } } }
Classificação da janela
O código do shell BattlEye itera sobre cada uma das janelas atualmente visíveis durante o jogo, ignorando as janelas de cima para baixo (por valor z).
GetWindowThreadProcessId
janela dentro do jogo são excluídos dessa enumeração, e isso é determinado chamando
GetWindowThreadProcessId
. Portanto, você pode vincular a função correspondente ao falso proprietário da janela para que o
BattlEye não verifique sua janela .
void window_handler::enumerate() { for (auto window_handle = GetTopWindow window_handle window_handle = GetWindow(window_handle, GW_HWNDNEXT),
Pesquisa anomalia
Se menos de duas janelas estiverem marcadas, uma notificação será enviada ao servidor. Provavelmente, isso é feito para impedir o patch das funções correspondentes que não permitem que o código do shell BattlEye examine qualquer janela:
void window_handler::check_count() { if (window_handler::windows_enumerated > 1) return
Classificação do processo
Ao chamar
CreateToolhelp32Snapshot
itera todos os processos em execução, mas
não processa nenhum erro , facilitando o patch e evitando os seguintes procedimentos de detecção:
Verificação de caminho
Se a imagem estiver dentro de pelo menos dois subdiretórios (contando a partir da raiz do disco), o sistema sinalizará os processos se o caminho para a imagem correspondente contiver pelo menos uma destas linhas:
Desktop Temp FileRec Documents Downloads Roaming tmp.ex notepad. ...\. cmd.ex
Se o caminho para o arquivo executável corresponder a uma dessas linhas, o servidor receberá uma notificação sobre o caminho para o arquivo executável, além de informações sobre se o processo pai é um dos seguintes (contém o bit de sinalizador correspondente enviado ao servidor):
steam.exe [0x01] explorer.exe [0x02] lsass.exe [0x08] cmd.exe [0x10]
Se o cliente não puder abrir o descritor com os direitos
QueryLimitedInformation
apropriados, ele definirá o bit de sinalizador
0x04
, se a causa do erro quando a chamada do
OpenProcess
falhar não for
ERROR_ACCESS_DENIED
, que nos fornece o último contêiner de enumeração para o valor do sinalizador correspondente:
enum BATTLEYE_PROCESS_FLAG { STEAM = 0x1, EXPLORER = 0x2, ERROR = 0x4, LSASS = 0x8, CMD = 0x10 }
Se o processo pai for steam, o sinalizador será definido instantaneamente para o usuário e o servidor será informado sobre isso com o ID de notificação
0x40
Nome da imagem
Se o processo atender a um dos muitos critérios apresentados abaixo, você definirá instantaneamente um sinalizador e isso será relatado ao servidor com o ID de notificação
0x38
"Loadlibr" "Rng " "A0E7FFFFFF81" "RNG " "90E54355" "2.6.ex" "TempFile.exe"
Sobreposição de jogos do Steam
O BattlEye monitora o processo de sobreposição de jogos do Steam, responsável pela sobreposição no jogo, conhecida pela maioria dos usuários do Steam. O nome completo do host da sobreposição dos Jogos Steam é
gameoverlayui.exe
; sabe-se que é frequentemente usado para renderizações de exploração, porque é muito fácil hackear e executar renderizações ilegais na janela do jogo. A verificação tem a seguinte condição:
file size != 0 && image name contains (case insensitive) gameoverlayu
Outras verificações específicas para a sobreposição de jogos Steam são quase idênticas aos procedimentos executados para o próprio processo do jogo, portanto, são omitidas no pseudo-código.
Varredura de memória de sobreposição de vapor
O processo de sobreposição de jogos Steam é verificado quanto a padrões e anomalias. Não fomos capazes de nos aprofundar na toca do coelho e descobrir para que servem esses padrões, porque eles são muito generalizados e provavelmente associados a módulos de truques.
void gameoverlay::pattern_scan(MEMORY_BASIC_INFORMATION memory_information) {
O procedimento de digitalização também procura anomalias na forma de código executável fora das imagens baixadas, sugerindo que os crackers injetaram o código no processo de sobreposição:
void gameoverlay::memory_anomaly_scan(MEMORY_BASIC_INFORMATION memory_information) {
Proteção de sobreposição de jogos do Steam
Se o processo de sobreposição de jogos do Steam estiver protegido por alguma proteção dos processos do Windows, como o
Light (WinTcb) , o servidor receberá uma notificação sobre isso.
void gameoverlay::protection_check(HANDLE process_handle) { auto process_protection = 0 NtQueryInformationProcess( process_handle, ProcessProtectionInformation, &process_protection, sizeof(process_protection), nullptr if (process_protection == 0)
Além disso, se a chamada
OpenProcess correspondente retornar
ERROR_ACCESS_DENIED ao processo de sobreposição, uma notificação será enviada sobre o usuário com o ID
3B
.
Módulos de classificação
Os módulos do processo de sobreposição de jogos do Steam também são pesquisados, em particular,
vgui2_s.dll
e
gameoverlayui.dll
. Várias verificações são realizadas para esses módulos, começando com
gameoverlayui.dll
.
Se essa condição for atendida:
[gameoverlayui.dll+6C779] == 08BE55DC3CCCCB8????????C3CCCCCC
, o
[gameoverlayui.dll+6C779] == 08BE55DC3CCCCB8????????C3CCCCCC
do
[gameoverlayui.dll+6C779] == 08BE55DC3CCCCB8????????C3CCCCCC
endereços no endereço armazenado em bytes
????????
. Se algum desses elementos da vtable estiver fora do módulo de origem gameoverlayui.dll ou apontar para uma instrução
int 3
, o usuário será reportado ao servidor com um ID de notificação
3B
.
void gameoverlay::scan_vtable(HANDLE process_handle, char* buffer, MODULEENTRY32 module_entry) { char function_buffer[16 for (vtable_index = 0 vtable_index < 20 vtable_index += 4) { NtReadVirtualMemory( process_handle, *(int*)&buffer[vtable_index], &function_buffer, sizeof(function_buffer), 0 if (*(int*)&buffer[vtable_index] < module_entry.modBaseAddr || *(int*)&buffer[vtable_index] >= module_entry.modBaseAddr + module_entry.modBaseSize || function_buffer[0] == 0xCC )
Um procedimento de verificação específico também é executado para o módulo
vgui2_s.dll
:
void vgui::scan() { if (!equals(vgui_buffer, "6A08B31FF561C8BD??????????FF96????????8BD????????8B1FF90")) { auto could_read = NtReadVirtualMemory( process_handle, module_entry.modBaseAddr + 0x48338, vgui_buffer, 8, 0) >= 0 constexpr auto pattern_offset = 0x48378
Este procedimento verifica alterações no deslocamento 48378
, que é o local da área de código: push 04 push offset aCBuildslaveSte_4 ; "c:\buildslave\steam_rel_client_win32"... push offset aAssertionFaile_7 ; "Assertion Failed: IsValidIndex(elem)"
O procedimento verifica uma alteração muito específica e aparentemente óbvia: push 04 push 00 push 02 push ??
Não foi possível encontrar uma cópia do vgui2_s.dll que não correspondia à primeira das duas verificações acima; portanto, não podemos descobrir qual vtable ela verifica.Fluxos de sobreposição de vapor
Os fluxos no processo de sobreposição de jogos Steam também são movidos: void gameoverlay::check_thread(THREADENTRY32 thread_entry) { const auto tread_handle = OpenThread(THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, 0, thread_entry.th32ThreadID if (thread_handle) { suspend_count = ResumeThread(thread_handle if (suspend_count > 0) { SuspendThread(thread_handle gameoverlay_thread_report.unknown = 0 gameoverlay_thread_report.report_id = 0x3B gameoverlay_thread_report.suspend_count = suspend_count battleye::report(&gameoverlay_thread_report, sizeof(gameoverlay_thread_report), 0 } if (GetThreadContext(thread_handle, &context) && context.Dr7) { gameoverlay_debug_report.unknown = 0 gameoverlay_debug_report.report_id = 0x3B gameoverlay_debug_report.debug_register = context.Dr0 battleye::report(&gameoverlay_debug_report, sizeof(gameoverlay_debug_report), 0 } } }
LSASS
O espaço de endereço de memória do processo Windows lsass.exe , também conhecido como processo da Autoridade de Segurança Local, também é verificado e todas as anomalias são relatadas ao servidor, como no caso das duas verificações anteriores: if (equals(process_entry.executable_path, "lsass.exe")) { auto lsass_handle = OpenProcess(QueryInformation, 0, (unsigned int)process_entry.th32ProcessID if (lsass_handle) { for (address = 0 NtQueryVirtualMemory(lsass_handle, address, 0, &lsass_memory_info, 0x30, &bytes_needed) >= 0 address = lsass_memory_info.base_address + lsass_memory_info.region_size) { if (lsass_memory_info.state == MEM_COMMIT && lsass_memory_info.type == MEM_PRIVATE && (lsass_memory_info.protect == PAGE_EXECUTE || lsass_memory_info.protect == PAGE_EXECUTE_READ || lsass_memory_info.protect == PAGE_EXECUTE_READWRITE)) {
O LSASS foi usado anteriormente em explorações para executar operações com memória, pois qualquer processo que precise de uma conexão com a Internet deve fornecer acesso a LSASS. No momento, o BattlEye está lidando com esse problema, limpando manualmente o identificador do processo das operações de leitura / gravação e, em seguida, anexando ReadProcessMemory
/ WriteProcessMemory
, redirecionando as chamadas para o driver BEDaisy. Em seguida, o BEDaisy decide se a operação de memória é legal. Se ele acredita que a operação é legal, ele continua e, caso contrário, deliberadamente liga a máquina em uma tela azul.Várias notificações
O BattlEye coleta várias informações e as envia ao servidor com o ID da notificação 3C
. Esta informação consiste nos seguintes elementos:- Qualquer janela com o sinalizador WS_EX_TOPMOST ou seus análogos:
- (Unicode)
- (Unicode)
- Window style
- Window extended style
- -
- -
- (VM_WRITE|VM_READ)
- :
- ….ContentPaksTslGame-WindowsNoEditor_assets_world.pak
- ….ContentPaksTslGame-WindowsNoEditor_ui.pak
- ….ContentPaksTslGame-WindowsNoEditor_sound.pak
- :
- ….BLGameCookedContentScriptBLGame.u
- NtGetContextThread
NoEye
O BattlEye implementa uma verificação bastante preguiçosa para detectar um rootkit acessível ao público para contornar esse anti-fraude chamado NoEye: o sistema usa GetFileAttributesExA para verificar o tamanho do arquivo BE_DLL.dll
se essa biblioteca for encontrada no disco. void noeye::detect() { WIN32_FILE_ATTRIBUTE_DATA file_information if (GetFileAttributesExA("BE_DLL.dll", 0, &file_information)) { noeye_report.unknown = 0 noeye_report.report_id = 0x3D noeye_report.file_size = file_information.nFileSizeLow battleye::report(&noeye_report, sizeof(noeye_report), 0 } }
Disponibilidade do driver
Verifica bip e dispositivos nulos; se houver, uma notificação é gerada. No estado normal, esses dois dispositivos não estão disponíveis no sistema e isso pode indicar que alguém ligou o dispositivo manualmente. Essa técnica é chamada seqüestro de dispositivo de driver. Isso é para garantir que os dados IOCTL sejam trocados com o driver mal-intencionado sem a necessidade de um objeto de driver separado para esse driver. void driver::check_beep() { auto handle = CreateFileA("\\.\Beep", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 if (handle != INVALID_HANDLE_VALUE) { beep_report.unknown = 0 beep_report.report_id = 0x3E battleye::report(&beep_report, sizeof(beep_report), 0 CloseHandle(handle } }
void driver::check_null() { auto handle = CreateFileA("\\.\Null", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0 if (handle != INVALID_HANDLE_VALUE) { null_report.unknown = 0 null_report.report_id = 0x3E battleye::report(&null_report, sizeof(null_report), 0 CloseHandle(handle } }
Delta do sono
Além disso, o BattlEye pode solicitar um segundo de inatividade do encadeamento atual e mede a diferença no número de ciclos antes e depois da inatividade (suspensão): void sleep::check_delta() { const auto tick_count = GetTickCount Sleep(1000 const auto tick_delta = GetTickCount() - tick_count if (tick_delta >= 1200) { sleep_report.unknown = 0 sleep_report.report_id = 0x45 sleep_report.delta = tick_delta battleye::report(&sleep_report, sizeof(sleep_report), 0 } }
7zip
Uma verificação de integridade muito lenta foi adicionada ao BattlEye para que os usuários não possam carregar a biblioteca 7zip nos processos de jogo e sobrescrever áreas. Isso foi feito pelos usuários para reduzir a gravidade das varreduras de padrões descritas anteriormente e a detecção de anomalias. O BattlEye simplesmente decidiu adicionar verificações de integridade para essa biblioteca 7zip específica. void module::check_7zip() { constexpr auto sz_7zipdll = "..\..\Plugins\ZipUtility\ThirdParty\7zpp\dll\Win64\7z.dll" const auto module_handle = GetModuleHandleA(sz_7zipdll if (module_handle && *(int*)(module_handle + 0x1000) != 0xFF1441C7) { sevenzip_report.unknown_1 = 0 sevenzip_report.report_id = 0x46 sevenzip_report.unknown_2 = 0 sevenzip_report.data1 = *(__int64*)(module_handle + 0x1000 sevenzip_report.data2 = *(__int64*)(module_handle + 0x1008 battleye::report(&sevenzip_report, sizeof(sevenzip_report), 0 } }
Camada de abstração de hardware
O BattlEye verifica a presença de uma biblioteca de camadas de abstração de hardware do Windows vinculada dinamicamente (hal.dll) e informa ao servidor se está carregada dentro do jogo. void module::check_hal() { const auto module_handle = GetModuleHandleA("hal.dll" if (module_handle) { hal_report.unknown_1 = 0 hal_report.report_id = 0x46 hal_report.unknown_2 = 2 hal_report.data1 = *(__int64*)(module_handle + 0x1000 hal_report.data2 = *(__int64*)(module_handle + 0x1008 battleye::report(&hal_report, sizeof(hal_report), 0 } }
Verificações de imagem
O BattlEye também verifica várias imagens carregadas no jogo. Esses módulos são imagens supostamente assinadas que são de alguma forma manipuladas, alterando seu comportamento para malicioso, mas não podemos dizer mais nada sobre eles, apenas sobre sua detecção:nvToolsExt64_1
void module::check_nvtoolsext64_1 { const auto module_handle = GetModuleHandleA("nvToolsExt64_1.dll" if (module_handle) { nvtools_report.unknown = 0 nvtools_report.report_id = 0x48 nvtools_report.module_id = 0x5A8 nvtools_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&nvtools_report, sizeof(nvtools_report), 0 } }
ws2detour_x96
void module::check_ws2detour_x96 { const auto module_handle = GetModuleHandleA("ws2detour_x96.dll" if (module_handle) { ws2detour_report.unknown = 0 ws2detour_report.report_id = 0x48 ws2detour_report.module_id = 0x5B5 ws2detour_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&ws2detour_report, sizeof(ws2detour_report), 0 } }
networkdllx64
void module::check_networkdllx64 { const auto module_handle = GetModuleHandleA("networkdllx64.dll" if (module_handle) { const auto dos_header = (DOS_HEADER*)module_handle const auto pe_header = (PE_HEADER*)(module_handle + dos_header->e_lfanew const auto size_of_image = pe_header->SizeOfImage if (size_of_image < 0x200000 || size_of_image >= 0x400000) { if (pe_header->sections[DEBUG_DIRECTORY].size == 0x1B20) { networkdll64_report.unknown = 0 networkdll64_report.report_id = 0x48 networkdll64_report.module_id = 0x5B7 networkdll64_report.data = pe_header->TimeDatestamp battleye::report(&networkdll64_report, sizeof(networkdll64_report), 0 } } else { networkdll64_report.unknown = 0 networkdll64_report.report_id = 0x48 networkdll64_report.module_id = 0x5B7 networkdll64_report.data = pe_header->sections[DEBUG_DIRECTORY].size battleye::report(&networkdll64_report, sizeof(networkdll64_report), 0 } } }
nxdetours_64
void module::check_nxdetours_64 { const auto module_handle = GetModuleHandleA("nxdetours_64.dll" if (module_handle) { nxdetours64_report.unknown = 0 nxdetours64_report.report_id = 0x48 nxdetours64_report.module_id = 0x5B8 nxdetours64_report.size_of_image = (PE_HEADER*)(module_handle + (DOS_HEADER*)(module_handle)->e_lfanew))->SizeOfImage battleye::report(&nxdetours64_report, sizeof(nxdetours64_report), 0 } }
nvcompiler
void module::check_nvcompiler { const auto module_handle = GetModuleHandleA("nvcompiler.dll" if (module_handle) { nvcompiler_report.unknown = 0 nvcompiler_report.report_id = 0x48 nvcompiler_report.module_id = 0x5BC nvcompiler_report.data = *(int*)(module_handle + 0x1000 battleye::report(&nvcompiler_report, sizeof(nvcompiler_report), 0 } }
wmp
void module::check_wmp { const auto module_handle = GetModuleHandleA("wmp.dll" if (module_handle) { wmp_report.unknown = 0 wmp_report.report_id = 0x48 wmp_report.module_id = 0x5BE wmp_report.data = *(int*)(module_handle + 0x1000 battleye::report(&wmp_report, sizeof(wmp_report), 0 } }
Identificadores de enumeração de módulo
Para referência, fornecemos o ID da enumeração para os módulos: enum module_id { nvtoolsext64 = 0x5A8, ws2detour_x96 = 0x5B5, networkdll64 = 0x5B7, nxdetours_64 = 0x5B8, nvcompiler = 0x5BC, wmp = 0x5BE
Digitalizar tabelas TCP
O código de shell BattlEye procura uma lista de conexões TCP para todo o sistema (conhecido como tabela TCP) e informa se o usuário está conectado a pelo menos um dos endereços IP do gateway Cloudflare pertencentes ao site de pagamento alemão -cheat chamado xera.ph . Esse mecanismo foi adicionado ao código do shell para detectar usuários com o iniciador em execução quando o jogo está em execução, tornando-os facilmente reconhecíveis. O único problema com esse mecanismo é que os endereços IP do gateway Cloudflare podem mudar de proprietário posteriormente. e se o novo proprietário distribuir software que se conecta aos servidores por meio de uma porta específica, sem dúvida, ocorrerão gatilhos anti-fraude positivos falsos. Usuários doprovedor de serviços xera.ph que pagam para enganarHá muito tempo, eles relatam que estão sendo capturados e os desenvolvedores não conseguem lidar com isso de nenhuma maneira. Entramos em contato com os desenvolvedores do xera.ph para informá-los sobre seu comportamento estúpido, mas eles nos entenderam mal e nos enviaram uma cópia gratuita, sem pensar que poderíamos hackear e liberá-lo. Nós não faremos isso, mas você provavelmente não deve enviar arquivos binários licenciados proprietários gratuitamente para as pessoas envolvidas na engenharia reversa e espera que eles não sejam pirateados. void network::scan_tcp_table { memset(local_port_buffer, 0, sizeof(local_port_buffer for (iteration_index = 0 iteration_index < 500 ++iteration_index) {
Tipos de Notificações
Aqui estão para referência todos os tipos conhecidos de notificações do código do shell: enum BATTLEYE_REPORT_ID { MEMORY_GUARD = 0x21, MEMORY_SUSPICIOUS = 0x2F, WINDOW_TITLE = 0x33, MEMORY = 0x35, PROCESS_ANOMALY = 0x38, DRIVER_BEEP_PRESENCE = 0x3E, DRIVER_NULL_PRESENCE = 0x3F, MISCELLANEOUS_ANOMALY = 0x3B, PROCESS_SUSPICIOUS = 0x40, LSASS_MEMORY = 0x42, SLEEP_ANOMALY = 0x45, MEMORY_MODULE_SPECIFIC = 0x46, GENERIC_ANOMALY = 0x48, MEMORY_MODULE_SPECIFIC2 = 0x5B, }