Skip to main content

asdf_overlay_dll/
lib.rs

1#![windows_subsystem = "windows"]
2
3//! Official DLL crate for attaching [`asdf_overlay`] to other processes.
4//! Using this DLL, the overlay can be controlled via cross-process IPC.
5//!
6//! Injection can be done using `asdf-overlay-client` crate.
7
8#[cfg(debug_assertions)]
9mod dbg;
10
11mod server;
12
13extern crate asdf_overlay_vulkan_layer;
14
15use anyhow::Context;
16use asdf_overlay::{
17    backend::{Backends, window::ListenInputFlags},
18    event_sink::OverlayEventSink,
19    initialize,
20};
21use asdf_overlay_common::{
22    ipc::create_ipc_addr,
23    request::{Request, WindowRequest},
24};
25use asdf_overlay_event::{OverlayEvent, WindowEvent};
26use core::time::Duration;
27use scopeguard::defer;
28use std::{ffi::OsStr, thread};
29use tokio::{
30    net::windows::named_pipe::{NamedPipeServer, ServerOptions},
31    runtime::Runtime,
32    time::sleep,
33};
34use tracing::{debug, error, trace, warn};
35use windows::{
36    Win32::{
37        Foundation::{GENERIC_READ, GENERIC_WRITE, HINSTANCE},
38        Security::{
39            ACL, AllocateAndInitializeSid,
40            Authorization::{
41                EXPLICIT_ACCESS_A, SET_ACCESS, SetEntriesInAclA, TRUSTEE_A, TRUSTEE_IS_SID,
42                TRUSTEE_IS_USER,
43            },
44            FreeSid, InitializeSecurityDescriptor, NO_INHERITANCE, PSECURITY_DESCRIPTOR, PSID,
45            SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR, SECURITY_WORLD_SID_AUTHORITY,
46            SetSecurityDescriptorDacl,
47        },
48        System::{
49            SystemServices::{
50                DLL_PROCESS_ATTACH, SECURITY_DESCRIPTOR_REVISION, SECURITY_WORLD_RID,
51            },
52            Threading::GetCurrentProcessId,
53        },
54    },
55    core::{BOOL, PSTR},
56};
57
58use crate::server::IpcServerConn;
59
60/// IPC server main loop.
61#[tracing::instrument(skip(server))]
62async fn run(server: NamedPipeServer) -> anyhow::Result<()> {
63    fn handle_window_event(hwnd: u32, req: WindowRequest) -> anyhow::Result<bool> {
64        let res = Backends::with_backend(hwnd, |backend| {
65            match req {
66                WindowRequest::SetPosition(position) => {
67                    backend.update_layout(|layout| {
68                        layout.position = (position.x, position.y);
69                    });
70                }
71
72                WindowRequest::SetAnchor(anchor) => {
73                    backend.update_layout(|layout| {
74                        layout.anchor = (anchor.x, anchor.y);
75                    });
76                }
77
78                WindowRequest::SetMargin(margin) => {
79                    backend.update_layout(|layout| {
80                        layout.margin = (margin.top, margin.right, margin.bottom, margin.left);
81                    });
82                }
83
84                WindowRequest::ListenInput(cmd) => {
85                    let mut flags = ListenInputFlags::empty();
86                    flags.set(ListenInputFlags::CURSOR, cmd.cursor);
87                    flags.set(ListenInputFlags::KEYBOARD, cmd.keyboard);
88
89                    backend.listen_input(flags);
90                }
91
92                WindowRequest::BlockInput(cmd) => {
93                    backend.block_input(cmd.block);
94                }
95
96                WindowRequest::SetBlockingCursor(cmd) => {
97                    backend.set_blocking_cursor(cmd.cursor);
98                }
99
100                WindowRequest::UpdateSharedHandle(shared) => {
101                    if let Err(err) = backend.update_surface(shared.handle) {
102                        error!("failed to open shared surface. err: {:?}", err);
103                        return false;
104                    }
105                }
106            }
107
108            true
109        });
110
111        Ok(res.unwrap_or(false))
112    }
113
114    let mut conn = IpcServerConn::new(server).await?;
115    let emitter = conn.create_emitter();
116    {
117        debug!("sending initial data");
118        // send existing windows
119        for backend in Backends::iter() {
120            let render = backend.render.lock();
121            let gpu_id = render.interop.gpu_id();
122            let size = render.window_size;
123            _ = emitter.emit(OverlayEvent::Window {
124                id: *backend.key() as _,
125                event: WindowEvent::Added {
126                    width: size.0,
127                    height: size.1,
128                    gpu_id,
129                },
130            });
131        }
132    }
133
134    OverlayEventSink::set(move |event| _ = emitter.emit(event));
135    defer!({
136        debug!("cleanup start");
137        OverlayEventSink::clear();
138        Backends::cleanup_backends();
139    });
140
141    while let Ok((req_id, req)) = conn.recv().await {
142        trace!("recv id: {req_id} req: {req:?}");
143
144        match req {
145            Request::Window { id, request } => {
146                conn.reply(req_id, handle_window_event(id, request)?)?;
147            }
148        }
149    }
150    Ok(())
151}
152
153/// IPC server listener.
154#[tracing::instrument(skip(create_server))]
155async fn run_server(
156    mut server: NamedPipeServer,
157    mut create_server: impl FnMut() -> anyhow::Result<NamedPipeServer>,
158) {
159    loop {
160        debug!("waiting ipc client...");
161        match server.connect().await {
162            Ok(_) => {
163                if let Err(err) = run(server).await {
164                    warn!("client connection ended unexpectedly. err: {:?}", err);
165                }
166            }
167            Err(err) => {
168                error!("failed to connect to client. err: {err:?}");
169            }
170        }
171
172        server = loop {
173            match create_server() {
174                Ok(server) => break server,
175                Err(err) => {
176                    error!("failed to create server. retrying after 5 seconds. err: {err:?}");
177                    sleep(Duration::from_secs(5)).await;
178                }
179            }
180        };
181    }
182}
183
184/// Main entry point for DLL.
185///
186/// # Safety
187/// Can be called by loader only. Must not be called manually.
188#[unsafe(no_mangle)]
189#[allow(non_snake_case, unused_variables)]
190pub unsafe extern "system" fn DllMain(dll_module: HINSTANCE, fdw_reason: u32, _: *mut ()) -> bool {
191    #[cfg(debug_assertions)]
192    fn setup_tracing() {
193        use tracing::level_filters::LevelFilter;
194
195        use crate::dbg::WinDbgMakeWriter;
196
197        tracing_subscriber::fmt::fmt()
198            .with_ansi(false)
199            .with_thread_ids(true)
200            .with_max_level(LevelFilter::TRACE)
201            .with_writer(WinDbgMakeWriter::new())
202            .init();
203    }
204
205    if fdw_reason != DLL_PROCESS_ATTACH {
206        return true;
207    }
208
209    // setup tracing first
210    #[cfg(debug_assertions)]
211    setup_tracing();
212
213    // setup tokio runtime
214    let Ok(rt) = Runtime::new() else {
215        error!("cannot create tokio runtime");
216        return false;
217    };
218    let _guard = rt.enter();
219
220    let pid = unsafe { GetCurrentProcessId() };
221    let module_handle = dll_module.0 as usize;
222    // setup first ipc server
223    let server = match create_ipc_server(create_ipc_addr(pid, module_handle as u32), true) {
224        Ok(server) => server,
225        Err(err) => {
226            error!("cannot open ipc server. err: {err:?}");
227            return false;
228        }
229    };
230    let create_server =
231        move || create_ipc_server(create_ipc_addr(pid, module_handle as u32), false);
232
233    thread::spawn(move || {
234        // initialize overlay
235        initialize(module_handle as _).expect("initialization failed");
236        debug!("hook installed");
237
238        rt.block_on(run_server(server, create_server))
239    });
240    true
241}
242
243/// Create a new IPC server using the given address.
244fn create_ipc_server(addr: impl AsRef<OsStr>, first: bool) -> anyhow::Result<NamedPipeServer> {
245    Ok(unsafe {
246        ServerOptions::new()
247            .first_pipe_instance(first)
248            .create_with_security_attributes_raw(
249                addr,
250                &mut SECURITY_ATTRIBUTES {
251                    nLength: 1,
252                    lpSecurityDescriptor: &mut create_everyone_security_desc()
253                        .context("failed to create Everyone security desc")?
254                        as *mut _ as _,
255                    bInheritHandle: BOOL(0),
256                } as *mut _ as _,
257            )?
258    })
259}
260
261/// Create Windows security descriptor allowing read/write permission to Everyone.
262fn create_everyone_security_desc() -> anyhow::Result<SECURITY_DESCRIPTOR> {
263    let mut everyone_sid = PSID::default();
264    unsafe {
265        AllocateAndInitializeSid(
266            &SECURITY_WORLD_SID_AUTHORITY,
267            1,
268            SECURITY_WORLD_RID as _,
269            0,
270            0,
271            0,
272            0,
273            0,
274            0,
275            0,
276            &mut everyone_sid,
277        )?;
278    }
279    defer!(unsafe {
280        FreeSid(everyone_sid);
281    });
282
283    let access = EXPLICIT_ACCESS_A {
284        grfAccessPermissions: GENERIC_READ.0 | GENERIC_WRITE.0,
285        grfAccessMode: SET_ACCESS,
286        grfInheritance: NO_INHERITANCE,
287        Trustee: TRUSTEE_A {
288            TrusteeForm: TRUSTEE_IS_SID,
289            TrusteeType: TRUSTEE_IS_USER,
290            ptstrName: PSTR(everyone_sid.0.cast()),
291            ..Default::default()
292        },
293    };
294
295    let mut pacl: *mut ACL = 0 as _;
296    unsafe {
297        SetEntriesInAclA(Some(&[access]), None, &mut pacl).ok()?;
298    }
299
300    let mut security_desc = SECURITY_DESCRIPTOR::default();
301    unsafe {
302        InitializeSecurityDescriptor(
303            PSECURITY_DESCRIPTOR(&mut security_desc as *mut _ as _),
304            SECURITY_DESCRIPTOR_REVISION,
305        )?;
306
307        SetSecurityDescriptorDacl(
308            PSECURITY_DESCRIPTOR(&mut security_desc as *mut _ as _),
309            true,
310            Some(pacl),
311            false,
312        )?;
313    }
314
315    Ok(security_desc)
316}