Welcome to MSDN Blogs Sign in | Join | Help

[Random] How to quickly view a binary's embedded manifest?

 

The "sigcheck" tool from sysinternals is of great help here. Use the "-m" option to view the embedded manifest.

 

 

Sigcheck v1.63 - File version and signature viewer

Copyright (C) 2004-2009 Mark Russinovich

Sysinternals - www.sysinternals.com

 

usage: \\live.sysinternals.com\tools\sigcheck.exe [-a][-h][-i][-e][-n][[-s]|[-v]|[-m]][-q][-r][-u][-c catalog file] <file or directory>

-a Show extended version information

-c Look for signature in the specified catalog file

-e Scan executable images only (regardless of their extension)

-h Show file hashes

-i Show catalog name and image signers

-m Dump manifest

-n Only show file version number

-q Quiet (no banner)

-r Check for certificate revocation

-s Recurse subdirectories

-u Show unsigned files only

-v Csv output

 

 

Here is an example. We'll use the sigcheck tool on notepad.

 

C:\>\\live.sysinternals.com\tools\sigcheck.exe -m c:\Windows\notepad.exe

 

Sigcheck v1.63 - File version and signature viewer

Copyright (C) 2004-2009 Mark Russinovich

Sysinternals - www.sysinternals.com

 

c:\windows\notepad.exe:

        Verified:       Signed

        Signing date:   5:30 AM 11/2/2006

        Publisher:      Microsoft Corporation

        Description:    Notepad

        Product:        Microsoft« Windows« Operating System

        Version:        6.0.6000.16386

        File version:   6.0.6000.16386 (vista_rtm.061101-2205)

        Manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<!-- Copyright (c) Microsoft Corporation -->

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity

    name="Microsoft.Windows.Shell.notepad"

    processorArchitecture="amd64"

    version="5.1.0.0"

    type="win32"/>

<description>Windows Shell</description>

<dependency>

    <dependentAssembly>

        <assemblyIdentity

            type="win32"

            name="Microsoft.Windows.Common-Controls"

            version="6.0.0.0"

            processorArchitecture="*"

            publicKeyToken="6595b64144ccf1df"

            language="*"

        />

    </dependentAssembly>

</dependency>

<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">

    <security>

        <requestedPrivileges>

            <requestedExecutionLevel level="asInvoker" uiAccess="false"/>

        </requestedPrivileges>

    </security>

</trustInfo>

</assembly>

 

 

FYI - You can "net use" into \\live.sysinternals.com\tools and use all the sysinternals tools like process explorer, process monitor etc. Isn't that cool?

 

 

 

Posted by mithuns | 0 Comments

Moving back to MSDN blogs

For various reasons, I'll be moving my blog back to MSDN (http://blogs.msdn.com/mithuns) from http://www.debugtricks.com. I'll be slowly moving content (blog posts, comments etc) back to this blog over the next couple of weeks.

 

  

Posted by mithuns | 0 Comments

New Blog home

 

This blog now has a new home - http://www.debugtricks.com. All contents have been migrated over. Please update your bookmarks and feeds if you haven't already done so.

 

 

Posted by mithuns | 0 Comments

SeImpersonatePrivilege

 

NOTE - I wrote this up more than a year back and for some mysterious reasons forgot to post it! Much of what is written here applies to Vista UAC accounts as well.

QUESTION - Under Windows Server 2003, can a "limited user" impersonate an "administrator"?

ANSWER –  As you are aware, there are four impersonation levels, each of which indicates the degree to which one user can impersonate another – SecurityAnonymous, SecurityIdentification, SecurityImpersonation, and SecurityDelegation. A limited user on Windows Server 2003 (or UAC admin on vista) needs SE_IMPERSONATE_PRIVILEGE enabled in order to impersonate the context of an "administrator" at any level above "SecurityIdentification".

Consider this snippet of code executed in the context of a limited user (non-admin). An admin's token is first obtained and duplicated to create an impersonation token at security level "SecurityImpersonation". The limited user then attempts to impersonate the admin. If impersonation succeeds, then it tries some operation that requires admin privileges (In this case, creating a key under HKLM\Software\Microsoft with write access).  

void wmain ()

{

    HANDLE  hPrivToken ;

    HANDLE  hPrivTokenImperson ;

 

    if ( 0 == LogonUser( L"administrator", L"MyMachine", L"MyPassWord",

                         LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,

                         & hPrivToken ) )

    {

        wprintf( L"Failed to logon admin. Last Error = %d\n",

                 GetLastError() ) ;

        return ;

    }

 

    if ( 0 == DuplicateTokenEx( hPrivToken, TOKEN_QUERY | TOKEN_IMPERSONATE, NULL,

                                SecurityImpersonation, TokenImpersonation,

                                & hPrivTokenImperson ) )

    {

        wprintf( L"Failed to obtain impersonation token. Last Error = %d\n",

                 GetLastError() ) ;

        return ;

    }

 

    if ( 0 == SetThreadToken( NULL, hPrivTokenImperson ) )

    {

        wprintf( L"Failed to impersonate administrator. Last Error = %d\n",

                 GetLastError() ) ;

        return ;       

    }

 

    //

    // Do something that requires admin privileges…………

    //

    if ( ERROR_SUCCESS != RegCreateKeyEx( HKEY_LOCAL_MACHINE,

                                          L"Software\\Microsoft\\RandomKey", 0, NULL,

                                          REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE,

                                          NULL, & hKey, & dwDisposition ) )

    {

        wprintf( L"RegCreateKeyEx FAILED. Last Error = %d\n",

                 GetLastError() ) ;

    }

    else

    {

        wprintf( L"RegCreateKeyEx SUCCEEDED.\n" ) ;

    }

 

    SetThreadToken( NULL, NULL ) ; // revert back

}

Under Windows XP, the limited user is successfully able to impersonate the admin at level SecurityImpersonation. However on Windows 2003 and Vista things are a bit different because of the security changes related to SE_IMPERSONATE_PRIVILEGE that I described above. Although the first call to SetThreadToken() succeeds, the impersonation is being sneakily carried out at level SecurityIdentification. As a result of which your privileged operation (writing to HKLM in this case) fails with an access-denied error. Sweet! On Windows Server 2003 and above, the ability to impersonate at SecurityImpersonation and above is given only to Network Service, Local Service, Local System and Administrators (On Vista you need to be running elevated as well to have SE_IMPERSONATE_PRIVILEGE enabled in the token).

   

   

Posted by mithuns | 0 Comments
Filed under:

DllImportAttribute.PreserveSig vs PreserveSigAttribute

DllImportAttribute.PreserveSig -

  • Mainly used in PInvoke Signatures
  • By default, while using DllImportAttribute to p-invoke, the DllImportAttribute.PreserveSig is set to true.
  • If false: Converts returned HRESULTS from unmanaged signature to managed exceptions if they are not S_OK.
  • If true: Does not convert returned HRESULT to exceptions.

   

PreserveSigAttribute (pseudo custom attribute)–

  • This is mainly used in COM interface definitions in managed code.
  • By default, for COM interface methods, the CLR does HRESULT to exception transformations (opposite behavior to that of pinvoke).
  • If attribute applied: Does not convert returned HRESULT to exceptions.
  • If attribute not applied: Default behavior of HRESULT to exception transformations.

My short-term memory is bad and I tend to forget things as soon as I learn them. So I jotted this down while I was reading Adam Nathan's excellent manuscript on COM interop. Hope you find this tidbit useful as well. 

   

   

Posted by mithuns | 0 Comments
Filed under:

Why does ICorDebug::SetManagedHandler() return E_NOINTERFACE?

 

Since I have no energy to type a long post today (sigh), I'll spare you the gory details on the cause of this error. Here are the main reasons why you would be seeing this error -

  • While expecting to debug a v2.0.xxxx managed app, you have forgotten to implement ICorDebugManagedCallback2 in your managed callback handler. The managed callback handler must implement both ICorDebugManagedCallback and ICorDebugManagedCallback2 in such cases.

 

class ManagedEngine : public ICorDebugManagedCallback, public ICorDebugManagedCallback2

{

}

 

 

  • While implementing QueryInterace(..), you probably forgot to account for ICorDebugManagedCallback2.

    

HRESULT __stdcall ManagedEngine::QueryInterface(const IID & iid, void** ppv)

{   

    if (iid == IID_IUnknown)

        * ppv = static_cast<ICorDebugManagedCallback*>(this); // original implementation

    else if (iid == IID_ICorDebugManagedCallback)

        * ppv = static_cast<ICorDebugManagedCallback*>(this);

    else if (iid == IID_ICorDebugManagedCallback2)

        * ppv = static_cast<ICorDebugManagedCallback2*>(this);

    else

    {

        * ppv = NULL;

        return E_NOINTERFACE;

    }

    reinterpret_cast<IUnknown*>(*ppv)->AddRef();

    return S_OK;

}

 

 

Posted by mithuns | 0 Comments

Pay attention to the calling convention!

 

Ever seen this run-time failure? - "The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention"

Whenever I get sloppy, I tend to write awful code as shown below -

FARPROC pDllFunc = NULL;

HRESULT (* pDllGetReqRuntimeVer) (LPWSTR, LPWSTR, DWORD, DWORD*);

HMODULE hModuleMscoree = LoadLibrary(L"mscoree.dll");

 

if(NULL != hModuleMscoree)

{

    pDllFunc = GetProcAddress(hModuleMscoree, "GetRequestedRuntimeVersion");

    if(NULL != pDllFunc)

    {

        pDllGetReqRuntimeVer = (HRESULT(*)(LPWSTR, LPWSTR, DWORD, DWORD*))pDllFunc;

        hr = pDllGetReqRuntimeVer(

                    thrParam->cmdLineSw.pwszImage,

                    wszDebuggeeVer,

                    sizeof(wszDebuggeeVer)/sizeof(wszDebuggeeVer[0]),

                    & dws);

         :

        :

        :

    }

}

When sloppy, I forget to use the correct calling convention while declaring function pointers. Then I run into the above run-time error while calling into various functions exported by dlls (mscoree!GetRequestedRuntimeVersion in above example). Most Win32 APIs are declared as __stdcall which expects the "callee" to cleanup the stack. I compile with /Gd (uses __cdecl as default convention). Because I forgot of mark the function pointer with __stdcall, the run-time is now expecting the "caller" to cleanup the stack. Fortunately I'm bailed out by the above run time check. To correct this the, function pointer must be declared and used as follows -

FARPROC pDllFunc = NULL;

HRESULT (__stdcall * pDllGetReqRuntimeVer) (LPWSTR, LPWSTR, DWORD, DWORD*);

HMODULE hModuleMscoree = LoadLibrary(L"mscoree.dll");

 

if(NULL != hModuleMscoree)

{

    pDllFunc = GetProcAddress(hModuleMscoree, "GetRequestedRuntimeVersion");

    if(NULL != pDllFunc)

    {

        pDllGetReqRuntimeVer = (HRESULT(__stdcall *)(LPWSTR, LPWSTR, DWORD, DWORD*))pDllFunc;

        hr = pDllGetReqRuntimeVer(

                    thrParam->cmdLineSw.pwszImage,

                    wszDebuggeeVer,

                    sizeof(wszDebuggeeVer)/sizeof(wszDebuggeeVer[0]),

                    & dws);

        :

        :

        :

    }

}

 

Given below are the native disassemblies (function prolog and epilog) when you use __cdecl and __stdcall respectively. As you can see, for the function marked as __cdecl, the "caller" is cleaning up the stack. While the "callee" does the needful when __stdcall is used.

When using __cdecl 

00412DB1  mov         esi,esp

00412DB3  lea         eax,[dws]

00412DB9  push        eax 

00412DBA  push        80h 

00412DBF  lea         ecx,[ebp-168h]

00412DC5  push        ecx 

00412DC6  mov         edx,dword ptr [ebp-48h]

00412DC9  mov         eax,dword ptr [edx+10h]

00412DCC  push        eax 

00412DCD  call        dword ptr [ebp-61Ch]

00412DD3  add         esp,10h  ß----------- stack is cleaned by caller!

00412DD6  cmp         esi,esp

00412DD8  call        @ILT+840(__RTC_CheckEsp) (41134Dh) ß------ The run time check

00412DDD  mov         dword ptr [ebp-18h],eax

 

When using __stdcall 

00412DB1  mov         esi,esp

00412DB3  lea         eax,[dws]

00412DB9  push        eax 

00412DBA  push        80h 

00412DBF  lea         ecx,[ebp-168h]

00412DC5  push        ecx 

00412DC6  mov         edx,dword ptr [ebp-48h]

00412DC9  mov         eax,dword ptr [edx+10h]

00412DCC  push        eax 

00412DCD  call        dword ptr [ebp-61Ch]

00412DD3  cmp         esi,esp ß--------- At this stage callee has already cleaned up stack

00412DD5  call        @ILT+840(__RTC_CheckEsp) (41134Dh) ß------ The run time check

00412DDA  mov         dword ptr [ebp-18h],eax

 

I should be paying more attention to the calling conventions and instead of relying on this run-time check. But it is nice to have this safety net.

 

Posted by mithuns | 0 Comments
Filed under:

Quote of the day!

When I score goals I'm great, when I don't I'm fat.

- Ronaldo

Posted by mithuns | 0 Comments
Filed under:

MDBG ver 2.1 released


Today we released the latest version of the managed debugger - MDbg. For the past few days, we've been scampering around to get this sample built, tested and released in time for Thanksgiving. Finally here we are - MDBG ver 2.1.

Among other things, the new sample includes -

  • Tons of bug fixes (duh).
  • An extension for IronPython. This is the stuff that I am most excited about (Please note that this python extension does not build by default. You'd need to download IronPython bins and add references. But once you do that....... :-) ).
  • A PDB to XML (and vice-versa) conversion tool.
  • Managed wrappers for native debugging APIs (More on Mike Stall's blog). This means you can debug native processes from a debugger written purely in managed code!

Please try out the new sample and do try to send us feedback on the MDbg forums.

Have a nice Thanksgiving everyone!

 

Posted by mithuns | 2 Comments

User Account Control and Split Tokens.

 

QUICK QUESTION - I am a member of the local administrators group on a Windows Vista Machine. How can I check whether a process launched under my context is running under UAC or running elevated?

QUICK ANSWER -

  1. Launch the debugger in elevated mode.
  2. Attach to process and fix symbols.
  3. Dump the process token and check if the group sid S-1-5-32-544 (Builtin\Administrators) has 'deny' attributes.
  4. If yes, then the process is running under UAC. Else it is running elevated.

0:001> !token -n
Thread is not impersonating. Using process token...
TS Session ID: 0x2
User: S-1-5-21-397955417-626881126-188441444-3417686 (User: DOMAIN\mithuns)
Groups:
 00 S-1-5-21-397955417-626881126-188441444-513 (Group: DOMAIN\Domain Users)
    Attributes - Mandatory Default Enabled
 01 S-1-1-0 (Well Known Group: localhost\Everyone)
    Attributes - Mandatory Default Enabled
 02 S-1-5-21-2509036279-1584907351-1836241972-1001 (Alias: MITHUNS7\Debugger Users)
    Attributes - Mandatory Default Enabled
 03 S-1-5-32-544 (Alias: BUILTIN\Administrators)
    Attributes - DenyOnly
 04 S-1-5-32-545 (Alias: BUILTIN\Users)
    Attributes - Mandatory Default Enabled

:

:

:


QUICK QUESTION – When people talk about UAC, I often hear the term “split token”. What exactly is it?

QUICK ANSWER - When a process runs under UAC, some privileges are completely stripped out of the process token. Which also means that you cannot call AdjustTokenPrivileges() to enable them. See below -

 

Token for elevated process

Privs:
 00 0x000000005 SeIncreaseQuotaPrivilege          Attributes -
 01 0x000000008 SeSecurityPrivilege               Attributes -
 02 0x000000009 SeTakeOwnershipPrivilege          Attributes -
 03 0x00000000a SeLoadDriverPrivilege             Attributes -
 04 0x00000000b SeSystemProfilePrivilege          Attributes -
 05 0x00000000c SeSystemtimePrivilege             Attributes -
 06 0x00000000d SeProfileSingleProcessPrivilege   Attributes -
 07 0x00000000e SeIncreaseBasePriorityPrivilege   Attributes -
 08 0x00000000f SeCreatePagefilePrivilege         Attributes -
 09 0x000000011 SeBackupPrivilege                 Attributes -
 10 0x000000012 SeRestorePrivilege                Attributes -
 11 0x000000013 SeShutdownPrivilege               Attributes -
 12 0x000000014 SeDebugPrivilege                  Attributes -
 13 0x000000016 SeSystemEnvironmentPrivilege      Attributes -
 14 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
 15 0x000000018 SeRemoteShutdownPrivilege         Attributes -
 16 0x000000019 SeUndockPrivilege                 Attributes -
 17 0x00000001c SeManageVolumePrivilege           Attributes -
 18 0x00000001d SeImpersonatePrivilege            Attributes - Enabled Default
 19 0x00000001e SeCreateGlobalPrivilege           Attributes - Enabled Default
 20 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
 21 0x000000022 SeTimeZonePrivilege               Attributes -
 22 0x000000023 SeCreateSymbolicLinkPrivilege     Attributes -


"SPLIT" token for process under UAC

Privs:
 00 0x000000013 SeShutdownPrivilege               Attributes -
 01 0x000000017 SeChangeNotifyPrivilege           Attributes - Enabled Default
 02 0x000000019 SeUndockPrivilege                 Attributes -
 03 0x000000021 SeIncreaseWorkingSetPrivilege     Attributes -
 04 0x000000022 SeTimeZonePrivilege               Attributes -


All the highlighted privileges are missing in the UAC split token (Note that 'SeDebugPrivilege' is absent too. Hence the need to launch a debugger elevated).

Related Post - http://blogs.msdn.com/greggm/archive/2006/03/30/565303.aspx

 

 

Data Breakpoint Oddities

 

 

NOTE: This is going to be a very long, verbose, bizarre and wierd post. So try not to fall asleep. Before I start, a big thanks must go to my colleague Seva Titov for pointing this issue out to me.

 

Ok…………Here we go…………….

 

Consider this sorry piece of code that I cooked up -

#include <windows.h>

#include <iostream>

 

#define BUF_SIZE 256

 

void wmain ( int argc, wchar_t * argv[] )

{

    WCHAR szBuf[BUF_SIZE] ;

    HANDLE hFile = NULL ;

    DWORD dwBytesRead = 0 ;

 

    // zero out the buffer

    memset( (void*) szBuf, 0x30, BUF_SIZE ) ;

 

    // open existing file....

    hFile = CreateFile( TEXT("dummy.txt"), GENERIC_READ, FILE_SHARE_READ,

                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) ;

    if ( INVALID_HANDLE_VALUE == hFile )

    {

        wprintf( L"CreateFileW() failed with %d\n",

                 GetLastError() ) ;

        return ;

    }

   

    // read the first 100 bytes from the file ......

    if ( 0 == ReadFile( hFile, (LPVOID) szBuf, 256, & dwBytesRead, NULL ) )

    {

        wprintf( L"ReadFile() failed with %d\n",

                 GetLastError() ) ;

    }

   

    CloseHandle( hFile ) ;

}

Yes Yes. The code is overly simplistic, buggy and devoid of error-checking etc etc. But it will easily bring my point across………Read On………………

 

Let us see what is this code is doing. A buffer (“szBuf”) is being filled up with the text from the file “dummy.txt”. Let us try and use data breakpoints to break-in as soon as this buffer is written to.  Assume that we have already put the executable under the debugger, fixed symbols, broken-in on the frame for Wmain() and already found the address of the buffer “szBuf”.

 

0:000> dv /v

0012fee4            argc = 1

0012fee8            argv = 0x00321660

0012fcc0     dwBytesRead = 0x2100210

0012fccc           hFile = 0x02100210

0012fcd8           szBuf = unsigned short [256]

 

 

Now we will set the data breakpoint to trigger on any write-access to 0x0012fcd8 (i.e. the buffer).  Theoretically the data-breakpoint should be triggered twice – Once by memset() and then by Kernel32!ReadFile() [This however will not be the case as we shall observe].

 

0:000> ba w4 0012fcd8

 

Now let us also set a breakpoint on Kernel32!CloseHandle() which is called from the last line of code before we exit Wmain().  This will give us a chance to break-in and examine the buffer before exiting Wmain(), irrespective of whether or not the data-breakpoint triggers.

 

0:000> bm kernel32!closehandle

  2: 7c809b77 @!"kernel32!CloseHandle"

 

At this point we have armed all the necessary breakpoints and now we will let the program run

 

0:000> g

 

Now the buffer will be zeroed out by the memset() function. This constitutes a write-access and hence our data-breakpoint will immediately trigger. Sure enough…….

 

 

Breakpoint 1 hit

eax=30303030 ebx=7ffdb000 ecx=0000003f edx=00000000 esi=0006fa08 edi=0012fcdc

eip=00411d55 esp=0012fbdc ebp=0012fedc iopl=0         nv up ei pl nz na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202

DataBreakpointOddity!memset+0x45:

00411d55 f3ab             rep     stosd                  es:0012fcdc=cccccccc

 

 

At this point we will note that memset() writes to the buffer in user mode. No kernel mode hanky-panky involved here. Let us dump out the buffer at this point and ensure that it has been indeed zeroed out –

 

 

0:000> db 0012fcd8

0012fcd8   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fce8   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fcf8   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fd08   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fd18   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fd28   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fd38   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

0012fd48   30 30 30 30 30 30 30 30-30 30 30 30 30 30 30 30   0000000000000000

 

 

Yup. Now we will jump out of memset() and back into Wmain().

 

0:000> gu

 

Again we will let the program continue its execution. We will be expecting it to break-in inside kernel32!ReadFile() when the buffer is written to.

           

            0:000> g

 

But you will observe that program execution continues straight onto kernel32!CloseHandle() where we had set a breakpoint earlier. It did not break-in at kernel32!ReadFile().

 

 

Breakpoint 2 hit

eax=000007e8 ebx=7ffdb000 ecx=7c801898 edx=7c90eb94 esi=0012fbf0 edi=0012fedc

eip=7c809b77 esp=0012fbe8 ebp=0012fedc iopl=0         nv up ei pl nz na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

kernel32!CloseHandle:

7c809b77 8bff             mov     edi,edi

 

 

Hmmm, maybe the buffer has not been written into. Let us examine this possibility too. So we will move back to the wmain() frame and dump out the buffer -

 

0:000> .frame 1

01 0012fedc 0041378f DataBreakpointOddity!wmain+0x146

 

0:000> db 0012fcd8

0012fcd8  57 68 61 74 2e 74 69 6d-65 2e 69 73 2e 69 74 3f  What.time.is.it?

0012fce8  2e 54 69 6d 65 2e 74 6f-2e 64 75 6d 70 2e 79 6f  .Time.to.dump.yo

0012fcf8  75 72 2e 67 6f 6f 67 6c-65 2e 73 68 61 72 65 73  ur.google.shares

0012fd08  2e 57 68 61 74 2e 74 69-6d 65 2e 69 73 2e 69 74  .What.time.is.it

0012fd18  3f 2e 54 69 6d 65 2e 74-6f 2e 64 75 6d 70 2e 79  ?.Time.to.dump.y

0012fd28  6f 75 72 2e 67 6f 6f 67-6c 65 2e 73 68 61 72 65  our.google.share

0012fd38  73 2e 57 68 61 74 2e 74-69 6d 65 2e 69 73 2e 69  s.What.time.is.i

0012fd48  74 3f 2e 54 69 6d 65 2e-74 6f 2e 64 75 6d 70 2e  t?.Time.to.dump.

 

 

Now clearly this buffer (which had been zeroed earlier) has been filled up. The data breakpoint was set to trigger on a write-access to the location 0x0012fcd8. However no breakpoint exception was generated even though this location was written to. Why did this happen? Is this a bug in the debugger?

 

 

Well….actually No. To understand why this happens, you’ll have to take a small trip to microprocessor land. For the sake of simplicity, let us just limit ourselves to 32 bit Intel Processors in this discussion.

 

Data breakpoints are implemented with the hardware support provided by your microprocessor. 32 bit Intel processors provide eight registers for exactly this purpose – Debug Registers DR0 – DR7. To give you a very brief overview, here is how it all works -

 

  • Each of the four registers DR0-DR3, hold 32 bit linear breakpoint addresses. This is the reason why you can only set a maximum of four data breakpoints on x86 machines (I do not know if this holds true for X64 and IA64 machines as well).
  • Registers DR4, DR5 are reserved registers.
  • The DR6 register is known as the Debug Status Register. It reports the conditions under which the breakpoint was triggered - Was the associated breakpoint condition (read/write/execute) met? Was the Debug Exception generated by Single-Stepping execution mode? Etc etc.
  • The DR7 is the big daddy who pulls all the strings and it is called the Debug Control register. It allows you to enable/disable any of the four breakpoints and to set the conditions under which the breakpoints trigger (read/write/execute).

 

As soon as you set the data-breakpoint, if you check your set of debug registers.

 

0:000> r dr0

dr0=0012fcd8

 

0:000> r dr7

dr7=000d0501

 

The DR0 register contains the 32 bit linear address of the buffer – 0x0012fcd8. Bit 0 of DR7 tells us that the breakpoint has been armed. Bit 16 of DR7 has been set, telling us that the data-breakpoint will only be triggered on a write access. Bits 18,19 indicate the size of the location to be monitored for access – in this case it is 4 bytes.

 

AS soon as any instruction makes a write-access to the 32 bit address being monitored (0x0012fcd8), the processor generates a debug exception and transfers control to the debugger’s debug loop. The debugger then checks the DR0-DR3, DR6 and DR7 registers to evaluate whether the breakpoint condition is met. If yes, then it breaks-in into the process and transfers control to the user; else it resumes execution.

 

 

However there is a small caveat- The processor cannot detect any accesses to a location if the physical address is used instead of the corresponding 32 bit virtual address. This means that if the linear address (0x0012fcd8) is translated to a physical address and accessed from kernel mode, then it will go undetected by the processor and the data-breakpoint will not trigger. Which also means the data-breakpoints will not trigger if there is DMA access. Coming to the point, APIs like ReadFile() and ReadProcessMemory() directly write to the buffer in kernel mode. Which is the reason why we our data-breakpoint did not trigger.

 

Errr……So what was the point of this post………………..?

 

“A user-mode data breakpoint will not be triggered if the location being watched is accessed from the kernel-mode”.

 

 

 

 

Easiest and laziest way to view a process's command line

 

QUESTION - What is the easiest way to view an arbitrary process's command line?

SHORT ANSWER - Fire up the debugger (ntsd/windbg), attach to the process, fix symbols and dump out the process's environment Block (PEB).

LONG ANSWER - For 32-bit processes running on x86 systems, the PEB is "generally" located at 0x7ffd6000. At an offset of 0x10 bytes from the start of the PEB, you will find the pointer to the RTL_USER_PROCESS_PARAMETERS structure. The pointer to the unicode string containing the command line is located at an offset of 0x040 from the start of the RTL_USER_PROCESS_PARAMETERS structure.

Suppose you start solitaire from the cmd shell with a command line option "/xyz". [The "/xyz" option really doesn't do anything; I am using this just as an example]

C:\> sol /xyz

Now attach a debugger to this running process and view the command line via the PEB.

0:001> !peb
PEB at 7ffd6000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            Yes
    ImageBaseAddress:         01000000
    Ldr                       001a1e90
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 001a1f28 . 001a2768
    Ldr.InLoadOrderModuleList:           001a1ec0 . 001a2bc0
    Ldr.InMemoryOrderModuleList:         001a1ec8 . 001a2bc8
            Base TimeStamp                     Module
         1000000 3b7d8480 Aug 17 13:54:24 2001 C:\WINDOWS\system32\sol.exe
        7c900000 411096b4 Aug 04 00:56:36 2004 C:\WINDOWS\system32\ntdll.dll
        7c800000 411096b4 Aug 04 00:56:36 2004 C:\WINDOWS\system32\kernel32.dll
        77c10000 41109752 Aug 04 00:59:14 2004 C:\WINDOWS\system32\msvcrt.dll
        77dd0000 411096a7 Aug 04 00:56:23 2004 C:\WINDOWS\system32\ADVAPI32.dll
        77e70000 43740e6d Nov 10 19:22:21 2005 C:\WINDOWS\system32\RPCRT4.dll
        77f10000 43b34feb Dec 28 18:54:35 2005 C:\WINDOWS\system32\GDI32.dll
        77d40000 42260159 Mar 02 10:09:29 2005 C:\WINDOWS\system32\USER32.dll
        6fc10000 3b7dfe43 Aug 17 22:33:55 2001 C:\WINDOWS\system32\CARDS.dll
        7c9c0000 433370f6 Sep 22 20:05:26 2005 C:\WINDOWS\system32\SHELL32.dll
        77f60000 43c2a517 Jan 09 10:01:59 2006 C:\WINDOWS\system32\SHLWAPI.dll
        5cb70000 411096ba Aug 04 00:56:42 2004 C:\WINDOWS\system32\ShimEng.dll
        76b40000 411096d6 Aug 04 00:57:10 2004 C:\WINDOWS\system32\WINMM.dll
        774e0000 42e5be93 Jul 25 21:39:47 2005 C:\WINDOWS\system32\ole32.dll
        77120000 411096f3 Aug 04 00:57:39 2004 C:\WINDOWS\system32\OLEAUT32.dll
        77be0000 411096cf Aug 04 00:57:03 2004 C:\WINDOWS\system32\MSACM32.dll
        77c00000 411096b7 Aug 04 00:56:39 2004 C:\WINDOWS\system32\VERSION.dll
        769c0000 411096b9 Aug 04 00:56:41 2004 C:\WINDOWS\system32\USERENV.dll
        5ad70000 411096bb Aug 04 00:56:43 2004 C:\WINDOWS\system32\UxTheme.dll
    SubSystemData:     00000000
    ProcessHeap:       000a0000
    ProcessParameters: 00020000
    WindowTitle:  'sol /xyz'
    ImageFile:    'C:\WINDOWS\system32\sol.exe'
    CommandLine:  'sol /xyz'
    DllPath:      'C:\WINDOWS\system32;'
    Environment:  00010000
        ALLUSERSPROFILE=C:\Documents and Settings\All Users
        CLIENTNAME=Console
        CommonProgramFiles=C:\Program Files\Common Files
        ComSpec=C:\WINDOWS\system32\cmd.exe
        FP_NO_HOST_CHECK=NO
        HOMEDRIVE=C:
        NUMBER_OF_PROCESSORS=1
        OS=Windows_NT
        PROCESSOR_ARCHITECTURE=x86
        PROCESSOR_IDENTIFIER=x86 Family 6 Model 13 Stepping 8, GenuineIntel
        PROCESSOR_LEVEL=6
        PROCESSOR_REVISION=0d08
        ProgramFiles=C:\Program Files
        PROMPT=$P$G
        SESSIONNAME=Console
        SystemDrive=C:
        SystemRoot=C:\WINDOWS

[The output has been edited for brevity]

 

You can view the internals of the PEB and RTL_USER_PROCESS_PARAMETERS blocks for any process using the following ntsd commands -

0:001> dt nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 SpareBool        : UChar
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
   +0x014 SubSystemData    : Ptr32 Void
   +0x018 ProcessHeap      : Ptr32 Void
   +0x01c FastPebLock      : Ptr32 _RTL_CRITICAL_SECTION
   +0x020 FastPebLockRoutine : Ptr32 Void
   +0x024 FastPebUnlockRoutine : Ptr32 Void
   +0x028 EnvironmentUpdateCount : Uint4B
   +0x02c KernelCallbackTable : Ptr32 Void
   +0x030 SystemReserved   : [1] Uint4B
   +0x034 AtlThunkSListPtr32 : Uint4B
   +0x038 FreeList         : Ptr32 _PEB_FREE_BLOCK
   +0x03c TlsExpansionCounter : Uint4B
   +0x040 TlsBitmap        : Ptr32 Void
   +0x044 TlsBitmapBits    : [2] Uint4B
   +0x04c ReadOnlySharedMemoryBase : Ptr32 Void
   +0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
   +0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
   +0x058 AnsiCodePageData : Ptr32 Void
   +0x05c OemCodePageData  : Ptr32 Void
   +0x060 UnicodeCaseTableData : Ptr32 Void
   +0x064 NumberOfProcessors : Uint4B
   +0x068 NtGlobalFlag     : Uint4B
   +0x070 CriticalSectionTimeout : _LARGE_INTEGER
   +0x078 HeapSegmentReserve : Uint4B
   +0x07c HeapSegmentCommit : Uint4B
   +0x080 HeapDeCommitTotalFreeThreshold : Uint4B
   +0x084 HeapDeCommitFreeBlockThreshold : Uint4B
   +0x088 NumberOfHeaps    : Uint4B
   +0x08c MaximumNumberOfHeaps : Uint4B
   +0x090 ProcessHeaps     : Ptr32 Ptr32 Void
   +0x094 GdiSharedHandleTable : Ptr32 Void
   +0x098 ProcessStarterHelper : Ptr32 Void
   +0x09c GdiDCAttributeList : Uint4B
   +0x0a0 LoaderLock       : Ptr32 Void
   +0x0a4 OSMajorVersion   : Uint4B
   +0x0a8 OSMinorVersion   : Uint4B
   +0x0ac OSBuildNumber    : Uint2B
   +0x0ae OSCSDVersion     : Uint2B
   +0x0b0 OSPlatformId     : Uint4B
   +0x0b4 ImageSubsystem   : Uint4B
   +0x0b8 ImageSubsystemMajorVersion : Uint4B
   +0x0bc ImageSubsystemMinorVersion : Uint4B
   +0x0c0 ImageProcessAffinityMask : Uint4B
   +0x0c4 GdiHandleBuffer  : [34] Uint4B
   +0x14c PostProcessInitRoutine : Ptr32
   +0x150 TlsExpansionBitmap : Ptr32 Void
   +0x154 TlsExpansionBitmapBits : [32] Uint4B
   +0x1d4 SessionId        : Uint4B
   +0x1d8 AppCompatFlags   : _ULARGE_INTEGER
   +0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
   +0x1e8 pShimData        : Ptr32 Void
   +0x1ec AppCompatInfo    : Ptr32 Void
   +0x1f0 CSDVersion       : _UNICODE_STRING
   +0x1f8 ActivationContextData : Ptr32 Void
   +0x1fc ProcessAssemblyStorageMap : Ptr32 Void
   +0x200 SystemDefaultActivationContextData : Ptr32 Void
   +0x204 SystemAssemblyStorageMap : Ptr32 Void
   +0x208 MinimumStackCommit : Uint4B


0:001> dt nt!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : Uint4B
   +0x004 Length           : Uint4B
   +0x008 Flags            : Uint4B
   +0x00c DebugFlags       : Uint4B
   +0x010 ConsoleHandle    : Ptr32 Void
   +0x014 ConsoleFlags     : Uint4B
   +0x018 StandardInput    : Ptr32 Void
   +0x01c StandardOutput   : Ptr32 Void
   +0x020 StandardError    : Ptr32 Void
   +0x024 CurrentDirectory : _CURDIR
   +0x030 DllPath          : _UNICODE_STRING
   +0x038 ImagePathName    : _UNICODE_STRING
   +0x040 CommandLine      : _UNICODE_STRING
   +0x048 Environment      : Ptr32 Void
   +0x04c StartingX        : Uint4B
   +0x050 StartingY        : Uint4B
   +0x054 CountX           : Uint4B
   +0x058 CountY           : Uint4B
   +0x05c CountCharsX      : Uint4B
   +0x060 CountCharsY      : Uint4B
   +0x064 FillAttribute    : Uint4B
   +0x068 WindowFlags      : Uint4B
   +0x06c ShowWindowFlags  : Uint4B
   +0x070 WindowTitle      : _UNICODE_STRING
   +0x078 DesktopInfo      : _UNICODE_STRING
   +0x080 ShellInfo        : _UNICODE_STRING
   +0x088 RuntimeData      : _UNICODE_STRING
   +0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR

 

But to do all of the above, you would need the symbols for ntdll.dll. Export symbols will not help. Public symbols will do. The public symbols can be downloaded from Microsoft's Public Symbol Server - http://msdl.microsoft.com/download/symbols. This public server can only be accessed via a debugger and is not browsable. You would just need to fix the SYMPATH as follows -

.sympath SRV*<DownstreamStore>*http://msdl.microsoft.com/download/symbols

Substitute your downstream symbol folder location for <DownstreamStore>. If you want to store the symbols in "C:\Temp" then set the SYMPATH as follows -

.sympath SRV*C:\Temp*http://msdl.microsoft.com/download/symbols

 

 

Posted by mithuns | 5 Comments
 
Page view tracker