Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshot 2020-11-12_05-12 broke debugging via attach-to-process in Wine ('safe attach' is now default, no longer configurable) #2525

Closed
rmi1974 opened this issue Nov 26, 2020 · 9 comments

Comments

@rmi1974
Copy link

rmi1974 commented Nov 26, 2020

Hello folks,

with the latest snapshot release snapshot_2020-11-12_05-12.zip the ability to attach to processes is broken in Wine.

Symptom: attaching to any 32-bit or 64-bit process results in x32dbg/x64dbg crash.
If x32dbg/x64dbg is registered as default crash handler you get a nice debugger crash "fork" bomb ;-)

It crashes on unimplemented NtCreateDebugObject() which is native API.

https://source.winehq.org/git/wine.git/blob/40d4fbe45997a1820296e7909ba2212518bcfacc:/dlls/ntdll/ntdll.spec#l162

Even with a small stub it wouldn't work because NtDebugActiveProcess is a stub as well.

https://source.winehq.org/git/wine.git/blob/40d4fbe45997a1820296e7909ba2212518bcfacc:/dlls/ntdll/ntdll.spec#l193

The native API used to implement remote breakin:

https://github.com/x64dbg/TitanEngine/blob/91f57815c886d6bef94f1b512d60c5e2d8bb43fe/TitanEngine/Global.Debugger.cpp#L246

static NTSTATUS NTAPI DbgUiConnectToDbg_()
{
    if(NtCurrentTeb()->DbgSsReserved[1] != NULL)
        return STATUS_SUCCESS;

    OBJECT_ATTRIBUTES ObjectAttributes;
    InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
    return NtCreateDebugObject(&NtCurrentTeb()->DbgSsReserved[1], DEBUG_ALL_ACCESS, &ObjectAttributes, 0);
}

// Source: https://github.com/mirror/reactos/blob/c6d2b35ffc91e09f50dfb214ea58237509329d6b/reactos/dll/win32/kernel32/client/debugger.c#L480
BOOL WINAPI DebugActiveProcess_(IN DWORD dwProcessId)
{
    /* Connect to the debugger */
    NTSTATUS Status = DbgUiConnectToDbg_();
    if(!NT_SUCCESS(Status))
    {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    /* Get the process handle */
    HANDLE Handle = ProcessIdToHandle(dwProcessId);
    if(!Handle)
    {
        return FALSE;
    }

    /* Now debug the process */
    Status = DbgUiDebugActiveProcess_(Handle);

    /* Close the handle since we're done */
    NtClose(Handle);

    /* Check if debugging worked */
    if(!NT_SUCCESS(Status))
    {
        /* Fail */
        BaseSetLastNTError(Status);
        return FALSE;
    }

    /* Success */
    return TRUE;
}

Apparently this debugging functionality which makes use of native API was switched to being a debug engine default here:

x64dbg/TitanEngine@0a1c323 ("Make SafeAttach the default")

Previously it could be controlled by SafeAttach config setting which is still there but now no longer usable:

SetEngineVariable(UE_ENGINE_SAFE_ATTACH, settingboolget("Engine", "SafeAttach"));

        SetEngineVariable(UE_ENGINE_SAFE_ATTACH, settingboolget("Engine", "SafeAttach"));

I will create Wine bugs about implementing the missing native API functionality. It might be low priority though.
A workaround exists: use last snapshot before the breaking change:

https://sourceforge.net/projects/x64dbg/files/snapshots/snapshot_2020-11-05_15-25.zip/download

I don't mind if you close this bug as "WONTFIX" since Wine is not really a supported "platform".
I just wanted to have this breaking change for Wine documented by bug reports on both projects.

Regards

@rmi1974
Copy link
Author

rmi1974 commented Nov 26, 2020

I've created following bug reports to track the problems in Wine project:

@mrexodia
Copy link
Member

Could you point me to a way to detect wine? It's very easy to do this the old way for wine (especially because the reason for this is to hide the attaching from the debuggee which would be quite pointless in wine).

@rmi1974
Copy link
Author

rmi1974 commented Nov 26, 2020

There are many ways to detect wine. The easiest would be to check for exports specific to Wine.

https://source.winehq.org/git/wine.git/blob/40d4fbe45997a1820296e7909ba2212518bcfacc:/dlls/ntdll/ntdll.spec#l1623

1624 @ cdecl -syscall wine_get_version()
1625 @ cdecl -syscall wine_get_build_id()
1626 @ cdecl -syscall wine_get_host_version(ptr ptr)

ntdll.dll.wine_get_version would be my preferred choice when testing for one famous Wine specific export.

I don't think these exports will ever be removed. It would break too many applications "ported" to Linux (in fact made to work with Wine) if these are no longer present. I documented this in a couple of bugs. These can be hidden though. Wine-Staging has a hack for this. But that's only for special cases when people want to prevent detection of Wine, at least w.r.t to famous exports.

https://github.com/wine-staging/wine-staging/blob/master/patches/ntdll-Hide_Wine_Exports/0001-ntdll-Add-support-for-hiding-wine-version-informatio.patch

See for example:

Alternatively you could check for well known registry keys.

  • HKEY_LOCAL_MACHINE\Software\Wine
  • HKEY_CURRENT_USER\Software\Wine

Many apps do these as well, or combination of both (Steam).

@dt-zero
Copy link

dt-zero commented Dec 24, 2020

Thanks for documenting this @rmi1974 and for the effort on root cause analysis.

I think it would be preferable to implement these native APIs in wine instead. (As per https://wiki.winehq.org/Developer_FAQ#How_can_I_detect_Wine.3F, it seems that is the general view among other developers)

As you mentioned, there are cases where we do intentionally have to hide exports, as was the case with EAC. Now I've run into additional applications that require NtCreateDebugObject support (that is beyond a simplistic stub) for anti debug checks. Since some of these apps are in scope for Proton, the priority might shift in favor of implementing these sooner rather than later.

Hence unless there is a really good reason for it, I don't think x64dbg should change anything for wine based on some runtime detections ; however having the work-around option in the GUI would resolve this in the meantime.

@rmi1974
Copy link
Author

rmi1974 commented Jan 28, 2021

Hello folks,

some update ...

https://bugs.winehq.org/show_bug.cgi?id=50194 ("x64dbg snapshots >= 2020-11-12_05-12 crash on unimplemented function ntdll.dll.NtCreateDebugObject when attaching to process") is now implemented.

https://source.winehq.org/git/wine.git/commitdiff/5ebc20c65609cc1b8643b427104ed386094a65c1

Next would be:

https://bugs.winehq.org/show_bug.cgi?id=50195 ("x64dbg snapshots >= 2020-11-12_05-12 need ntdll.dll.NtDebugActiveProcess implementation to debug process via attach") which is still a stub.

Regards

@rmi1974
Copy link
Author

rmi1974 commented Feb 1, 2021

Hello folks,

good news: Wine now implements all the needed native debugging API.
It works nicely with the current snapshot.

Copy/pasta from my comment: https://bugs.winehq.org/show_bug.cgi?id=50195#c1


This is fixed by commit https://source.winehq.org/git/wine.git/commitdiff/46b84e7a83beae7484e6daac16739a2b9238f68e ("ntdll: Implement NtDebugActiveProcess() and NtRemoveProcessDebug().") and related commits in the series.

Thanks Alexandre.

$ git remote -v
...
origin	git://source.winehq.org/git/wine.git (fetch)

$ git log --oneline 46b84e7a83b^...7999af82448
7999af82448 ntdll: Implement NtWaitForDebugEvent().
d848a25b765 ntdll: Implement DbgUiConvertStateChangeStructure().
c8f5cced471 ntdll: Implement NtDebugContinue().
7bebf7db8c2 ntdll: Implement NtSetInformationDebugObject().
46b84e7a83b ntdll: Implement NtDebugActiveProcess() and NtRemoveProcessDebug().

Latest x64dbg snapshot used for testing:

https://web.archive.org/web/20210127234740/https://github.com/x64dbg/x64dbg/releases/download/snapshot/snapshot_2021-01-14_13-25.zip

Prerequisite:

  • workaround for bug 45916 (WINEDLLOVERRIDES=msvcr120,msvcp120=n,b)

Tests with x32dbg:

  • start 32-bit process in debugger (entry point)
  • attaching to running 32-bit process (pause/breakin)
  • symbol loading (Wine builtins PDB)
  • further debugging (stepping, breakpoints)

Tests with x64dbg:

  • start 64-bit process in debugger (entry point)
  • attaching to running 64-bit process (pause/breakin)
  • symbol loading (Wine builtins PDB)
  • further debugging (stepping, breakpoints)

Felt a bit like unwrapping late christmas gift ;-)

$ sha1sum snapshot_2021-01-14_13-25.zip 
e39756243f419db6f4d2213227ab266ae0618163  snapshot_2021-01-14_13-25.zip

$ du -sh snapshot_2021-01-14_13-25.zip 
32M	snapshot_2021-01-14_13-25.zip

$ wine --version
wine-6.1-38-gcfbbde2abce

This will be part of Wine 6.2 release in a bit less than two weeks (unless people build/test Wine daily from Git like me).
After the release of Wine 6.2 I propose to resolve this ticket as 'WONTFIX'.
If people want to use recent x64dbg they should upgrade to Wine >= 6.2.
No need to reintroduce old code / deprecated things.

Regards

@mrexodia
Copy link
Member

mrexodia commented Feb 1, 2021 via email

@rmi1974
Copy link
Author

rmi1974 commented Feb 2, 2021

Wow, that is great news! Did you also test it with the GleeBug debug engine per chance?

I've switched the engines and basic debugging seems to work. Same tests as with TitanEngine.
I guess most of the known issues are present with Wine as well: https://github.com/x64dbg/GleeBug/issues

One feature that never worked with both Engines is "pause".
One has to use breakpoints on frequently called code / API / syscalls to work around.
I can certainly investigate the problem and provide analysis.
It may take some time though as my bug triaging queue is huge - as always ;-)

@kaiytech
Copy link

To any lost souls out there: issue does not occur anymore in newest versions of Wine. Currently on 7.0-rc2. x64dbg works flawlessly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants