Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Sept/Oct 2019 Issue of The NT Insider available


Download PDF here: http://insider.osr.com/2019/ntinsider_2019_01.pdf

It’s a particularly BIG issue, too: 40 pages of technical goodness, ranging from WDF to Minifilters. Check it out.
Before Posting...
Please check out the Community Guidelines in the Announcements and Administration Category.

Pass .NET Structure per IOCTL to KMDF

Daniel_GeersDaniel_Geers Member Posts: 3
Hello,

i have a windows service written in c#.
I try to pass a struct with string arrays with an ioctl to the windows driver, but it doesn't work. The Array-size is known at runtime.

The following is the c#-structure:

public struct ACTIVATE_MONITOR_INFO
{

public int hEventHandle;
public string[] HashValue;
}

And the c++ struct:

typedef struct _REGISTER_EVENT
{
HANDLE hEvent;
wchar_t **HashValue

} REGISTER_EVENT , *PREGISTER_EVENT ;

If i remove the string-Array and wchar_t the event-handle is working fine.

Can anyone help me with the problem?

kind regards
Daniel

Comments

  • Jason_StephensonJason_Stephenson Member Posts: 53
    Have you tried sending some simple test data across the managed/unmanaged boundary?

    C#:
    ACTIVATE_MONITOR_INFO example = new ACTIVATE_MONITOR_INFO
    {
    hEventHandle = 2,
    HashValue = new string[2]
    {
    "Test1",
    "AnotherTest"
    }
    };

    Is this populated in _REGISTER_EVENT correctly? Are you passing the correct sizes to the system apis?
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,103
    xxxxx@freenet.de wrote:
    > i have a windows service written in c#.
    > I try to pass a struct with string arrays with an ioctl to the windows driver, but it doesn't work. The Array-size is known at runtime.

    Your ioctl structure is poorly designed. It's a very bad idea to pass
    user-mode addresses in an ioctl buffer, much less the address of a list
    of other addresses. The size of a pointer is different for 32-bit and
    64-bit applications, and your driver is required to handle that. And
    the address space for a user-mode process can evaporate suddenly.

    If you need to pass multiple strings, it would have been much smarter to
    do it like a REG_MULTI_SZ registry value, where the buffer contain a
    list of strings separated by null bytes and terminated by a double null.


    > The following is the c#-structure:
    >
    > public struct ACTIVATE_MONITOR_INFO
    > {
    >
    > public int hEventHandle;
    > public string[] HashValue;
    > }

    Don't you have to mark this as [StructLayout(LayoutKind.Sequential)]?
    Otherwise, you get .NET memory layout, not unmanaged layout.

    --
    Tim Roberts, xxxxx@probo.com
    Providenza & Boekelheide, Inc.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • Bob_Ammerman-2Bob_Ammerman-2 Member - All Emails Posts: 56
    I don't have the details at hand, but the user should annotate the structure appropriately and use marshalling to convert to unmanaged form. The standard marshalling can handle mapping from a managed string to or from a zero-terminated string of fixed size. (it can also marshal to a pointer, but as previously noted passing user-space pointers to an IOCTL is a very bad idea). Marshalling an array of strings is, I believe, an exercise for the user. I don't think the standard marshalling can handle that.

    ? Bob Ammerman
    ? xxxxx@ramsystems.biz
    716.864.8337

    138 Liston St
    Buffalo, NY 14223
    www.ramsystems.biz


    > -----Original Message-----
    > From: xxxxx@lists.osr.com [mailto:bounce-614435-
    > xxxxx@lists.osr.com] On Behalf Of Tim Roberts
    > Sent: Monday, August 15, 2016 12:25 PM
    > To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    > Subject: Re: [ntdev] Pass .NET Structure per IOCTL to KMDF
    >
    > xxxxx@freenet.de wrote:
    > > i have a windows service written in c#.
    > > I try to pass a struct with string arrays with an ioctl to the windows driver,
    > but it doesn't work. The Array-size is known at runtime.
    >
    > Your ioctl structure is poorly designed. It's a very bad idea to pass user-mode
    > addresses in an ioctl buffer, much less the address of a list of other
    > addresses. The size of a pointer is different for 32-bit and 64-bit applications,
    > and your driver is required to handle that. And the address space for a user-
    > mode process can evaporate suddenly.
    >
    > If you need to pass multiple strings, it would have been much smarter to do it
    > like a REG_MULTI_SZ registry value, where the buffer contain a list of strings
    > separated by null bytes and terminated by a double null.
    >
    >
    > > The following is the c#-structure:
    > >
    > > public struct ACTIVATE_MONITOR_INFO
    > > {
    > >
    > > public int hEventHandle;
    > > public string[] HashValue; }
    >
    > Don't you have to mark this as [StructLayout(LayoutKind.Sequential)]?
    > Otherwise, you get .NET memory layout, not unmanaged layout.
    >
    > --
    > Tim Roberts, xxxxx@probo.com
    > Providenza & Boekelheide, Inc.
    >
    >
    > ---
    > NTDEV is sponsored by OSR
    >
    > Visit the list online at:
    > <http://www.osronline.com/showlists.cfm?list=ntdev>;
    >
    > MONTHLY seminars on crash dump analysis, WDF, Windows internals and
    > software drivers!
    > Details at <http://www.osr.com/seminars>;
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > <http://www.osronline.com/page.cfm?name=ListServer>;
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    The "StructLayoutAttribute class " MSDN page is interesting. It shows how to build a C# struct that matches the C struct SYSTEMTIME to perform the Win32 API call GetSystemTime(). This is a good starting point.

    https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute(v=vs.110).aspx
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    Hi,

    as Tim said, unfortunately your struct design is poor and wont work that way. The most fearsome here is the pointer that could point to anything in your kernel space. The marshaller of the .NET CLR crossing from managed to unmanaged needs to know how exactly it has to pass the data and in what way. The magic behind this is CLR attributes. Important things are packing, layout and data width. All can be arranged by attributes attached to the struct and members. You should try these definitions. The 100 is just an example and you should replace it by your appropriate string size. Char type is always unicode in internal net representation:

    [StructLayout(
    LayoutKind.Sequential,
    CharSet = CharSet.Unicode)]
    public struct Argument
    {
    public int hEventHandle;

    [MarshalAs(UnmanagedType.LPWStr, SizeConst = 100)]
    public string HashValue;
    }

    or possibly this one:

    [StructLayout(
    LayoutKind.Sequential,
    CharSet = CharSet.Unicode)]
    unsafe public struct Argument
    {
    public int hEventHandle;
    fixed char HashValue[100];
    }

    From experience you can pass nearly anything from userland to kernel space in C# as long as you keep the contract on booth sides. NEVER EVER pass pointers, since these can change very quickly! Also keep in your mind, that a CLR string is totally different from an UNICODE_STRING. Go for some web research for more info.

    you should have a closer look here and related information: https://msdn.microsoft.com/en-us/library/s9ts558h.aspx

    hope this helps

    K.
  • Daniel_GeersDaniel_Geers Member Posts: 3
    Hi,

    thanks a lot for your replies.
    I've read the postings (MSDN) but i think i'm too stupid.
    Sorry i think my first post was a little bit confusing.
    In C# i have a string array with multiple hash values which will be filled during runtime with hash values from a database. So when i declared the string array in the structure i can't use a fixed size.
    Can i use the following declaration and fill the information before i pass it to the driver:

    public struct ACTIVATE_MONITOR_INFO
    {
    public int hEventHandle;
    [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)]
    String[] ar;
    int size;
    }

    And later then:

    var actInfo = new ACTIVATE_MONITOR_INFO();
    actInfo.hEventHandle = _kernelEventHandle.ToInt32(); //THIS WORKS

    IntPtr actInfoPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(ACTIVATE_MONITOR_INFO)));

    var ret = NativeFile.DeviceIoControl(handle, ServiceInstaller.IOCTL_REGISTER_EVENT, actInfoPtr, Marshal.SizeOf(typeof(ACTIVATE_MONITOR_INFO)),IntPtr.Zero, 0, out retSize, IntPtr.Zero);

    Sorry for my questions i really try to understand whats the problem and how to solve it.

    Daniel
  • Bob_Ammerman-2Bob_Ammerman-2 Member - All Emails Posts: 56
    I can only reinforce what has been said before. You _cannot_ safely pass any pointers to kernel mode. You will need to convert your entire input to a single contiguous data structure that can be passed down to the driver. Perhaps the easiest way is to do something like this (untested code):

    int totallen = 0;
    foreach (string s in strings)
    totallen += s.Length;

    // Allocate a buffer big enough to hold everything
    ushort[] buffer = new ushort[1 + strings.Length + totallen];

    int p = 0; // pointer into buffer
    buffer[p++] = (ushort)strings.Count;
    foreach (string s in strings)
    {
    buffer[p++]=(ushort)s.Length;
    foreach (char ch in s)
    Buffer[p++] = (ushort)ch;
    }

    Now 'buffer' will start with the number of strings, followed by each string stored in Unicode with it length first.

    You can now marshal 'buffer' to an unmanaged buffer and pass it down to your IOCTL (waves hands...)

    * Bob


    ? Bob Ammerman
    ? xxxxx@ramsystems.biz
    716.864.8337

    138 Liston St
    Buffalo, NY 14223
    www.ramsystems.biz


    > -----Original Message-----
    > From: xxxxx@lists.osr.com [mailto:bounce-614461-
    > xxxxx@lists.osr.com] On Behalf Of xxxxx@arcor.de
    > Sent: Tuesday, August 16, 2016 12:57 AM
    > To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    > Subject: RE:[ntdev] Pass .NET Structure per IOCTL to KMDF
    >
    > Hi,
    >
    > as Tim said, unfortunately your struct design is poor and wont work that way.
    > The most fearsome here is the pointer that could point to anything in your
    > kernel space. The marshaller of the .NET CLR crossing from managed to
    > unmanaged needs to know how exactly it has to pass the data and in what
    > way. The magic behind this is CLR attributes. Important things are packing,
    > layout and data width. All can be arranged by attributes attached to the
    > struct and members. You should try these definitions. The 100 is just an
    > example and you should replace it by your appropriate string size. Char type
    > is always unicode in internal net representation:
    >
    > [StructLayout(
    > LayoutKind.Sequential,
    > CharSet = CharSet.Unicode)]
    > public struct Argument
    > {
    > public int hEventHandle;
    >
    > [MarshalAs(UnmanagedType.LPWStr, SizeConst = 100)]
    > public string HashValue;
    > }
    >
    > or possibly this one:
    >
    > [StructLayout(
    > LayoutKind.Sequential,
    > CharSet = CharSet.Unicode)]
    > unsafe public struct Argument
    > {
    > public int hEventHandle;
    > fixed char HashValue[100];
    > }
    >
    > From experience you can pass nearly anything from userland to kernel space
    > in C# as long as you keep the contract on booth sides. NEVER EVER pass
    > pointers, since these can change very quickly! Also keep in your mind, that a
    > CLR string is totally different from an UNICODE_STRING. Go for some web
    > research for more info.
    >
    > you should have a closer look here and related information:
    > https://msdn.microsoft.com/en-us/library/s9ts558h.aspx
    >
    > hope this helps
    >
    > K.
    >
    > ---
    > NTDEV is sponsored by OSR
    >
    > Visit the list online at:
    > <http://www.osronline.com/showlists.cfm?list=ntdev>;
    >
    > MONTHLY seminars on crash dump analysis, WDF, Windows internals and
    > software drivers!
    > Details at <http://www.osr.com/seminars>;
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > <http://www.osronline.com/page.cfm?name=ListServer>;
  • Bob_Ammerman-2Bob_Ammerman-2 Member - All Emails Posts: 56
    Ah... just noticed you are also trying to pass down a handle. What it is a handle to and how did you create it?

    If it is safe to pass that handle down you can add it to your buffer of ushort values by using the BitConverter class first to convert the int to bytes, and then again to reconvert the bytes to two ushort's:

    byte[] bytes = BitConverter.GetBytes(handle);
    buffer[p++] = BitConverter.ToUInt16(bytes,0);
    buffer[p++] = BItConverter.ToUInt16(bytes,2);

    You'd put the value back together in the kernel code.

    * Bob


    ? Bob Ammerman
    ? xxxxx@ramsystems.biz
    716.864.8337

    138 Liston St
    Buffalo, NY 14223
    www.ramsystems.biz


    > -----Original Message-----
    > From: xxxxx@lists.osr.com [mailto:bounce-614489-
    > xxxxx@lists.osr.com] On Behalf Of Robert Ammerman
    > Sent: Tuesday, August 16, 2016 7:21 AM
    > To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    > Subject: RE: RE:[ntdev] Pass .NET Structure per IOCTL to KMDF
    >
    > I can only reinforce what has been said before. You _cannot_ safely pass any
    > pointers to kernel mode. You will need to convert your entire input to a
    > single contiguous data structure that can be passed down to the driver.
    > Perhaps the easiest way is to do something like this (untested code):
    >
    > int totallen = 0;
    > foreach (string s in strings)
    > totallen += s.Length;
    >
    > // Allocate a buffer big enough to hold everything ushort[] buffer = new
    > ushort[1 + strings.Length + totallen];
    >
    > int p = 0; // pointer into buffer
    > buffer[p++] = (ushort)strings.Count;
    > foreach (string s in strings)
    > {
    > buffer[p++]=(ushort)s.Length;
    > foreach (char ch in s)
    > Buffer[p++] = (ushort)ch;
    > }
    >
    > Now 'buffer' will start with the number of strings, followed by each string
    > stored in Unicode with it length first.
    >
    > You can now marshal 'buffer' to an unmanaged buffer and pass it down to
    > your IOCTL (waves hands...)
    >
    > * Bob
    >
    >
    > ? Bob Ammerman
    > ? xxxxx@ramsystems.biz
    > 716.864.8337
    >
    > 138 Liston St
    > Buffalo, NY 14223
    > www.ramsystems.biz
    >
    >
    > > -----Original Message-----
    > > From: xxxxx@lists.osr.com [mailto:bounce-614461-
    > > xxxxx@lists.osr.com] On Behalf Of xxxxx@arcor.de
    > > Sent: Tuesday, August 16, 2016 12:57 AM
    > > To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    > > Subject: RE:[ntdev] Pass .NET Structure per IOCTL to KMDF
    > >
    > > Hi,
    > >
    > > as Tim said, unfortunately your struct design is poor and wont work that
    > way.
    > > The most fearsome here is the pointer that could point to anything in your
    > > kernel space. The marshaller of the .NET CLR crossing from managed to
    > > unmanaged needs to know how exactly it has to pass the data and in what
    > > way. The magic behind this is CLR attributes. Important things are packing,
    > > layout and data width. All can be arranged by attributes attached to the
    > > struct and members. You should try these definitions. The 100 is just an
    > > example and you should replace it by your appropriate string size. Char
    > type
    > > is always unicode in internal net representation:
    > >
    > > [StructLayout(
    > > LayoutKind.Sequential,
    > > CharSet = CharSet.Unicode)]
    > > public struct Argument
    > > {
    > > public int hEventHandle;
    > >
    > > [MarshalAs(UnmanagedType.LPWStr, SizeConst = 100)]
    > > public string HashValue;
    > > }
    > >
    > > or possibly this one:
    > >
    > > [StructLayout(
    > > LayoutKind.Sequential,
    > > CharSet = CharSet.Unicode)]
    > > unsafe public struct Argument
    > > {
    > > public int hEventHandle;
    > > fixed char HashValue[100];
    > > }
    > >
    > > From experience you can pass nearly anything from userland to kernel
    > space
    > > in C# as long as you keep the contract on booth sides. NEVER EVER pass
    > > pointers, since these can change very quickly! Also keep in your mind, that
    > a
    > > CLR string is totally different from an UNICODE_STRING. Go for some web
    > > research for more info.
    > >
    > > you should have a closer look here and related information:
    > > https://msdn.microsoft.com/en-us/library/s9ts558h.aspx
    > >
    > > hope this helps
    > >
    > > K.
    > >
    > > ---
    > > NTDEV is sponsored by OSR
    > >
    > > Visit the list online at:
    > > <http://www.osronline.com/showlists.cfm?list=ntdev>;
    > >
    > > MONTHLY seminars on crash dump analysis, WDF, Windows internals and
    > > software drivers!
    > > Details at <http://www.osr.com/seminars>;
    > >
    > > To unsubscribe, visit the List Server section of OSR Online at
    > > <http://www.osronline.com/page.cfm?name=ListServer>;
    >
    > ---
    > NTDEV is sponsored by OSR
    >
    > Visit the list online at:
    > <http://www.osronline.com/showlists.cfm?list=ntdev>;
    >
    > MONTHLY seminars on crash dump analysis, WDF, Windows internals and
    > software drivers!
    > Details at <http://www.osr.com/seminars>;
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > <http://www.osronline.com/page.cfm?name=ListServer>;
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,103
    xxxxx@freenet.de wrote:
    > thanks a lot for your replies.
    > I've read the postings (MSDN) but i think i'm too stupid.
    > Sorry i think my first post was a little bit confusing.

    No, I don't think it was.


    > In C# i have a string array with multiple hash values which will be filled during runtime with hash values from a database. So when i declared the string array in the structure i can't use a fixed size.

    You have focused on the solution you've chosen, not the problem you have
    to solve. The user/kernel boundary is not as infinitely flexibly as a
    function call boundary. You have to think about your problem in a
    different way. You could, for example, concatenate the strings into one
    large string, embedding the event handle in the first few bytes.
    Consider this:

    using System;
    using System.Collections.Generic;

    public struct ActivateMonitorInfo
    {
    public int hEventHandle;
    public int count;
    public List<String> array;

    public byte[] ToBytes()
    {
    int length = 4 + 4;
    count = array.Count;
    for( int i = 0; i < count; i++ )
    {
    length += array[i].Length * 2 + 2;
    }

    byte[] temp = new byte[length];

    int iCur = 0;
    System.Buffer.BlockCopy(
    BitConverter.GetBytes(hEventHandle), 0,
    temp, iCur, 4
    );
    iCur += 4;

    System.Buffer.BlockCopy(
    BitConverter.GetBytes(count), 0,
    temp, iCur, 4
    );
    iCur += 4;

    for( int i = 0; i < count; i++ )
    {
    Console.WriteLine( "Doing string {0}.", i );
    System.Buffer.BlockCopy(
    array[i].ToCharArray(), 0,
    temp, iCur, array[i].Length * 2
    );
    iCur += array[i].Length * 2 + 2;
    }

    return temp;
    }
    }

    public static class Program {
    static public void Main()
    {
    ActivateMonitorInfo ami = new ActivateMonitorInfo();
    ami.hEventHandle = 0x1234;
    ami.array = new List<String>();
    ami.array.Add( "Hello" );
    ami.array.Add( "world" );
    ami.array.Add( "from" );
    ami.array.Add( "me" );

    byte[] buffer = ami.ToBytes();
    Console.WriteLine( "Buffer is {0} bytes", buffer.Length );
    Console.WriteLine( BitConverter.ToString(buffer) );
    }
    }

    The "ToBytes" function returns a byte array that you can pass to kernel
    mode safely. The "count" member tells the kernel how many strings to
    expect, and each one is terminated by a null.

    --
    Tim Roberts, xxxxx@probo.com
    Providenza & Boekelheide, Inc.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • Doron_HolanDoron_Holan Member - All Emails Posts: 10,444
    And shouldn't it be IntPtr hEventHandle? If this is a kmdf driver you will need to convert the handle to the event object in the io preprocessing routine where you have the correct app context.

    Get Outlook for Android

    From: Tim Roberts
    Sent: Tuesday, August 16, 10:58 AM
    Subject: Re: [ntdev] Pass .NET Structure per IOCTL to KMDF
    To: Windows System Software Devs Interest List

    xxxxx@freenet.de wrote: > thanks a lot for your replies. > I've read the postings (MSDN) but i think i'm too stupid. > Sorry i think my first post was a little bit confusing. No, I don't think it was. > In C# i have a string array with multiple hash values which will be filled during runtime with hash values from a database. So when i declared the string array in the structure i can't use a fixed size. You have focused on the solution you've chosen, not the problem you have to solve. The user/kernel boundary is not as infinitely flexibly as a function call boundary. You have to think about your problem in a different way. You could, for example, concatenate the strings into one large string, embedding the event handle in the first few bytes. Consider this: using System; using System.Collections.Generic; public struct ActivateMonitorInfo { public int hEventHandle; public int count; public List array; public byte[] ToBytes() { int length = 4 + 4; count = array.Count; for( int i = 0; i < count; i++ ) { length += array[i].Length * 2 + 2; } byte[] temp = new byte[length]; int iCur = 0; System.Buffer.BlockCopy( BitConverter.GetBytes(hEventHandle), 0, temp, iCur, 4 ); iCur += 4; System.Buffer.BlockCopy( BitConverter.GetBytes(count), 0, temp, iCur, 4 ); iCur += 4; for( int i = 0; i < count; i++ ) { Console.WriteLine( "Doing string {0}.", i ); System.Buffer.BlockCopy( array[i].ToCharArray(), 0, temp, iCur, array[i].Length * 2 ); iCur += array[i].Length * 2 + 2; } return temp; } } public static class Program { static public void Main() { ActivateMonitorInfo ami = new ActivateMonitorInfo(); ami.hEventHandle = 0x1234; ami.array = new List(); ami.array.Add( "Hello" ); ami.array.Add( "world" ); ami.array.Add( "from" ); ami.array.Add( "me" ); byte[] buffer = ami.ToBytes(); Console.WriteLine( "Buffer is {0} bytes", buffer.Length ); Console.WriteLine( BitConverter.ToString(buffer) ); } } The "ToBytes" function returns a byte array that you can pass to kernel mode safely. The "count" member tells the kernel how many strings to expect, and each one is terminated by a null. -- Tim Roberts, xxxxx@probo.com Providenza & Boekelheide, Inc. --- NTDEV is sponsored by OSR Visit the list online at: MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers! Details at To unsubscribe, visit the List Server section of OSR Online at
    d
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    If you can develop a driver, you can develop a library (DLL) written in the C language that would perform communications tasks between user-mode and kernel-mode.

    So, you would have a layered model with the C# app on top, the DLL in the middle, and the driver at the bottom.

    The DLL's exported APIs would handle "standard" C types like HANDLE and WCHAR arrays so that the interface betwwen the C# app and the DLL is simple. The DLL and the driver would share the same struct definitions so they communications are made easy and consistent as well.

    This is interesting because you can, this way, ensure that data is properly layed out in the user-mode/kernel-mode channel and you can continue to use C# for the user-mode app without too much "unsafe" code.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    >This is interesting because you can, this way, ensure that data is properly layed out in the
    >user-mode/kernel-mode channel and you can continue to use C# for the user-mode app without too
    >much "unsafe" code.

    Looking back at the inital question, i dont think that there is any need for a n-tier solution in this case. Passing such a "simple" piece of memory doesnt need anything in between. There are really very rare cases you cant fully translate C style definitions to C# CLR and if you hit such a case, then you could even operate on raw memory (data arrays, memory streams, etc.) in C# and pass it simply as a single package of memory block to the DeviceIoControl. We even had some neat examples here. Another approach is shared memory which is very well documented here and as well on ms pages:

    http://www.osronline.com/article.cfm?article=39
    https://support.microsoft.com/en-us/kb/191840

    K.
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    >Looking back at the inital question, i dont think that there is any need for a n-tier solution in this case.

    I do think there is.

    >Passing such a "simple" piece of memory doesnt need anything in between...

    So why does the OP request help ?

    >There are really very rare cases you cant fully translate C style definitions to C# CLR and if you hit such a case, then you could even operate on raw memory (data arrays, memory streams, etc.) in C# and pass it simply as a single package of memory block to the DeviceIoControl. We even had some neat examples here.

    Another just plain stupid statement. Using C# to do C! Really stupid.

    >Another approach is shared memory which is very well documented here and as well on ms pages...

    Off topic...
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    >Another just plain stupid statement. Using C# to do C! Really stupid.

    this is your opinion
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    >this is your opinion

    "Criticism is easy, and art is difficult."
  • Doron_HolanDoron_Holan Member - All Emails Posts: 10,444
    The second alternative is to use managed C++ to create a small assembly that does the marshalling. Since you can more simply describe the native struct AND provide a managed call interface, you get the best of both from the language. Not much else, but for this it suites its purposes

    -----Original Message-----
    From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
    Sent: Tuesday, August 16, 2016 10:48 PM
    To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    Subject: RE:[ntdev] Pass .NET Structure per IOCTL to KMDF

    >this is your opinion

    "Criticism is easy, and art is difficult."

    ---
    NTDEV is sponsored by OSR

    Visit the list online at: <http://www.osronline.com/showlists.cfm?list=ntdev>;

    MONTHLY seminars on crash dump analysis, WDF, Windows internals and software drivers!
    Details at <http://www.osr.com/seminars>;

    To unsubscribe, visit the List Server section of OSR Online at <http://www.osronline.com/page.cfm?name=ListServer>;
    d
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    >The second alternative is to use managed C++ to create a small assembly that does the marshalling. Since you can more simply describe the native struct AND provide a managed call interface, you get the best of both from the language. Not much else, but for this it suites its purposes

    Yes, even better.
  • Bob_Ammerman-2Bob_Ammerman-2 Member - All Emails Posts: 56
    Well, my opinion is that adding a C (or C++) DLL Is just an unneeded complication. Unless performance is of the utmost importance the BitConverter class and marshalling are your friends. If desired, this low-level mangling of things could be easily hidden from the end application.

    * Bob


    ? Bob Ammerman
    ? xxxxx@ramsystems.biz
    716.864.8337

    138 Liston St
    Buffalo, NY 14223
    www.ramsystems.biz


    > -----Original Message-----
    > From: xxxxx@lists.osr.com [mailto:bounce-614561-
    > xxxxx@lists.osr.com] On Behalf Of xxxxx@arcor.de
    > Sent: Tuesday, August 16, 2016 10:35 PM
    > To: Windows System Software Devs Interest List <xxxxx@lists.osr.com>
    > Subject: RE:[ntdev] Pass .NET Structure per IOCTL to KMDF
    >
    > >Another just plain stupid statement. Using C# to do C! Really stupid.
    >
    > this is your opinion
    >
    > ---
    > NTDEV is sponsored by OSR
    >
    > Visit the list online at: <http://www.osronline.com/showlists.cfm?list=ntdev>;
    >
    > MONTHLY seminars on crash dump analysis, WDF, Windows internals and
    > software drivers!
    > Details at <http://www.osr.com/seminars>;
    >
    > To unsubscribe, visit the List Server section of OSR Online at
    > <http://www.osronline.com/page.cfm?name=ListServer>;
  • Daniel_GeersDaniel_Geers Member Posts: 3
    Thank you very much for your patience,

    i've tried it by concating the string hash-values, convert them to a byte array and pass it down to driver by the following code:

    string _HashValues =
    *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*

    static byte[] GetBytes(string str)
    {
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
    }

    byte[] inBuffer = GetBytes(_HashValues);
    DeviceIoControl(handle, ServiceInstaller.IOCTL_UPDATE_PROG_TABLE, inBuffer, inBuffer.Length, outBuffer, 0, out bytesReturned, IntPtr.Zero);

    My last problem (and question, i promise) is that sometimes not the correct string-value appears in the driver:

    wchar_t* test = (wchar_t*)Irp->AssociatedIrp.SystemBuffer;
    DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "test-wchar_t: %ls\n",test);

    The output looks like this (at the end of the string additional character are appended):

    *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*e2-806e6f6e6963
    or
    *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*???}}}
    (but not always)

    I think i make a great mistake somewhere
  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,103
    xxxxx@freenet.de wrote:
    > i've tried it by concating the string hash-values, convert them to a byte array and pass it down to driver by the following code:
    >
    > string _HashValues =
    > *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*
    >
    > static byte[] GetBytes(string str)
    > {
    > byte[] bytes = new byte[str.Length * sizeof(char)];
    > System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    > return bytes;
    > }
    >
    > byte[] inBuffer = GetBytes(_HashValues);
    > DeviceIoControl(handle, ServiceInstaller.IOCTL_UPDATE_PROG_TABLE, inBuffer, inBuffer.Length, outBuffer, 0, out bytesReturned, IntPtr.Zero);
    >
    > My last problem (and question, i promise) is that sometimes not the correct string-value appears in the driver:
    >
    > wchar_t* test = (wchar_t*)Irp->AssociatedIrp.SystemBuffer;
    > DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "test-wchar_t: %ls\n",test);
    >
    > The output looks like this (at the end of the string additional character are appended):
    >
    > *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*e2-806e6f6e6963
    > or
    > *14cf73d771fa977a9f1cbaa5c301f912*7f0e061f5b6f311013968503d4c1d052*???}}}
    > (but not always)
    >
    > I think i make a great mistake somewhere

    Your mistake is not stepping back to think about what you are doing.

    Say your string is 5 characters long, "ABCDE". You are allocating 10
    bytes. You then copy 10 bytes, so the buffer looks like this:
    41 00 42 00 43 00 44 00 45 00

    You then pass that to kernel mode, where you try to DbgPrint it with
    %ls. What exactly does "%ls" mean? You are telling printf "I am
    passing you a zero-terminated Unicode string." But you are lying to
    printf, because your string does not have a zero terminator. Printf
    doesn't know where the end of the string is.

    So, the data is being passed correctly. It is your debug print that is
    wrong. If you really want to print the string, there are several
    options. You could wrap the buffer in a UNICODE_STRING structure and
    print it using %Z:

    UNICODE_STRING us = {
    irpStack->Parameters.DeviceIoControl.InputBufferLength,
    irpStack->Parameters.DeviceIoControl.InputBufferLength,
    Irp->AssociatedIrp.SystemBuffer
    };
    DebugPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_ERROR_LEVEL, "test-wchar_t:
    %Z\n", &us );

    Or, you can pass the length in:
    DebugPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_ERROR_LEVEL, "test-wchar_t:
    %.*ls\n",
    irpStack->Parameters.DeviceIoControl.InputBufferLength,
    Irp->AssociatedIrp.SystemBuffer
    );

    Why are you not writing a KMDF driver?

    --
    Tim Roberts, xxxxx@probo.com
    Providenza & Boekelheide, Inc.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
Writing WDF Drivers 21 Oct 2019 OSR Seminar Space & ONLINE
Internals & Software Drivers 18 Nov 2019 Dulles, VA
Kernel Debugging 30 Mar 2020 OSR Seminar Space
Developing Minifilters 27 Apr 2020 OSR Seminar Space & ONLINE