3F / DllExport

.NET DllExport with .NET Core support (aka 3F/DllExport aka DllExport.bat)
MIT License
940 stars 131 forks source link

How to pass sting as the argument to the exported function #117

Closed 3xpl01tc0d3r closed 4 years ago

3xpl01tc0d3r commented 4 years ago

The code snippet

    [DllExport("Execute")]
    public static void Execute(string pname)
    {

        ProcessStartInfo startInfo = new ProcessStartInfo();
        startInfo.FileName = pname;
        Process.Start(startInfo);
    }

Is there any way by which we can pass the string values as the argument while executing the .net code using rundll32

  rundll32 Demo,Execute calc.exe
3F commented 4 years ago

Firstly, wrong signature. You need offset to lpszCmdLine argument because rundll32 will call this like:

void CALLBACK
EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

Secondly, as you can see above, it will be pointer to allocated string. [? About data types]

I also remind everyone: Our DllExport supports export for executable modules ( .exe ). That is, you can try something related in your case but without rundll32. It also provides +x64 support.

3xpl01tc0d3r commented 4 years ago

Hi, Thanks for pointing about lpszCmdLine. I am able to read the complete command line string using GetCommandLineA() function and then use string functions to fetch the arguments.

3F commented 4 years ago

Wait, why GetCommandLine ? you already have an access to LPSTR lpszCmdLine, eg.

// ~ rundll32 Demo,Execute calc.exe 1234 abc ...
string args = (CharPtr)lpszCmdLine
// "calc.exe 1234 abc ..."
3xpl01tc0d3r commented 4 years ago

Since I am not using EntryPoint function I dont think so I can directly access lpszCmdLine Below is the final code snippet which worked for me. Do let me know if there is another way to do the similar thing or I misunderstood your comments

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern string GetCommandLineA();

    [DllExport("Execute", CallingConvention = CallingConvention.StdCall)]
    public static void Execute()
    {
        ProcessStartInfo startInfo = new ProcessStartInfo();

        string val = GetCommandLineA();
        string fname = "Execute";
        int sval = val.IndexOf(fname);
        int fval = sval + fname.Length+1;
        startInfo.FileName = val.Substring(fval);
        Process.Start(startInfo);
    }
3F commented 4 years ago

Yes, because rundll32 will access to mentioned EntryPoint function instead of you. You just need to fix your signature due to +8 bytes offset to lpszCmdLine if we're talking about rundll32

~
[DllExport]
public static void Execute(IntPtr _, IntPtr __, IntPtr lpsz)
{
    string args = (CharPtr)lpsz
}

Or full signature. I'm not sure, but I think I also illustrated this moment in my screencasts to this project. Or where it was

Btw: specific int32 should be also ok because of rundll32 at least for the offset. That is, you can also like:

[DllExport]
public static void Execute(long _, IntPtr lpszCmdLine)
{
   string args = (CharPtr)lpsz
}

upd2: just checked, all works as I expected.

3xpl01tc0d3r commented 4 years ago

I could not find how can we use (CharPtr) for converting the IntPtr lpszCmdLine to string. If possible can you help me with some reference link or guide me on how can I use (CharPtr) ? It will also help me to simplify my code

3F commented 4 years ago

Oh right, sorry, (CharPtr)/(WCharPtr) are available if you have Conari engine https://github.com/3F/Conari

In general, it doesn't matter which way are you trying with lpszCmdLine. Even MarshalAsAttribute. The important thing is that you have it :) *access to lpszCmdLine

Again, please read our wiki.