How to write a Perl, Python, or Ruby program to change the memory of another process on Windows?

后端 未结 6 1791
终归单人心
终归单人心 2020-12-30 12:20

I wonder if Perl, Python, or Ruby can be used to write a program so that it will look for 0x12345678 in the memory of another process (probably the heap, for both data and c

相关标签:
6条回答
  • 2020-12-30 12:49

    It is possible to do so if you have attached your program as a debugger to the process, which should be possible in those languages if wrappers around the appropriate APIs exist, or by directly accessing the windows functions through something like ctypes (for python). However, it may be easier to do in a more low-level language, since in higher level ones you'll have to be concerned with how to translate highlevel datatypes to lower ones etc.

    Start by calling OpenProcess on the process to debug, with the appropriate access requested (you'll need to be an Admin on the machine / have fairly high privileges to gain access). You should then be able to call functions like ReadProcessMemory and WriteProcessMemory to read from and write to that process's memory.

    [Edit] Here's a quick python proof of concept of a function that successfully reads memory from another process's address space:

    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)
    

    Note that you'll need to determine where in memory to look for things - most of that address space is going to be unmapped, thought there are some standard offsets to look for things like the program code, dlls etc.

    0 讨论(0)
  • 2020-12-30 12:51

    Well, the fun part is getting access to the other process's memory. CheatEngine does it by running your entire OS under a virtual machine that allows memory protection to be defeated. There's also the 'running under a debugger' model, generally meaning start the target application as a child process of the modifying application, with elevated privileges. See the Win32 API for lots of fun stuff about that.

    In Perl, once you had the requisite access, you'd probably want to interact with it using Win32::Security::Raw.

    0 讨论(0)
  • 2020-12-30 13:05

    It is possible to implement the entire process in one of the languages listed but a compiled language would be better for memory scanning (speed considerations if nothing else). There is a dll (with source) called SigScan available that, while tailored for a specific game, could probably be modified to suite your needs with minimal effort.

    Building on Brian's correct answer here's a quick and dirty example of using a dll to get your address from within python. This is, of course, specific to the DLLs implementation. "Module name" would generally be the dll name as displayed in Cheat Engines "Enumerate DLLs and Symbols" dialog.

    With Brian's example as a guideline and MSDN you could easily extend this with your own WriteProcessMemory method.

    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)
    0 讨论(0)
  • 2020-12-30 13:06

    I initially thought this was not possible but after seeing Brian's comment, I searched CPAN and lo and behold, there is Win32::Process::Memory:

    C:\> ppm install Win32::Process::Info
    C:\> ppm install Win32::Process::Memory
    

    The module apparently uses the ReadProcessMemory function: Here is one of my attempts:

    #!/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($_[0], 0x20), "\n";
    });
    
    sub ErrorReport{
        Win32::FormatMessage( Win32::GetLastError() );
    }
    
    END { $process->Kill(0) if $process }
    

    Output:

    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
    
    0 讨论(0)
  • 2020-12-30 13:11

    I wrote Proc::Memory and its underlying library libvas for this purpose. It just calls {Read,Write}ProcessMemory under the hood on Windows, but it also supports other platforms. Example:

    my $mem = Proc::Memory->new(pid => $$); 
    $mem->poke(0x12345678, 'L') = 12;
    
    0 讨论(0)
  • 2020-12-30 13:12

    There are ways to do do this using Process injection, delay load library etc.

    I don't see you doing it from the tools you have listed. This is C and assembler country and beginning to get you into virus writing territory. Once you get it to work, any anti-virus packages will veto it running and try and isolate it. So you better really want to do this.

    "With power comes much ...."

    Good luck

    0 讨论(0)
提交回复
热议问题