Pin to Unpin SystemDefaultUILanguage

Synopsis

Ok, enough with the pun. Here, we'll tackle a custom challenge to control execution flow. The idea behind this is to instrument environment dependent malware samples so they can be modified on the fly without needing to manually patch the binary. Intel's PIN[1], a dynamic binary instrumentation (DBI) tool, enables us to scale a custom solution for automated sandbox runs.

Back Story

Malware sometimes checks to see if the machine it is running on is its intended target. For example, a specific country.

In this example, the program uses the Windows API call GetSystemDefaultUILanguage to restrict where it will execute. According to MSDN [2] GetSystemDefaultUILanguage is also known as the "install language" on Windows Vista and later. Simply put, it is the language used to display the Windows installation instructions to the user when the OS is being installed. While not perfect, it can narrow the target base of the malware, as this value can't be overwritten by a registry setting or the like (to my knowledge).

One final note before we get started.

Demo code for both the sample and PIN tool are provided.

The demo sample is benign, though you will have to compile it and the pin tool yourself.

Demo Code

First, we need a working program to instrument. Should be a simple copy and paste, but please take note of the following before compiling:

  • GetSystyemDefaultUILanguage[3] return value is a uint16 value representing a language set.
  • The true condition needs to be set to something that is not the install language on the PC.
    • Randomly picked one from this list[4]
  • Compile as a x86 binary

Here is our simple custom program:

#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Kernel32.lib")

#define STRICT
#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    if (GetSystemDefaultUILanguage() == 0x40E)
        MessageBox(NULL, "Hi there! Wait while I do something bad!", "Success", MB_OK);
    else
        MessageBox(NULL, "Hello, mundane world.", "Failure", MB_OK);

    return 0;
}

Code Snippet 1 - Demo App

This should compile succesfully using Visual Studio 2017 compiler from the command line.

CompileTestProgram
Screenshot 1 - Compile Demo App

Run the program and confirm that the "Failure" message is displayed.

ProgramFailureMessage-1
Screenshot 2 - Run Demo App

Now on to creating a PIN tool to trick the program into executing the true branch!

PIN Tool Creation

PIN Dynamic Binary Instrumentation

Not going to go into to much detail around how PIN works (still working out all of the details myself). At its core, it loads a binary into memory and injects addition code into the binary allowing us to trace or modify as we see fit. The additional code that we control is defined via a custom DLL passed to pin.exe as a command line argument. This custom DLL is what we need to build.

See the Appendix for instructions on how to get a PIN project set up in Visual Studio.

Custom PIN Tool

Now let's review how the tool was built; minus all the additional personal heartburn experienced with learning PIN by doing!

Before we start modifying our MyPinTool.cpp template, we need a plan.

First let's look through the PIN documentation[5] for something that would allow us to instrument the if condition to make it true. RTN_Replace[6] looks promising! RTN_Replace can replace a function in the binary with a custom function using the same signature.

Wiring

The entry point of the PIN program is the main function. To wire in the RTN_Replace function we first need to call IMG_AddInstrumentFunction from the main function.

int main(int argc, char *argv[])
{
...
    IMG_AddInstrumentFunction(Image, 0);
...
}

Code Snippet 2 - Pin Tool main function

According to the documentation IMG_AddInstrumentFunction[7] is used to add a function used to instrument at instruction granularity. Put another way, the function passed in as the first argument contains our RTN_Replace code. Let's take a look at our instrumentation function, Image.

VOID Image(IMG img, VOID *v)
{
    RTN sysDefUILangRtn = RTN_FindByName(img, GETSYSTEMDEFAULTUILANGUAGE);
    if (RTN_Valid(sysDefUILangRtn)) {
        RTN_Open(sysDefUILangRtn);		
	
        if (newLangCode > 0)
            RTN_Replace(sysDefUILangRtn, (AFUNPTR)SetSystemDefaultUILanguage);
            
        RTN_Close(sysDefUILangRtn);
    }
}

Code Snippet 3 - Pin Tool instrumentation function

The Image function is where the wiring for RTN_Replace takes place. The code snippet above is a modified template provided by one of the other examples in the PIN download. The lines we are most interested are:

  • RTN sysDefUILangRtn = RTN_FindByName(img, GETSYSTEMDEFAULTUILANGUAGE);
    • RTN_FindByName[8]: Finds a function in the binary by it's name.
    • The second parameter is the name of the function we want to replace.
    • The full code example[9] will show where GETSYSTEMDEFAULTUILANGUAGE is defined.
  • RTN_Replace(sysDefUILangRtn, (AFUNPTR)SetSystemDefaultUILanguage);
    • The first parameter is the routine (or function) to be replaced. Aka the return value from the RTN_FindByName function call.
    • The second parameter is the new function that the original function is to be replaced with. This will contain our custom code to change the return value to a value we control.

Next let's look at the replacement function for GetSystemDefaultUILanguage.

UINT16 __stdcall SetSystemDefaultUILanguage() {
    return (UINT16)newLangCode;
}

Code Snippet 4 - Pin Tool Replacement Function

It's relatively straightforward. It returns a uint16 value assigned to newLangCode.

There is one more piece to cover. How is newLangCode set?

We could hard code this value, but let's take a more dynamic approach. Using KNOB we can add a PIN tool command line parameter. The source below shows how we can add a KNOB. Notice the return type is UINT32. For some perfectly logical reason unknown to me at this time, KNOB/Visual Studio complained when UINT16 was used. We will cast this later, but should not need to worry about losing bytes during the casting process; provided we stick with the allowed values :).

KNOB<UINT32>   KnobLangugeCode(KNOB_MODE_WRITEONCE,  "pintool",
"c", "", "value of the language code to set");

Code Snippet 5 - Pin Tool KNOB

Now we can pass the language hex value via the command line using -c (third parameter) and have newLangCode set in the main function like so:

newLangCode = KnobLangugeCode.Value();

Code Snippet 6 - Get KNOB Value

The complete pin tool code can be found here[10].

PIN Tool Execution

Now we have a working pintool! The arguments we need when calling pin.exe with our custom DLL are:

  • -t: Our custom DLL
  • -c: The hex value of the language key to use. Aka the custom KNOB we added.
  • --: Our custom executable we want to instrument

And finally, pin.exe can be found in the root of the extracted pin folder.

Putting it all together, the command in PowerShell looks like this.

.\pin.exe -t .\source\tools\MyPinTool\Debug\MyPinTool.dll -c 0x40C -- ..\InstallLangProj\code_gui.exe

Code Snippet 7 - Call From PowerShell w/ 0x40C
TestPinToolFailure
Screenshot 3 - Run Pin w/ 0x40C

Notice an incorrect value was given for the success branch in the above example.

Here is it again but with the correct value for the success path!

.\pin.exe -t .\source\tools\MyPinTool\Debug\MyPinTool.dll -c 0x40E -- ..\InstallLangProj\code_gui.exe

Code Snippet 8 - Call From PowerShell w/ 0x40E
TestPinToolSucess
Screenshot 4 - Run Pin w/ 0x40E

Success!!

Conclusion

Today we learned how to adjust program execution flow at run time to achieve a desired result. There is still much to PIN, but hopefully this article will help you on the path to dynamic binary instrumentation! If anyone knows ways to improve what was written here, please reach out to me on twitter @xor_hex.

Appendix

Setting up a C++ Pin Project in Visual Studio 2017

  1. Download and extract the PIN package[11] onto a Windows machine with Visual Studio 2017 installed.
    1. PIN Version 3.5
  2. Drill into %PinExtractedRootFolder%\source\tools\MyPinTool and open MyPinTool.vcxproj in Visual Studio
  3. Visual Studio will ask to upgrade the project. Select the provided defaults.
    1. ProjectUpgradeOptions
  4. Try and build the tool.
    1. If the following message appears: Error C1083 Cannot open include file: 'xed-iclass-enum.h': No such file or directory
      1. Add the following to Configuration Properties -> C/C++ -> Additional Include Directories
        1. ..\..\..\extras\xed-ia32\include\xed
  5. Try building again
    1. If an error around SAFESEH appears
      1. Set Configuration Properties -> Linker -> Image Has Safe Exception Handlers to
        1. No (/SAFESEH:NO)
    2. ImageHasSafeExceptionHandlers
  6. Try building again
    1. If a bunch of errors around LNK2001 appear[12]
      1. Add this to Configuration Properties -> Linker -> Input -> Additional Dependencies
        1. crtbeginS.obj
  7. Build one last time and hopefully it now works!

Use MyPinTool.cpp file as a template to create the custom pin tool.


  1. PIN ↩︎

  2. GetSystemDefaultUILanguage ↩︎

  3. GetSystemDefaultUILanguage ↩︎

  4. Language Identifier Constants and Strings ↩︎

  5. PIN Documentation ↩︎

  6. RTN_Replace ↩︎

  7. IMG_AddInstrumentFunction ↩︎

  8. RTN_FindByName ↩︎

  9. PIN Tool Code ↩︎

  10. PIN Tool Code ↩︎

  11. PIN Download ↩︎

  12. Fixing Intel PIN Visual Studio project files ↩︎

comments powered by Disqus