blabberer
December 29th, 2012, 14:06
Mint77 posted in the forum asking how to increase the run dialog
(Start => Run / Winkey + R) MRU (Most Recently Used ) contents
It is aptly named MRU which implies it holds only a few entries and not all the entries from times immemorial
It seems he had earlier been told by someone at MSDN that the max limit was 26 entries and the older entries drop out on FIFO (First In First Out) basis
So if the MRU list is full the and we have a new contender the oldest entry drops out and the latest entry is dropped in
Logically right and practically right
But logic and practicality never works when it comes to reversing
He posted that he was told there was no way to increase the limit and was looking for some ideas to realize his fantasy
So I tossed in a some time and took a look.
A few minutes in procmon / windbg and the 27th entry is overloaded into RunMruList
http://www.woodmann.com/forum/attachment.php?attachmentid=2697
WARNING:
do not mimic the methods shown. Without understanding the implications of various locks held like spinlocks , critical sections, , interlocked cmpexchg in critical system processes it may crash and may cause BSOD
RUN Dialog is handled by explorer.exe
Lets fire procmon and capture pertinent events in explorer.exe
(be sure to configure symbols in procmon prior to capturing events so that stack is properly displayed you can point it to your _NT_SYMBOL_PATH cache )
a default procmon captures too much events
procmon monitors file events , registry events network event and profiling events
apart from process / thread events
an enormous amount of spew ensues as a result
we need to set a filter
we know we are interested in explore.exe events only
we also are interested in MRU so set a filter for MRU in path
click filter menu and select filter submenu (ctrl + L hotkey)
process monitor filter will popup
1st time
2nd time
in the 1st drop down select
process
path
in the 2nd drop down select
is
contains
in the 3rd drop down type in
explorer.exe
MRU
in the 4th drop down select
include
include
click add after each time if all went well the pane should look like this
http://www.woodmann.com/forum/attachment.php?attachmentid=2698
click apply and let procmon roll
goto start => hit run or do Winkey+R and execute a program
preferably one which is not available in MRU
or clear the MRU via taskbar and start menu properties and type in a new program
so that we have a complete log
a fresh run will see the following entries notice reggsetvalue that is highlighted
we are interested in its stack which is show below
http://www.woodmann.com/forum/attachment.php?attachmentid=2699
so AddMruStringW is where something interesting might be available lets close procmon for now and run a debugger to check th AddMruStringW function in explorer.exe
-pn attaches to a running process by specifying the process name
-c Specifies the initial debugger command to run at start-up.
In the initial command we are setting breakpoint on the api we saw in procmon and ask windbg to continue its execution
We now go to start=>run and enter any program to execute so that we shall break in windbg and we can examine the state
lets check the stack
it looks the same as in procmon
lets check the arguments to the function
since this is x86 32bit three arguments are shown in stack if we want more we need to play with esp register
note: be aware in x64 the firstthree four args are passed in registers
the first three args as per kb
what is the first argument
0:017> dd poi(esp+4) l4
00139220 00000002 0000001a 7c80aa36 00000828
you should recognize the 0x1a
0:017> .formats 1a
Evaluate expression:
Hex: 0000001a
Decimal: 26 <--------------------
0:017> du poi(esp+8)
039bee20 "cmd\1" <------------- yes this is our input
so since this is an argument check who pushed it
ub return address in stack viz
what is in esi
0:017> r esi
esi=00139220
where did esi get that value
ub retn_address length > default on trial and error till we locate esi
or use ida
or ollydbg register highlighting functionality
we see esi got the value from the return value of a Function
so we would need to check this function
so either InterlockedExchange return
0:016> ln poi(SHELL32!_imp__CreateMRUListW)
(7ca2b2b9) SHELL32!CreateMRUListW | (7ca2b320) SHELL32!CFSFolder::_CreatePerClassDefExtIcon
Exact matches:
SHELL32!CreateMRUListW (<no parameter info>
return Is passed into esi
Remember the warnings about lock synchronization
This structure is protected by lock is what we can understand
Something like
I leave the CreateMruList() analysis to readers
Hint it makes an indirect call ïŠ and goes on to set a reg key
Lets see what this global contains
there lies our esi to AddStringMruW
lets memory modify it
lets detach windbg from explorer and keep adding strings to runmru
and check was it successful did we get our 27th string in runmru ?
yes we got it
warning again
please do not mimic this in system critical process
also this is not a full reverse it doesn�t mean that you can set the value to 0xffffffff
and have the full liberty of 2^32 � 1 strings in runmru
think before hand
what may happen to alloc memory
how the buffers are allocated why is it a protected global
can increasing beyond 26 corrupt further structures
where did the RunMRULIST get its next character from apart from �abcdef�..xyz�
who else uses this set ba r4 breaks
happy runmruing
(Start => Run / Winkey + R) MRU (Most Recently Used ) contents
It is aptly named MRU which implies it holds only a few entries and not all the entries from times immemorial
It seems he had earlier been told by someone at MSDN that the max limit was 26 entries and the older entries drop out on FIFO (First In First Out) basis
So if the MRU list is full the and we have a new contender the oldest entry drops out and the latest entry is dropped in
Logically right and practically right
But logic and practicality never works when it comes to reversing
He posted that he was told there was no way to increase the limit and was looking for some ideas to realize his fantasy
So I tossed in a some time and took a look.
A few minutes in procmon / windbg and the 27th entry is overloaded into RunMruList
http://www.woodmann.com/forum/attachment.php?attachmentid=2697
WARNING:
do not mimic the methods shown. Without understanding the implications of various locks held like spinlocks , critical sections, , interlocked cmpexchg in critical system processes it may crash and may cause BSOD
RUN Dialog is handled by explorer.exe
Lets fire procmon and capture pertinent events in explorer.exe
(be sure to configure symbols in procmon prior to capturing events so that stack is properly displayed you can point it to your _NT_SYMBOL_PATH cache )
a default procmon captures too much events
procmon monitors file events , registry events network event and profiling events
apart from process / thread events
an enormous amount of spew ensues as a result
we need to set a filter
we know we are interested in explore.exe events only
we also are interested in MRU so set a filter for MRU in path
click filter menu and select filter submenu (ctrl + L hotkey)
process monitor filter will popup
1st time
2nd time
in the 1st drop down select
process
path
in the 2nd drop down select
is
contains
in the 3rd drop down type in
explorer.exe
MRU
in the 4th drop down select
include
include
click add after each time if all went well the pane should look like this
http://www.woodmann.com/forum/attachment.php?attachmentid=2698
click apply and let procmon roll
goto start => hit run or do Winkey+R and execute a program
preferably one which is not available in MRU
or clear the MRU via taskbar and start menu properties and type in a new program
so that we have a complete log
a fresh run will see the following entries notice reggsetvalue that is highlighted
we are interested in its stack which is show below
http://www.woodmann.com/forum/attachment.php?attachmentid=2699
so AddMruStringW is where something interesting might be available lets close procmon for now and run a debugger to check th AddMruStringW function in explorer.exe
Code:
windbg -pn explorer.exe -c "bp Shell32!AddMruStringW;g"
-pn attaches to a running process by specifying the process name
-c Specifies the initial debugger command to run at start-up.
In the initial command we are setting breakpoint on the api we saw in procmon and ask windbg to continue its execution
We now go to start=>run and enter any program to execute so that we shall break in windbg and we can examine the state
Code:
Breakpoint 0 hit
eax=0274ee20 ebx=00000000 ecx=7c9c95ba edx=000000fb esi=01c73ff8 edi=00000009 eip=7ca2b28d esp=0274ebf8 ebp=0274f0b0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
SHELL32!AddMRUStringW:
7ca2b28d 8bff mov edi,edi
lets check the stack
Code:
0:017> kb
ChildEBP RetAddr Args to Child
039bebf4 7ca408e2 00139220 039bee20 00150202 SHELL32!AddMRUStringW
039bf0b0 7ca4073f 039bf128 7ca40091 039bf0ec SHELL32!CRunDlg::OKPushed+0x1ce
039bf0c0 7e418734 00150202 00000111 00000001 SHELL32!RunDlgProc+0x121
039bf0ec 7e423ce4 7ca40091 00150202 00000111 USER32!InternalCallWinProc+0x28
039bf158 7e423b30 0009e158 7ca40091 00150202 USER32!UserCallDlgProcCheckWow+0x146
039bf1a0 7e423d5c 00000000 00000111 00000001 USER32!DefDlgProcWorker+0xa8
039bf1bc 7e418734 00150202 00000111 00000001 USER32!DefDlgProcW+0x22
039bf1e8 7e418816 7e423d3a 00150202 00000111 USER32!InternalCallWinProc+0x28
039bf250 7e42927b 0009e158 7e423d3a 00150202 USER32!UserCallWinProcCheckWow+0x150
039bf28c 7e4292e3 005aa750 00566d80 00000001 USER32!SendMessageWorker+0x4a5
039bf2ac 7e431cde 00150202 00000111 00000001 USER32!SendMessageW+0x7f
039bf2dc 7e42763c 00150202 0057edc0 00170222 USER32!IsDialogMessageW+0x41f
039bf318 7e4249c4 00150202 00170222 00000001 USER32!DialogBox2+0x144
039bf340 7e424a06 7c9c0000 7cc26c50 00170222 USER32!InternalDialogBox+0xd0
039bf360 7e4247ea 7c9c0000 7cc26c50 00170222 USER32!DialogBoxIndirectParamAorW+0x37
039bf384 7ca4033c 7c9c0000 000003eb 00170222 USER32!DialogBoxParamW+0x3f
039bf3cc 7ca402c8 7c9c0000 000003eb 00170222 SHELL32!SHFusionDialogBoxParam+0x3b
039bf400 0102129f 00170222 00000000 039bf834 SHELL32!RunFileDlg+0xc4
039bfa40 010210f3 00170222 00000000 01be80c0 Explorer!_RunFileDlg+0x12f
039bfee0 77f69598 000003b8 01be71e0 77f6957b Explorer!CTray::_RunDlgThreadProc+0x29a
039bfef8 7c927ac2 01be71e0 7c97e440 00160a70 SHLWAPI!ExecuteWorkItem+0x1d
039bff40 7c927b03 77f6957b 01be71e0 0009f298 ntdll!RtlpWorkerCallout+0x70
039bff60 7c927bc5 00000000 01be71e0 00160a70 ntdll!RtlpExecuteWorkerRequest+0x1a
039bff74 7c927b9c 7c927ae9 00000000 01be71e0 ntdll!RtlpApcCallout+0x11
039bffb4 7c80b729 00000000 0274ec60 0274ec60 ntdll!RtlpWorkerThread+0x87
039bffec 00000000 7c910250 00000000 00000000 kernel32!BaseThreadStart+0x37
it looks the same as in procmon
lets check the arguments to the function
since this is x86 32bit three arguments are shown in stack if we want more we need to play with esp register
note: be aware in x64 the first
the first three args as per kb
Code:
ChildEBP RetAddr Args to Child
039bebf4 7ca408e2 00139220 039bee20 00150202 SHELL32!AddMRUStringW
what is the first argument
0:017> dd poi(esp+4) l4
00139220 00000002 0000001a 7c80aa36 00000828
you should recognize the 0x1a
0:017> .formats 1a
Evaluate expression:
Hex: 0000001a
Decimal: 26 <--------------------
0:017> du poi(esp+8)
039bee20 "cmd\1" <------------- yes this is our input
so since this is an argument check who pushed it
ub return address in stack viz
Code:
0:017> ub 7ca408e2
7ca408c2 68b4959c7c push offset SHELL32!`string' (7c9c95b4)
7ca408c7 8d8570fdffff lea eax,[ebp-290h]
7ca408cd 50 push eax
7ca408ce ff15341c9c7c call dword ptr [SHELL32!_imp__StrCatBuffW (7c9c1c34)]
7ca408d4 8d8570fdffff lea eax,[ebp-290h]
7ca408da 50 push eax
7ca408db 56 push esi <-------------------
7ca408dc ff1584b2a27c call dword ptr [SHELL32!_imp__AddMRUStringW (7ca2b284)]
what is in esi
0:017> r esi
esi=00139220
where did esi get that value
ub retn_address length > default on trial and error till we locate esi
or use ida
or ollydbg register highlighting functionality
we see esi got the value from the return value of a Function
Code:
0:017> ub 7ca408e2 l40
7ca408b2 e8b2faffff call SHELL32!OpenRunDlgMRU (7ca40369)
7ca408b7 8bf0 mov esi,eax <------------------------
7ca408b9 3bf3 cmp esi,ebx
7ca408bb 742b je SHELL32!CRunDlg::OKPushed+0x1d4 (7ca408e8)
7ca408bd 6806010000 push 106h
7ca408c2 68b4959c7c push offset SHELL32!`string' (7c9c95b4)
7ca408c7 8d8570fdffff lea eax,[ebp-290h]
7ca408cd 50 push eax
7ca408ce ff15341c9c7c call dword ptr [SHELL32!_imp__StrCatBuffW (7c9c1c34)]
7ca408d4 8d8570fdffff lea eax,[ebp-290h]
7ca408da 50 push eax
7ca408db 56 push esi
7ca408dc ff1584b2a27c call dword ptr [SHELL32!_imp__AddMRUStringW (7ca2b284)]
so we would need to check this function
Code:
0:016> uf SHELL32!OpenRunDlgMRU
SHELL32!OpenRunDlgMRU+0x19:
7ca4002f 2145fc and dword ptr [ebp-4],eax
7ca40032 8d45e8 lea eax,[ebp-18h]
7ca40035 50 push eax
7ca40036 c745e818000000 mov dword ptr [ebp-18h],18h
7ca4003d c745ec1a000000 mov dword ptr [ebp-14h],1Ah
7ca40044 c745f002000000 mov dword ptr [ebp-10h],2
7ca4004b c745f401000080 mov dword ptr [ebp-0Ch],80000001h
7ca40052 c745f810109d7c mov dword ptr [ebp-8],offset SHELL32!`string'+0x10 (7c9d1010)
7ca40059 ff15b0b2a27c call dword ptr [SHELL32!_imp__CreateMRUListW (7ca2b2b0)]
7ca4005f e922030000 jmp SHELL32!OpenRunDlgMRU+0x49 (7ca40386)
SHELL32!OpenRunDlgMRU:
7ca40369 8bff mov edi,edi
7ca4036b 55 push ebp
7ca4036c 8bec mov ebp,esp
7ca4036e 83ec18 sub esp,18h
7ca40371 6a00 push 0
7ca40373 6894f6bc7c push offset SHELL32!g_hMRURunDlg (7cbcf694)
7ca40378 ff15b4139c7c call dword ptr [SHELL32!_imp__InterlockedExchange (7c9c13b4)]
7ca4037e 85c0 test eax,eax
7ca40380 0f84a9fcffff je SHELL32!OpenRunDlgMRU+0x19 (7ca4002f)
SHELL32!OpenRunDlgMRU+0x49:
7ca40386 c9 leave
7ca40387 c3 ret
so either InterlockedExchange return
0:016> ln poi(SHELL32!_imp__CreateMRUListW)
(7ca2b2b9) SHELL32!CreateMRUListW | (7ca2b320) SHELL32!CFSFolder::_CreatePerClassDefExtIcon
Exact matches:
SHELL32!CreateMRUListW (<no parameter info>

Remember the warnings about lock synchronization
This structure is protected by lock is what we can understand
Something like
Code:
void somecrapfunct(void)
{
If ( (intcmpexch(global_) ) == 0)
{
CreateMruListW(.........);
}
return ;
}
I leave the CreateMruList() analysis to readers
Hint it makes an indirect call ïŠ and goes on to set a reg key
Code:
7ca4004b c745f401000080 mov dword ptr [ebp-0Ch],80000001h
7ca40052 c745f810109d7c mov dword ptr [ebp-8],offset SHELL32!`string'+0x10 (7c9d1010)
0:016> du /c 40 7c9d1010
7c9d1010 "Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU"
C:\WinDDK\7600.16385.1\inc\api>grep -ir "#define HKEY_current_user" *
WINREG.H:#define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG
)0x80000001) )
WINREG.H:#define HKEY_CURRENT_USER_LOCAL_SETTINGS (( HKEY ) (ULONG_PTR)((LONG
)0x80000007) )
C:\WinDDK\7600.16385.1\inc\api>
Lets see what this global contains
Code:
0:016> dd poi(SHELL32!g_hMRURunDlg) l4
xxxxxxxx 00000002 0000001a 7c80aa36 000008fc
i removed the address coz i wrote this blog over several days so the address might confuse reader
for a single session the allocated address will remain same here xxxxxxxxx will be 00139220
in a single session output
there lies our esi to AddStringMruW
lets memory modify it
Code:
read warning in last paragraph about address assume 00139220 instead of 02522df4 for a single uninterrupted
session (it will be whatever it was in [esp+4] when you broke into windbg)
0:016> dd poi(SHELL32!g_hMRURunDlg)+4 L1
02522df4 0000001a
0:016> ed poi(SHELL32!g_hMRURunDlg)+4 0x1b < lets add one more string
0:016> dd poi(SHELL32!g_hMRURunDlg)+4 L1
02522df4 0000001b
lets detach windbg from explorer and keep adding strings to runmru
and check was it successful did we get our 27th string in runmru ?

yes we got it
warning again
please do not mimic this in system critical process
also this is not a full reverse it doesn�t mean that you can set the value to 0xffffffff
and have the full liberty of 2^32 � 1 strings in runmru
think before hand
what may happen to alloc memory
how the buffers are allocated why is it a protected global
can increasing beyond 26 corrupt further structures
where did the RunMRULIST get its next character from apart from �abcdef�..xyz�
Code:
0:016> .formats 61+0n26
Chars: ...{ +1 from z
what happens when this runs out of byte limit 0xff recycle or crash ? is it bytelimit or wordlimit ? or omg INT64
who else uses this set ba r4 breaks
happy runmruing
