microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

EntryPointNotFoundException - wintrust.dll

joeloff opened this issue · comments

Actual behavior

Created a .NET 7.0 console application that calls into WinVerifyTrust. I run into the following exception

System.EntryPointNotFoundException: Unable to find an entry point named 'WinVerifyTrust' in DLL 'WINTRUST.dll'

I created a second console application without CsWin32 with the following:

        private static void Main(string[] args)
        {
            WinVerifyTrust(IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
        }

        [DllImport("wintrust.dll", SetLastError = true)]
        [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
        public static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData);

This obviously still fails because of the null pointers, but the external method is successfully resolved. I also ran dumpbin /exports wintrust.dll just to make sure.

The only thing I can think of is that perhaps I messed up the data structures and somehow overwrite the function pointer returned when the library is loaded.

Expected behavior

Expected the API call to succeed.

Repro steps

  1. NativeMethods.txt content:
WinVerifyTrust
WINTRUST_DATA
WINTRUST_ACTION*
HWND
  1. NativeMethods.json content (if present):
  1. Any of your own code that should be shared?
using Windows.Win32.Foundation;
using Windows.Win32.Security.WinTrust;
using static Windows.Win32.PInvoke;

namespace WinTrust
{

    public class Program
    {
        public static unsafe bool IsAuthentiCodeSigned(string path)
        {
            WINTRUST_FILE_INFO* fi = stackalloc WINTRUST_FILE_INFO[1];
            WINTRUST_DATA* td = stackalloc WINTRUST_DATA[1];

            fixed (char* p = Path.GetFullPath(path))
            {
                fi[0].pcwszFilePath = p;
                fi[0].cbStruct = (uint)sizeof(WINTRUST_FILE_INFO);
                fi[0].hFile = (HANDLE)IntPtr.Zero;
                fi[0].pgKnownSubject = null;
            }

            Guid policyGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;

            td[0].cbStruct = (uint)sizeof(WINTRUST_DATA);
            td[0].pPolicyCallbackData = null;
            td[0].pSIPClientData = null;
            td[0].dwUIChoice = WINTRUST_DATA_UICHOICE.WTD_UI_NONE;
            td[0].fdwRevocationChecks = WINTRUST_DATA_REVOCATION_CHECKS.WTD_REVOKE_NONE;
            td[0].dwUnionChoice = WINTRUST_DATA_UNION_CHOICE.WTD_CHOICE_FILE;
            td[0].dwStateAction = WINTRUST_DATA_STATE_ACTION.WTD_STATEACTION_VERIFY;
            td[0].hWVTStateData = (HANDLE)IntPtr.Zero;
            td[0].pwszURLReference = null;
            td[0].dwUIContext = 0;
            td[0].Anonymous.pFile = fi;

            int lstatus = WinVerifyTrust((HWND)IntPtr.Zero, ref policyGuid, td);
            
            return lstatus == 0;
        }

        private static void Main(string[] args)
        {
            Console.WriteLine(IsAuthentiCodeSigned(args[0]));
        }
    }
}

Context

  • CsWin32 version: 0.3.46-beta
  • Win32Metadata version (if explicitly set by project):
  • Target Framework: net7.0
  • LangVersion (if explicitly set by project): [e.g. 9]

Thanks for your report. When I try with 0.3.49-beta, to generate only WinVerifyTrust and use this code:

using static Windows.Win32.PInvoke;

unsafe
{
    WinVerifyTrust(default, default, default);
}

I get an AV, which as you say is expected given the null pointers. I don't get an EntryPointNotFoundException.
When I compare your non-CsWin32 simple repro to what CsWin32 minimally generates, it looks equivalent.

So then I took your full repro and it succeeded too. But it's only lucky, because you have at least one bug, in that your only pin Path.GetFullPath(path) long enough to set a pointer, but that pointer can become invalid as soon as you exit the fixed block, which is before the native code uses the pointer.

Oh man, that never registered with me, I'll fix that up right away.

The bug persists though in my original project.

I created a 3rd project, which only had the minimal API and calling it with default parameters work just fine, no entry point issue. I'm sure this is going to be something silly - this has to be corrupted memory or something getting overwritten. I'll keep the issue open for now and update with anything I discover.

Closing this. In my haste, I decided to name my console app wintrust, so it created a wintrust.dll which is not the same as wintrust.dll in system32, which explains why my random consoleapp123 works just fine.