{"id":48496454,"url":"https://github.com/veecore/retarget","last_synced_at":"2026-04-11T16:01:00.751Z","repository":{"id":349316182,"uuid":"1201854470","full_name":"veecore/retarget","owner":"veecore","description":"Work-in-progress cross-platform function, Objective-C, and COM hooking for macOS and Windows.","archived":false,"fork":false,"pushed_at":"2026-04-06T16:46:02.000Z","size":899,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T12:34:26.859Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/veecore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-05T08:57:30.000Z","updated_at":"2026-04-06T16:46:07.000Z","dependencies_parsed_at":null,"dependency_job_id":"9a1a6134-88e6-4622-99ab-915f221d4a27","html_url":"https://github.com/veecore/retarget","commit_stats":null,"previous_names":["veecore/retarget"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/veecore/retarget","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veecore%2Fretarget","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veecore%2Fretarget/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veecore%2Fretarget/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veecore%2Fretarget/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/veecore","download_url":"https://codeload.github.com/veecore/retarget/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/veecore%2Fretarget/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31556239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-04-07T12:30:45.187Z","updated_at":"2026-04-08T13:00:45.348Z","avatar_url":"https://github.com/veecore.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# retarget\n\n`retarget` is a typed hook crate for macOS and Windows with a deliberately\nsmall, convenient public surface.\n\nIt is built to make native hooks feel straightforward in Rust:\n\n- exported functions\n- Objective-C methods\n- COM methods\n\nThe intended flow is simple:\n\n- declare hooks near the code that owns them\n- optionally log or observe hits\n- call `install_registered_hooks()` once\n\nThe crate is still evolving in the open, so the API should be treated as\nexperimental for now, but the direction is clear: less boilerplate, fewer\nruntime concepts, and a more readable hook story.\n\nWhat it is trying to be:\n\n- keep the public API root-first\n- make hook declarations small and typed\n- keep macro-support details internal\n- keep observation generic and separate from product reporting\n- make the common path obvious and ergonomic\n\nIf you want the short version, `retarget` is trying to make this kind of work\nfeel normal:\n\n- declare a hook in ordinary Rust\n- install once near startup\n- watch regular code flow through the detour\n\nQuick feel:\n\n```rust\nuse std::fs::File;\nuse std::io::ErrorKind;\n\nuse retarget::{hook, install_registered_hooks};\n\n#[cfg(target_os = \"macos\")]\n#[hook::c]\nunsafe extern \"C\" fn open(\n    _path: *const libc::c_char,\n    _flags: libc::c_int,\n    _mode: libc::mode_t,\n) -\u003e libc::c_int {\n    unsafe {\n        *libc::__error() = libc::ENOENT;\n    }\n    -1\n}\n\n#[cfg(target_os = \"windows\")]\n#[hook::c((\"kernel32.dll\", \"CreateFileW\"))]\nunsafe extern \"system\" fn create_file_w(\n    _path: *const u16,\n    _access: u32,\n    _share: u32,\n    _security: *const std::ffi::c_void,\n    _creation: u32,\n    _flags: u32,\n    _template: *mut std::ffi::c_void,\n) -\u003e *mut std::ffi::c_void {\n    unsafe {\n        windows_sys::Win32::Foundation::SetLastError(\n            windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND,\n        );\n    }\n    windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE\n}\n\nfn main() -\u003e std::io::Result\u003c()\u003e {\n    install_registered_hooks()?;\n\n    let error = File::open(\"Cargo.toml\").unwrap_err();\n    assert_eq!(error.kind(), ErrorKind::NotFound);\n\n    Ok(())\n}\n```\n\nThat is the smallest useful loop:\n\n- declare the hook\n- install once\n- call normal Rust code and see the replacement take effect\n\nIf you also want to observe hook hits, add `#[hook::observer]` and\n`#[hook::observe(...)]`. That path is optional, not the starting point.\n\nSome of the design choices behind that feel:\n\n- `hook::c` should describe what to hook, not how installation works\n- `hook::com_impl` is the main COM story, so related hooks can live in one\n  inherent impl block\n- observation is event-oriented and local, not a big retained runtime\n- lower-level target types still exist when you need them, but they are not the\n  first thing the crate asks you to touch\n\n## Examples\n\n- [`examples/interception_story.rs`](examples/interception_story.rs) shows the lightweight\n  \"print on hit\" observation path in one file while still changing what\n  `std::fs::File::open(...)` sees.\n- [`examples/screenshot_agent`](examples/screenshot_agent) is a single package\n  that carries the injected `cdylib`, a `screenshot_victim` bin, and a\n  `screenshot_demo` bin for the full one-command run.\n\nThat screenshot demo is the most fun place to start if you want the crate to\nfeel real. It launches a small victim app that captures the screen with `xcap`,\nthen injects a hook library that swaps the live screenshot out for a synthetic\nframe.\n\nSo instead of a toy return-value demo, you get a real before-and-after:\n\n- the victim captures the real desktop first\n- the agent gets injected into that running process\n- later captures come back as our synthetic frame instead\n\nRun the full screenshot demo like this:\n\n```bash\ncargo run --manifest-path examples/screenshot_agent/Cargo.toml --features inject --bin screenshot_demo\n```\n\nIt builds the victim and agent, starts the victim, lets a few captures succeed,\nand then injects the screenshot agent automatically. It is intentionally a\nshowpiece: one command, one victim, one injected library, one visible change.\n\nThis is a live desktop demo rather than a normal CI target. It wants a real\nscreen-capture environment and the usual OS permissions for screenshot APIs.\n\nIf you want to do the two steps yourself, you still can:\n\n```bash\ncargo run --manifest-path examples/screenshot_agent/Cargo.toml --bin screenshot_victim\n```\n\nThen inject the agent into the printed PID:\n\n```bash\ncargo run --manifest-path examples/screenshot_agent/Cargo.toml --features inject --bin inject -- \u003cpid\u003e /tmp/retarget-screenshot-agent.log\n```\n\nThe screenshot agent is meant to be loaded with [`hook-inject`](https://crates.io/crates/hook-inject).\nIts injector path looks like this:\n\n```rust\nuse hook_inject::{inject_process, Library, Process};\nuse std::ffi::CString;\n\nlet pid = 1234;\nlet process = Process::from_pid(pid)?;\nlet log_path = CString::new(\"/tmp/retarget-screenshot-agent.log\")?;\n\nlet library = Library::from_crate(\"examples/screenshot_agent\")?\n    .with_data(log_path);\n\nlet _injected = inject_process(process, library)?;\n# Ok::\u003c(), Box\u003cdyn std::error::Error\u003e\u003e(())\n```\n\nThat keeps the \"write hooks in Rust\" story and the \"load them into another\nprocess\" story nicely separate.\n\nWarnings:\n\n- The crate is still experimental, so expect API churn while the surface\n  settles.\n- Install hooks as early as practical in process startup.\n- Anything under `retarget::__macro_support` and generated `__retarget_*`\n  names is internal and may change without notice.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveecore%2Fretarget","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fveecore%2Fretarget","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fveecore%2Fretarget/lists"}