2

I've discovered undname.exe today and tried it on several functions. I got some incorrect results and I'm wondering why. I see two main reasons that could do that:

  • The mangling can change between msvc versions and I should find the version of undname.exe that fit my target
  • The tool I use to extract the function names gives inaccurate results

Here's an example of a wrong result:

Undecoration of :- "?GetClassNameW@User@@QAE?AVFName@@H@Z"
is :- "public: class FName __thiscall User::GetClassNameW(int)"

After trying this signature and receiving a stack error, I looked at the disassembled code of GetClassNameW and realised the function signature actually was void(__thiscall* user_getClassNameW)(User*, FName*, int);

I do not know the exact version of msvc used to compile the example and it seems that the only way to download undname.exe is to install visual studio, so I can't easily test this.

Any idea where the problem could come from?

2

As @blabb says undname is correct so it's your other tool that is 'incorrect'. However, it's worth looking at this in more detail as it's probably not as incorrect as you think.

To understand this though you have to delve a little into ABIs and think how a C++ function call works in practice.

Firstly, a C++ member function is in some sense like a C function with a 'hidden' this pointer passed as the first parameter. (This is what __thiscall is saying behind the scenes.)

// this function
void class::member();
// is really
void __thiscall class::member();

// and works like
void __stdcall class_member( Class* this );

Secondly, the caller of function returning a ('complicated') struct/class is responsible for allocating the memory for the struct/class and passing the function a pointer to this memory. The function can then write the relevant details there. Hence -

// this function
complex_struct_return_type function();  

// works like
void function( complex_struct_return_type * );

Putting these together (and ignoring accessiility specifiers)-

// this function
FName __thiscall User::GetClassNameW( int param );

// works like
void __stdcall User_GetClassNameW( User* this, FName* pointer_to_return_value, int param );

You will see this is very similar is essence to your other tool's output. I'd just observe that -

  • it is still labelling the function as __thiscall. This doesn't quite make sense but is probably being done to indicate that the first parameter has special treatment in 32-bit code.
  • it is changing the case of the initial letters. This again is strictly incorrect.

How these parameters get passed (by msvc) depends on whether it's compiled for 32 or 64-bit .

  • For 32-bit code, this has special treatment and is passed in ecx with the rest of the parameters on the stack.
  • For 64-bit code, this is treated as like the other parameters and hence, being the first of up to 4 parameters being passed in registers will be in rcx
| improve this answer | |
  • I didn't know about the complex struct return thingy. Intuitively I thought it would put the address in eax instead of on the stack. Thanks for your answer! – Stud Apr 10 at 11:36
1

there are lot of external utilities that can demangle a decorated name
try looking for vc++filt /
or some online demangler like this

or you can probably code one using dbghelp api UndecorateSymbolName() in python with ctype / pywin32

also undname isn't erring your second guess must be right

I just prototyped a similar function and looked at its demangled name
it is entirely different from your demangle name here is a src code and output

#include <windows.h>
#include <dbghelp.h>
#include <stdio.h>
void uselessname(HWND hwnd,LPWSTR lpcname,int mcnt)
{
    printf("%p %ws %d\n",hwnd,lpcname,mcnt);
    printf("%s\n%s\n%s\n",__FUNCTION__,__FUNCDNAME__,__FUNCSIG__);   

}
int main (void) 
{
    HWND cwnd =NULL;
    wchar_t cname[50] = {L"MANGLE AND DEMANGLE"};
    int cnt = 50;
    uselessname(cwnd,cname,cnt);
    char ret[0x100] = {0};
    UnDecorateSymbolName("?GetClassNameW@User@@QAE?AVFName@@H@Z",ret,0x100,0);
    printf("%s\n",ret);
    return 0;
}

output

gcw.exe
0000000000000000 MANGLE AND DEMANGLE 50
uselessname
?uselessname@@YAXPEAUHWND__@@PEA_WH@Z   <<<<<<<<<<<<<<<<<<<<<<<<<< must be like this
void __cdecl uselessname(struct HWND__ *,wchar_t *,int)
public: class FName __thiscall User::GetClassNameW(int) <<<<< result of yours 
which is ?GetClassNameW@User@@QAE?AVFName@@H@Z
| improve this answer | |
  • Thank you for your detailed answer, I definitly learned from it! – Stud Apr 10 at 11:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.