[General] va_list in C# menu

User Tag List

Results 1 to 11 of 11
  1. #1
    Frosttall's Avatar Active Member
    Reputation
    64
    Join Date
    Feb 2011
    Posts
    261
    Thanks G/R
    16/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    [General] va_list in C#

    Hello,

    my question isn't about directly about WoW, but I'd like to ask you for help anyways.

    I'm trying to use GreyMagic in order to Detour a Cdecl - C++ signature looks like this:
    Code:
    int Naked PacketSend(DWORD type,LPCSTR format,...)
    and this is what I got so far in C#
    Code:
            [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi, SetLastError=true)]
            private delegate int SendPacketDetour(uint type, string format, IntPtr args);
    
            private int Callback(uint type, string format, IntPtr args)
            {
                    return (int)_presentHook.CallOriginal(type, format, args);
            }

    The content could look like this:
    type = 0x12
    format = "bbd"
    ... = va_list

    The C#-Declaration works only partly: Type and format are read correctly, but 'IntPtr args' seems to be wrong.
    Returning the control back to the original doesn't crash but cause some arguments not getting sent to the server.

    The following packet gets sent correctly:
    Code:
    Type: 0x5
    Format: d
    whereas every packet with more than one argument like the following doesn't get sent:
    Code:
    Type: 0x12
    Format: bbb
    This means the IntPtr I'm using is not enough.



    I've tried various solutions like working with params object[], params IntPtr[], object[] etc, but this didn't work and isn't the equivalent of va_list. C# itself contains a keyword which was made for Cdecl external functions like the following:
    Code:
    [System.Runtime.InteropServices.DllImportAttribute("unmanaged.dll", EntryPoint = "VarSum", 
                CallingConvention=System.Runtime.InteropServices.CallingConvention.Cdecl)]
            public static extern int VarSum(int nargs, __arglist);
    Unfortunately is __arglist only valid for imported function and throws the following exception in case it's used with an UnmanagedFunctionPointer:
    __arglist is not valid in this context

    Has somebody ever experienced that case and could point me to the right direction?
    Last edited by Frosttall; 08-01-2013 at 05:45 AM.

    [General] va_list in C#
  2. #2
    Master674's Avatar Elite User
    Reputation
    487
    Join Date
    May 2008
    Posts
    578
    Thanks G/R
    2/23
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That won't work. It's a function where the caller pushes a unknown number of arguments onto the stack and the function which is called can only determine the number when it parses the format string. You won't be able to detour that function from C# at least not without any asm.

    Edit: You couldn't even detour that in C++ without any dumb hacks.

    Edit2: It is not even possible to forward arguments from such a function other than doing nothing in the forwarder function and directly jump out as call does a push retaddr
    Last edited by Master674; 08-01-2013 at 01:31 PM.

  3. #3
    DarthTon's Avatar Contributor
    Reputation
    171
    Join Date
    Apr 2010
    Posts
    108
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well, I think it's partly possible. Let's assume you know maximum number of args vararg function can receive.
    For example, function always receive less than 5 variable arguments. So you can make such delegate and detour

    Code:
    int VarFn(int type, String fmt, IntPtr a1, IntPtr a2, IntPtr a3, IntPtr a4, IntPtr a5);
    
    [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
    delegate int TheDelegate1(int a1, String a2, IntPtr a3, IntPtr a4, IntPtr a5, IntPtr a6, IntPtr a7);
    This will work because varargs are on the stack, they just sit there after last non-variable argument. And you will get first 5 of them into a3-a7. Using 'format' argument, you can get number of args passed, and their types. Remember that arguments a3-a7 are just raw data from stack.

    To call an original function you use same delegate. Just fill appropriate number of args and set unused ones to IntPtr.Zero, original function will ignore them anyway. And because of cdecl convention, stack pointer will be always restored by caller function, no matter how many actual parameters callee used.

  4. #4

  5. #5
    Master674's Avatar Elite User
    Reputation
    487
    Join Date
    May 2008
    Posts
    578
    Thanks G/R
    2/23
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wtf, they seriously thought about something like this? Sorry for my response then, I had big problems hooking such a function from C++.
    I wonder how RuntimeArgumentHandle works though since you have to clean up the stack from your detour after you called the original function, so it somehow needs to know the number of arguments.

    Edit: Another option just came to my mind: You could use a software breakpoint to detour that function, that way you would be allowed to run your detour code before returning to the original function.
    Last edited by Master674; 08-02-2013 at 08:22 AM.

  6. #6
    jjaa's Avatar Contributor
    Reputation
    245
    Join Date
    Dec 2006
    Posts
    562
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Variable argument functions are cleaned by the caller. So the caller will always just clean up the number of arguments that they set on the stack. If another place in the code calls the function again with a different number, then that call will clean a different number of arguments cleaned from the stack. The caller always knows the size of the arguments passed to the function on the stack.

  7. #7
    Master674's Avatar Elite User
    Reputation
    487
    Join Date
    May 2008
    Posts
    578
    Thanks G/R
    2/23
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by jjaa View Post
    Variable argument functions are cleaned by the caller. So the caller will always just clean up the number of arguments that they set on the stack. If another place in the code calls the function again with a different number, then that call will clean a different number of arguments cleaned from the stack. The caller always knows the size of the arguments passed to the function on the stack.
    Yeah I know that but that wont work because you invoke the original function from your hook code. So you are the caller in your hook.

  8. #8
    Bananenbrot's Avatar Contributor
    Reputation
    153
    Join Date
    Nov 2009
    Posts
    384
    Thanks G/R
    1/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't think you can use RuntimeArgumentHandle (RAH) with that delegate, because RAH is the equivalent to an va_list parameter, which is not the same as accepting ... . In other words:
    Code:
    void foo(int bar, ...);
    isn't the same as
    Code:
    void foo(int bar, va_list args);
    . The latter can be decepted via the equivalent function and RAH, the former would have no equivalent in C#/.NET, unless the marshaler would provide the conversion from ... to va_list in the trampoline it allocates for UnmanagedFunctionPointers.
    Prove me wrong though.

  9. #9
    Frosttall's Avatar Active Member
    Reputation
    64
    Join Date
    Feb 2011
    Posts
    261
    Thanks G/R
    16/3
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Alright I've been able to route the function trough my detour without crashing or loosing any packets. The next step is converting the IntPtrs into their equivalents, but here is what I got so far:

    PHP Code:
            [UnmanagedFunctionPointer(CallingConvention.CdeclCharSet CharSet.AnsiSetLastError true)]
            private 
    delegate int SendPacketDetour(uint typestring formatIntPtr arg1IntPtr arg2IntPtr arg3IntPtr arg4IntPtr arg5IntPtr arg6IntPtr arg7IntPtr arg8IntPtr arg9IntPtr arg10IntPtr arg11IntPtr arg12IntPtr arg13IntPtr arg14);

     private static 
    int SendHookCallback(uint opcodestring formatIntPtr arg1IntPtr arg2IntPtr arg3IntPtr arg4IntPtr arg5IntPtr arg6IntPtr arg7IntPtr arg8IntPtr arg9IntPtr arg10IntPtr arg11IntPtr arg12IntPtr arg13IntPtr arg14)
            {
                
    int argumentsToPush 0;
                for (
    int i 0format.Length+= 1)
                {
                    switch (
    format[i])
                    {
                        case 
    'b':
                            
    argumentsToPush += 1;
                            break;
                        case 
    'd':
                            
    argumentsToPush += 1;
                            break;
                        case 
    'f':
                            
    argumentsToPush += 2;
                            break;
                        case 
    'u':
                            
    argumentsToPush += 1;
                            break;
                        case 
    's':
                            
    argumentsToPush += 1;
                            break;
                        case 
    'm':
                            
    argumentsToPush += 2;
                            break;
                        default:
                            
    Logger.Log("Unknown type: " format[i]);
                            break;
                    }
                }


                return (int)
    _sendHook.CallOriginal(opcodeformat,
                    
    argumentsToPush >= arg1 IntPtr.Zero,
                    
    argumentsToPush >= arg2 IntPtr.Zero,
                    
    argumentsToPush >= arg3 IntPtr.Zero,
                    
    argumentsToPush >= arg4 IntPtr.Zero,
                    
    argumentsToPush >= arg5 IntPtr.Zero,
                    
    argumentsToPush >= arg6 IntPtr.Zero,
                    
    argumentsToPush >= arg7 IntPtr.Zero,
                    
    argumentsToPush >= arg8 IntPtr.Zero,
                    
    argumentsToPush >= arg9 IntPtr.Zero,
                    
    argumentsToPush >= 10arg10IntPtr.Zero,
                    
    argumentsToPush >= 11arg11IntPtr.Zero,
                    
    argumentsToPush >= 12arg12IntPtr.Zero,
                    
    argumentsToPush >= 13arg13IntPtr.Zero,
                    
    argumentsToPush >= 14arg14IntPtr.Zero
                    
    );
            } 
    It's ugly but at least it's working

    Thank you guys so far
    Last edited by Frosttall; 08-02-2013 at 02:42 PM.

  10. #10
    jjaa's Avatar Contributor
    Reputation
    245
    Join Date
    Dec 2006
    Posts
    562
    Thanks G/R
    0/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Master674 View Post
    Yeah I know that but that wont work because you invoke the original function from your hook code. So you are the caller in your hook.
    Ahh i get what you mean now. Without some hacky detour, it's a bit annoying. Its rather easy to read the args out of the va_list but you either need to replace the whole function or do a hacky detour. The C# example is the same thing as va_list. You will still have some stack cleaning issues.

  11. #11
    MaiN's Avatar Elite User
    Reputation
    335
    Join Date
    Sep 2006
    Posts
    1,047
    Thanks G/R
    0/10
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wow, cool to see something so old actually found.

    Originally Posted by Master674 View Post
    Wtf, they seriously thought about something like this? Sorry for my response then, I had big problems hooking such a function from C++.
    I wonder how RuntimeArgumentHandle works though since you have to clean up the stack from your detour after you called the original function, so it somehow needs to know the number of arguments.

    Edit: Another option just came to my mind: You could use a software breakpoint to detour that function, that way you would be allowed to run your detour code before returning to the original function.
    Originally Posted by Bananenbrot View Post
    I don't think you can use RuntimeArgumentHandle (RAH) with that delegate, because RAH is the equivalent to an va_list parameter, which is not the same as accepting ... . In other words:
    Code:
    void foo(int bar, ...);
    isn't the same as
    Code:
    void foo(int bar, va_list args);
    . The latter can be decepted via the equivalent function and RAH, the former would have no equivalent in C#/.NET, unless the marshaler would provide the conversion from ... to va_list in the trampoline it allocates for UnmanagedFunctionPointers.
    Prove me wrong though.
    You all raise valid points, and I have no idea how that would work back then. Wow. That is really strange. I just tested it again in a more artificial manner, and it's definitely not working correctly.

    Anyway, C# actually has support for native variadic functions through the __arglist keyword (which is undocumented), but that keyword is only valid in function declarations (such a pinvokes, so it is possible to pinvoke printf, for example), not in delegates. Indeed, if I had to hook a variadic function today, it would probably involve some ASM stub to do the original call correctly, and then just get a pointer to the first arg and pass that off to my hook stub.
    [16:15:41] Cypher: caus the CPU is a dick
    [16:16:07] kynox: CPU is mad
    [16:16:15] Cypher: CPU is all like
    [16:16:16] Cypher: whatever, i do what i want

Similar Threads

  1. Model editing in general patch 2.0
    By shade599 in forum World of Warcraft Model Editing
    Replies: 4
    Last Post: 12-27-2006, 02:54 PM
  2. Tanaris General Chat in StormWind?
    By Clocky in forum World of Warcraft General
    Replies: 6
    Last Post: 12-06-2006, 02:36 AM
  3. General Rajaxx
    By Snor11 in forum World of Warcraft Guides
    Replies: 0
    Last Post: 11-04-2006, 06:53 PM
  4. Info on taking Ragnaros (And other MC general info)
    By Cush in forum World of Warcraft Guides
    Replies: 4
    Last Post: 05-28-2006, 03:53 AM
  5. bug up General Rajaxx in AQ20
    By impulse102 in forum World of Warcraft Exploits
    Replies: 2
    Last Post: 05-05-2006, 04:43 PM
All times are GMT -5. The time now is 11:17 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search