定位未導出的函數位址(SHCreateProcess)
搬運自我的百度空間,文章基于windows7 64位
我們要Hook shell32.dll的SHCreateProcess這個函數,苦于他沒有被導出,也無從知道位址。
其實我們還是有辦法的。windbg有功能叫做棧回朔技術,可以看到函數的調用者,函數的調用者的調用者,函數的調用者的調用者的調用者。。。。。。。。。。。。。。。。。。(略過1000字)
原理是什麼呢?我們知道函數的調用是靠棧來完成的,每當調用一個函數,都會把函數的傳回位址存入棧中。
windbg就是看棧中的傳回位址來确定調用者的。
好,鋪叙完畢。看看SHCreateProcess這個函數,他的位址我們雖然不知道,但是我們知道他會調用CreateProcessW,那我們Hook了CreateProcessW後,假裝調用ShellExecute(内部會使用SHCreateProcess函數),回朔調用者,就能得到SHCreateProcess的大緻位址了(因為得到的是CreateProcessW傳回到SHCreateProcess内部的位址,而不是SHCreateProcess的首位址,是以我說是“大緻位址”)。
(2016-8-3更新:有朋友私信問我SHCreateProcess的原型,我當時在網上搜得到,現在看了看好像找不到了,還好我的工程裡還有,定義如下:
typedef int (__stdcall *PSHCreateProcess)(int p1,HANDLE hToken,wchar_t *lpApplicationName,wchar_t * lpCommandLine,DWORD dwCreationFlags,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation,int p2,char p3,int p4);
)
先來說32位版Win7:
windbg反彙編SHCreateProcess看到,他調用了函數IsUserAnAdmin,是一個導出函數,我們Hook之
具體怎麼Hook我就不說了,直接到關鍵步驟:
//32Bit
long myIsUserAnAdmin()
{
void* aa=0;
__asm
{
mov eax, [ebp+4]
mov aa,eax
}
long i,key,key2;
bool ok=false;
for (i=(long)aa;i>=(long)aa-0x200000;i--)
{
key=*(long*)i;
key2=*(long*)(i+4);
if (key==0x8b144d8b && key2==0x8d890845 )
break;
}
for (;i>=(long)aa-0x200000;i--)
{
key=*(long*)i;
if (key==0x90909090)
{
ok=true;
break;
}
}
if(ok)
{
i+=4;
HMODULE hMod=GetModuleHandleW(L"shell32.dll");
pSHCreateProcess =(PSHCreateProcess)i;
}
else
{
pSHCreateProcess =0;
}
return 1;
}
HANDLE RunAsAdmin( HWND hWnd, WCHAR* pFile,WCHAR* lpParam,WCHAR* pDir)
{
PFShellExecuteExW pFun;
if (oldShellExecuteExW==0)
{
pFun=ShellExecuteExW;
}
else
{
pFun=oldShellExecuteExW;
}
SHELLEXECUTEINFOW sei;
ZeroMemory(&sei,sizeof(sei));
sei.cbSize = sizeof(sei);
sei.hwnd = hWnd;
sei.fMask = 0x00000100|SEE_MASK_NOCLOSEPROCESS;
sei.lpFile = pFile;
sei.lpVerb = L"runas";
sei.lpParameters=lpParam;
sei.lpDirectory=pDir;
//sei.lpParameters = PChar(aParameters);
sei.nShow = SW_SHOWNORMAL;
pFun(&sei);
return sei.hProcess;
}
void* LocateFunc()
{
HMODULE hMod=GetModuleHandleW(L"shell32.dll");
PVOID pFun=GetProcAddress(hMod,"IsUserAnAdmin");
PVOID pOld=pFun;
//cout<<"mod "<<(long)hMod<<" Addr:"<<(long)pFun<<endl;
Hook(&pOld,myIsUserAnAdmin);
TerminateProcess(RunAsAdmin(0,L"explorer.exe",0,0),0);
UnHook(&pOld,myIsUserAnAdmin);
return pSHCreateProcess;
}
long UnHook(void ** ppFun,PVOID pDetour)
{
// DetourRestoreAfterWith();
DetourTransactionBegin();
//Detour
DetourUpdateThread(GetCurrentThread());
DetourDetach(ppFun,pDetour);
return DetourTransactionCommit();
}
long Hook(void ** ppOld,PVOID pNew)
{
// DetourRestoreAfterWith();
DetourTransactionBegin();
//Detour
DetourUpdateThread(GetCurrentThread());
DetourAttach(ppOld,pNew);
return DetourTransactionCommit();
}
代碼解釋:
__asm
{
mov eax, [ebp+4]
mov aa,eax
}
用來獲得棧中的傳回位址,儲存到aa指針中,注意傳回位址是在SHCreateProcess的中間部分,而不是函數頭部位址,是以仍然無法被Hook函數所用。
我們得到了函數的大體位置,接下來的代碼使用搜尋特征值的辦法得到函數位址。
反彙編SHCreateProcess看到:
SHELL32!_SHCreateProcess+0x182:
00000000`75c7385e e81a0ce8ff call SHELL32!IsUserAnAdmin (00000000`75af447d)
00000000`75c73863 85c0 test eax,eax
00000000`75c73865 0f84c71ce4ff je SHELL32!_SHCreateProcess+0x18e (00000000`75ab5532)
這裡調用了IsUserAnAdmin,我們得到的傳回指針就在這裡,從這裡開始往前找。
WindowsAPI函數頭的特征是在他之前會有幾個NOP指令,機器碼0x90,理論上從傳回位址向前搜尋NOP NOP NOP NOP(0x90909090),再把指針加4,就可以得到SHCreateProcess的函數指針了。
可是實際上,在WOW64版的32位Shell32中,SHCreateProcess的函數空間裡竟然莫名其妙的還有另一個函數CExecuteApplication::_TryCreateProcess,如果單純搜尋NOP,會誤入歧途到CExecuteApplication::_TryCreateProcess函數頭中,那我們hook的就不是_SHCreateProcess了。
是以代碼中我先搜尋了函數頭附近的key==0x8b144d8b && key2==0x8d890845,然後搜尋NOP即可。
64位DLL中,沒有調用IsUserAnAdmin,那我們隻好直接HOOK CreateProcess來找出_SHCreateProcess的位置。
要注意的是之前棧回朔的彙編代碼在64位貌似無效,我使用了RtlGetCallersAddress這個ntdll的API,來回朔調用者。而且,檢視了64位的DLL後,發現隻要搜尋0x90909090這個特征碼就可以啦。
//64Bit
int __stdcallmyCreateProcessW(wchar_t* lpApplicationName,intlpCommandLine,DWORD dwCreationFlags,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation)
{
void* aa=0;
HMODULE hMod=GetModuleHandleW(L"ntdll.dll");
PRtlGetCallersAddress pFun=(PRtlGetCallersAddress)GetProcAddress(hMod,"RtlGetCallersAddress");
void* b1,*b2;
pFun(&b1,&b2);
aa=b1;
INT64 i;
longkey,key2;
boolok=false;
for(i=(longlong)aa;i>=(longlong)aa-0x000000200000;i--)
{
key=*(long*)i;
if(key==0x90909090)
{
ok=true;
break;
}
}
if(ok)
{
i+=4;
pSHCreateProcess=(PSHCreateProcess)i;
}
else
{
pSHCreateProcess=0;
}
return0;
}
2013-06-02