{"id":20261742,"url":"https://github.com/rbspy/read-process-memory","last_synced_at":"2025-04-05T00:09:28.294Z","repository":{"id":45196089,"uuid":"70688422","full_name":"rbspy/read-process-memory","owner":"rbspy","description":"Read memory from another process","archived":false,"fork":false,"pushed_at":"2024-02-06T01:58:07.000Z","size":71,"stargazers_count":121,"open_issues_count":1,"forks_count":19,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-28T23:09:09.983Z","etag":null,"topics":["diagnostics","inspection","memory"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rbspy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"License.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-12T10:18:10.000Z","updated_at":"2025-02-25T13:44:20.000Z","dependencies_parsed_at":"2024-06-21T05:48:15.606Z","dependency_job_id":"eddb6158-b5dd-4a13-93ce-f714d43d71c6","html_url":"https://github.com/rbspy/read-process-memory","commit_stats":{"total_commits":79,"total_committers":11,"mean_commits":7.181818181818182,"dds":0.5189873417721519,"last_synced_commit":"9d801f7f46d1c023136cc830a591ec05c771b2d5"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbspy%2Fread-process-memory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbspy%2Fread-process-memory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbspy%2Fread-process-memory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rbspy%2Fread-process-memory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rbspy","download_url":"https://codeload.github.com/rbspy/read-process-memory/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247266564,"owners_count":20910836,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["diagnostics","inspection","memory"],"created_at":"2024-11-14T11:26:52.334Z","updated_at":"2025-04-05T00:09:28.274Z","avatar_url":"https://github.com/rbspy.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GitHub Actions Build status](https://github.com/rbspy/read-process-memory/actions/workflows/build.yml/badge.svg)](https://github.com/rbspy/read-process-memory/actions/workflows/build.yml) ![Cirrus CI Build status](https://api.cirrus-ci.com/github/luser/read-process-memory.svg) [![crates.io](https://img.shields.io/crates/v/read-process-memory.svg)](https://crates.io/crates/read-process-memory) [![](https://docs.rs/read-process-memory/badge.svg)](https://docs.rs/read-process-memory)\n\nA crate to read memory from another process. Code originally taken from the [rbspy](https://github.com/rbspy/rbspy/) project. This crate has now returned home to the `rbspy` GitHub organization. :)\n\n# Example\n\nThis example re-executes itself as a child process in order to have a separate process to use for demonstration purposes. If you need to read memory from a process that you are spawning, your usage should look very similar to this:\n\n```rust\nuse std::convert::TryInto;\nuse std::env;\nuse std::io::{self, BufReader, BufRead, Read, Result};\nuse std::process::{Command, Stdio};\n\nuse read_process_memory::{\n  Pid,\n  ProcessHandle,\n  CopyAddress,\n  copy_address,\n};\n\nfn main() -\u003e Result\u003c()\u003e {\n    if env::args_os().len() \u003e 1 {\n      // We are the child.\n      return in_child();\n    }\n    // Run this executable again so we have a child process to read.\n    let mut child = Command::new(env::current_exe()?)\n        .stdin(Stdio::piped())\n        .stdout(Stdio::piped())\n        .arg(\"child\")\n        .spawn()?;\n\n    // Get a ProcessHandle to work with.\n    let handle: ProcessHandle = (\u0026child).try_into().unwrap();\n\n    // The child process will print the address to read from on stdout.\n    let mut stdout = BufReader::new(child.stdout.take().unwrap());\n    let mut addr_string = String::new();\n    stdout.read_line(\u0026mut addr_string)?;\n    let address = usize::from_str_radix(addr_string.trim(), 16).unwrap();\n\n    // Try to read 10 bytes from that address\n    let bytes = copy_address(address, 10, \u0026handle)?;\n    println!(\"Read: {:?}\", bytes);\n\n    // Tell the child to exit by closing its stdin.\n    drop(child.stdin.take());\n    // And wait for it to exit.\n    child.wait()?;\n    Ok(())\n}\n\nfn in_child() -\u003e Result\u003c()\u003e {\n    // Allocate a 10-byte Vec for the parent to read.\n    let readable_bytes: Vec\u003cu8\u003e = vec![\n        0xc0, 0x72, 0x80, 0x79, 0xeb, 0xf1, 0xbc, 0x87, 0x06, 0x14,\n    ];\n    // Print the address of the Vec to stdout so the parent can find it.\n    println!(\"{:x}\", readable_bytes.as_ptr() as usize);\n    // Now wait to exit until the parent closes our stdin, to give\n    // it time to read the memory.\n    let mut buf = Vec::new();\n    // We don't care if this succeeds.\n    drop(io::stdin().read_to_end(\u0026mut buf));\n    Ok(())\n}\n\n```\n\n# How it works\n\nHere's a summary, with some C pseudocode, of how the `read-process-memory`\ncrate works under the hood on each of the platforms it supports. The three\ninputs are:\n\n* `PID`: the process ID to read from\n* `LENGTH`: how much memory to read\n* `ADDRESS`: the address to read from\n\n## Linux:\n\nUses [process_vm_readv](https://man7.org/linux/man-pages/man2/process_vm_readv.2.html)\n\n```c\nvoid* TARGET = (void*) 0x123412341324;\nstruct iovec local;\nlocal.iov_base = calloc(LENGTH, sizeof(char));\nlocal.iov_len = LENGTH;\nstruct iovec remote;\nremote[0].iov_base = TARGET;\nremote[0].iov_len = LENGTH;\nprocess_vm_readv(PID, local, 2, remote, 1, 0);\n```\n\n## Mac OS:\n\nUses [vm_read_overwrite](https://developer.apple.com/documentation/kernel/1585371-vm_read_overwrite)\n\n```c\nmach_port_name_t task;\ntask_for_pid(mach_task_self(), PID, \u0026task)\nvm_size_t read_len = LENGTH;\nchar result[LENGTH];\nvm_read_overwrite(task, TARGET, LENGTH, \u0026result, \u0026read_len)\n```\n\n## FreeBSD:\n\nUses [ptrace](https://man.freebsd.org/cgi/man.cgi?query=ptrace). This one stops the process to read from it.\n\n```c\n// attach\nint wait_status = 0;\nattach_status = ptrace(PT_ATTACH, PID, null, 0);\nwaitpid(PID, \u0026wait_status, 0);\nWIFSTOPPED(wait_status)\nchar result[LENGTH];\ndesc = PtraceIoDesc {\n  piod_op: PIOD_READ_D,\n  piod_offs: TARGET;\n  piod_addr: \u0026result;\n  piod_len: LENGTH,\n};\n// read data\nptrace(PT_IO, PID, \u0026desc, 0);\n// detach\nptrace(PT_DETACH, PID, null, 0);\n```\n\n## Windows:\n\nUses [ReadProcessMemory](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory):\n\n```c\nchar result[LENGTH];\nReadProcessMemory(PID, ADDRESS, \u0026result, LENGTH, null);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbspy%2Fread-process-memory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frbspy%2Fread-process-memory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frbspy%2Fread-process-memory/lists"}