├── LICENSE
├── PSMemory.Format.ps1xml
├── PSMemory.Types.ps1xml
├── PSMemory.psd1
├── PSMemory.psm1
└── README.md
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2019, Tobias Heilig
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/PSMemory.Format.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Default
6 |
7 | PSMemory.Reference
8 |
9 |
10 |
11 |
12 |
13 |
14 | 20
15 |
16 |
17 |
18 |
19 |
20 |
21 | 10
22 |
23 |
24 |
25 | 14
26 |
27 |
28 |
29 | 8
30 | right
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | '0x' + $_.Address.ToString('X16')
39 |
40 |
41 |
42 | Value
43 |
44 |
45 | Type
46 |
47 |
48 | Protection
49 |
50 |
51 | Page
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/PSMemory.Types.ps1xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PSMemory.Reference
5 |
6 |
7 | RegionEnd
8 |
9 | $this.RegionStart + $this.RegionSize
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/PSMemory.psd1:
--------------------------------------------------------------------------------
1 | @{
2 | RootModule = 'PSMemory.psm1'
3 | ModuleVersion = '1.1.0'
4 | GUID = '54d44ebf-3b85-428e-9030-a6b7581a50c2'
5 | Author = 'Tobias Heilig'
6 | Copyright = '3-Clause BSD Copyright 2019 Tobias Heilig'
7 | Description = 'Windows 64 Bit Memory Scanner'
8 | FunctionsToExport = @('Format-Memory','Search-Memory','Compare-Memory','Update-Memory')
9 | FormatsToProcess = @('PSMemory.Format.ps1xml')
10 | TypesToProcess = @('PSMemory.Types.ps1xml')
11 | }
12 |
--------------------------------------------------------------------------------
/PSMemory.psm1:
--------------------------------------------------------------------------------
1 | # BSD 3-Clause License
2 | #
3 | # Copyright(c) 2019, Tobias Heilig
4 | # All rights reserved.
5 | #
6 | # Redistribution and use in source and binary forms, with or without
7 | # modification, are permitted provided that the following conditions are met:
8 | #
9 | # 1. Redistributions of source code must retain the above copynotice, this
10 | # list of conditions and the following disclaimer.
11 | #
12 | # 2. Redistributions in binary form must reproduce the above copynotice,
13 | # this list of conditions and the following disclaimer in the documentation
14 | # and/or other materials provided with the distribution.
15 | #
16 | # 3. Neither the name of the copyholder nor the names of its
17 | # contributors may be used to endorse or promote products derived from
18 | # this software without specific prior written permission.
19 | #
20 | # THIS SOFTWARE IS PROVIDED BY THE COPYHOLDERS AND CONTRIBUTORS "AS IS"
21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | # DISCLAIMED. IN NO EVENT SHALL THE COPYHOLDER OR CONTRIBUTORS BE LIABLE
24 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
31 |
32 | try {
33 | & {
34 | $ErrorActionPreference = 'Stop'
35 | [void] [PSMemory.Native]
36 | }
37 | } catch {
38 | Add-Type -TypeDefinition @"
39 | using System;
40 | using System.Runtime.InteropServices;
41 | namespace PSMemory {
42 | public class Native {
43 | [StructLayout(LayoutKind.Sequential)]
44 | public struct SYSTEM_INFO {
45 | public ushort wProcessorArchitecture;
46 | public ushort wReserved;
47 | public uint dwPageSize;
48 | public IntPtr lpMinimumApplicationAddress;
49 | public IntPtr lpMaximumApplicationAddress;
50 | public UIntPtr dwActiveProcessorMask;
51 | public uint dwNumberOfProcessors;
52 | public uint dwProcessorType;
53 | public uint dwAllocationGranularity;
54 | public ushort wProcessorLevel;
55 | public ushort wProcessorRevision;
56 | };
57 |
58 | [StructLayout(LayoutKind.Sequential)]
59 | public struct MEMORY_BASIC_INFORMATION64 {
60 | public ulong BaseAddress;
61 | public ulong AllocationBase;
62 | public int AllocationProtect;
63 | public int __alignment1;
64 | public ulong RegionSize;
65 | public int State;
66 | public int Protect;
67 | public int Type;
68 | public int __alignment2;
69 | }
70 |
71 | [DllImport("kernel32.dll", SetLastError = true)]
72 | public static extern IntPtr OpenProcess(
73 | uint processAccess,
74 | bool bInheritHandle,
75 | int processId);
76 |
77 | [DllImport("kernel32.dll", SetLastError = true)]
78 | public static extern void GetNativeSystemInfo(
79 | ref SYSTEM_INFO lpSystemInfo);
80 |
81 | [DllImport("kernel32.dll", SetLastError = true)]
82 | public static extern int VirtualQueryEx(
83 | IntPtr hProcess,
84 | IntPtr lpAddress,
85 | out MEMORY_BASIC_INFORMATION64 lpBuffer,
86 | uint dwLength);
87 |
88 | [DllImport("kernel32.dll", SetLastError = true)]
89 | public static extern bool ReadProcessMemory(
90 | IntPtr hProcess,
91 | IntPtr lpBaseAddress,
92 | byte[] lpBuffer,
93 | Int32 nSize,
94 | out IntPtr lpNumberOfBytesRead);
95 |
96 | [DllImport("kernel32.dll", SetLastError = true)]
97 | public static extern bool WriteProcessMemory(
98 | IntPtr hProcess,
99 | IntPtr lpBaseAddress,
100 | byte[] lpBuffer,
101 | Int32 nSize,
102 | out IntPtr lpNumberOfBytesWritten);
103 |
104 | [DllImport("kernel32.dll", SetLastError=true)]
105 | public static extern bool CloseHandle(
106 | IntPtr hHandle);
107 |
108 | [DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
109 | public static extern int memcmp(
110 | byte[] b1,
111 | byte[] b2,
112 | long count);
113 | }
114 | }
115 | "@
116 | }
117 |
118 |
119 | function New-Win32Exception {
120 | param(
121 | $LastWin32Error,
122 | $From
123 | )
124 | $e = [ComponentModel.Win32Exception]$LastWin32Error
125 | [ComponentModel.Win32Exception]::New(
126 | "$From (0x$($e.HResult.ToString('x8'))): $($e.Message)"
127 | )
128 | }
129 |
130 |
131 | function Format-Memory {
132 | [Alias('fm')]
133 | [OutputType([PSObject[]])]
134 | [CmdletBinding()]
135 | param(
136 | [Parameter(Mandatory, ValueFromPipeline, Position=0)]
137 | [Microsoft.PowerShell.Commands.GroupInfo]
138 | $Reference
139 | )
140 |
141 | $Reference | Select-Object -ExpandProperty Group
142 |
143 | <#
144 | .SYNOPSIS
145 | Format memory references
146 |
147 | .DESCRIPTION
148 | Turn a memory reference group object into readable format.
149 |
150 | .PARAMETER Reference
151 | A memory reference Microsoft.PowerShell.Commands.GroupInfo object as returned
152 | by the *-Memory Cmdlets in this module.
153 |
154 | .INPUTS
155 | Microsoft.PowerShell.Commands.GroupInfo
156 |
157 | .OUTPUTS
158 | System.Management.Automation.PSObject
159 | #>
160 | }
161 |
162 |
163 | function Search-Memory {
164 | [Alias('srmem')]
165 | [OutputType([Microsoft.PowerShell.Commands.GroupInfo])]
166 | [CmdletBinding()]
167 | param(
168 | [Parameter(Mandatory, ValueFromPipeline)]
169 | [System.Diagnostics.Process]
170 | $Process,
171 |
172 | [Parameter(Mandatory, Position=0)]
173 | [System.Collections.Hashtable]
174 | $Values
175 | )
176 |
177 | $searchObjects = foreach ($type in $Values.Keys) {
178 | foreach ($val in $Values[$type]) {
179 | $size, $bytes = switch ($type) {
180 | 'Byte' {
181 | 1, $val
182 | }
183 | 'Short' {
184 | 2, [BitConverter]::GetBytes($val)
185 | }
186 | 'Int' {
187 | 4, [BitConverter]::GetBytes($val)
188 | }
189 | 'Long' {
190 | 8, [BitConverter]::GetBytes($val)
191 | }
192 | 'Bytes' {
193 | $val.Length, $val
194 | }
195 | 'String' {
196 | $val.Length, [Text.Encoding]::ASCII.GetBytes($val)
197 | }
198 | default {
199 | Write-Error -Exception (New-Object System.ArgumentException) `
200 | -Category InvalidData -TargetObject $Values -ErrorAction Stop `
201 | -Message "Unsupported value type '$type'. Supported " + `
202 | "value types are Byte, Short, Int, Long, String and Bytes."
203 | }
204 | }
205 | [PSCustomObject]@{
206 | value = $val
207 | type = $type
208 | size = $size
209 | bytes = $bytes
210 | }
211 | }
212 | }
213 |
214 | # PROCESS_QUERY_INFORMATION (0x0400) | PROCESS_VM_READ (0x10)
215 | if (($processHandle = [PSMemory.Native]::OpenProcess(
216 | 0x0400 -bor 0x10,
217 | $false,
218 | $Process.Id)) -eq [IntPtr]::Zero
219 | ) {
220 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
221 | throw New-Win32Exception $e -From OpenProcess
222 | }
223 |
224 | $systemInfo = New-Object PSMemory.Native+SYSTEM_INFO
225 | [PSMemory.Native]::GetNativeSystemInfo([ref]$systemInfo)
226 | $minAddress = [long]$systemInfo.lpMinimumApplicationAddress
227 | $maxAddress = [long]$systemInfo.lpMaximumApplicationAddress
228 |
229 | $memoryInfo = New-Object PSMemory.Native+MEMORY_BASIC_INFORMATION64
230 | $memoryInfoSize = [Runtime.InteropServices.Marshal]::SizeOf($memoryInfo)
231 |
232 | $progressTimer = [System.Diagnostics.Stopwatch]::StartNew()
233 |
234 | $searchResult = `
235 | for ($baseAddress = $minAddress; $baseAddress -lt $maxAddress; $baseAddress += $memoryRegionSize) {
236 | if ([PSMemory.Native]::VirtualQueryEx(
237 | $processHandle,
238 | $baseAddress,
239 | [ref]$memoryInfo,
240 | $memoryInfoSize) -eq 0
241 | ) {
242 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
243 | throw New-Win32Exception $e -From VirtualQueryEx
244 | }
245 |
246 | $memoryRegionSize = [long]$memoryInfo.RegionSize
247 |
248 | if ($progressTimer.Elapsed.TotalMilliseconds -ge 500) {
249 | $percentComplete = ($baseAddress-$minAddress)/($maxAddress-$minAddress)*100
250 | Write-Progress -Activity 'Searching Virtual Address Space' `
251 | -Status "$baseAddress/$maxAddress" -PercentComplete $percentComplete
252 | $progressTimer.Restart()
253 | }
254 |
255 | # PAGE_GUARD (0x100)
256 | if ($memoryInfo.Protect -band 0x100) {
257 | continue
258 | }
259 |
260 | # MEM_COMMIT (0x1000)
261 | # PAGE_READWRITE (0x04) | PAGE_WRITECOPY (0x08) | PAGE_EXECUTE_READWRITE (0x40) | PAGE_EXECUTE_WRITECOPY (0x80)
262 | if ($memoryInfo.State -band 0x1000 -and
263 | $memoryInfo.Protect -band (0x04 -bor 0x08 -bor 0x40 -bor 0x80)
264 | ) {
265 | $buffer = [byte[]]::New($memoryRegionSize)
266 | if ([PSMemory.Native]::ReadProcessMemory(
267 | $processHandle,
268 | $baseAddress,
269 | $buffer,
270 | $memoryRegionSize,
271 | [ref][IntPtr]::Zero) -eq 0
272 | ) {
273 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
274 | throw New-Win32Exception $e -From ReadProcessMemory
275 | }
276 |
277 | foreach ($obj in $searchObjects) {
278 | $offset = -1
279 | while (($offset = [array]::IndexOf($buffer, $obj.bytes[0], $offset+1)) -ge 0) {
280 | if ([PSMemory.Native]::memcmp(
281 | $buffer[$offset..($offset+$obj.size-1)],
282 | $obj.bytes,
283 | $obj.size) -eq 0
284 | ) {
285 | $match = [PSCustomObject]@{
286 | ProcessName = $Process.Name
287 | ProcessId = $Process.Id
288 | Address = $baseAddress + $offset
289 | Value = $obj.value
290 | Type = $obj.type
291 | Size = $obj.size
292 | Page = switch ($memoryInfo.Type) {
293 | 0x1000000 {
294 | 'Image'
295 | }
296 | 0x40000 {
297 | 'Mapped'
298 | }
299 | 0x20000 {
300 | 'Private'
301 | }
302 | }
303 | Protection = switch ($memoryInfo.Protect) {
304 | 0x04 {
305 | 'ReadWrite'
306 | }
307 | 0x08 {
308 | 'WriteCopy'
309 | }
310 | 0x40 {
311 | 'ExecuteReadWrite'
312 | }
313 | 0x80 {
314 | 'ExecuteWriteCopy'
315 | }
316 | }
317 | RegionStart = $memoryInfo.BaseAddress
318 | RegionSize = $memoryRegionSize
319 | }
320 | $match.PSObject.TypeNames.Insert(0, 'PSMemory.Reference')
321 | $match
322 | }
323 | }
324 | }
325 | }
326 | }
327 |
328 | $searchResult | Group-Object -Property ProcessId
329 |
330 | [void] [PSMemory.Native]::CloseHandle($processHandle)
331 |
332 | <#
333 | .SYNOPSIS
334 | Search process memory
335 |
336 | .DESCRIPTION
337 | Search any values within the virtual address space of a process.
338 |
339 | .PARAMETER Process
340 | A System.Diagnostics.Process object as returned by the Get-Process Cmdlet
341 | representing the process whose memory to scan.
342 |
343 | .PARAMETER Values
344 | A System.Collections.Hashtable containing typed values to search for. The keys of
345 | the hashtable define the data type while the corresponding values may contain a
346 | comma-separated list of concrete values of that type to search for. Valid data
347 | types respectively hashtable keys are Byte, Short, Int, Long, String and Bytes.
348 |
349 | .INPUTS
350 | System.Diagnostics.Process
351 | System.Collections.Hashtable
352 |
353 | .OUTPUTS
354 | Microsoft.PowerShell.Commands.GroupInfo
355 |
356 | .COMPONENT
357 | Windows API
358 |
359 | .EXAMPLE
360 | Get-Process notepad | Search-Memory -Values @{Int=1234,5678; String='Notepad'}
361 | #>
362 | }
363 |
364 |
365 | function Compare-Memory {
366 | [Alias('crmem')]
367 | [OutputType([Microsoft.PowerShell.Commands.GroupInfo])]
368 | [CmdletBinding()]
369 | param(
370 | [Parameter(Mandatory, ValueFromPipeline, Position=0)]
371 | [Microsoft.PowerShell.Commands.GroupInfo]
372 | $Reference,
373 |
374 | [ScriptBlock]
375 | $Filter,
376 |
377 | [switch]
378 | $Increased,
379 |
380 | [switch]
381 | $Decreased,
382 |
383 | [switch]
384 | $Changed,
385 |
386 | [switch]
387 | $Unchanged
388 | )
389 |
390 | # PROCESS_QUERY_INFORMATION (0x0400) | PROCESS_VM_READ (0x10)
391 | if (($processHandle = [PSMemory.Native]::OpenProcess(
392 | 0x0400 -bor 0x10,
393 | $false,
394 | $Reference.Name)) -eq [IntPtr]::Zero
395 | ) {
396 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
397 | throw New-Win32Exception $e -From OpenProcess
398 | }
399 |
400 | $compareResult = `
401 | foreach ($ref in $Reference.Group) {
402 | $buffer = [byte[]]::New($ref.Size)
403 | if ([PSMemory.Native]::ReadProcessMemory(
404 | $processHandle,
405 | $ref.Address,
406 | $buffer,
407 | $ref.Size,
408 | [ref][IntPtr]::Zero) -eq 0
409 | ) {
410 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
411 | throw New-Win32Exception $e -From ReadProcessMemory
412 | }
413 |
414 | $value = switch ($ref.Type) {
415 | 'Byte' {
416 | $buffer[0]
417 | }
418 | 'Short' {
419 | [BitConverter]::ToInt16($buffer, 0)
420 | }
421 | 'Int' {
422 | [BitConverter]::ToInt32($buffer, 0)
423 | }
424 | 'Long' {
425 | [BitConverter]::ToInt64($buffer, 0)
426 | }
427 | 'Bytes' {
428 | $buffer
429 | }
430 | 'String' {
431 | [Text.Encoding]::ASCII.GetString($buffer)
432 | }
433 | }
434 |
435 | $match = $ref.PSObject.Copy()
436 | $match.Value = $value
437 |
438 | if ($ref.Type -in 'Byte','Short','Int','Long') {
439 | if ($Increased.IsPresent -and ($value -gt $ref.Value)) {
440 | $match
441 | continue
442 | }
443 |
444 | if ($Decreased.IsPresent -and ($value -lt $ref.Value)) {
445 | $match
446 | continue
447 | }
448 | }
449 |
450 | if ($Changed.IsPresent -and ($value -ne $ref.Value)) {
451 | $match
452 | continue
453 | }
454 |
455 | if ($Unchanged.IsPresent -and ($value -eq $ref.Value)) {
456 | $match
457 | continue
458 | }
459 |
460 | if ($PSBoundParameters.ContainsKey('Filter') -and ($ref | Where-Object $Filter)) {
461 | $match
462 | continue
463 | }
464 | }
465 |
466 | $compareResult | Group-Object -Property ProcessId
467 |
468 | <#
469 | .SYNOPSIS
470 | Compare process memory
471 |
472 | .DESCRIPTION
473 | Compare memory references found by the Search-Memory Cmdlet with their current in-memory
474 | values as present in the virtual address space of the process.
475 |
476 | .PARAMETER Reference
477 | A Microsoft.PowerShell.Commands.GroupInfo object representing memory references as returned by
478 | the Search-Memory or Compare-Memory Cmdlets.
479 |
480 | .PARAMETER Filter
481 | A System.Management.Automation.ScriptBlock representing a filter being applied to the memory
482 | references whether to include them in the result.
483 |
484 | .PARAMETER Increased
485 | Include those memory references in the result whose in-memory value has increased. Only applies
486 | to numerical values.
487 |
488 | .PARAMETER Decreased
489 | Include those memory references in the result whose in-memory value has decreased. Only applies
490 | to numerical values.
491 |
492 | .PARAMETER Changed
493 | Include those memory references in the result whose in-memory value has changed.
494 |
495 | .PARAMETER Unchanged
496 | Include those memory references in the result whose in-memory value has not changed.
497 |
498 | .INPUTS
499 | Microsoft.PowerShell.Commands.GroupInfo
500 | System.Management.Automation.ScriptBlock
501 |
502 | .OUTPUTS
503 | Microsoft.PowerShell.Commands.GroupInfo
504 |
505 | .COMPONENT
506 | Windows API
507 |
508 | .EXAMPLE
509 | Get-Process notepad | Search-Memory -Values @{Int=1234} -OutVariable matches
510 | $matches | Compare-Memory -Increased -Filter {$_.Value -lt 1000}
511 | #>
512 | }
513 |
514 |
515 | function Update-Memory {
516 | [CmdletBinding()]
517 | [Alias('udmem')]
518 | param(
519 | [Parameter(Mandatory, ValueFromPipeline, Position=0)]
520 | [Microsoft.PowerShell.Commands.GroupInfo]
521 | $Reference,
522 |
523 | [Parameter(Mandatory, ParameterSetName='Byte')]
524 | [byte]
525 | $Byte,
526 |
527 | [Parameter(Mandatory, ParameterSetName='Short')]
528 | [int16]
529 | $Short,
530 |
531 | [Parameter(Mandatory, ParameterSetName='Int')]
532 | [int32]
533 | $Int,
534 |
535 | [Parameter(Mandatory, ParameterSetName='Long')]
536 | [long]
537 | $Long,
538 |
539 | [Parameter(Mandatory, ParameterSetName='String')]
540 | [string]
541 | $String,
542 |
543 | [Parameter(Mandatory, ParameterSetName='Bytes')]
544 | [byte[]]
545 | $Bytes
546 | )
547 |
548 | $valueSize, $value, $valueBytes = switch ($PSCmdlet.ParameterSetName) {
549 | 'Byte' {
550 | 1, $Byte, $Byte
551 | }
552 | 'Short' {
553 | 2, $Short, [BitConverter]::GetBytes($Short)
554 | }
555 | 'Int' {
556 | 4, $Int, [BitConverter]::GetBytes($Int)
557 | }
558 | 'Long' {
559 | 8, $Long, [BitConverter]::GetBytes($Long)
560 | }
561 | 'Bytes' {
562 | $Bytes.Length, $Bytes, $Bytes
563 | }
564 | 'String' {
565 | $String.Length, $String,[Text.Encoding]::ASCII.GetBytes($String)
566 | }
567 | }
568 |
569 | # PROCESS_QUERY_INFORMATION (0x0400) | PROCESS_VM_WRITE (0x20)
570 | if (($processHandle = [PSMemory.Native]::OpenProcess(
571 | 0x0400 -bor 0x20,
572 | $false,
573 | $Reference.Name)) -eq [IntPtr]::Zero
574 | ) {
575 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
576 | throw New-Win32Exception $e -From OpenProcess
577 | }
578 |
579 | $updateResult = `
580 | foreach ($ref in $Reference.Group) {
581 | if ([PSMemory.Native]::WriteProcessMemory(
582 | $processHandle,
583 | $ref.Address,
584 | $valueBytes,
585 | $valueSize,
586 | [ref][IntPtr]::Zero) -eq 0
587 | ) {
588 | $e = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
589 | throw New-Win32Exception $e -From WriteProcessMemory
590 | }
591 |
592 | $newRef = $ref.PSObject.Copy()
593 | $newRef.Value = $value
594 | $newRef.Type = $PSCmdlet.ParameterSetName
595 | $newRef.Size = $valueSize
596 |
597 | $newRef
598 | }
599 |
600 | $updateResult | Group-Object -Property ProcessId
601 |
602 | <#
603 | .SYNOPSIS
604 | Update process memory
605 |
606 | .DESCRIPTION
607 | Update in-memory values as present in the virtual address space of a process represented by
608 | memory references as returned from the Search-Memory and Compare-Memory Cmdlets.
609 |
610 | .PARAMETER Reference
611 | A Microsoft.PowerShell.Commands.GroupInfo object representing memory references as returned by
612 | the Search-Memory or Compare-Memory Cmdlet.
613 |
614 | .PARAMETER Byte
615 | An 8-Bit numerical value to update the in-memory value represented by the memory reference with.
616 |
617 | .PARAMETER Short
618 | A 16-Bit numerical value to update the in-memory value represented by the memory reference with.
619 |
620 | .PARAMETER Int
621 | A 32-Bit numerical value to update the in-memory value represented by the memory reference with.
622 |
623 | .PARAMETER Long
624 | A 64-Bit numerical value to update the in-memory value represented by the memory reference with.
625 |
626 | .PARAMETER String
627 | A string value to update the in-memory value represented by the memory reference with.
628 |
629 | .PARAMETER Bytes
630 | A byte array value to update the in-memory value represented by the memory reference with.
631 |
632 | .INPUTS
633 | Microsoft.PowerShell.Commands.GroupInfo
634 |
635 | .OUTPUTS
636 | Microsoft.PowerShell.Commands.GroupInfo
637 |
638 | .COMPONENT
639 | Windows API
640 |
641 | .EXAMPLE
642 | Get-Process notepad | Search-Memory -Values @{Int=1234} | Update-Memory -Int 4321
643 |
644 | .EXAMPLE
645 | Get-Process notepad | Search-Memory -Values @{Long=123456789} -OutVariable matches
646 | $matches | Compare-Memory -Changed
647 | $matches | Update-Memory -Long 123456789
648 | #>
649 | }
650 |
651 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://www.powershellgallery.com/packages/PSMemory)
6 | 
7 | 
8 | [](https://www.codacy.com/app/off-world/PSMemory?utm_source=github.com&utm_medium=referral&utm_content=off-world/PSMemory&utm_campaign=Badge_Grade)
9 |
10 | ___
11 |
12 | **PSMemory** is a 64 bit windows memory scanner written in PowerShell hence fully automation capable.
13 |
14 | ___
15 |
16 | ## Description
17 |
18 | ### Cmdlets
19 |
20 | #### `Search-Memory`
21 |
22 | searches the virtual address space of a process for specific values returning references to the memory they reside in.
23 | Besides the value itself these references contain other related information such as the concrete memory address or the protection of
24 | the page the value was found in. A search can be specified by the `-Values` parameter in the form of a hashtable where the *keys* define
25 | data types and the corresponding *values* define the values of that data type to be searched for as a comma-separated list. Valid data
26 | types to be specified as *keys* for the search table are
27 | - **Byte** for 8 bit numerical values
28 | - **Short** for 16 bit numerical values
29 | - **Int** for 32 bit numerical values
30 | - **Long** for 64 bit numerical values
31 | - **String** for ASCII text of arbitrary length
32 | - **Bytes** for Unicode byte arrays of arbitrary length
33 |
34 | **Example**: a search for two 32 bit numerical values *1234* and *5678* as well as the text *Notepad* within the memory of the process *notepad* saving the result in a variable *notepad* for further processing may look like
35 | ```Powershell
36 | Get-Process notepad | Search-Memory -Values @{
37 | Int = 1234, 5678
38 | String = 'Notepad'
39 | } -OutVariable notepad
40 | ```
41 |
42 | #### `Compare-Memory`
43 |
44 | compares those references' values as present in memory when the reference was created or last updated to the current
45 | in-memory value. With the `-Changed` and `-Unchanged` parameters each reference will be matched whose in-memory value has either
46 | changed in any way or stood the same. For numerical values exclusively there are additionally the `-Increased` and `-Decreased` parameters which track if the in-memory value did either become greater or lower. For everything else there is the `-Filter` parameter where a PowerShell ScriptBlock may be supplied with a custom comparison criteria.
47 |
48 | **Example**: given the above search now keep only those references whose in-memory value is either exactly *42* or has increased and update the reference result variable
49 | ```Powershell
50 | $notepad | Compare-Memory -Increased -Filter {$_.Value -eq 42} -OutVariable notepad
51 | ```
52 |
53 | #### `Update-Memory`
54 |
55 | updates the current in-memory value referenced by a reference. The new value to be written may be supplied by one of the data type parameters depending on what value of what size to write.
56 |
57 | **Example:** after filtering the memory references above now update each remaining referenced in-memory value with a new 32 bit numerical value of *9876*
58 | ```Powershell
59 | $notepad | Update-Memory -Int 9876
60 | ```
61 | #### `Format-Memory`
62 |
63 | formats reference objects as returned by **all** the aforementioned Cmdlets into formatted and human readable output.
64 |
65 | **Example:**
66 | ```Powershell
67 | Get-Process notepad | Search-Memory -Values @{Int = 42} -OutVariable notepad | Format-Memory
68 | ```
69 | or
70 | ```Powershell
71 | $notepad | Compare-Memory -Increased -Filter {$_.Value -eq 42} | Format-Memory
72 | ```
73 | Alternatively, you can use the alias `fm`.
74 |
75 | ## Installation
76 |
77 | Install from [PowerShell Gallery](https://www.powershellgallery.com/packages/PSMemory)
78 |
79 | ```Powershell
80 | Install-Module -Name PSMemory
81 | ```
82 | or
83 | ```Shell
84 | git clone https://github.com/tobiohlala/PSMemory
85 | ```
86 |
--------------------------------------------------------------------------------