Come scrivere un programma Perl, Python o Ruby per cambiare la memoria di un altro processo su Windows?
-
06-07-2019 - |
Domanda
Mi chiedo se Perl, Python o Ruby possano essere usati per scrivere un programma in modo che cercherà 0x12345678 nella memoria di un altro processo (probabilmente l'heap, sia per i dati che per i dati del codice) e quindi se viene trovato , cambiarlo in 0x00000000? È qualcosa di simile a Cheat Engine , che può fare qualcosa del genere su Windows.
Soluzione
Inizialmente pensavo che ciò non fosse possibile ma dopo aver visto il commento di Brian, ho cercato CPAN ed ecco, c'è Win32 :: Process :: Memory :
C:\> ppm install Win32::Process::Info
C:\> ppm install Win32::Process::Memory
Apparentemente il modulo utilizza ReadProcessMemory
funzione: ecco uno dei miei tentativi:
#!/usr/bin/perl
use strict; use warnings;
use Win32;
use Win32::Process;
use Win32::Process::Memory;
my $process;
Win32::Process::Create(
$process,
'C:/opt/vim/vim72/gvim.exe',
q{},
0,
NORMAL_PRIORITY_CLASS,
q{.}
) or die ErrorReport();
my $mem = Win32::Process::Memory->new({
pid => $process->GetProcessID(),
access => 'read/query',
});
$mem->search_sub( 'VIM', sub {
print $mem->hexdump( Inizialmente pensavo che ciò non fosse possibile ma dopo aver visto il commento di Brian, ho cercato CPAN ed ecco, c'è Win32 :: Process :: Memory :
C:\> ppm install Win32::Process::Info
C:\> ppm install Win32::Process::Memory
Apparentemente il modulo utilizza ReadProcessMemory
funzione: ecco uno dei miei tentativi:
C:\Temp> proc
0052A580 : 56 49 4D 20 2D 20 56 69 20 49 4D 70 72 6F 76 65 : VIM - Vi IMprove
0052A590 : 64 20 37 2E 32 20 28 32 30 30 38 20 41 75 67 20 : d 7.2 (2008 Aug
0052A5F0 : 56 49 4D 52 55 4E 54 49 4D 45 3A 20 22 00 : VIMRUNTIME: ".
0052A600 : 20 20 66 61 6C 6C 2D 62 61 63 6B 20 66 6F 72 20 : fall-back for
0052A610 : 24 56 : $V
Output:
<*>[0], 0x20), "\n";
});
sub ErrorReport{
Win32::FormatMessage( Win32::GetLastError() );
}
END { $process->Kill(0) if $process }
Output:
<*>Altri suggerimenti
È possibile farlo se hai collegato il tuo programma come debugger al processo, cosa che dovrebbe essere possibile in quelle lingue se esistono wrapper attorno alle API appropriate, o accedendo direttamente alle funzioni di Windows attraverso qualcosa come ctypes (per pitone). Tuttavia, potrebbe essere più facile farlo in una lingua di livello più basso, poiché in quelle di livello superiore dovrai preoccuparti di come tradurre i tipi di dati di alto livello in quelli di livello inferiore ecc.
Inizia chiamando OpenProcess sul processo per eseguire il debug, con l'accesso appropriato richiesto (devi avere un amministratore sulla macchina / avere privilegi abbastanza alti per ottenere l'accesso). Dovresti quindi essere in grado di chiamare funzioni come ReadProcessMemory e WriteProcessMemory per leggere e scrivere su quel processo la memoria.
[Modifica] Ecco una rapida dimostrazione di pitone del concetto di una funzione che legge correttamente la memoria dallo spazio degli indirizzi di un altro processo:
import ctypes
import ctypes.wintypes
kernel32 = ctypes.wintypes.windll.kernel32
# Various access flag definitions:
class Access:
DELETE = 0x00010000
READ_CONTROL= 0x00020000
SYNCHRONIZE = 0x00100000
WRITE_DAC = 0x00040000
WRITE_OWNER = 0x00080000
PROCESS_VM_WRITE = 0x0020
PROCESS_VM_READ = 0x0010
PROCESS_VM_OPERATION = 0x0008
PROCESS_TERMINATE = 0x0001
PROCESS_SUSPEND_RESUME = 0x0800
PROCESS_SET_QUOTA = 0x0100
PROCESS_SET_INFORMATION = 0x0200
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_DUP_HANDLE = 0x0040
PROCESS_CREATE_THREAD = 0x0002
PROCESS_CREATE_PROCESS = 0x0080
def read_process_mem(pid, address, size):
"""Read memory of the specified process ID."""
buf = ctypes.create_string_buffer(size)
gotBytes = ctypes.c_ulong(0)
h = kernel32.OpenProcess(Access.PROCESS_VM_READ, False, pid)
try:
if kernel32.ReadProcessMemory(h, address, buf, size, ctypes.byref(gotBytes)):
return buf
else:
# TODO: report appropriate error GetLastError
raise Exception("Failed to access process memory.")
finally:
kernel32.CloseHandle(h)
Nota che dovrai determinare dove in memoria cercare le cose - la maggior parte di quello spazio degli indirizzi sarà non mappato, pensando che ci siano alcuni offset standard per cercare cose come il codice del programma, le dll ecc.
Bene, la parte divertente è ottenere l'accesso alla memoria dell'altro processo. CheatEngine lo fa eseguendo l'intero sistema operativo in una macchina virtuale che consente di annullare la protezione della memoria. Esiste anche il modello "running under a debugger", che in genere significa avviare l'applicazione di destinazione come processo figlio dell'applicazione di modifica, con privilegi elevati. Vedi API Win32 per tante cose divertenti a tale proposito.
In Perl, una volta ottenuto l'accesso richiesto, probabilmente vorrai interagire con esso usando Win32 :: Security :: Raw .
Ci sono modi per farlo usando l'iniezione di processo, la libreria di caricamento ritardato ecc.
Non ti vedo farlo dagli strumenti che hai elencato. Questo è il paese C e assemblatore e sta iniziando a portarti nel territorio di scrittura dei virus. Una volta che funziona, tutti i pacchetti anti-virus lo porranno in esecuzione e proveranno a isolarlo. Quindi è meglio che tu voglia davvero farlo.
" Con il potere arriva molto .... "
Buona fortuna
È possibile implementare l'intero processo in una delle lingue elencate ma una lingua compilata sarebbe migliore per la scansione della memoria (considerazioni sulla velocità se non altro). Esiste una dll (con sorgente) chiamata SigScan disponibile che, sebbene su misura per un gioco specifico, potrebbe probabilmente essere modificata per soddisfare le tue esigenze con il minimo sforzo.
Basandoci sulla risposta corretta di Brian, ecco un esempio veloce e sporco dell'uso di una dll per ottenere il tuo indirizzo da Python. Questo è, ovviamente, specifico per l'implementazione delle DLL. " Nome del modulo " sarebbe generalmente il nome della dll come visualizzato in Cheat Engines "Enumerate DLLs and Symbols" finestra di dialogo.
Con l'esempio di Brian come linea guida e MSDN puoi facilmente estenderlo con il tuo metodo WriteProcessMemory.
import win32defines
import win32process
import win32gui
from ctypes import *
SigScan = cdll.SigScan
kernel32 = windll.kernel32
addresses = {"Value1" : {"sigArg1" : "b0015ec390518b4c24088d4424005068",
"sigArg2" : 36,
"address" : None,
"size" : 32
},
"Value2" :{"sigArg1" : "3b05XXXXXXXX741285c0",
"sigArg2" : None,
"address" : None,
"size" : 32
}
}
def read_process_mem(pid, address, size):
"""Read memory of the specified process ID."""
buf = create_string_buffer(size)
gotBytes = c_ulong(0)
h = kernel32.OpenProcess(win32defines.PROCESS_VM_READ, False, pid)
try:
if kernel32.ReadProcessMemory(h, address, buf, size, byref(gotBytes)):
return buf
else:
# TODO: report appropriate error GetLastError
raise Exception("Failed to access process memory.")
finally:
kernel32.CloseHandle(h)
if __name__ == "__main__":
pid, id = None, None
## HWND
hwnd = win32gui.FindWindowEx(0, 0, 0, "Window Name here")
## pid
pid = win32process.GetWindowThreadProcessId(hwnd)[-1]
## Initialize the sigscan dll
SigScan.InitializeSigScan(pid, "Module Name")
## Find all the addresses registered
for key in addresses.keys():
addresses[key]["address"] = SigScan.SigScan(addresses[key]["sigArg1"],
addresses[key]["sigArg2"])
## Allow the scanner to clean up
SigScan.FinalizeSigScan()
for key in addresses.keys():
if addresses[key]["address"] != None:
print repr(read_process_mem(pid, addresses[key]["address"],
addresses[key]["size"]).raw)
Ho scritto Proc :: Memory e la sua libreria sottostante libvas a questo scopo. Chiama semplicemente {Read, Write} ProcessMemory
sotto il cofano di Windows, ma supporta anche altre piattaforme. Esempio:
my $mem = Proc::Memory->new(pid => $);
$mem->poke(0x12345678, 'L') = 12;