Git Product home page Git Product logo

Comments (23)

veeenu avatar veeenu commented on August 12, 2024 1

All good, I don't know if I have another DX11 application handy to see if I can replicate it with.

I think it would be best if you used the one that you originally got the failure with -- so we can determine whether it has some special circumstances that cause hudhook to crash on reentry. Another DX11 application could behave differently.

I'm reopening the ticket as I'd rather keep track of a spurious issue, than forget about something that could be an actual issue. I'll close it eventually if I don't get further feedback in a long while, but in the meantime feel free to share your findings here!

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024 1

Yep, seems to have done the trick if you ignore the traces/commented out line in my fork here:
https://github.com/Sunderous/hudhook/blob/b4cad7822fd55359a46c7ba6fd63134ef6ef0eb9/src/hooks/dx11.rs#L302-L400

If I move the instance handle up to the function's scope so it's available for cleanup later, and move the dummy class name into a single spot just to be safe, then it lets me call UnregisterClassA(class_name, hinstance) after destroying the window.

This appears to have solved my issue, I can tweak the code, re-compile, and inject as much as I want.

from hudhook.

etra0 avatar etra0 commented on August 12, 2024 1

Oh I'm gonna test this as well!

I noticed the same behavior, I can deinject and inject as many times I want, but as soon as I recompile it's a guaranteed crash.

I'll see if this fixed this issue!

from hudhook.

etra0 avatar etra0 commented on August 12, 2024 1

I did some small testing (deinjected and injected thrice) and it seems to be working better, yay!

previous behavior would simply crash at second injection after changing some bit of code.

from hudhook.

etra0 avatar etra0 commented on August 12, 2024 1

I'll give it a spin this afternoon!

from hudhook.

etra0 avatar etra0 commented on August 12, 2024 1

I did a quick test with for my tool for The Witcher 3 (litcher) using a branch with my changes rebased on your branch and everything seems to keep working. I tested making changes three times, and I also tested deinjecting a debug and then injecting a release version, and it works. Previously that would simply crash at the second reinjection

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

Well I'm just a bit impatient I guess haha, after like 10 more minutes looking at the code I found your eject function:

hudhook/src/lib.rs

Lines 175 to 186 in 8afac9c

/// Functions that manage the lifecycle of hooks.
///
/// ## Ejecting a DLL
///
/// To eject your DLL, invoke the [`eject`] method from anywhere in your
/// render loop. This will disable the hooks, free the console (if it has
/// been created before) and invoke `FreeLibraryAndExitThread`.
///
/// Befor calling [`eject`], make sure to perform any manual cleanup (e.g.
/// dropping/resetting the contents of static mutable variables).
///
/// [`eject`]: lifecycle::eject

Putting it there for anyone else who might stumble across this. Might be worth adding that to the readme example so it's front and center, but all good on my end! Thanks again.

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

Sorry, going to re-open this as I think there's still a problem.

Right now, if I inject -> eject() it's all good. But if I try to re-inject the same process again it crashes the second time.

So there may still be something that the eject() cleanup isn't doing correctly, that makes installing the hook a second time crash >.<

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

Hey, thank you for your feedback!

Injecting and then using the eject method are the correct thing to do. For an example of usage, check the practice tool; you want to invoke the function from the same thread that is doing the rendering, just to be sure.

If the process crashes again on second injection, I suggest you check where the crash is happening with a debugger like WinDbg or x64dbg. I suggest you do that with an empty render loop, i.e. something that just renders some static text, to rule out non-idempotent memory modifications you may have done to your target process (e.g. non-reverted injections in code caves).

If you can determine hudhook is itself the cause of crashing for your game, please gather and report here as much information as possible so I can look into it.

Thank you!

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

All good, I don't know if I have another DX11 application handy to see if I can replicate it with. But I'll do a little more analysis first, and close this for now since I can't prove if it's the app or hookhud yet. Thanks.

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

So instead of wiring up a debugger, I grabbed some simple injection code and wired that up instead of using an off the shelf injector.

And doing it that way, I can't re-produce it, so I have to assume it was something the other injector was doing, all good in your code!

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

Gah, sorry but it still feels like there's something odd going on here, I might need some help diving deeper into it.

Right now I can re-inject as much as I want no problem.

But as soon as I change the DLL code, and re-compile, the next injection fails/crashes the host process 100% of the time. Doesn't matter what the change is. I can restart the app, inject the same DLL that just crashed it, and it'll work until I tweak the code again and re-compile. Which makes it impossible to iterate.

I'm fairly confident the crash is happening when it tries to setup the DX11 hook after being recompiled, the simple log trace just stops here:

image

And x64 dbg reports the last error as 0x582 = ERROR_CLASS_ALREADY_EXISTS:

image

I don't see that error show up at all during successful injections, so it seems unique to the crash.

But I'm still pretty new to reversing, so I'm not sure how to correlate what would change between compiles (other than binary size) that might impact re-installing the DX11 hook.

If you've got any suggestions where to poke at either in x64 debug or the code itself I can try and get some more details.

Thanks.

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

So my guess is that for some reason it's this dummy window class:

hudhook/src/hooks/dx11.rs

Lines 302 to 345 in 0ddab44

fn get_present_addr() -> (DXGISwapChainPresentType, DXGISwapChainResizeBuffersType) {
trace!("Getting IDXGISwapChain::Present addr...");
unsafe extern "system" fn def_window_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
DefWindowProcA(hwnd, msg, wparam, lparam)
}
let hwnd = {
let hinstance = unsafe { GetModuleHandleA(None) }.unwrap();
let wnd_class = WNDCLASSA {
style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(def_window_proc),
hInstance: hinstance,
lpszClassName: PCSTR("HUDHOOK_DUMMY\0".as_ptr()),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: HICON(0),
hCursor: HCURSOR(0),
hbrBackground: HBRUSH(0),
lpszMenuName: PCSTR(null()),
};
unsafe {
RegisterClassA(&wnd_class);
CreateWindowExA(
WINDOW_EX_STYLE(0),
PCSTR("HUDHOOK_DUMMY\0".as_ptr()),
PCSTR("HUDHOOK_DUMMY\0".as_ptr()),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0,
0,
16,
16,
HWND(0),
HMENU(0),
hinstance,
null(),
)
}
};

It would make sense that was getting thrown because the DUMMY class was never unregistered, but it doesn't explain why it would only happen after re-compiling. I'd expect it would happen on any injection after the first one.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

Oh, great catch! So, if I understood correctly, the only relevant difference is unregistering the class? This could actually be a fix for other renderers as well, that probably also aren't unregistering the dummy window class.

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

Correct. That's just how I got it to work, but as long as you have a way of getting the hInstance from the window, so you can destroy window -> unregister class with:

DestroyWindow(hWnd)
UnregisterClassA(className, hInstance)

That's all that matters as far as I can tell.

I read somewhere the ordering matters, that you have to destroy first before unregistering the class. But I wasn't using the right build when I had the order reversed, or when I had some code that tries to pull the hInstance out of the window handle, so I can't say for sure it matters haha.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

Great! I've renamed the issue and will keep it open to track the necessary modification to all DX hooks. Thank you!

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

Change applied to DX11 and DX12 hooks.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

I've had another idea which I'm not 100% sure is going to pan out -- I'm going to try and create a swap chain straight on the desktop HWND. This could be either a disaster or a huge success, but would get rid of a lot of code which I would very much like.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

@etra0 @Sunderous turns out my idea might work after all!

Would you guys mind giving this branch a spin?

It works for me both with Dark Souls III and Elden Ring. I'm happy because I can trim away so much code 😅

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

Hey, sorry I had to switch to the DX9 hook explicitly for my use case, and then tore that down to just the present hook because I was having a lot of issues trying to stabilize the repeated injection behavior (may not have been necessary, but when in doubt simplify haha).

So I probably don't have a good test case for this right now, but I'll look at the code and see if I can implement the desktop approach in my fork for dx9 and let you know how it goes.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

So I probably don't have a good test case for this right now, but I'll look at the code and see if I can implement the desktop approach in my fork for dx9 and let you know how it goes.

I applied the change to Dx9 as well, so it's all the more welcome if you can test that against Dx9 since I don't have a use case for it myself 😅

from hudhook.

Sunderous avatar Sunderous commented on August 12, 2024

So I threw the new code in there for finding the function addresses with GetDesktopWindow, switched to your detour repo to make that compile again (I'm guessing detour 2 added a breaking change, or just wouldn't work with the new approach?).

And seems to inject/re-inject fine so dx9 working as far as I can tell haha.

from hudhook.

veeenu avatar veeenu commented on August 12, 2024

Thank you! 😊

New Rust nightly has a change that breaks detours-rs, I opened a PR here to fix that. Unfortunately this is blocking new releases until that itself gets a new release on crates.io.

from hudhook.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.