StackWatcher (French)
CrazYvan <ivanlef0u119yahoofr> Thursday, December 28 2006 11:23.59 CST


Plop, c'est les vacances et IvanleMad ne peut s'arr�ter de coder, toujours en
qu�te de satisfation personnelle, la masturbation ne lui suffit plus il doit
coder, toujours plus vite, toujours plus loin, toujours plus profond.

Ainsi dans ces p�r�grinations il jouait avec le fameux tools de Mark
Russinovitch, Process Explorer, et tombe sur une fonction permettant de voir la
pile d'appel d'un Thread, son esprit commenca � entr� en �ruption. "Comment ce
put1 de b4t@rd de ca race de programme fonctionne ffs ?!". Cet ainsi qu'il
d�cida � reverse l'irr�versible (O_o).

Effectivement apr�s cette superbe intro r�alis�e par mon Dr Hide, je vous
propose un tool, cod� par mes soins. Son objectif ! Etre capable de lire la
m�moire d'un autre process afin de parcourir la pile de chaque thread et d'y
retrouver les fonctions utilis�es. Cela para�t dingue mais c'est faisable, si le
tool du ruskoff est capable de le faire alors pourquoir pas le mien :]
Bon en lisant la doc des API d'aide au d�buggage je suis tomb� sur  
StackWalk64(), une API permettant de retrouver les appels � partir de la pile,
il suffit de lui donner un HANDLE sur le process, un autre sur le Thread stop�
au pr�alable, quelques infos sur l'�tat du Thread et hop (burp!) elle se
d�brouille pour nous retrouver les saved eip, W00t.

Comme c'est nowel je me suis que c'etait bien joli d'avoir les addr d'appel,
mais que ca serait encore mieux d'avoir les noms de ces fonctions. Le programme
utilise donc les symbols, fournit par MS, qui doivent �tre downloader sur son
site avec les debugings tools, ici :
http://www.microsoft.com/whdc/devtools/debugging/default.mspx

Je peux vous dire que cela ma pas prit la t�te pour faire marcher ce petit bout
de code, la doc sur le net n'�tant pas foisonnante ....

------------CUT HERE--------------------

#include <windows.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#include <stdio.h>

#pragma comment (lib, "advapi32.lib")
#pragma comment (lib, "dbghelp.lib")

int NameToPid(char *ProcessName)
{
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;

hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hProcessSnap==INVALID_HANDLE_VALUE)
{
printf("Error with CreateToolhelp32Snapshot: 0x%x\n",GetLastError() );
}

pe32.dwSize = sizeof(PROCESSENTRY32);

if( !Process32First(hProcessSnap, &pe32 ))
{
printf("Error with Process32First: %d\n",GetLastError());  
CloseHandle(hProcessSnap);    
}

while(Process32Next(hProcessSnap,&pe32)!=0)
{
if(_stricmp(pe32.szExeFile,ProcessName)==0) //_stricmp fuck la case sensitive
{
CloseHandle(hProcessSnap);
return pe32.th32ProcessID;
}
}

CloseHandle(hProcessSnap);
return 0;
}


DWORD EnablePrivilege(char *Privilege)
{
HANDLE hToken;
DWORD Ret=1;
TOKEN_PRIVILEGES TP;
LUID Luid;

if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
printf("Error with OpenProcessToken: %d\n", GetLastError());
Ret=0;
goto bye;
}

if(!LookupPrivilegeValue(NULL, Privilege, &TP.Privileges[0].Luid))
{
printf("Error with LookupPrivilegeValue: %d\n", GetLastError());
Ret=0;
goto bye;

}

TP.PrivilegeCount=1;
TP.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

if(!AdjustTokenPrivileges(hToken,
false,
&TP,
NULL,
NULL,
NULL))
{
printf("Error with AdjustTokenPrivileges: %d\n", GetLastError());
Ret=0;
goto bye;

}

bye:
if(hToken)
CloseHandle(hToken);

return Ret;
}


int main(int argc, char * argv[])
{
HANDLE hProcess, hThread, hThreadSnap;
DWORD PID, TID;
DWORD64 Displacement;
THREADENTRY32 Th32;
STACKFRAME64 StackFrame;
IMAGEHLP_MODULE64 IM;
CONTEXT Context;
PSYMBOL_INFO pSI;

SymSetOptions(SYMOPT_UNDNAME|SYMOPT_DEFERRED_LOADS);

RtlSecureZeroMemory(&Th32, sizeof(THREADENTRY32));
RtlSecureZeroMemory(&IM, sizeof(IMAGEHLP_MODULE64));

IM.SizeOfStruct=sizeof(IMAGEHLP_MODULE64);

if(argc!=2)
return 0;

PID=NameToPid(argv[1]);
if(!PID)
{
printf("Error with NameToPid : %d\n", GetLastError());
return 0;
}

//pour les process system
EnablePrivilege("SeDebugPrivilege");
                  
hProcess=OpenProcess(PROCESS_VM_OPERATION|
PROCESS_VM_WRITE|
PROCESS_VM_READ|
PROCESS_CREATE_THREAD|
PROCESS_QUERY_INFORMATION,
FALSE, PID);
if(!hProcess)
{
printf("Error with OpenProcess : %d\n", GetLastError());
goto cleanup;
}

/*
fInvadeProcess
[in] If this value is TRUE, enumerates the loaded modules for the process and effectively
calls the SymLoadModule64 function for each module.
*/
if(!SymInitialize(hProcess, NULL, true))
{
printf("Error with SymInitialize : %x\n", GetLastError());
goto cleanup;
}

hThreadSnap=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
Th32.dwSize=sizeof(THREADENTRY32);

if(!Thread32First(hThreadSnap,&Th32))
{
printf("Error with Thread32First : %d\n", GetLastError());
goto cleanup;
}

//on se place sur le 1er thread de notre process
while(Th32.th32OwnerProcessID!=PID)
Thread32Next(hThreadSnap,&Th32);

pSI=(PSYMBOL_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SYMBOL_INFO)+MAX_SYM_NAME);
if(!pSI)
goto cleanup;

//tant que le thread appartient au process
while(Th32.th32OwnerProcessID==PID)
{
TID=Th32.th32ThreadID;
printf("\nThread ID : %d\n", TID);

RtlSecureZeroMemory(pSI, sizeof(SYMBOL_INFO)+MAX_SYM_NAME);
pSI->SizeOfStruct=sizeof(SYMBOL_INFO);
pSI->MaxNameLen=MAX_SYM_NAME;

RtlSecureZeroMemory(&Context, sizeof(CONTEXT));
//EIP, EBP, ESP
Context.ContextFlags=CONTEXT_CONTROL;

RtlSecureZeroMemory(&StackFrame, sizeof(STACKFRAME64));

hThread=OpenThread(THREAD_QUERY_INFORMATION|THREAD_SUSPEND_RESUME|THREAD_GET_CONTEXT, false, TID);
if(!hThread)
{
printf("Error with OpenThread : %d\n", GetLastError());
goto cleanup;
}

//met en pause le thread
SuspendThread(hThread);

if(!GetThreadContext(hThread, &Context))
{
printf("Error with GetThreadContext : %d\n", GetLastError());
goto cleanup;
}

   StackFrame.AddrPC.Offset=Context.Eip;
   StackFrame.AddrPC.Mode=AddrModeFlat;
   StackFrame.AddrFrame.Offset=Context.Ebp;
   StackFrame.AddrFrame.Mode=AddrModeFlat;
   StackFrame.AddrStack.Offset=Context.Esp;
StackFrame.AddrStack.Mode=AddrModeFlat;

StackFrame.AddrReturn.Mode=AddrModeFlat;

/*
printf("Eip : 0x%x\n", Context.Eip);
printf("Esp : 0x%x\n", Context.Esp);
printf("Ebp : 0x%x\n", Context.Ebp);
*/

//parcourt la pile
do
{
//printf("AddrReturn : 0x%x\n", StackFrame.AddrReturn.Offset);

//va fouiller la stack
if(!StackWalk64(IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&StackFrame,
&Context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL))
{
// Note that StackWalk64 generally does not set the last error code.
printf("Error with StackWalk");
//return 0;
}

if(SymFromAddr(hProcess, (DWORD)StackFrame.AddrReturn.Offset, &Displacement, pSI))
{
if(SymGetModuleInfo64(hProcess, pSI->ModBase, &IM))
printf("0x%x : %s!%s+0x%x\n",(DWORD)pSI->Address, IM.ModuleName, pSI->Name, Displacement);
}
//else
//printf("Error with SymFromAddr : %d\n", GetLastError());

}while(StackFrame.AddrReturn.Offset!=0);


//relance le thread
ResumeThread(hThread);

CloseHandle(hThread);

Thread32Next(hThreadSnap,&Th32);
}

cleanup:

if(hProcess)
CloseHandle(hProcess);

SymCleanup(hProcess);

return 0;
}

------------CUT HERE--------------------

A l'utilisation ca donne :
C:\ProgHack\c>StackWatcher cmd.exe

Thread ID : 392
0x7c91e9b4 : ntdll!ZwWaitForSingleObject+0xc
0x7c802540 : kernel32!WaitForSingleObjectEx+0xa8
0x7c802520 : kernel32!WaitForSingleObject+0x12
0x4ad02cf4 : CMD!WaitProc+0x18
0x4ad02ff8 : CMD!ExecPgm+0x3fa
0x4ad02d42 : CMD!ECWork+0x84
0x4ad02dcb : CMD!ExtCom+0x40
0x4ad0145d : CMD!FindFixAndRun+0xcf
0x4ad01375 : CMD!Dispatch+0x137
0x4ad03ff1 : CMD!main+0x216
0x4ad05056 : CMD!mainCRTStartup+0x125
0x7c816fb4 : kernel32!BaseProcessStart+0x23

Impressive n'est-il pas ? :}
L'int�ret ca peut �tre de savoir par ou est pass� un code pour �xecut� une
action. Dans l'exemple suivant j'ai mit un BP sur l'API native ZwCreateProcessEx
qui vous vous en doutez sert a cr�e � process, je l'ai mit dans explorer.exe,
donc quand je vais lancer un process le programme va �tre stop� par le
breakpoint et si on lance StackWatcher on peut voir :


[...]
Breakpoint sur ZwCreateProcessEx de ntdll.dll l'api native qui call le noyau
pour lancer un process
Thread ID : 2008
0x7c819513 : kernel32!CreateProcessInternalW+0x1327
0x7c802332 : kernel32!CreateProcessW+0x2c
0x7ca11f93 : SHELL32!_SHCreateProcess+0x387
0x7ca11e7a : SHELL32!CShellExecute::_DoExecCommand+0xb4
0x7ca11e21 : SHELL32!CShellExecute::_TryInvokeApplication+0x49
0x7ca118b9 : SHELL32!CShellExecute::ExecuteNormal+0xb1
0x7ca11866 : SHELL32!ShellExecuteNormal+0x30
0x7ca117cb : SHELL32!ShellExecuteExW+0x8d
0x7ca1e4e4 : SHELL32!_InvokePidl+0x9f
0x7ca1e421 : SHELL32!CShellExecMenu::_InvokeOne+0xa0
0x7ca1e347 : SHELL32!CShellExecMenu::InvokeCommand+0xa7
0x7ca1e2ad : SHELL32!HDXA_LetHandlerProcessCommandEx+0xa5
0x7ca1e1b5 : SHELL32!CDefFolderMenu::InvokeCommand+0x17f
0x7ca300b2 : SHELL32!CShellLink::TargetContextMenu::InvokeCommand+0x22
0x7ca2fe91 : SHELL32!CShellLink::_InvokeCommandAsync+0x337
0x7ca2fe57 : SHELL32!CShellLink::InvokeCommand+0x259
0x7ca1e2ad : SHELL32!HDXA_LetHandlerProcessCommandEx+0xa5
0x7ca1e1b5 : SHELL32!CDefFolderMenu::InvokeCommand+0x17f
0x77f98355 : SHLWAPI!SHInvokeCommandsOnContextMenu+0x174
0x77f9926d : SHLWAPI!SHInvokeCommand+0x63
0x77f9932d : SHLWAPI!SHInvokeDefaultCommand+0x15
0x102c9a0 : explorer!CStartMenuHost::ExecItem+0x17
0x7cb6d9d7 : SHELL32!CStartMenuCallback::_ExecItem+0x17
0x7cb6f32e : SHELL32!CStartMenuCallback::CallbackSM+0xe0
0x7ca23edd : SHELL32!CMenuSFToolbar::CallCB+0xd9
0x7cba7520 : SHELL32!CMenuSFToolbar::v_ExecItem+0x8e
0x7cba40bf : SHELL32!CMenuToolbarBase::_DropDownOrExec+0xa6
0x7ca24a04 : SHELL32!CMenuToolbarBase::_OnNotify+0x2bf
0x7ca24401 : SHELL32!CMenuSFToolbar::_OnNotify+0x109
0x7ca2439a : SHELL32!CMenuToolbarBase::OnWinEvent+0x60
0x7ca2435a : SHELL32!CMenuSFToolbar::OnWinEvent+0x6b
0x7ca2428f : SHELL32!CMenuBand::OnWinEvent+0x1f8
0x7ca24159 : SHELL32!CMenuSite::v_WndProc+0xd9
0x7ca30e35 : SHELL32!CImpWndProc::s_WndProc+0x65
0x77d1870c : USER32!InternalCallWinProc+0x28
0x77d1875f : USER32!UserCallWinProcCheckWow+0x150
0x77d1b7d3 : USER32!SendMessageWorker+0x4a5
0x77d1b8ba : USER32!SendMessageW+0x7f
0x773aa3d1 : comctl32!CCSendNotify+0xc20
0x773ff831 : comctl32!TBSendUpClick+0x5f
0x77404778 : comctl32!TBOnLButtonUp+0x13b
0x77404bdb : comctl32!ToolbarWndProc+0xb30
0x77d1870c : USER32!InternalCallWinProc+0x28
0x77d1875f : USER32!UserCallWinProcCheckWow+0x150
0x77d1c5ee : USER32!CallWindowProcAorW+0x98
0x77d1c64a : USER32!CallWindowProcW+0x1b
0x773a1b3d : comctl32!CallOriginalWndProc+0x1a
0x773a1e6e : comctl32!CallNextSubclassProc+0x3c
0x773a2026 : comctl32!DefSubclassProc+0x46
0x7ca0ef96 : SHELL32!CSFToolbar::_DefWindowProc+0xb8
0x7ca0ef46 : SHELL32!CNotifySubclassWndProc::_SubclassWndProc+0x7d
0x773a1e6e : comctl32!CallNextSubclassProc+0x3c
0x773a207b : comctl32!MasterSubclassProc+0x54
0x77d1870c : USER32!InternalCallWinProc+0x28
0x77d1875f : USER32!UserCallWinProcCheckWow+0x150
0x77d188f1 : USER32!DispatchMessageWorker+0x306
0x77d18a01 : USER32!DispatchMessageW+0xf
0x100199d : explorer!CTray::_MessageLoop+0xd9
0x1011e62 : explorer!CTray::MainThreadProc+0x29
0x77f5422b : SHLWAPI!WrapperThreadProc+0x94
0x7c80b64c : kernel32!BaseThreadStart+0x37

Thread Id : XXX
[...]


Ouf ca fait de la peur :]
En tout cas je pense qu'il y moyen de bien s'amuser avec ce tools on peut
apprendre de bonne chose sur le fonctionnement de certains binaire system. Par
contre mieux vaut avoir les symbols pour ceux-ci si vous voulez des r�sultats
probants.


Enfin en reversant Process Explorer j'ai remarqu� que le mofo utilisait un
driver afin de lire aussi la pile d'appel du thread dans le noyau, je suis
en train de continuer le combat afin que mon code le fasse aussi. Il m'a fallut
reverser le binaire, dumper le driver, reverse le driver ...Bref ca ma bien
prit la tete pour capter le bordel. Je suis en train de recoder un tool qui
reprend son fonctionnement  mais code est over crade, avec des bouts d'asm
partout et du DeviceIoControl() � la pelle donc celui la ca m'etonnerais que je le montre :]

En attendant vous avez de quoi vous amuser. Je vous file un .rar avec le binaire
les sources et les dll requises, enjoy !

http://membres.lycos.fr/moi118118/StackWatcher.rar

Ivanlef0u
"je te fist comme ca, comme ca et comme ca !"

Comments
pedram Posted: Friday, December 29 2006 13:17.07 CST
Any bilingual user out there willing to post an English translation of this blog entry as a comment? ;-)

msuiche Posted: Saturday, December 30 2006 09:53.47 CST
With slang please ! :)

MohammadHosein Posted: Tuesday, January 9 2007 05:47.29 CST
altavista's bablefish is kinda good , but please try not to use slangs ! :D

halvar Posted: Tuesday, January 9 2007 07:38.14 CST
I can translate if you guys wish, but it's french geek style, which is usually nsfw ;)

Anyhow, you should all learn french, even if it's just to be able to read MISC.

msuiche Posted: Wednesday, January 10 2007 09:32.26 CST
However, there was a "german" version of MISC haha

carib Posted: Friday, January 12 2007 12:44.07 CST
"It's holiday time and YvanTheMad can't stop coding, always looking for personal satisfaction; mas.....tion doesn't give him enough; he must code and keep on coding..."

for the rest, check out
google's translation