
Uma ferramenta de linha de comando para rastreamento de API do Windows para binários Golang.
Uma ferramenta de rastreamento da API do Windows via linha de comando para binários Golang.
Nota: Esta ferramenta é uma PoC e um protótipo em andamento, portanto, trate-a como tal. Feedbacks são sempre bem-vindos!
Embora os programas Golang contenham muitas nuances em relação à forma como são construídos e seu comportamento em tempo de execução, eles ainda precisam interagir com a camada do SO e isso significa que em algum momento eles precisam chamar funções da API do Windows.
O pacote runtime do Go contém uma função chamada asmstdcall e essa função é uma espécie de "gateway" usada para interagir com a API do Windows. Como é esperado que esta função chame as funções da API do Windows, podemos assumir que ela precisa ter acesso a informações como o endereço da função e seus parâmetros, e é aí que as coisas começam a ficar mais interessantes.
Asmstdcall recebe um único parâmetro que é um ponteiro para algo semelhante à seguinte estrutura:
struct LIBCALL {
DWORD_PTR Addr;
DWORD Argc;
DWORD_PTR Argv;
DWORD_PTR ReturnValue;
[...]
}
Alguns desses campos são preenchidos após a chamada da função da API, como o valor de retorno, outros são recebidos pelo asmstdcall, como o endereço da função, o número de argumentos e a lista de argumentos. Independentemente de quando são definidos, fica claro que a função asmstdcall manipula muitas informações interessantes sobre a execução de programas compilados em Golang.
O gftrace aproveita o asmstdcall e seu funcionamento para monitorar campos específicos da estrutura mencionada e registrar para o usuário. A ferramenta é capaz de registrar o nome da função, seus parâmetros e também o valor de retorno de cada função do Windows chamada por uma aplicação Golang. Tudo isso sem a necessidade de fazer hook em nenhuma função da API ou ter uma assinatura para ela.
A ferramenta também tenta ignorar todo o ruído da inicialização do runtime do Go e registrar apenas funções chamadas após isso (ou seja, funções do pacote main).
Se quiser saber mais sobre este projeto e pesquisa, confira o blogpost.
Baixe a última release.
gftrace.exe <filepath> <params>
Tudo o que você precisa fazer é especificar quais funções deseja rastrear no arquivo gftrace.cfg, separando-as por vírgula sem espaços:
CreateFileW,ReadFile,CreateProcessW
As funções exatas da API do Windows que um método X do Golang de um pacote Y chamaria em um cenário específico só podem ser determinadas analisando o próprio método ou tentando adivinhá-las. Existem algumas características interessantes que podem ser usadas para determiná-las. Por exemplo, aplicações Golang parecem sempre preferir chamar funções do conjunto "Wide" e "Ex" (ex: CreateFileW, CreateProcessW, GetComputerNameExW, etc), então você pode considerar isso durante sua análise.
O arquivo de configuração padrão contém várias funções que já testei (pelo menos a maioria delas) e posso afirmar com certeza que podem ser chamadas por uma aplicação Golang em algum momento. Tentarei atualizá-lo eventualmente.
Rastreando CreateFileW() e ReadFile() em um arquivo Golang simples que chama "os.ReadFile" duas vezes:
- CreateFileW("C:\Users\user\Desktop\doc.txt", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0x168 (360)
- ReadFile(0x168, 0xc000108000, 0x200, 0xc000075d64, 0x0) = 0x1 (1)
- CreateFileW("C:\Users\user\Desktop\doc2.txt", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0x168 (360)
- ReadFile(0x168, 0xc000108200, 0x200, 0xc000075d64, 0x0) = 0x1 (1)
Rastreando CreateProcessW() no malware TunnelFish:
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000ace98, 0xc0000acd68) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000c4ec8, 0xc0000c4d98) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc00005eec8, 0xc00005ed98) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe", "powershell /c "Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn; Get-Recipient | Select Name -ExpandProperty EmailAddresses -first 1 | Select SmtpAddress | ft -hidetableheaders"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000bce98, 0xc0000bcd68) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000c4ef0, 0xc0000c4dc0) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000acec0, 0xc0000acd90) = 0x1 (1)
- CreateProcessW("C:\WINDOWS\system32\cmd.exe", "cmd /c "wmic computersystem get domain"", 0x0, 0x0, 0x1, 0x80400, "=C:=C:\Users\user\Desktop", 0x0, 0xc0000bcec0, 0xc0000bcd90) = 0x1 (1)
[...]
Rastreando múltiplas funções no malware Sunshuttle:
- CreateFileW("config.dat.tmp", 0x80000000, 0x3, 0x0, 0x3, 0x1, 0x0) = 0xffffffffffffffff (-1)
- CreateFileW("config.dat.tmp", 0xc0000000, 0x3, 0x0, 0x2, 0x80, 0x0) = 0x198 (408)
- CreateFileW("config.dat.tmp", 0xc0000000, 0x3, 0x0, 0x3, 0x80, 0x0) = 0x1a4 (420)
- WriteFile(0x1a4, 0xc000112780, 0xeb, 0xc0000c79d4, 0x0) = 0x1 (1)
- GetAddrInfoW("reyweb.com", 0x0, 0xc000031f18, 0xc000031e88) = 0x0 (0)
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x1f0 (496)
- WSASend(0x1f0, 0xc00004f038, 0x1, 0xc00004f020, 0x0, 0xc00004eff0, 0x0) = 0x0 (0)
- WSARecv(0x1f0, 0xc00004ef60, 0x1, 0xc00004ef48, 0xc00004efd0, 0xc00004ef18, 0x0) = 0xffffffff (-1)
- GetAddrInfoW("reyweb.com", 0x0, 0xc000031f18, 0xc000031e88) = 0x0 (0)
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x200 (512)
- WSASend(0x200, 0xc00004f2b8, 0x1, 0xc00004f2a0, 0x0, 0xc00004f270, 0x0) = 0x0 (0)
- WSARecv(0x200, 0xc00004f1e0, 0x1, 0xc00004f1c8, 0xc00004f250, 0xc00004f198, 0x0) = 0xffffffff (-1)
[...]
Rastreando múltiplas funções no agente do framework DeimosC2:
- WSASocketW(0x2, 0x1, 0x0, 0x0, 0x0, 0x81) = 0x130 (304)
- setsockopt(0x130, 0xffff, 0x20, 0xc0000b7838, 0x4) = 0xffffffff (-1)
- socket(0x2, 0x1, 0x6) = 0x138 (312)
- WSAIoctl(0x138, 0xc8000006, 0xaf0870, 0x10, 0xb38730, 0x8, 0xc0000b746c, 0x0, 0x0) = 0x0 (0)
- GetModuleFileNameW(0x0, "C:\Users\user\Desktop\samples\deimos.exe", 0x400) = 0x2f (47)
- GetUserProfileDirectoryW(0x140, "C:\Users\user", 0xc0000b7a08) = 0x1 (1)
- LookupAccountSidw(0x0, 0xc00000e250, "user", 0xc0000b796c, "DESKTOP-TEST", 0xc0000b7970, 0xc0000b79f0) = 0x1 (1)
- NetUserGetInfo("DESKTOP-TEST", "user", 0xa, 0xc0000b7930) = 0x0 (0)
- GetComputerNameExW(0x5, "DESKTOP-TEST", 0xc0000b7b78) = 0x1 (1)
- GetAdaptersAddresses(0x0, 0x10, 0x0, 0xc000120000, 0xc0000b79d0) = 0x0 (0)
- CreateToolhelp32Snapshot(0x2, 0x0) = 0x1b8 (440)
- GetCurrentProcessId() = 0x2584 (9604)
- GetCurrentDirectoryW(0x12c, "C:\Users\user\AppData\Local\Programs\retoolkit\bin") = 0x39 (57)
[...]
O gftrace é publicado sob a Licença GPL v3. Por favor, consulte o arquivo chamado LICENSE para mais informações.