this script will get you TEB address, like origional C` assembly code support for x86 x64 instruction // # gs:[30h] // # fs:[18h] from there you can get process id, Env` block, lot of useful info. All the properties you can get can be found here. TEB https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm how it work? you can't just call, gs:[30h] using ps`, not supported you can do, built dummy delegate, attach to memory this memory can be written with shell code, AKA Assmably code, as byte's. than, just call the delegate, to run your Assm` code. how to convert assm` code to byte's ? ask chatGpt or gemini, they good at that. just make sure delegate signature is match ass`m code. why it look very suspicious ? that also kind a way to inject bad code to process, so, beware what you running. you can run legit notepad with bad memory and than notepa will run as usual meanwhile, some malware will sit on your computer, run under legit process is it low level ? the only most low level way to call pure assmebly using powershell didnt find other way, other than using c Compiler Code.! Code: using namespace System.Reflection using namespace System.Reflection.Emit using namespace System.Runtime.InteropServices using namespace System.Management.Automation # Thread Environment Block # https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm Function Get-TebPointer { if (!([PSTypeName]'TEB').Type) { Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class TEB { public delegate IntPtr GetPebAddressDelegate(); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr VirtualAlloc(IntPtr a, uint b, uint c, uint d); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool VirtualFree(IntPtr a, uint b, uint c); } "@ -PassThru | Out-Null } $asmCode = if ([IntPtr]::Size -gt 4) { [byte[]] @( # mov rax, 0x65, 0x48, 0x8B, 0x04, 0x25, # gs:[30h] 0x30, 0x00, 0x00, 0x00, # ret 0xC3 ) } else { [byte[]] @( # mov eax, 0x64, 0xA1, #fs:[18h] 0x18, 0x00, 0x00, 0x00, # retn 0xC3 ) } $TepPtr = [IntPtr]::Zero $TepPtr = [TEB]::VirtualAlloc(0, $asmCode.Length, 12288, 64) if ($TepPtr -eq [IntPtr]::Zero) { $lastError = [Marshal]::GetLastWin32Error() throw "Failed to allocate memory. Win32 Error: $lastError" } [marshal]::Copy($asmCode, 0x0, $TepPtr, $asmCode.Length) try { $Func = [Marshal]::GetDelegateForFunctionPointer($TepPtr,[TEB+GetPebAddressDelegate]) return $Func.Invoke() } finally { $Func = $null [TEB]::VirtualFree($TepPtr, 0, 0x8000) | Out-Null } } $tebPtr = Get-TebPointer $is64Bit = [IntPtr]::Size -eq 8 $offBase = [PSCustomObject]@{ procID = if ($is64Bit) { 0x40 } else { 0x20 } envBlock = if ($is64Bit) { 0x60 } else { 0x30 } ntBuild = if ($is64Bit) { 0x120 } else { 0xAC } } $ProcID = [Marshal]::ReadIntPtr( [IntPtr]::Add($tebPtr, $offBase.procID), 0x00) $EnvPtr = [Marshal]::ReadIntPtr( $tebPtr, $offBase.envBlock) $Build = [int][Marshal]::ReadInt16( $EnvPtr, $offBase.ntBuild) Clear-Host Write-Host write-host "Process ID --> $ProcID" write-host "Nt Build --> $Build" write-host "Peb Addr --> $EnvPtr" write-host
Darki add now few comment's, i think they usefull. help undersand this asm` s**ti code the first question, come to my mind is, since TEB is act` GS/FS at 0x00, why you need such --> mov rax, gs:[0x30] well, it store value at rax, value of [Read Pointer at gs:[0x30] --> AKA self de reference and this value is actualy is a pointer to gs:[0x00], the start of TEB str` and than, we can Add [Add offset to Pointer], Move [Move == Add offset, Read value] And than let's say, we we pointer for ClientID, which is TEB & 0x40 add rax, 0x40 // gs:[0x40] Or Read value of 0x60, and get Pointer for PEB block mov rax, [rax + 0x60] // gs:[0x60] what is definition / difference between add / move so, most accurate definition i saw is ADD is for addition/subtraction. MOV is to move a value to a certain place in memory ~~~~~~~ How it basicly work. Code: Online x86 / x64 Assembler and Disassembler https://defuse.ca/online-x86-assembler.htm add rax, 0x?? --> Add 0x?? Bytes, From Position mov Type, [Type + 0x??] --> Move to 0x?? Offset, read Value So, Example Read Pointer Value, & Also, Add 0x?? From Value // Return to gs:[0x00], store value at rax // (NtCurrentTeb) -eq ([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30)) // ([marshal]::ReadIntPtr((NtCurrentTeb),0x40)) -eq ([marshal]::ReadIntPtr((([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30))),0x40)) ** mov rax, gs:[0x30] // Move (de-ref`) -or Add\[+], and store value ** mov Type, [Type + 0x??] ** add rax, 0x?? // Ret value ** Ret Inst` & Text Code: if ($is64) { $addClient = [byte[]]@([byte]0x48,[byte]0x83,[byte]0xC0,[byte]0x40) # add rax, 0x40 // gs:[0x40] $movPeb = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x60) # mov rax, [rax + 0x60] // gs:[0x60] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x18) # mov rax, [rax + 0x18] $movParams = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x20) # mov rax, [rax + 0x20] $basePtr = [byte[]]@([byte]0x65,[byte]0x48,[byte]0x8B,[byte]0x04, # mov rax, gs:[0x30] #// Self dereference pointer at gs:[0x30], [byte]0x25,[byte]0x30,[byte]0x00,[byte]0x00, #// so, effectually, return to gs->0x0 [byte]0x00) } else { $addClient = [byte[]]@([byte]0x83,[byte]0xC0,[byte]0x20) # add eax, 0x20 // fs:[0x20] $movPeb = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x30) # mov eax, [eax + 0x30] // fs:[0x30] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x0C) # mov eax, [eax + 0x0c] $movParams = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x10) # mov eax, [eax + 0x10] $basePtr = [byte[]]@([byte]0x64,[byte]0xA1,[byte]0x18, # mov eax, fs:[0x18] #// Self dereference pointer at fs:[0x18], [byte]0x00,[byte]0x00,[byte]0x00) #// so, effectually, return to fs->0x0 }
Darki got some time to play with Asm (mostly ask AI` bot, how to build the nececery inst`) yes, darki still dont know assmebly, but darki will learn, someday. (maybe the basic) So, after little play, i got some interesting results. -SelfCheck Mode, check value against API Result, -Mode --> like it sound Code: # Mode options for retrieving the TEB address: # Return -> value returned directly in CPU register # Pinned -> use a managed variable pinned in memory # Buffer -> use an unmanaged temporary buffer # GCHandle -> use a GCHandle pinned buffer # Remote -> placeholder mode (not supported in this host) // work only in C# enviroment.! $Method --> ZwAllocateVirtualMemory -or ZwAllocateVirtualMemoryEx ---- > Call without parametes --> Get TEB [GS/FS Offset 0x0] Optional Offset --> i was very nice. Add the most important Stract` you will ever need. Code: [switch]$ClientID, [switch]$Peb, [switch]$Ldr, [switch]$Parameters, Self Check -->! Code: GetCurrentProcessId Test TEB offset 0x40 value: 4868 ClientID Process Pointer: 4868 GetCurrentProcessId(): 4868 GetCurrentThreadId Test TEB offset 0x48 value: 10676 ClientID Thread Pointer: 10676 GetCurrentThreadId(): 10676 RtlGetCurrentPeb Test TEB offset 0x60 value: 529731895296 NtCurrentTeb -Peb returned: 529731895296 RtlGetCurrentPeb(): 529731895296 RtlGetCurrentServiceSessionId Test Service Session Id: 0 PEB offset 0x90 value: 0 RtlGetCurrentTransaction Test Current Transaction: 0 PEB offset 0x17B8 value: 0 RtlGetCurrentUmsThread Test TEB offset 0x1480 value: 0 Status: 0xC00000BB UMS TEB pointer: 0 NtCurrentTeb Mode Test WARNING: Mode: Return. TypeOf:GetAddress Default Mode Ptr: 529893228544 WARNING: Mode: Return. TypeOf:GetAddress Return Mode Ptr: 529893228544 WARNING: Mode: Buffer. TypeOf:GetAddressByPointer Buffer Mode Ptr: 529893228544 WARNING: Mode: [REF]. TypeOf:GetAddressByReference Pinned Mode Ptr: 529893228544 WARNING: Mode: GCHandle. TypeOf:GetAddressByPointer GCHandle Mode Ptr: 529893228544 Remote Mode Test * Manual Check Callback TEB: 0x7B56600000 PID: 4868, TID: 10676 PID match? True, TID match? True * Callback Check WARNING: Mode 'Remote' is not supported in this host yet. Skipping execution. Code --> ! Code: <# * Thread Environment Block (TEB) * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm * Process Environment Block (PEB) * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm [TEB] --> NT_TIB NtTib; 0x00 ----> Struct { ... PNT_TIB Self; <<<<< 0x30 } NT_TIB #> Function NtCurrentTeb { param ( # Mode options for retrieving the TEB address: # Return -> value returned directly in CPU register # Pinned -> use a managed variable pinned in memory # Buffer -> use an unmanaged temporary buffer # GCHandle -> use a GCHandle pinned buffer # Remote -> placeholder mode (not supported in this host) [Parameter(Mandatory = $false, Position = 1)] [ValidateSet("Return" ,"Pinned", "Buffer", "GCHandle", "Remote")] [string]$Mode = "Return", # Allocation method for virtual memory [Parameter(Mandatory = $false, Position = 2)] [ValidateSet("Base", "Extend")] [string]$Method = "Base", # Optional flags to select which fields to read from TEB/PEB [switch]$ClientID, [switch]$Peb, [switch]$Ldr, [switch]$Parameters, # Enable logging/debug output [Parameter(Mandatory = $false, Position = 7)] [switch]$Log = $false, # Self Check Function [Parameter(Mandatory = $false, Position = 8)] [switch]$SelfCheck ) if ($SelfCheck) { Clear-Host Write-Host $isX64 = [IntPtr]::Size -gt 4 Write-Host "`nGetCurrentProcessId Test" -ForegroundColor Green $Offset = if ($isX64) {0x40} else {0x20} $procPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), $Offset) Write-Host ("TEB offset 0x{0:X} value: {1}" -f $Offset, $procPtr) $clientIDProc = [Marshal]::ReadIntPtr((NtCurrentTeb -ClientID), 0x0) Write-Host ("ClientID Process Pointer: {0}" -f $clientIDProc) Write-Host ("GetCurrentProcessId(): {0}" -f [TEB]::GetCurrentProcessId()) Write-Host "`nGetCurrentThreadId Test" -ForegroundColor Green $threadPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), ($Offset + [IntPtr]::Size)) Write-Host ("TEB offset 0x{0:X} value: {1}" -f ($Offset + [IntPtr]::Size), $threadPtr) $clientIDThread = [Marshal]::ReadIntPtr((NtCurrentTeb -ClientID), [IntPtr]::Size) Write-Host ("ClientID Thread Pointer: {0}" -f $clientIDThread) Write-Host ("GetCurrentThreadId(): {0}" -f [TEB]::GetCurrentThreadId()) Write-Host "`nRtlGetCurrentPeb Test" -ForegroundColor Green # Offset-based read from TEB $Offset = if ($isX64) {0x60} else {0x30} $pebPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), $Offset) Write-Host ("TEB offset 0x{0:X} value: {1}" -f $Offset, $pebPtr) # Using NtCurrentTeb -Peb mode $pebViaFunction = NtCurrentTeb -Peb Write-Host ("NtCurrentTeb -Peb returned: {0}" -f $pebViaFunction) # Using [TEB] helper function $pebViaTEB = [TEB]::RtlGetCurrentPeb() Write-Host ("RtlGetCurrentPeb(): {0}" -f $pebViaTEB) Write-Host "`nRtlGetCurrentServiceSessionId Test" -ForegroundColor Green $serviceSessionId = [TEB]::RtlGetCurrentServiceSessionId() Write-Host ("Service Session Id: {0}" -f $serviceSessionId) $Offset = if ($isX64) {0x90} else {0x50} $sessionPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Peb), $Offset) Write-Host ("PEB offset 0x{0:X} value: {1}" -f $Offset, $sessionPtr) Write-Host "`nRtlGetCurrentTransaction Test" -ForegroundColor Green $transaction = [TEB]::RtlGetCurrentTransaction() Write-Host ("Current Transaction: {0}" -f $transaction) $Offset = if ($isX64) {0x17B8} else {0x0FAC} $txnPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Peb), $Offset) Write-Host ("PEB offset 0x{0:X} value: {1}" -f $Offset, $txnPtr) Write-Host "`nRtlGetCurrentUmsThread Test" -ForegroundColor Green $Offset = if ($isX64) {0x1480} else {0x0E10} $umsPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), $Offset) Write-Host ("TEB offset 0x{0:X} value: {1}" -f $Offset, $umsPtr) $ppTEB = [IntPtr]::new(1) $status = [TEB]::RtlGetCurrentUmsThread([ref]$ppTEB) Write-Host ("Status: 0x{0:X}" -f $status) Write-Host ("UMS TEB pointer: {0}" -f $ppTEB) Write-Host "`nNtCurrentTeb Mode Test" -ForegroundColor Green $defaultPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Log), [IntPtr]::Size) Write-Host ("Default Mode Ptr: {0}" -f $defaultPtr) $returnPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Return -Log), [IntPtr]::Size) Write-Host ("Return Mode Ptr: {0}" -f $returnPtr) $bufferPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Buffer -Log), [IntPtr]::Size) Write-Host ("Buffer Mode Ptr: {0}" -f $bufferPtr) $pinnedPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Pinned -Log), [IntPtr]::Size) Write-Host ("Pinned Mode Ptr: {0}" -f $pinnedPtr) $gcHandlePtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode GCHandle -Log), [IntPtr]::Size) Write-Host ("GCHandle Mode Ptr: {0}" -f $gcHandlePtr) if (!$isX64) { return } Write-Host "`nRemote Mode Test" -ForegroundColor Green Write-Host "* Manual Check" -ForegroundColor Magenta $callbackDelegate = [TEB]::GetCallback() $callbackDelegate.Invoke((NtCurrentTeb)) Write-Host "* Callback Check" -ForegroundColor Magenta NtCurrentTeb -Mode Remote Write-Host return } if ($Ldr -or $Parameters) { $Peb = $true } if ($Ldr -and $Parameters) { throw "Cannot specify both -Ldr and -Parameters. Choose one." } if ($ClientID -and $Peb) { throw "Cannot specify both -ClientID and -Peb. Choose one." } if ($Mode -eq 'Remote') { Write-Warning "Mode 'Remote' is not supported in this host yet. Skipping execution." return } if (!([PSTypeName]'TEB').Type) { Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public static class TEB { public delegate IntPtr GetAddress(); public delegate void GetAddressByPointer(IntPtr Ret); public delegate void GetAddressByReference(ref IntPtr Ret); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void CallbackDelegate(IntPtr value); public static CallbackDelegate GetCallback() { return new CallbackDelegate((IntPtr val) => { IntPtr uniqueProcessIdPtr = Marshal.ReadIntPtr(val, 0x40); IntPtr uniqueThreadIdPtr = Marshal.ReadIntPtr(val, 0x48); uint pidApi = GetCurrentProcessId(); uint tidApi = GetCurrentThreadId(); long pidFromTeb64 = uniqueProcessIdPtr.ToInt64(); uint pidFromTeb32 = (uint)pidFromTeb64; long tidFromTeb64 = uniqueThreadIdPtr.ToInt64(); uint tidFromTeb32 = (uint)tidFromTeb64; Console.WriteLine("Callback TEB: 0x{0:X}", val.ToInt64()); Console.WriteLine("PID: {0}, TID: {1}", pidFromTeb32, tidFromTeb32); Console.WriteLine("PID match? {0}, TID match? {1}", pidApi==pidFromTeb32, tidApi==tidFromTeb32); }); } [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr CreateRemoteThreadEx( IntPtr hProcess, IntPtr lpThreadAttributes, // SECURITY_ATTRIBUTES*; null => IntPtr.Zero UIntPtr dwStackSize, IntPtr lpStartAddress, // LPTHREAD_START_ROUTINE (function pointer) IntPtr lpParameter, // LPVOID uint dwCreationFlags, IntPtr lpAttributeList, // LPPROC_THREAD_ATTRIBUTE_LIST; null => IntPtr.Zero out uint lpThreadId // optional thread id; out 0 if not needed ); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern uint WaitForSingleObject( IntPtr hHandle, uint dwMilliseconds ); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, UIntPtr ZeroBits, ref UIntPtr RegionSize, uint AllocationType, uint Protect ); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwAllocateVirtualMemoryEx( IntPtr ProcessHandle, ref IntPtr BaseAddress, ref UIntPtr RegionSize, uint AllocationType, uint Protect, IntPtr ExtendedParameters, uint ParameterCount ); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwFreeVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, ref UIntPtr RegionSize, uint FreeType ); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern uint GetCurrentProcessId(); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern uint GetCurrentThreadId(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentPeb(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentServiceSessionId(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentTransaction(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int RtlGetCurrentUmsThread(ref IntPtr ppTEB); } "@ -PassThru | Out-Null } function Build-ASM-Shell { <# Online x86 / x64 Assembler and Disassembler https://defuse.ca/online-x86-assembler.htm add rax, 0x?? --> Add 0x?? Bytes, From Position mov Type, [Type + 0x??] --> Move to 0x?? Offset, read Value So, Example Read Pointer Value, & Also, Add 0x?? From Value // Return to gs:[0x00], store value at rax // (NtCurrentTeb) -eq ([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30)) // ([marshal]::ReadIntPtr((NtCurrentTeb),0x40)) -eq ([marshal]::ReadIntPtr((([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30))),0x40)) ** mov rax, gs:[0x30] // Move (de-ref`) -or Add\[+], and store value ** mov Type, [Type + 0x??] ** add rax, 0x?? // Ret value ** Ret #> $shellcode = [byte[]]@() $is64 = [IntPtr]::Size -gt 4 $ret = [byte[]]([byte]0xC3) if ($is64) { $addClient = [byte[]]@([byte]0x48,[byte]0x83,[byte]0xC0,[byte]0x40) # add rax, 0x40 // gs:[0x40] $movPeb = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x60) # mov rax, [rax + 0x60] // gs:[0x60] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x18) # mov rax, [rax + 0x18] $movParams = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x20) # mov rax, [rax + 0x20] $basePtr = [byte[]]@([byte]0x65,[byte]0x48,[byte]0x8B,[byte]0x04, # mov rax, gs:[0x30] #// Self dereference pointer at gs:[0x30], [byte]0x25,[byte]0x30,[byte]0x00,[byte]0x00, #// so, effectually, return to gs->0x0 [byte]0x00) $InByRef = [byte[]]@([byte]0x48,[byte]0x89,[byte]0x01) # mov [rcx], rax #// moves the 64-bit value from the RAX register #// into the memory location pointed to by the RCX register. } else { $addClient = [byte[]]@([byte]0x83,[byte]0xC0,[byte]0x20) # add eax, 0x20 // fs:[0x20] $movPeb = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x30) # mov eax, [eax + 0x30] // fs:[0x30] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x0C) # mov eax, [eax + 0x0c] $movParams = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x10) # mov eax, [eax + 0x10] $basePtr = [byte[]]@([byte]0x64,[byte]0xA1,[byte]0x18, # mov eax, fs:[0x18] #// Self dereference pointer at fs:[0x18], [byte]0x00,[byte]0x00,[byte]0x00) #// so, effectually, return to fs->0x0 $InByRef = [byte[]]@( [byte]0x8B, [byte]0x4C, [byte]0x24, [byte]0x04, # mov ecx, [esp + 4] ; load first argument pointer from stack into ECX [byte]0x89, [byte]0x01 # mov [ecx], eax ; store 32-bit value from EAX into memory pointed by ECX ) } if ($Mode -eq 'Remote') { # Test only, work well in C#, PS` well, no, # and it also x64 version.! # x64 shellcode: call callback with TEB as argument [byte[]] $shellcode = [byte[]]@( # Save the callback pointer from rcx to rax 0x48, 0x89, 0xC8, # mov rax, rcx # Get the TEB address from gs:[0x30] and store it in rcx 0x65, 0x48, 0x8B, 0x0C, 0x25, 0x30, 0x00, 0x00, 0x00, # mov rcx, gs:[0x30] # Align the stack to a 16-byte boundary. 0x48, 0x83, 0xEC, 0x28, # sub rsp, 40 # Call the function pointer saved in rax 0xFF, 0xD0, # call rax # Restore the stack 0x48, 0x83, 0xC4, 0x28, # add rsp, 40 # Return from the shellcode 0xC3 # ret ) return $shellcode } $shellcode = $basePtr if ($ClientID) { $shellcode += $addClient } if ($Peb) { $shellcode += $movPeb if ($Ldr) { $shellcode += $movLdr } if ($Parameters) { $shellcode += $movParams } } if ($Mode -ne "Return") {$shellcode += $InByRef} $shellcode += $ret $shellcode } $shellcode = Build-ASM-Shell $len = $shellcode.Length $baseAddress = [IntPtr]::Zero $regionSize = [uintptr]::new($len) $ntStatus = if ($Method -eq "Base") { [TEB]::ZwAllocateVirtualMemory( [IntPtr]::new(-1), [ref]$baseAddress, [UIntPtr]::new(0x00), [ref]$regionSize, 0x3000, 0x40) } else { [TEB]::ZwAllocateVirtualMemoryEx( [IntPtr]::new(-1), [ref]$baseAddress, [ref]$regionSize, 0x3000, 0x40, [IntPtr]0,0) } if ($Mode -eq 'Remote') { $callbackDelegate = [TEB]::GetCallback() $callbackHandle = [GCHandle]::Alloc($callbackDelegate) $callbackPtr = [Marshal]::GetFunctionPointerForDelegate($callbackDelegate) # Create remote thread to execute shellcode with callback pointer $createdThreadId = 0 $Prochandle = [Process]::GetCurrentProcess().Handle $threadHandle = [TEB]::CreateRemoteThreadEx( $Prochandle, # pseudo handle // [IntPtr]::new(-1) [IntPtr]::Zero, # no SECURITY_ATTRIBUTES [UIntPtr]::Zero, # default stack size $baseAddress, # shellcode start $callbackPtr, # passed in RCX 0, # run immediately [IntPtr]::Zero, # no attributes [ref]$createdThreadId ) $callbackHandle.Free() if ($threadHandle -eq [IntPtr]::Zero) { Write-Host "CreateRemoteThreadEx failed: $([Marshal]::GetLastWin32Error())" } else { Write-Host "Thread created. TID = $createdThreadId" [TEB]::WaitForSingleObject($threadHandle, 0xFFFFFFFF) } } if ($ntStatus -ne 0) { throw "ZwAllocateVirtualMemory failed with result: $ntStatus" } try { $Address = [IntPtr]::Zero [marshal]::Copy($shellcode, 0x00, $baseAddress, $len) switch ($Mode) { "Return" { if ($log) { Write-Warning "Mode: Return. TypeOf:GetAddress" } $Address = [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddress]).Invoke() } "Buffer" { if ($log) { Write-Warning "Mode: Buffer. TypeOf:GetAddressByPointer" } $baseAdd = [marshal]::AllocHGlobal([IntPtr]::Size) [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByPointer]).Invoke($baseAdd) $Address = [marshal]::ReadIntPtr($baseAdd) [marshal]::FreeHGlobal($baseAdd) } "GCHandle" { if ($log) { Write-Warning "Mode: GCHandle. TypeOf:GetAddressByPointer" } $gcHandle = [GCHandle]::Alloc($Address, [GCHandleType]::Pinned) $baseAdd = $gcHandle.AddrOfPinnedObject() [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByPointer]).Invoke($baseAdd) $gcHandle.Free() } "Pinned" { if ($log) { Write-Warning "Mode: [REF]. TypeOf:GetAddressByReference" } [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByReference]).Invoke([ref]$Address) } } return $Address } finally { [TEB]::ZwFreeVirtualMemory( [IntPtr]::new(-1), [ref]$baseAddress, [ref]$regionSize, 0x4000) | Out-Null } }
So, darky, tried move lot of C# logic, into PS`1 and now remote method, work from ps`1, So, it now have Ret Method, Using Params Intptr, By Ref, and also by call back delegate, Defination --> in C# Code, logic now fully managed by powershell. this is very intersting way, to use callback + ASM with powershell. So, my first attemp's, ended with ISE crash or fail, after few day's, we got a progres. now all work. as it should for both arch' x64, x86. And also run fine in ISE enviroment. Also, delegate now accept 2 paraemters [this, pTeb], to make asm' logic OK [or start overwrite parameters, less ok, but work.!] this also can be useful for other things, not just for TEB address i think i will use it in future, BUT even for TEB address, its still a good ide'a And also mimic same Macro as NtCurrentTeb which cant be accable in managed code for safety reason i guess Code: using namespace System using namespace System.Diagnostics using namespace System.Reflection using namespace System.Reflection.Emit using namespace System.Management.Automation using namespace System.Runtime.InteropServices <# * Thread Environment Block (TEB) * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/teb/index.htm * Process Environment Block (PEB) * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/pebteb/peb/index.htm [TEB] --> NT_TIB NtTib; 0x00 ----> Struct { ... PNT_TIB Self; <<<<< gs:[0x30] / fs:[0x18] } NT_TIB #> if (!([PSTypeName]'TEB').Type) { $TEB = @" using System; using System.Runtime.InteropServices; public static class TEB { public delegate IntPtr GetAddress(); public delegate void GetAddressByPointer(IntPtr Ret); public delegate void GetAddressByReference(ref IntPtr Ret); public static IntPtr CallbackResult; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void CallbackDelegate(IntPtr callback, IntPtr TEB); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void RemoteThreadDelgate(IntPtr callback); public static CallbackDelegate GetCallback() { return new CallbackDelegate((IntPtr del, IntPtr val) => { CallbackResult = val; }); } [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwAllocateVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, UIntPtr ZeroBits, ref UIntPtr RegionSize, uint AllocationType, uint Protect ); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwAllocateVirtualMemoryEx( IntPtr ProcessHandle, ref IntPtr BaseAddress, ref UIntPtr RegionSize, uint AllocationType, uint Protect, IntPtr ExtendedParameters, uint ParameterCount ); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern int ZwFreeVirtualMemory( IntPtr ProcessHandle, ref IntPtr BaseAddress, ref UIntPtr RegionSize, uint FreeType ); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern uint GetCurrentProcessId(); [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)] public static extern uint GetCurrentThreadId(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentPeb(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentServiceSessionId(); [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)] public static extern IntPtr RtlGetCurrentTransaction(); } "@ Add-Type -TypeDefinition $TEB -ErrorAction Stop } Function NtCurrentTeb { param ( # Mode options for retrieving the TEB address: # Return -> value returned directly in CPU register # Pinned -> use a managed variable pinned in memory # Buffer -> use an unmanaged temporary buffer # GCHandle -> use a GCHandle pinned buffer # Remote -> using Callback, to receive to TEB pointer [Parameter(Mandatory = $false, Position = 1)] [ValidateSet("Return" ,"Pinned", "Buffer", "GCHandle", "Remote")] [string]$Mode = "Return", # Allocation method for virtual memory [Parameter(Mandatory = $false, Position = 2)] [ValidateSet("Base", "Extend")] [string]$Method = "Base", # Optional flags to select which fields to read from TEB/PEB [switch]$ClientID, [switch]$Peb, [switch]$Ldr, [switch]$Parameters, # Enable logging/debug output [Parameter(Mandatory = $false, Position = 7)] [switch]$Log = $false, # Self Check Function [Parameter(Mandatory = $false, Position = 8)] [switch]$SelfCheck ) function Build-ASM-Shell { <# Online x86 / x64 Assembler and Disassembler https://defuse.ca/online-x86-assembler.htm add rax, 0x?? --> Add 0x?? Bytes, From Position mov Type, [Type + 0x??] --> Move to 0x?? Offset, read Value So, Example Read Pointer Value, & Also, Add 0x?? From Value // Return to gs:[0x00], store value at rax // (NtCurrentTeb) -eq ([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30)) // ([marshal]::ReadIntPtr((NtCurrentTeb),0x40)) -eq ([marshal]::ReadIntPtr((([Marshal]::ReadIntPtr((NtCurrentTeb), 0x30))),0x40)) ** mov rax, gs:[0x30] // Move (de-ref`) -or Add\[+], and store value ** mov Type, [Type + 0x??] ** add rax, 0x?? // Ret value ** Ret #> $shellcode = [byte[]]@() $is64 = [IntPtr]::Size -gt 4 $ret = [byte[]]([byte]0xC3) if ($is64) { $addClient = [byte[]]@([byte]0x48,[byte]0x83,[byte]0xC0,[byte]0x40) # add rax, 0x40 // gs:[0x40] $movPeb = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x60) # mov rax, [rax + 0x60] // gs:[0x60] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x18) # mov rax, [rax + 0x18] $movParams = [byte[]]@([byte]0x48,[byte]0x8B,[byte]0x40,[byte]0x20) # mov rax, [rax + 0x20] $basePtr = [byte[]]@([byte]0x65,[byte]0x48,[byte]0x8B,[byte]0x04, # mov rax, gs:[0x30] #// Self dereference pointer at gs:[0x30], [byte]0x25,[byte]0x30,[byte]0x00,[byte]0x00, #// so, effectually, return to gs->0x0 [byte]0x00) $InByRef = [byte[]]@([byte]0x48,[byte]0x89,[byte]0x01) # mov [rcx], rax #// moves the 64-bit value from the RAX register #// into the memory location pointed to by the RCX register. } else { $addClient = [byte[]]@([byte]0x83,[byte]0xC0,[byte]0x20) # add eax, 0x20 // fs:[0x20] $movPeb = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x30) # mov eax, [eax + 0x30] // fs:[0x30] // RtlGetCurrentPeb $movLdr = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x0C) # mov eax, [eax + 0x0c] $movParams = [byte[]]@([byte]0x8B,[byte]0x40,[byte]0x10) # mov eax, [eax + 0x10] $basePtr = [byte[]]@([byte]0x64,[byte]0xA1,[byte]0x18, # mov eax, fs:[0x18] #// Self dereference pointer at fs:[0x18], [byte]0x00,[byte]0x00,[byte]0x00) #// so, effectually, return to fs->0x0 $InByRef = [byte[]]@( [byte]0x8B, [byte]0x4C, [byte]0x24, [byte]0x04, # mov ecx, [esp + 4] ; load first argument pointer from stack into ECX [byte]0x89, [byte]0x01 # mov [ecx], eax ; store 32-bit value from EAX into memory pointed by ECX ) } $shellcode = $basePtr if ($ClientID) { $shellcode += $addClient } if ($Peb) { $shellcode += $movPeb if ($Ldr) { $shellcode += $movLdr } if ($Parameters) { $shellcode += $movParams } } if ($Mode -ne "Return") {$shellcode += $InByRef} $shellcode += $ret $shellcode } if ($SelfCheck) { Clear-Host Write-Host $isX64 = [IntPtr]::Size -gt 4 Write-Host "`nGetCurrentProcessId Test" -ForegroundColor Green $Offset = if ($isX64) {0x40} else {0x20} $procPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), $Offset) Write-Host ("TEB offset 0x{0:X} value: {1}" -f $Offset, $procPtr) $clientIDProc = [Marshal]::ReadIntPtr((NtCurrentTeb -ClientID), 0x0) Write-Host ("ClientID Process Pointer: {0}" -f $clientIDProc) Write-Host ("GetCurrentProcessId(): {0}" -f [TEB]::GetCurrentProcessId()) Write-Host "`nGetCurrentThreadId Test" -ForegroundColor Green $threadPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), ($Offset + [IntPtr]::Size)) Write-Host ("TEB offset 0x{0:X} value: {1}" -f ($Offset + [IntPtr]::Size), $threadPtr) $clientIDThread = [Marshal]::ReadIntPtr((NtCurrentTeb -ClientID), [IntPtr]::Size) Write-Host ("ClientID Thread Pointer: {0}" -f $clientIDThread) Write-Host ("GetCurrentThreadId(): {0}" -f [TEB]::GetCurrentThreadId()) Write-Host "`nRtlGetCurrentPeb Test" -ForegroundColor Green # Offset-based read from TEB $Offset = if ($isX64) {0x60} else {0x30} $pebPtr = [Marshal]::ReadIntPtr((NtCurrentTeb), $Offset) Write-Host ("TEB offset 0x{0:X} value: {1}" -f $Offset, $pebPtr) # Using NtCurrentTeb -Peb mode $pebViaFunction = NtCurrentTeb -Peb Write-Host ("NtCurrentTeb -Peb returned: {0}" -f $pebViaFunction) # Using [TEB] helper function $pebViaTEB = [TEB]::RtlGetCurrentPeb() Write-Host ("RtlGetCurrentPeb(): {0}" -f $pebViaTEB) Write-Host "`nRtlGetCurrentServiceSessionId Test" -ForegroundColor Green $serviceSessionId = [TEB]::RtlGetCurrentServiceSessionId() Write-Host ("Service Session Id: {0}" -f $serviceSessionId) $Offset = if ($isX64) {0x90} else {0x50} $sessionPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Peb), $Offset) Write-Host ("PEB offset 0x{0:X} value: {1}" -f $Offset, $sessionPtr) Write-Host "`nRtlGetCurrentTransaction Test" -ForegroundColor Green $transaction = [TEB]::RtlGetCurrentTransaction() Write-Host ("Current Transaction: {0}" -f $transaction) $Offset = if ($isX64) {0x17B8} else {0x0FAC} $txnPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Peb), $Offset) Write-Host ("PEB offset 0x{0:X} value: {1}" -f $Offset, $txnPtr) Write-Host "`nNtCurrentTeb Mode Test" -ForegroundColor Green $defaultPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Log), [IntPtr]::Size) Write-Host ("Default Mode Ptr: {0}" -f $defaultPtr) $returnPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Return -Log), [IntPtr]::Size) Write-Host ("Return Mode Ptr: {0}" -f $returnPtr) $bufferPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Buffer -Log), [IntPtr]::Size) Write-Host ("Buffer Mode Ptr: {0}" -f $bufferPtr) $pinnedPtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Pinned -Log), [IntPtr]::Size) Write-Host ("Pinned Mode Ptr: {0}" -f $pinnedPtr) $gcHandlePtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode GCHandle -Log), [IntPtr]::Size) Write-Host ("GCHandle Mode Ptr: {0}" -f $gcHandlePtr) $callbackHandlePtr = [Marshal]::ReadIntPtr((NtCurrentTeb -Mode Remote -Log), [IntPtr]::Size) Write-Host ("Remote Mode Ptr: {0}" -f $callbackHandlePtr) Write-Host return } if ($Ldr -or $Parameters) { $Peb = $true } if ($Ldr -and $Parameters) { throw "Cannot specify both -Ldr and -Parameters. Choose one." } if ($ClientID -and $Peb) { throw "Cannot specify both -ClientID and -Peb. Choose one." } if ($Mode -eq 'Remote') { [TEB]::CallbackResult = 0 $callback = [Teb]::GetCallback(); $callbackPtr = [Marshal]::GetFunctionPointerForDelegate($callback); if ([IntPtr]::Size -eq 8) { $shellcode = [byte[]]@( 0x48, 0x89, 0xC8, ## mov rax, rcx ## Move function address (first param) to rax. 0x65, 0x48, 0x8B, 0x14, 0x25, 0x30, 0x00, 0x00, 0x00, ## mov rdx, gs:[0x30] ## Set second param (rdx) from a known memory location. 0x48, 0x83, 0xEC, 0x28, ## sub rsp, 40 ## Allocate space on the stack for the call. 0xFF, 0xD0, ## call rax ## Call the function using the address from rax. 0x48, 0x83, 0xC4, 0x28, ## add rsp, 40 ## Clean up the stack. 0xC3 ## ret ## Return to the caller. ); } elseif ([IntPtr]::Size -eq 4) { $shellcode = [byte[]]@( 0x64, 0xA1, 0x18, 0x00, 0x00, 0x00, ## mov eax, fs:[0x18] ## Get a specific address from the Thread Information Block. 0x50, ## push eax ## Push this address onto the stack to use later. 0x8B, 0x44, 0x24, 0x08, ## mov eax, [esp + 8] ## Get a second value (a function pointer or argument) from the stack. 0x50, ## push eax ## Push this value onto the stack as well. 0xFF, 0x14, 0x24, ## call [esp] ## Call the function whose address is now at the top of the stack. 0x83, 0xC4, 0x08, ## add esp, 8 ## Clean up the two values we pushed on the stack. 0xC3 ## ret ## Return to the calling code. ) } $baseAddress = [IntPtr]::Zero; $regionSize = [UIntPtr]::new($shellcode.Length); $status = [TEB]::ZwAllocateVirtualMemory( [IntPtr]::new(-1), [ref] $baseAddress, [UIntPtr]::new(0x00), [ref] $regionSize, 0x3000, ## MEM_COMMIT | MEM_RESERVE 0x40 ## PAGE_EXECUTE_READWRITE ); if ($status -ne 0) { return; } ## Copy shellcode to allocated memory [Marshal]::Copy($shellcode, 0, $baseAddress, $shellcode.Length); $handle = [IntPtr]::Zero try { $Caller = [Marshal]::GetDelegateForFunctionPointer($baseAddress, [TEB+RemoteThreadDelgate]); $handle = [gchandle]::Alloc($Caller, [GCHandleType]::Normal) $Caller.Invoke($callbackPtr); } catch {} finally { Start-Sleep -Milliseconds 400 if ($handle.IsAllocated) { $handle.Free() } } $status = [TEB]::ZwFreeVirtualMemory( [IntPtr]::new(-1), [ref] $baseAddress, [ref] $regionSize, 0x8000 ## MEM_RELEASE ); if ($Log) { Write-Warning "Mode: Remote. TypeOf: Callback Delegate" } if (-not [TEB]::CallbackResult -or [TEB]::CallbackResult -eq [IntPtr]::Zero) { throw "Failure to get results from callback!" } $isX64 = [IntPtr]::Size -eq 8 if ($ClientID) { if ($isX64) { return [IntPtr]::Add([TEB]::CallbackResult, 0x40) } else { return [IntPtr]::Add([TEB]::CallbackResult, 0x20) } } if ($Peb) { if ($isX64) { $CallbackResult = [Marshal]::ReadIntPtr([TEB]::CallbackResult, 0x60) if ($Ldr) { $CallbackResult = [Marshal]::ReadIntPtr($CallbackResult, 0x18) } if ($Parameters) { $CallbackResult = [Marshal]::ReadIntPtr($CallbackResult, 0x20) } } else { $CallbackResult = [Marshal]::ReadIntPtr([TEB]::CallbackResult, 0x30) if ($Ldr) { $CallbackResult = [Marshal]::ReadIntPtr($CallbackResult, 0x0c) } if ($Parameters) { $CallbackResult = [Marshal]::ReadIntPtr($CallbackResult, 0x10) } } return $CallbackResult } return [TEB]::CallbackResult } $shellcode = Build-ASM-Shell $len = $shellcode.Length $baseAddress = [IntPtr]::Zero $regionSize = [uintptr]::new($len) $ntStatus = if ($Method -eq "Base") { [TEB]::ZwAllocateVirtualMemory( [IntPtr]::new(-1), [ref]$baseAddress, [UIntPtr]::new(0x00), [ref]$regionSize, 0x3000, 0x40) } else { [TEB]::ZwAllocateVirtualMemoryEx( [IntPtr]::new(-1), [ref]$baseAddress, [ref]$regionSize, 0x3000, 0x40, [IntPtr]0,0) } if ($ntStatus -ne 0) { throw "ZwAllocateVirtualMemory failed with result: $ntStatus" } try { $Address = [IntPtr]::Zero [marshal]::Copy($shellcode, 0x00, $baseAddress, $len) switch ($Mode) { "Return" { if ($log) { Write-Warning "Mode: Return. TypeOf:GetAddress" } $Address = [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddress]).Invoke() } "Buffer" { if ($log) { Write-Warning "Mode: Buffer. TypeOf:GetAddressByPointer" } $baseAdd = [marshal]::AllocHGlobal([IntPtr]::Size) [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByPointer]).Invoke($baseAdd) $Address = [marshal]::ReadIntPtr($baseAdd) [marshal]::FreeHGlobal($baseAdd) } "GCHandle" { if ($log) { Write-Warning "Mode: GCHandle. TypeOf:GetAddressByPointer" } $gcHandle = [GCHandle]::Alloc($Address, [GCHandleType]::Pinned) $baseAdd = $gcHandle.AddrOfPinnedObject() [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByPointer]).Invoke($baseAdd) $gcHandle.Free() } "Pinned" { if ($log) { Write-Warning "Mode: [REF]. TypeOf:GetAddressByReference" } [Marshal]::GetDelegateForFunctionPointer( $baseAddress,[TEB+GetAddressByReference]).Invoke([ref]$Address) } } return $Address } finally { [TEB]::ZwFreeVirtualMemory( [IntPtr]::new(-1), [ref]$baseAddress, [ref]$regionSize, 0x4000) | Out-Null } } NtCurrentTeb -SelfCheck