Archive: Windows Built-In strings


Windows Built-In strings
I was wondering if anybody knew how to get the strings that are available as descriptions for the windows Built-in accounts.

For example the Administrator is described as Built-in account for administering the computer/domain and the Guest is described as Built-in account for guest access to the computer/domain. Where can I find these strings in a language independent way?

I do not want to pull them out from the system (using say NetUserGetInfo API calls) because they may have been tampered with. Is there a dll or any other file that contains these strings?

The same goes for the default usernames (Administrator, Guest). Is there a library where they are stored so that I can get them out? Especially if the original names have been changed (and I still want to know what the original name was).

CF


I think you can check the SID or somthing like that and tell who the original admin was


Thanks for the input Anders, but I do not think that this is what I am after ... If the original account has been renamed all I can get is the new name using its SID. What I need is to find, say in a French version of windows, what is the equivalent of 'Administrator' without asking the system for the administrator's SID (since that SID may point to a renamed account). The same for any other language ...
CF


If I remember correctly, the Administrator *ALWAYS* has the same (special, reserved) SID...


Maybe my initial post was not clear enough …

I know that the admin's SID is ALWAYS S-1-5-xx-xxx-xxx-xxx-500 for the local machine (do not pay attention to the number of xs used here). However you can rename that account (change the username) from 'Administrator' or whatever it is in your language to anything you like. So the question was 'how do I find what the original name was, in any language, if all I have is the SID?' Obviously in english it would be 'Administrator' How about in any other language? Is there a file that contains those names?

For example, if you create a shortcut to cmd.exe and you name it @shell32.dll,-22022 then the above will show a localized version of 'Command Prompt'. So the string is picked from shell32.dll regardless of the language used and is always localized. Is there something similar for the default windows accounts?
...
CF


You may want to read this interesting article and the linked resources to get an idea of why your approach seems to be a bad idea :)
http://blogs.msdn.com/michkap/archiv...27/507404.aspx
Here is a list of "well-known SIDs" for your reference:
http://support.microsoft.com/default...;EN-US;Q243330
And some sample code about how to deal with localized names etc.:
http://support.microsoft.com/kb/157234/en-us


:)
Thanks for the tip, however it is still out of context ...
I am using the SID to get the name. The problem is that if the name has been altered by a user, I still want to find out what the original name was.

In other words, if in a Chinese system the administrator has been renamed to root, can you tell how to find out what the original username was, in Chinese? How do you do that? That's why I assumed that there may be a localized string somewhere that I could use ...

I do not have an approach yet, this is what I am trying to get through, I don't know how to do this, but I need to know what the original name was :)

CF


Geez, first of all, why would you need the original name? It isn't useful anymore after the rename. One hint though: if I remember correctly, your "My Files" folder does not change when you rename your account. Dirty hack but probably functional :)


Taking a step backward here:

Why would you need to know the name of the Administrator account? It sounds a lot like perhaps all you really need is Administrator permissions, correct? If so, perhaps a plugin like the xtinfo plugin or the user manager plugin might be of more help.

But, maybe I'm just not seeing the whole picture here...:confused:


Geez, first of all, why would you need the original name?
To avoid any further misunderstandings I should probably explain exactly what I am after:

I have a scenario where, after renaming the Administrator account in any language I want to create a fake account called 'Administrator', or whatever that name should be in the local language. The new account is stripped from all its privileges and is disabled by default. Some people feel that this will save their system from an attack that will try to get the administrator's password or some other info/function. In my point of view this is useless since, if I where to hack anything out of a system, I would use the SID of the administrator and not the username.

However, the need remains in the above scenario to create an account called Administrator. In the rare case that the original admin account has already been renamed, there is no way (to my knowledge) to get the original name. In a US/UK version of windows, that's easy, you call that account 'Administrator'. What about in the rest of the languages? Hence my question: How do I find the default name of the admin account? Is there a string stored somewhere?

I hope this is as clear as I think it is ... The 'dirty hack' suggested by OldGrumpy is the only option until now, but I am still thinking that the system stores the default names in some dll and then the APIs are reading them.

CF

I'll post that question to a specialist board. Let's see what they come up with :) Maybe these accounts are only created by the Windows installation program ;) I'll keep you updated on the results.


> How do I find the default name of the admin account? Is there a string stored somewhere?

Yes there is. You can retreive the info from a DLL resource string. Do first on a clean OS a search in ALL files (suprgrep) and the ntake a resource hacker e.g. http://www.heaventools.com/ PE tools etc.

Change the resource, retry if it worked.


Onad, I did that today on my W2K machine, and couldn't find any suitable occurrences :)


Try using PE-Explorer on something like advapi32.dll and you'll see why it wont work :)
I have tried looking for the string 'Administrator' before but could not find it. Not to mention that using PEExplorer for each DLL in system32 is probably not the way to go. Or at least not until you post the question in case somebody has alreday tried it before :D

CF


You can simply search for string (both ANSI and Unicode) in multiple files - there's plenty of tools that can do that, (including some of the better text editors) out there, you don't need PEExplorer for that.

However, these are probably set during installation of OS and such default names/desriptions would be present only somewhere in setup files on CD.
So probably there's no reliable way to get them without asking user to insert installation disk.


galil's suggestion made me look up again inside the system folder, for unicode equivalents of the strings I was after. I finally used UltraEdit to do a unicode search and got all the strings I was after in samsrv.dll. I am not sure if this is what I am after but it is a good start :)

I will experiment with that file to see if I can indeed get what I want but if anyone knows of another way it would be most helpful if you could post it here

CF


I think I managed to achieve what I was after, although I cannot check it on another language since I am running an english version of windows.
Here is what I did:

!define LOAD_LIBRARY_AS_DATAFILE     0x00000002
!define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R0'
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_ALLOCATE_BUFFER}
StrCpy $1 8192 ; messageID for string 'Administrator'
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i 0, *w .R3, i 0, i n) i .R4'

IntOp $9 $R4 * 4
System::Call '*(&w$9)i.R3'
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i r2, i R3, i r3, i n) i .R4 ?e'
System::Call '*$R3(w .R9)'
System::Call 'kernel32::LocalFree(i R3)i.R8'
System::Free $R0
I had to call FormatMessageW twice: the first time I got the size of the message that I extracted and the second time I allocated an appropriate buffer to store it. Since it is a unicode string I don't want to pass it to a variable in NSIS, but leave it at a buffer instead and then pass that buffer to the next function (not shown here)

I think this should work for every system. The main trick is the usage of the language ID (here I pass it as zero on the 4th parameter of FormatMessage). A better way would be to detect the system default (or the user default) language and then pass it on to the system call.

As always any input is welcome :)

CF

I'm very surprised because I though my favorite search tool would be capable of doing unicode searches too. Obviously, it is not :(
Thanks for the investigations, CancerFace. BTW, and fully OT, why did you choose this strange nickname? :)


Hmmm, it may be a bit more complicated after all. By default FormatMessage appends a trailing new line character at the end of the message unless an array of formatting instructions is supplied. And this is where I am lost since this page doesn't make much sense :(
I'll have to play a bit more with this ...

Also I should probably unload the DLL using FreeLibrary :

System::Call 'kernel32:FreeLibrary(i R1) i.R8'
instead of using System::Free
BTW, and fully OT, why did you choose this strange nickname?
I chose that nickname when I was 18 or so and have been using it ever since without realizing most of the time how awful it sounds ... It used to be a joke from a sailing instructor that I had as a kid, and I can assure you that it sounded funny in my mother language (Greek) but certainly not in English ... Nothing to do with my face or any form of cancer, if that's what you're asking :D

CF

[Edit] Looking at the code I posted again I reallized that I am allocating far more space than needed for my buffer. No need to run the FormatMessage function twice, just create the buffer and call the function in one go:
!define LOAD_LIBRARY_AS_DATAFILE   0x00000002
!define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R0'
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_ALLOCATE_BUFFER}
StrCpy $1 8192 ; messageID for string 'Administrator'

System::Call '*(&w4)i.R3'
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i r2, i R3, i r3, i n) i .R4'
System::Call '*$R3(w .R9)'
System::Call 'kernel32::LocalFree(i R3)i.R8'
System::Call 'kernel32:FreeLibrary(i R1) i.R8'

I think it's worth to expand the functionality and put it into a separate plugin. When I get to have some idle time at my hands, I'll take a shot at it. The code will look much nicer and cleaner when all that formatting stuff is put into a dll. And the string manipulation will become easier, too ;)


This is tricky.
The string that I am extracting from SAMSRV.DLL has a new line appended at its end. I do not want to copy the string to an NSIS variable since it is unicode, but I want to get rid of the new line. Is there an easy way to trim a string while it exists on a memory buffer, without getting it into a variable? The FormatMessage API call returns the number of TCHARS stored in the buffer (including the new line?) so I also know its size.

I can't seem to find an easy way to do this. :mad:

Any input is welcome as usual

CF


I just had a look at the resources of samsrv.dll and it has no new line char at the end. Did you make sure to create a buffer large enough to carry the wide string and a terminating wide char (zero)? Did you fill the buffer with zeros before using it? It's a common mistake to allocate a buffer and just use it. A call to RtlZeroMemory doesn't take much time but can help a lot :)
I have to take a look at the plugin SDK before I can write up an appropriate plugin. But I am sure it's much more comfort to have a plugin sorting out all that crap ;)
And by the way, wouldn't it be nice to have the string converted to ANSI? I know there are pros and cons about that, what do you think?


Check out my previous posts, the whole point is to have the string in a buffer as unicode. The buffer is allocated by the FormatMessage function automatically (I am using ${FORMAT_MESSAGE_ALLOCATE_BUFFER}) so it doesn't really matter what I allocate. I could even use '*(&w1)i.R3' and still the API would expand this, in order to accommodate the string. Not to mention that if I try to allocate the buffer myself (without allowing the API to do so) I don't get anything out. That's why at some point I was calling the function twice so that I would get the string size first, and then allocate a buffer based on that size.

The new line is appended by the API and there should be an easy way to get rid of it. I was unable to track down something similar on the web, since all the examples I came across use 'i n' or 'i 0' as the last parameter for the call (ie no formatting array). According to MSDN it should be possible to define formatting options. I found a page that shows how to use different inserts in order to format a message but this doesn't make any sense in my case. I should be able to use an escape sequence (%0) to tell the function to remove the new line but I have failed so far to achieve that ...

Thanks for the input though :)

CF


From looking at how FormatMessageW works, you have to supply the %0 at the end as wide chars. Did you try that? If you supply the wide char equivalent of "%1%0" as the formatting template to the API, you should get what you want :)


If you supply the wide char equivalent of "%1%0" as the formatting template to the API, you should get what you want
Haven't figured how to do this yet. I think this can be done if you extract the string from the buffer, but I may be wrong ... :)

However I just realized that if you look at something for looooong enough you tend to forget what it says ...
The function ignores regular line breaks in the message definition text. The function stores hard-coded line breaks in the message definition text into the output buffer. The function generates no new line breaks.
So if I add FORMAT_MESSAGE_MAX_WIDTH_MASK to the call the line break disappears! I am still left with a space at the end of the string but I am sure the answer in getting rid of that is here as well :)

So now this is working and I am not getting the new line appended:
!define LOAD_LIBRARY_AS_DATAFILE      0x00000002
!define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
!define FORMAT_MESSAGE_MAX_WIDTH_MASK 0x000000FF
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R0'
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_ALLOCATE_BUFFER}
IntOp $0 $0 + ${FORMAT_MESSAGE_MAX_WIDTH_MASK}
StrCpy $1 8192 ; messageID for string 'Administrator'
StrCpy $2 0
StrCpy $3 0
System::Call '*()i.R3'
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i r2, i R3, i r3, i n) i .R4'
System::Call '*$R3(w .R9)'
System::Call 'kernel32::LocalFree(i R3)i.R8'
System::Call 'kernel32:FreeLibrary(i R1) i.R8'


CF

[Edit] I kept forgetting to define $2 and $3 as 0 throughout the thread ...

I saw that statement in the docs, too - I just wasn't sure about what it would do. MSDN tends to be cryptic ;) Can't you just get the string length and write two zeros at the end of it? :) Additional zero bytes won't do any harm as long as you don't write beyond the end of the buffer.


I am stuck with this. Although I get the wide string in a buffer, a space is appended at the end and I do not know how to get rid of it. I tried copying the buffer over to a shorter one with no success. I tried setting the size of the buffer before calling FormatMessage (removing the FORMAT_MESSAGE_ALLOCATE_BUFFER parameter and using the 6th parameter to define the buffer size) without any results either. If anyone can see a way out I will appreciate the help :)

CF


Tried to specify /SIZE for System::Copy?


Without any success. For any size (>2) the whole string with the space at the end is copied over to the new buffer even if the new buffer is allocated in advance:

System::Alloc 2
Pop $R6
System::Copy /2 $R6 $R3
System::Call '*$R6(w.R9)'
$R9 always contains a space at the end ...

CF

I guess it would be a good idea to convert it to a plugin as already suggested. There is no API for trimming newlines that I could find on MSDN.

-Stu


I'll have a look at howto make an NSIS plugin in the next days. I'm quite busy with my job but I think I can find a little time to get it done.


Well I can do it tomorrow if you'd like me to.

-Stu


That would be really great :)


Any ideas what to call the plugin?

-Stu


Wow, I am impressed :)
I went away for half an hour and now there is already the possibility for a plugin!

Let me slow things down just for a sec here. I am almost convinced that the function itself is appending that space at the end of the string. Its syntax is quite strange as every parameter that you pass depends on the others. For example, there is the possibility to define the size of the buffer allocated for the string (if you don't use the FORMAT_MESSAGE_ALLOCATE_BUFFER flag) however I may be allocating the wrong amount of memory and that's why I am not getting anything out. If I call the function once then I can get the size of the string:

!define LOAD_LIBRARY_AS_DATAFILE    0x00000002
!define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
!define FORMAT_MESSAGE_MAX_WIDTH_MASK 0x000000FF
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R0'
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_ALLOCATE_BUFFER}
IntOp $0 $0 + ${FORMAT_MESSAGE_MAX_WIDTH_MASK}
StrCpy $1 8192 ; messageID for string 'Administrator'
StrCpy $2 0
StrCpy $3 0
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i r2, *w .R3, i r3, i n) i .R4'
Now $R3 contains the string with a space at the end and $R4 is the length (in TCHARS) of the string. For the above example, the string is 'Administrator' on my system, and the function reports 14 TCHARS. Is that 13 + 1 (1 being the space)? If yes then I can call the function a second time, only now I will not allow it to allocate the buffer but I will create one and use it:
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_MAX_WIDTH_MASK}
IntOp $9 $R4 - 1 ; # TCHARS of string
IntOp $3 $9 * 2 ; # of bytes for the string
System::Alloc $3
Pop $R3
System::Call 'kernel32::FormatMessageW(i r0, i R0, i r1, i r2, i R3, i r9, i n) i .R4 ?e'
Pop $R8
The above code does not work however and I am not sure why. The error is 122 (ERROR_INSUFFICIENT_BUFFER).

Any ideas?

CF

I've got the code in a plugin and trimmed the new lines. What do you need to do with the string after that point?

-Stu


Thanks a lot by the way :) I guess I'll have to dive into making plugins myself soon ...

Is it possible to pass it in a buffer so that I can directly pipe it to another function? I do not want to have it in a variable because it may contain unicode characters.

Ideally I would like to pass to the plugin:
1. the library name
2. the # of the string to pull out
3. (optionaly) the system language (or you could use GetSystemDefaultLangID to query the system language and then pass it to FormatMessage)

and then I would get the string in a buffer so that I can use it directly for another API call ...

Where you able to get rid of the space appended at the end of the string?


CF

[Edit] This plugin can have a more general use I guess. You could pass the string to a buffer and to some variable, or think about dealing with message strings as well instead of strings from tables etc ...


I didn't use FORMAT_MESSAGE_MAX_WIDTH_MASK so there was a \r\n and no space. It isn't possible to pass a string from one plugin to another without storing it in an NSIS variable in between. You'd have to do everything in the same plugin.

If I use GetSystemDefaultLangID the FormatMessage call fails.

-Stu


What about calling the plugin (Get)SystemMessageStrings? I don't know if with our without "Get" is better :) Specifying the system language for the plugin seems to be superfluous to me, but it could be useful to distinguish between the system language and the user language. So we could pass a flag to the plugin that decides which of the two will be used.
Another question would be who allocates and frees the buffer memory. I'd suggest that the plugin allocates the memory, and frees it when it gets unloaded. That way, it can be ensured that no memory leaks occur. (I hope...)

Edit: I think GetResourceString is a better name :)
2nd edit: I think the plugin should be able to convert unicode to ansi (using the system functions) optionally to enhance the comfort for nsis components. As long as nsis can't cope with unicode properly, that could be a huge help. It isn't a problem to put the string into a buffer and let the system convert it back later when needed. From 2K onwards, the system converts all ansi strings to unicode internally, anyway :)


GOT IT!!!!! :)

After a lot of trial-and-error runs here is what worked for me:

!define FORMAT_MESSAGE_IGNORE_INSERTS    0x00000200
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
!define FORMAT_MESSAGE_MAX_WIDTH_MASK 0x000000FF


# Get the system Language ID
System::Call 'kernel32::GetSystemDefaultLangID(i v)i .R0'

# First load the library
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R1'

# get the string out ($R2) and get its size on $R3
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_IGNORE_INSERTS}
IntOp $0 $0 + ${FORMAT_MESSAGE_MAX_WIDTH_MASK}
StrCpy $1 8192
StrCpy $2 ${NSIS_MAX_STRLEN}

# prepare a buffer to accept that string
System::Call '*(&w${NSIS_MAX_STRLEN})i.R2'
System::Call 'kernel32::FormatMessageW(i r0, i R1, i r1, i R0, i R2, i r2, i n) i .R3'

; Free the DLL handle
System::Call 'kernel32::FreeLibrary(i R1)i .R8'
Now the string is stored in $R2 but if I try to get it out, say using
System::Call '*$R2(w.R9)'
my program fails. However if I pass this buffer directly to another API it works! Well at least in XP ... Not sure why I am not able to get the string out in $R9 though.

I passed the buffer to the NetUserAdd API and managed to create a user called 'Administrator'

:)

I will test it in 2k as well and then report back ...

CF

[Edit] Oups, I was a bit too fast getting excited :(
It works but the name still contains a space at the end ... It works in 2k as well ...

Archive: Windows Built-In strings


Make sure you call LocalFree to free the buffer memory when you're finished using it.

-Stu


Yes I am freeing the buffer after I pass it to the next API.
I am also still interested in your plugin Stu since I can't get rid of that !$@!#$ space at the end of the string :(

CF


Problem solved!

The trick is to copy the buffer (as galil pointed out at some point) although it looks like the buffer does not contain anything:

!define FORMAT_MESSAGE_IGNORE_INSERTS  0x00000200
!define FORMAT_MESSAGE_FROM_HMODULE 0x00000800
!define FORMAT_MESSAGE_MAX_WIDTH_MASK 0x000000FF


# Get the system Language ID
System::Call 'kernel32::GetSystemDefaultLangID(i v)i .R0'

# First load the library
System::Call 'kernel32::LoadLibraryExW(w "$SYSDIR\SAMSRV.DLL", i n, i ${LOAD_LIBRARY_AS_DATAFILE}) i .R1'

# get the string out ($R2) and get its size on $R3
StrCpy $0 ${FORMAT_MESSAGE_FROM_HMODULE}
IntOp $0 $0 + ${FORMAT_MESSAGE_IGNORE_INSERTS}
IntOp $0 $0 + ${FORMAT_MESSAGE_MAX_WIDTH_MASK}
StrCpy $1 8192
StrCpy $2 ${NSIS_MAX_STRLEN}

# prepare a buffer to accept that string
System::Call '*(&w${NSIS_MAX_STRLEN})i.R2'
System::Call 'kernel32::FormatMessageW(i r0, i R1, i r1, i R0, i R2, i r2, i n) i .R3'

# trim one character and copy to another buffer
IntOp $8 $R3 - 1
IntOp $9 $8 * 2
System::Alloc $9
Pop $R6
System::Copy /$9 $R6 $R2

; Free the DLL handle
System::Call 'kernel32::FreeLibrary(i R1)i .R8'
Now $R6 contains the string with the exact size without any space at the end. Passing this buffer to another API works just fine. However if I try to get the string out to some NSIS variable the code fails. I am not sure why, but since it does what I was after I don't really care at this point :)

All that remains now is to test this in a non english OS and see if the localized name is picked in the system language from samsrv.dll!

A warm 'thank you' to all who contributed in this thread!

CF

I saw this thread and I thought that this is relevant info (posted by apmolyneux):

I see you're getting the language of USER.EXE, which is good thinking and will work for legacy Windows versions.

However, it won't work properly for users who are running the MUI (Multilingual User Interface) version of Windows 2000 or XP. USER.EXE is always "English (US)" in a MUI environment. I'm told the Enterprise and Ultimate versions of Vista will also be MUI.
I was not looking for user.exe but for samsrv.dll. I installed the french MUI on a virtual PC and changed the system and user language to french. I run the code shown at my last post and I managed to pick the french equivalent of 'Administrator' using FormatMessage (that's Administrateur :) ). The interesting part though is that resource hacker reports that samsrv.dll has a message table with lang 1033 (English) and not 1036 (French)! A binary comparison of the file from the french MUI and that of the original english version of XP showed that they are the same. :tinfoil:

Does anybody understand how this is working? How can the system pick the correct localized strings from a DLL that doesn't seem to contain them?

There is also a folder present in my $WINDIR ($WINDIR\mui\FALLBACK\040C) which contains the file samsrv.dll.mui, which in fact has a message table with the correct lang/strings. How did the API call get redirected to this file? Has anyone noticed something similar?

CF

That is interesting. I guess the English strings are stored in the DLL itself, whereas any other languages are stored in the seperate file.

-Stu


Added a WIKI page for this thread :D

CF