Log in

View Full Version : winapihelp plugin for ollydbg 1.10


blabberer
July 22nd, 2013, 13:29
WinApiHelp plugin for ollydbg 1.10

I had been a big fan of compiled html help (*.chm) and with its demise i used to resort to various methods
to create help files (basically for ollydbg usage) as Microsoft's latest help thingies never matched the ease
that chm file offered especially for offline mode usage

chm was simple you download the package double click and there you have everything in front of you
but the hhx hhc were never standalone
they required monstrous packages accompanying them to run and weren't adaptable even then

recently out of sheer circumstances
(i had ~ 600 mb data left on a prepaid and expiry date was closing in had to keep awake few nights
soothing a little bundle of frayed nerves and i ended up downloading the offline content for visual studio 2010 express
(win32 and com development and windows driver kit)

help -> manage help settings -> settings -> i want to use local help
install content from online add add update

and once it was there i clicked F1 on MessageBoxA in editor

a few seconds elapsed one tray icon popped up (help library agent ) and Firefox opened with the help file

now curiosity always kills the cat it is said

wtf is help lib agent ? why should i keep it open ? why it complains links will die and all help will cease to exist for me if i close it ?
what is the queer protocol in Firefox 127.0.0.1:\\ no http:\\ / ftp:\\ ?? Local-host ??

who is looking for that ? the big daddy at ms wants to know how i am helping myself ??

and last but not least of the question can i use it in a standalone mode without having to run umpteen components in the background ?


ollydbg by default used winhelp.chm (circa 19XX) for resolving symbolic help and it is damn outdated though quiet useful still

never saw a good substitute for it (there is a plugin by iirc mario vilas but it patched ollydbg and sort of leeched online help when i saw it )

no good offline substitute

thus this plugin was born

if you have installed any ms products (vs2010 express etc etc it installs help library for context sensitive help (F1 help))

use the help library manger to install contents locally (cute manager can update the content to latest version of the page on MSDN )

provide the path of the HelpLibraryAgent.exe to this plugin
provide the path of your default browser to this plugin
and simply hit F1 voila api help will be on screen in a jiffy

and surprise surprise surprise

you can even add your own help file to the local store and get it back with F1

i have tested this plugin on v1.0 (vs2010 express MS-HELP) on xp sp3 vm only

if anyone having vs2012 / vs2013 v1 . v2 . v3 etc and facing a few features leave back comments it will be appreciated.


SURPRISE DETAILS

supposing you are working on a ollydbg 2.10 h plugin and for all api help you are forced to visit ollydbg.de online everytime
(more surprise once you have the contents locally it will show up in both ollydbg with this plugin and visual-studio directly )
highlight and F1 in vs2010 on say Absolutizepath and you have it in screen
select the line with Absolutizepath in ollydbg and F1 you have it on screen

by adding all the help files to your local store you can have all the api details offline

wget the help files from ollydbg.de

wget -c -np -mirror http://www.ollydbg.de/Help/Absolutizepath.htm

this wilL fetch around ~175 files navigate the downloaded tree to HELP folder and

run this bat file (should be self explanatory ) you would need gnuwin32 sed / 7zip and html tidy in path

html tidy to convert the html files to be xml compliant
sed to delete the <DOCTYPE declaration insert xml header and add meta tags in <head> </head>
7zip to zip the converted files (zip is renamed to mshc )
create an msha file which help library manager recognizes for installing local content)
update and you have all the F1 help for available apis

mshc created thus have one wrinkle still left behind
the relative links do not work

the problem is each html help file needs an unique id for which i provide the file name
but relative links needs to be fixed in each file to a valid ms-xhelp:// protocol

i got bored hacking sed to look for <a href ="t_disasm.htm">t_disasm</a>
and convert it to <a href="ms-xhelp://<uniqueid><text></a> in each and every file

anyone wishing to contribute are welcome



Code:
xcopy help odbg2pluginhelp /EVQI

cd odbg2pluginhelp

for /F %%I in ('dir /b *.htm') do tidy -quiet -wrap 0 -modify -numeric -utf8 -asxml %%I

for /F %%I in ('dir /b *.htm') do sed -i "1,2d" %%I
del sed*
for /F %%I in ('dir /b *.htm') do sed -i "1,1s/^/<?xml version=\"1.0\" encoding=\"utf-8\"?>\n/" %%I
del sed*
for /F %%I in ('dir /b *.htm') do sed -i "/<head>/a <meta name=\"Microsoft.Help.F1\" content=\"%%~nI\" />\n<meta name=\"Microsoft.Help.Id\" content=\"%%~nI%%~xI\" />\n" %%I
del sed*
"c:\Program Files\7-Zip\7z.exe" a odbg2pluginhelp.zip

ren odbg2pluginhelp.zip odbg2pluginhelp.mshc

echo ^<html xmlns="http://www.w3.org/1999/xhtml"^> > helpcontentsetup.msha
echo ^<head^> >> helpcontentsetup.msha
echo ^<title^>odbg2pluginhelp^</title^> >> helpcontentsetup.msha
echo ^</head^> >> helpcontentsetup.msha
echo ^<body class="vendor-book"^> >> helpcontentsetup.msha
echo ^<div class="details"^> >> helpcontentsetup.msha
echo ^<span class="vendor"^>odbg^</span^> >> helpcontentsetup.msha
echo ^<span class="locale"^>en-us^</span^> >> helpcontentsetup.msha
echo ^<span class="product"^>ollydbg2.01h^</span^> >> helpcontentsetup.msha
echo ^<span class="name"^>odbgpluginhelp^</span^> >> helpcontentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^<div class="package-list"^> >> helpcontentsetup.msha
echo ^<div class="package"^> >> helpcontentsetup.msha
echo ^<span class="name"^>odbg2pluginhelp^</span^> >> helpcontentsetup.msha
echo ^<a class="current-link" href="odbg2pluginhelp.mshc"^>Help.mshc^</a^> >> helpcontentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^</body^> >> helpcontentsetup.msha
echo ^</html^> >> helpcontentsetup.msha


the source code for the plugin is shown below

a compiled binary and src also attached at the end


Code:
#include <windows.h>
#include <stdio.h>
#include <psapi.h>
#include <iphlpapi.h>
#include "plugin.h"

#pragma warning(disable : 6335)

#pragma comment(lib, "iphlpapi.lib"
#pragma comment(lib, "psapi.lib"
#pragma comment(lib, "ws2_32.lib"

HINSTANCE hinst;
HWND hwmain;
DWORD pid = NULL;
u_short LocalPort = NULL;
char pathtohelplib[MAX_PATH];
char pathtodefbrowser[MAX_PATH];
PMIB_TCPTABLE_OWNER_PID pTcpTable = NULL;

DWORD findhelplibagent(void)
{
DWORD ProcessIds[1024], BytesReturned, TotalProcesses,Status;
unsigned int i;
if ( !EnumProcesses( ProcessIds, sizeof(ProcessIds), &BytesReturned ) )
{
Addtolist(0,1,"Enum Process Failed\n";
Status = 0xffffffff;
return Status;
}
TotalProcesses = BytesReturned / sizeof(DWORD);
for ( i = 0; i < TotalProcesses; i++ )
{
if( ProcessIds[I] != 0 )
{
char ProcessName[MAX_PATH] = {"unknown"};
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessIds[I] );
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeed;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeed) )
{
GetModuleBaseNameA( hProcess, hMod, ProcessName, sizeof(ProcessName)/sizeof(char) );
if (_strnicmp("HelpLibAgent.exe",ProcessName,sizeof("HelpLibAgent.exe" ) == NULL)
{
pid = ProcessIds[I];
CloseHandle( hProcess );
Status = 1;
return Status;
}
else
{
CloseHandle( hProcess );
}
}
}
}
}
Status = 0;
return Status;
}
int Getlibagentpidandport()
{
struct in_addr LocalIpAddr;
char szLocalAddr[128];
DWORD dwSize = NULL;
DWORD ret , ishlplibagentrunning;

ishlplibagentrunning = findhelplibagent();
if ( ishlplibagentrunning == -1)
{
Addtolist(0,1, "EnumProcess Failed\n";
return FALSE;
}
else if ( ishlplibagentrunning == 0)
{
Addtolist(0,1, "HelpLibraryAgent is Not Running starting it\n";
STARTUPINFOA sinfo;
PROCESS_INFORMATION pinfo;
memset(&sinfo,0,sizeof(sinfo));
memset(&pinfo,0,sizeof(pinfo));
sinfo.cb = sizeof(sinfo);
CreateProcessA(
NULL,pathtohelplib,NULL,NULL,FALSE,NULL,NULL,NULL,&sinfo,&pinfo);
WaitForInputIdle(pinfo.hProcess,INFINITE);
ishlplibagentrunning = findhelplibagent();
if ( ishlplibagentrunning != 1)
{
Addtolist(0,1,"cant start HelpLibAgent.exe\n";
return FALSE;
}
else
{
Sleep(10000);
}
}
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL, NULL);
if(ret == ERROR_INVALID_PARAMETER)
{
Addtolist(0,1,"GetExtendedTcpTable failed\n";
return FALSE;
}
else if (ret == ERROR_INSUFFICIENT_BUFFER )
{
pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize*sizeof(wchar_t)+10);
memset(pTcpTable,0,dwSize*sizeof(wchar_t)+10);
dwSize = dwSize*sizeof(wchar_t)+10;
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_LISTENER, NULL);
if ( ret!= NO_ERROR)
{
Addtolist(0,1,"GetExtendedTcpTable failed again with %x\n",ret);
free(pTcpTable);
return FALSE;
}
else if (ret == NO_ERROR)
{
for (ULONG i =0; i<pTcpTable->dwNumEntries;i++)
{
if(pTcpTable->table[I].dwOwningPid == pid)
{
LocalIpAddr.S_un.S_addr =pTcpTable->table[I].dwLocalAddr;
strcpy_s(szLocalAddr, sizeof (szLocalAddr), inet_ntoa(LocalIpAddr));
LocalPort = ntohs((u_short)pTcpTable->table[I].dwLocalPort);
}
}
}
}
free(pTcpTable);
return TRUE;
}
void ExecuteQuery(PCHAR symname)
{
char buff [0x300];
Getlibagentpidandport();
_snprintf_s(
buff,
_countof(buff),
sizeof(buff)-10,
"http://127.0.0.1:%d/help/0-%u/ms.help?product=VS&productVersion=100&method=f1&query=%s",
LocalPort,
pid,
symname
);
ShellExecuteA(NULL,"open",pathtodefbrowser,buff,NULL,SW_SHOWNORMAL);
}

BOOL WINAPI DllMain( HINSTANCE hi, DWORD reason, LPVOID reserved )
{
UNREFERENCED_PARAMETER( reserved );
if (reason==DLL_PROCESS_ATTACH)
hinst=hi;
return 1;
};
extc int _export _cdecl ODBG_Plugindata( char shortname[32] )
{
strcpy_s( shortname, 30, "winapihelp" );
return PLUGIN_VERSION;
};
extc int _export _cdecl ODBG_Plugininit( int ollydbgversion, HWND hw, ulong *features )
{
UNREFERENCED_PARAMETER( features );
if (ollydbgversion<PLUGIN_VERSION)
{
return -1;
}
Addtolist(0,0,"WinApiHelp Plugin by blabberer";
hwmain=hw;
Pluginreadstringfromini(hinst,"vs2010helplibagent",pathtohelplib,"helplibagent path not found";
Pluginreadstringfromini(hinst,"defaultbrowser",pathtodefbrowser,"default browser path not found";
if (_strnicmp(pathtohelplib,"helplibagent path not found",sizeof("helplibagent path not found") == 0)
{
memset(&pathtohelplib,0,sizeof(pathtohelplib));
Browsefilename ("CHOOSE PATH TO HELPLIBRARYAGENT.EXE",pathtohelplib,".exe",0);
if(*pathtohelplib !='\0')
{
Pluginwritestringtoini(hinst,"vs2010helplibagent",pathtohelplib);
Addtolist(0,1,"path to HelpLibraryAgent.exe is %s",pathtohelplib);
}
}
if (_strnicmp(pathtodefbrowser,"default browser path not found",sizeof("default browser path not found") == 0)
{
memset(&pathtodefbrowser,0,sizeof(pathtodefbrowser));
Browsefilename ("CHOOSE PATH TO DEFAULT BROWSER",pathtodefbrowser,".exe",0);
if(*pathtodefbrowser !='\0')
{
Pluginwritestringtoini(hinst,"defaultbrowser",pathtodefbrowser);
Addtolist(0,1,"path to default browser is %s",pathtodefbrowser);
}
}

return 0;
};
extc int _export _cdecl ODBG_Pluginshortcut( int origin,int ctrl,int alt,int shift,int key, void *item )
{
t_dump* cpudump = NULL;
t_disasm disas;
uchar *decoderes = NULL;
char cmd[MAXCMDSIZE];
char jmpconst[TEXTLEN];
char jmpaddr[TEXTLEN];
char adrconst[TEXTLEN];
char immconst[TEXTLEN];
if( ( origin == PM_DISASM) && (ctrl == 0) && (alt == 0) && (shift == 0) && (key == VK_F1) )
{
cpudump = (t_dump*)item;
ulong psize;
int jcc,jaddr,adrc,immc;
decoderes = Finddecode(cpudump->sel0,&psize);
Readcommand(cpudump->sel0,cmd);
Disasm((uchar *)cmd,sizeof(cmd),cpudump->sel0,decoderes,&disas,DISASM_ALL,Getcputhreadid());
jcc = Findsymbolicname(disas.jmpconst,jmpconst);
jaddr = Findsymbolicname(disas.jmpaddr,jmpaddr);
adrc = Findsymbolicname(disas.adrconst,adrconst);
immc = Findsymbolicname(disas.immconst,immconst);
if( jcc > 1)
{
ExecuteQuery(jmpconst);
}
else if ( jaddr> 1)
{
ExecuteQuery(jmpaddr);
}
else if( adrc > 1 )
{
ExecuteQuery(adrconst);
}
else if (immc > 1)
{
ExecuteQuery(immconst);
}
return 1;
}
return 0;
}
winapihelp_plugin_odbg110.rar (223.5 KB)

Kayaker
July 24th, 2013, 01:02
Well, you made me install what I said I would never install, the VS offline help. But now that the dirty deed is done it will probably be handy. Thank you, I think.

It took me a while to figure out what this plugin was about, but then realized when I pressed F1 in the Olly CPU window when the cursor was on an instruction with a symbolic name, it would kick up my browser. Unfortunately that's all I can get it to do.

I installed the Visual C++, VS SDK and Win32/COM Development offline help components for VS 2010 Ultimate. Within VS if I press F1 for MessageBoxW for example, it will dutifully display the help viewer for that function.

If I open notepad in Olly, search for all intermodular calls, find a similar call to MessageBoxW, highlight it in the CPU window, and then press F1 in Olly, the plugin opens Firefox and displays my home page. But nothing further, it doesn't seem to search for the offline help.

What I do get is a message in red in the Olly log window:
HelpLibraryAgent is Not Running starting it
cant start HelpLibAgent.exe

That's not quite true because an instance of the service will start up when I press F1 in Olly, the little icon tells me so.
I get the same 2 messages even if a previous instance of HelpLibAgent is running.


I haven't tried to debug the ExecuteQuery command or check the http://127.0.0.1: query string for validity. I'm running Win7 x64, Olly itself seems to work fine.

One other minor point, when you first install the plugin it asks for the location of "HelpLibraryAgent.exe". That's a bit confusing to search for because it's actually the file HelpLibAgent.exe needed.
Perhaps it might also help the user by pointing the dialog box to the default location, \Program Files\Microsoft Help Viewer\v1.0\HelpLibAgent.exe

That's all I got right now.

blabberer
July 24th, 2013, 03:11
hi Kayaker

thanks for testing it out

yes you need to install the offline help so since you have done the dirty deed it should be usefull (you can simply unzip all the .mshc files to a multi GB folder and can .chm it but loading the chm in a 1 gb ram machine kills the machine to a bloody crawl and unusable for any search)

yes if you highlight a line with symbolic information and hit F1 the help should popup similar to vs2010 f1

it should pickup any symbol (apis , structures , whatnot )
theoretically if it pops up in vs this plugin should make it popup in ollydbg too)

if you are reversing and create a helpfile yourself all your user named symbols if they have help should popup

Code:



:\>dir /b batforsample.bat
batforsample.bat

:\>type batforsample.bat

for /F %%I in ('dir /b *.htm') do tidy -quiet -wrap 0 -modify -numeric -utf8 -as
xml %%I

for /F %%I in ('dir /b *.htm') do sed -i "s/href=\"/href=\"ms-xhelp:\/\/\/?Id=/g
" %%I
del sed*
for /F %%I in ('dir /b *.htm') do sed -i "1,2d" %%I
del sed*
for /F %%I in ('dir /b *.htm') do sed -i "1,1s/^/<?xml version=\"1.0\" encoding=
\"utf-8\"?>\n/" %%I
del sed*
for /F %%I in ('dir /b *.htm') do sed -i "/<head>/a <meta name=\"Microsoft.Help.
F1\" content=\"%%~nI\" />\n<meta name=\"Microsoft.Help.Id\" content=\"%%~nI%%~xI
\" />\n" %%I
del sed*
"c:\Program Files\7-Zip\7z.exe" a kayker.zip

ren kayker.zip kayrev.mshc

echo ^<html xmlns="http://www.w3.org/1999/xhtml"^> > helpcontentsetup.msha
echo ^<head^> >> helpcontentsetup.msha
echo ^<title^>kayaker^</title^> >> helpcontentsetup.msha
echo ^</head^> >> helpcontentsetup.msha
echo ^<body class="vendor-book"^> >> helpcontentsetup.msha
echo ^<div class="details"^> >> helpcontentsetup.msha
echo ^<span class="vendor"^>kaya^</span^> >> helpcontentsetup.msha
echo ^<span class="locale"^>en-us^</span^> >> helpcontentsetup.msha
echo ^<span class="product"^>specialexe^</span^> >> helpcontentsetup.msha
echo ^<span class="name"^>reversed^</span^> >> helpcontentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^<div class="package-list"^> >> helpcontentsetup.msha
echo ^<div class="package"^> >> helpcontentsetup.msha
echo ^<span class="name"^>forkayaker^</span^> >> helpcontentsetup.msha
echo ^<a class="current-link" href="kayrev.mshc"^>Help.mshc^</a^> >> help
contentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^</div^> >> helpcontentsetup.msha
echo ^</body^> >> helpcontentsetup.msha
echo ^</html^> >> helpcontentsetup.msha


:\>copy ..\..\www.ollydbg.de\Help\Addsorteddata.htm .
1 file(s) copied.

:\>ren Addsorteddata.htm samplecallforodbgplugin.htm

:\>batforsample.bat

------------------

:\>dir /b kay*
kayrev.mshc

:\>

updating local store with this bs you should be able to F1 and get the page back
if you named an unknown call to samplecallforodbgplugin see snaps below



call <symbol> e9 calls 0040100E E8 07000000 CALL msgbox.MessageBoxA
jmp <symbol> ff25 jumps 0040101A > $- FF25 08204000 JMP NEAR DWORD PTR DS:[<&user32.MessageBoxA>]
mov [foo] , blah 7C9011EC 803D 94E0977C 0>CMP BYTE PTR DS:[RtlpNotAllowingMultipleActivation]>
pushes 00401002 68 00304000 PUSH OFFSET msgbox.MsgCaption

i atm dont have facility to check with any other os apart from winxp sp3

so if you say you see cant start helplibagent.exe

then it might be EnumProcesses is failing or OpenProcess is failing (lack of permissions ?/ UAC ?/ elevated whatever ?)
can you tell me what is the status returned (-1) or (0) ? it needs 1

if you want i can send you a ifdef(dbg) version with lots of AddtoList() calls

as to BrowseFileName yes that should be corrected (infact i corrected it in _strnicmp but left it as it is in the dialog
will do it

2780

Kayaker
July 25th, 2013, 22:29
The problem seems to be with EnumProcessModules

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).

"EnumProcessModules error 299" is a googleable issue but I haven't found a solution yet.

The same thing occurs with 64 bit calc or notepad. Your OpenProcess loop finds the x64 PID OK, but EnumProcessModules fails with ERROR_PARTIAL_COPY, thence (thence?) GetModuleBaseNameA can't find the process name.

I think an alternative method of finding the HelpLibAgent.exe process name would be needed for this to work on x64. The plugin does start a copy of the service running under Olly all right, it just can't enum by name that it's actually running.

blabberer
July 26th, 2013, 00:29
thanks for the test
i added a typedef for Ex version (vista+) and added code to fetch it dynamically (LoadLib.getproc)

Code:


typedef BOOL ( WINAPI *PEnumProcessModulesEx )(
__in HANDLE hProcess,
__out_bcount(cb) HMODULE *lphModule,
__in DWORD cb,
__out LPDWORD lpcbNeeded,
__in DWORD dwFilterFlag
);



HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessIds[I] );
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeed;
BOOL enuprocret = FALSE;
PEnumProcessModulesEx EnumProcModulesEx;
enuprocret = EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeed);
if ( ( enuprocret ) == FALSE)
{
if (GetLastError() == ERROR_PARTIAL_COPY)
{
HMODULE hModule = LoadLibraryA("psapi.dll";
if (hModule)
{

if ( ( EnumProcModulesEx = (PEnumProcessModulesEx )GetProcAddress(hModule,"EnumProcessModuleEx" ) != NULL)
{
EnumProcModulesEx(hProcess, &hMod, sizeof(hMod), &cbNeed,LIST_MODULES_ALL);
}
}
}
}
GetModuleBaseNameA( hProcess, hMod, ProcessName, sizeo


please check if the attached compiled binay works as expected

edit dont it wont work it seems

Quote:

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).


someone posts some kinda solution have to try

http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/

http://msdn.microsoft.com/en-us/library/ms683217%28VS.85%29.aspx

Kayaker
July 26th, 2013, 00:49
Haven't tried the new binary, but I had already tried the Ex version, didn't work.

EnumProcessModulesEx
This function is intended primarily for 64-bit applications. If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option is ignored and the function provides the same results as the EnumProcessModules ("http://msdn.microsoft.com/en-us/library/windows/desktop/ms682631%28v=vs.85%29.aspx") function.

Kayaker
July 27th, 2013, 00:59
Some progress. I replaced EnumProcessModules and GetModuleBaseNameA with GetProcessImageFileName as suggested.

GetProcessImageFileName returns the device path. The proper way to handle that might be to use QueryDosDevice to substitute the drive name and then parse the path. Instead I just used StrRChrW (link to Shlwapi.lib) to extract the process name.

Here is a working replacement code snippet for the findhelplibagent() proc:


Code:

HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessIds[I] );
if (NULL != hProcess)
{
TCHAR ProcessName[MAX_PATH];

if (GetProcessImageFileName(hProcess, ProcessName, MAX_PATH))
{
// \Device\HarddiskVolume2\Program Files\Microsoft Help Viewer\v1.0\HelpLibAgent.exe

PTSTR pFilename = StrRChrW(ProcessName, NULL, '\\');

if (pFilename)
{
pFilename = pFilename+1;
Addtolist(0,1,"Process name is %ls", pFilename); // HelpLibAgent.exe

if (_wcsnicmp(L"HelpLibAgent.exe", pFilename, sizeof(L"HelpLibAgent.exe") == NULL)
{
pid = ProcessIds[I];
CloseHandle( hProcess );
Status = 1;
return Status;

} else {

CloseHandle( hProcess );
}
}
}
}


findhelplibagent() now returns 1 if HelpLibAgent is running. However F1 only kicks up Firefox with my homepage (or a blank page if set that way), no help info.

The buffer string passed to ShellExecuteA, when on the symbolic GetModuleHandleA is

http://127.0.0.1:0/help/0-1252/ms.help?product=VS&productVersion=100&method=f1&query=GetModuleHandleA

If I compare that to the url used in VS HelpViewer itself I find the problems:

http://127.0.0.1:47873/help/1-1252/ms.help?product=VS&productVersion=100&method=f1&query=GetModuleHandleA

If I enter this string into a browser (with the VS HelpLibAgent running) it displays properly, so in principle your plugin works!

You can see that the first string doesn't have a valid port number, so I'll have to check that portion of the code.
I see already that there is no match in the loop line
if(pTcpTable->table[I].dwOwningPid == pid)
Something to do with the flags for GetExtendedTcpTable required for x64 perhaps?


Also, the PID portion of the string /0-%u/ should be /1-%u/

I'm not sure why the hardcoded "0" value worked for you, but this link also shows the url using "1" instead.

http://connect.microsoft.com/VisualStudio/feedback/details/559054/local-help-does-not-work

blabberer
July 27th, 2013, 12:32
yes while i was working on this url format i read it should/must ?? be 1
but it worked with 0 too vs 2010 also had 0 in my machine

the 1 or 2 or 0 belongs to session id

you need query session status or something like that and sprintf it in place of hard coded
0

in xp it works with 0 because there is only one session i think Session-0

and from vista+ services have been isolated to session 0 and other process // ?? run in separate session

i wont be able to check anything until 2/8

so experiment with SessionStatus id

i read some thing about wmi or wtsquery which retrieves it check

oh you also posted there is no port number ?? i didnt read it first pass
not sure about it why it is blank for you

the default port is always 47873 unless some super hacker chose to modify it
(and on modifying the port there is a lengthy process with httpconfig.exe (resource kit tool for xp/2003 and a completely differnt tool for vista+ ) to register it with ms-xhelp protocol so you can possibly hardcode it and it shouldn't fail

edit
http://msdn.microsoft.com/en-us/library/windows/desktop/aa383838%28v=vs.85%29.aspx

also port no can be leeched from regitry hklm\\\\...\\v1.0

Kayaker
July 27th, 2013, 13:06
The port issue could be an Ipv6 thing. Process Explorer shows that my HelpLibAgent is running Ipv6 (as well as Ipv4) and winsock2. In which case all the Tcp flags need to be changed to suit, but unfortunately the header definitions are a bit of a mess.

blabberer
July 27th, 2013, 13:19
Quote:
[Originally Posted by Kayaker;95109]The port issue could be an Ipv6 thing. Process Explorer shows that my HelpLibAgent is running Ipv6 (as well as Ipv4) and winsock2. In which case all the Tcp flags need to be changed to suit, but unfortunately the header definitions are a bit of a mess.


you need to access local port in both in this structure
ipv4 ipv6

MIB_TCPROW and MIB_TCP6ROW
af_inet4 af_inet6

Kayaker
July 27th, 2013, 20:09
You're right, rumor has it that the VS 2010 help system operates on http://127.0.0.1:47873, so hardcoding that port with session id = 1 works just fine. I gave up on programming ipv6 for now and just removed the GetExtendedTcpTable code, since the port is already known.

This could be a pretty useful plugin for in depth Olly analysis anytime further symbolic information details are desired. Plus the ability to make your own local stores such as the Olly API descriptions, handy for plugin writing.

Nice job!

blabberer
August 3rd, 2013, 02:24
@kayaker
thanks
see if compiling and running this snippet produces Messagebox() help in firefox
uncomment to printf all processes running and thier session id and UserSid for debugging purposes if reqd

also tell me what is the result if you run sysinternals tcpvcon -a "helplibagent.exe"
i just glanced through it and i see it uses the GetExtendedTcpTable() function like i use
if it return the port ok in your setup then i think i should be able to retrieve it too

this is a standalone exe not a dll so copy paste to xxxx.cpp open a vs2012 cmd prompt do cl xxxx.cpp and run the xxxx.exe
Code:


#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wtsapi32.h>
#include <Sddl.h>

#pragma comment(lib, "Wtsapi32.lib"
#pragma comment(lib, "advapi32.lib"
#pragma comment(lib, "shell32.lib"

int _tmain(int argc, _TCHAR* argv[])
{
PWTS_PROCESS_INFO ppProcessInfo = NULL;
DWORD pCount;
LPTSTR stringsid;
if ( ( WTSEnumerateProcesses(WTS_CURRENT_SERVER_HANDLE,NULL,1,&ppProcessInfo,&pCount)) == NULL)
{
printf("WtsEnumProc Failed with %x\n",GetLastError());
return FALSE;
}
for (DWORD i = 0; i< pCount ; i++)
{
//ConvertSidToStringSid(*(PSID *)&ppProcessInfo[I].pUserSid ,&stringsid);
//printf(
// "%04d %04d %23s %s\n",
// *(DWORD *)&ppProcessInfo[I].SessionId ,
// *(DWORD *)&ppProcessInfo[I].ProcessId ,
// *(LPTSTR *)&ppProcessInfo[I].pProcessName,
// stringsid
// );
if (_strnicmp("HelpLibAgent.exe",*(LPTSTR *)&ppProcessInfo[I].pProcessName,sizeof("HelpLibAgent.exe" ) == NULL)
{
char buff [0x300];
_snprintf_s(
buff,
_countof(buff),
sizeof(buff)-10,
"http://127.0.0.1:47873/help/%d-%u/ms.help?product=VS&productVersion=100&method=f1&query=%s",
*(DWORD *)&ppProcessInfo[I].SessionId,
*(DWORD *)&ppProcessInfo[I].ProcessId,
"MessageBoxW"
);

ShellExecuteA(NULL,"open","c:\\program files\\Mozilla Firefox\\firefox.exe",buff,NULL,SW_SHOWNORMAL);
}
//LocalFree(stringsid);
}
WTSFreeMemory(*(PVOID *)&ppProcessInfo);
return TRUE;
}


Kayaker
August 3rd, 2013, 04:40
Quote:
[Originally Posted by blabberer;95141]see if compiling and running this snippet produces Messagebox() help in firefox


Why yes it does

0001 2668 HelpLibAgent.exe S-1-5-21-1462975667-1899876396-3390445299-1000

The x64 path to firefox is actually c:\\Program Files (x86)\\, but beyond that it works.

Tcpview (or netstat) doesn't grok the process name, so it doesn't see "helplibagent". Tcpview at least displays the owning process "system", which netstat even under elevated privs doesn't.

Tcpview:
System 4 TCP Owner-PC 47873 Owner-PC 0 LISTENING

Netstat -ab
Proto Local Address Foreign Address State
TCP 127.0.0.1:47873 Owner-PC:0 LISTENING
Can not obtain ownership information



So WTSEnumerateProcesses may be the solution?
Notice it's TCP not TCPv6 (which other connections are running under), so you may not have to worry about the whole ipv6 thing, maybe it was just a matter of getting the process name correctly under x64 from an x32 app.

blabberer
August 5th, 2013, 05:35
yes it does then that's good to hear

so is Wts THE solution ?
maybe

copy paste compile and run this for me in xpsp3 it shows the port (47873) in both ipv4 and ipv6 mode

tell me does it do it for you ?


Code:

// getexttcptab.cpp : Defines the entry point for the console application.
//

// define win32lemee to avoid conflicting winsock1.1 header
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif


#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>


#pragma comment(lib, "iphlpapi.lib"
#pragma comment(lib, "ws2_32.lib"

BOOL Ipv4OpenPortsAndPid(void)
{


PMIB_TCPTABLE_OWNER_PID pTcpTable = NULL;
DWORD dwSize = NULL, ret, dwState, dwOwningPid;
struct in_addr LocalIpAddr;
char szLocalAddr[128] = {0};
u_short LocalPort = NULL;

printf("======================\nchecking ipv4 ip address and ports\n======================\n";
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,NULL);
if ( ( ret != NO_ERROR ) && ( ret == ERROR_INSUFFICIENT_BUFFER ) && (dwSize > NULL ))
{
pTcpTable = (PMIB_TCPTABLE_OWNER_PID)malloc(dwSize);
memset(pTcpTable,0,dwSize);
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET,TCP_TABLE_OWNER_PID_ALL,NULL);
if (ret != NO_ERROR)
{
printf("getextendedtcptable failed with %x\n.ret";
free(pTcpTable);
return FALSE;
}
else if ( ret == NO_ERROR )
{
for (ULONG i =0; i<pTcpTable->dwNumEntries;i++)
{
LocalIpAddr.S_un.S_addr =pTcpTable->table[I].dwLocalAddr;
strcpy_s(szLocalAddr, sizeof (szLocalAddr), inet_ntoa(LocalIpAddr));
LocalPort = ntohs((u_short)pTcpTable->table[I].dwLocalPort);
dwState = pTcpTable->table[I].dwState;
dwOwningPid = pTcpTable->table[I].dwOwningPid;
printf(
"ipv4\nPid %d\nLocalAddr %s\nLocalPort %d\ndwState %d\n\n",
dwOwningPid,
szLocalAddr,
LocalPort,
dwState
);
}
}
free (pTcpTable);
}
return TRUE;
}


BOOL Ipv6OpenPortsAndPid(void)
{


PMIB_TCP6TABLE_OWNER_PID pTcpTable = NULL;
DWORD dwSize = NULL, ret, dwState, dwOwningPid;

u_short LocalPort = NULL;

printf("======================\nchecking ipv6 ip address and ports\n======================\n";
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET6,TCP_TABLE_OWNER_PID_ALL,NULL);
if ( ( ret != NO_ERROR ) && ( ret == ERROR_INSUFFICIENT_BUFFER ) && (dwSize > NULL ))
{
pTcpTable = (PMIB_TCP6TABLE_OWNER_PID)malloc(dwSize);
memset(pTcpTable,0,dwSize);
ret = GetExtendedTcpTable(pTcpTable,&dwSize,TRUE,AF_INET6,TCP_TABLE_OWNER_PID_ALL,NULL);
if (ret != NO_ERROR)
{
printf("getextendedtcptable failed with %x\n.ret";
free(pTcpTable);
return FALSE;
}
else if ( ret == NO_ERROR )
{
for (ULONG i =0; i<pTcpTable->dwNumEntries;i++)
{


LocalPort = ntohs((u_short)pTcpTable->table[I].dwLocalPort);
dwState = pTcpTable->table[I].dwState;
dwOwningPid = pTcpTable->table[I].dwOwningPid;
printf(
"ipv6\nPid %d\n local address %s\nLocalPort %d\ndwState %d\n\n",
dwOwningPid,
pTcpTable->table[I].ucLocalAddr[0],
LocalPort,
dwState
);
}
}
free (pTcpTable);
}
return TRUE;
}


int _tmain(int argc, _TCHAR* argv[])
{
Ipv4OpenPortsAndPid();
Ipv6OpenPortsAndPid();
return 0;

}





output

Code:


cptab\Release>getexttcptab.exe | grep -A 3 -B 3 -i .*Local.*47873
ipv4
Pid 3640
LocalAddr 0.0.0.0
LocalPort 47873
dwState 2

if it worked then i will merge both into the dll replacing all the psapi stuff


ipv4
--
ipv6
Pid 3640
local address (null)
LocalPort 47873
dwState 2

cptab\Release>tlist | grep 3640
3640 HelpLibAgent.exe


Kayaker
August 5th, 2013, 20:43
Code:
======================
checking ipv4 ip address and ports
======================
ipv4
Pid 4
LocalAddr 127.0.0.1
LocalPort 47873
dwState 2

======================
checking ipv6 ip address and ports
======================

nop

blabberer
August 6th, 2013, 02:43
Quote:
[Originally Posted by Kayaker;95149]
Code:
======================
======================
checking ipv6 ip address and ports
======================

nop


this output is reverse of what you described in your first post it seems you don't have ipv6 resolved but ipv4 is ok
then the code in the first branch should have been ok ??
whatever your machine gives a port and it should be sufficient for the purposes i think
let me find some time to merge this back into the dll

btw what is the following command's output

cmd.exe netsh interface ipv6 show address

Kayaker
August 6th, 2013, 04:04
Hmm. I think the initial problem was EnumProcessModules not being able to find x64 helplibagent from the 32 bit code, but you found a way around that.

I did mention ipv6 because Process Explorer shows helplibagent loads ipv6 dlls (wship6.dll - Winsock2 Helper DLL (TL/IPv6)), but it also loads ipv4 dlls (WSHTCPIP.DLL - Winsock2 Helper DLL (TL/IPv4)).
However this was before your latest test which shows only ipv4 is actually used, as well as the tcpview/netstat tests which show TCP is the connection used for port 47873, not TCP6. So that whole ipv6 thing might have been a distraction.

Note that in my output from your last test the PID is still 4 (system), whereas you seem to get a real PID that you can tie to a process name. So I think it still remains to be seen if you can confirm port 47873 = helplibagent from first principles.

Indy
August 6th, 2013, 08:39
mb IE ?

blabberer
August 6th, 2013, 15:06
Quote:
[Originally Posted by Kayaker;95151] the PID is still 4 (system)


arrrgh i probably ignored it now re looking i also see the local address is different as well i get a 0.0.0.0 while your output shows 127.0.0.1

dont know have to see whats the problem google seems to be full of win7 ate my port complaints especially for apache running on port 80

and some hacks like freeing up the http service (net stop http)

ok ill implement the wts and hardcode the port and update the archives for now

Kayaker
August 7th, 2013, 00:26
You may have read in C:/Program Files/Microsoft Help Viewer/v1.0/ReadMe_ENG.htm that the method of changing the default port of 47873 is by adding the string registry value AgentPort under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Help\v1.0 with the value of the port to use.

To support that possibility the plugin could check the registry for a modified port, or maybe more simply just give an option for the user to set the port the plugin "hardcodes" if the default one isn't used and store that in ollydbg.ini.

I checked the .NET helplibagent.exe and port 47873 is hardcoded as a string value (ldstr ":47873". I doubt that even an uberl33t is going to modify the port by hacking the file itself.

blabberer
August 7th, 2013, 07:07
Quote:
[Originally Posted by Kayaker;95161]You may have read in C:/Program Files/Microsoft Help Viewer/v1.0/ReadMe_ENG.htm that the method


yeah thats what i meant when i posted in an earlier reply

Quote:

the default port is always 47873 unless some super hacker chose to modify it
(and on modifying the port there is a lengthy process with httpconfig.exe (resource kit tool for xp/2003 and a completely differnt tool for vista+ ) to register it with ms-xhelp protocol so you can possibly hardcode it and it shouldn't fail


also port no can be leeched from regitry hklm\\\\...\\v1.0


but i hate hard coding so was doing all this crap

i could have simply nagged the user for pid , session id , port and exepath , and could have got away with it

but it sucks and i will hate to be an user of such a nag

(if it was proclaimed by ms that no one dare use 47873 we strongly discourage and actively prohibit its usage and will cause sad faces looking blue in your machine i would have hard coded it but they leave an option to modify it but not an option to retrieve the existing or modified port
transparently that sucks )

well no point cribbing lets enjoy the hard code with a soft click

thanks for your tests and test results
appreciated

Kayaker
August 7th, 2013, 10:06
I agree, it's kind of unsatisfying not being able to determine the port used. I'll try to test later if the helplibagent instance the plugin spawns gives a valid pid with tcpview, which i think should give you the edge you need. The plugin could simply use that rather than testing for any existing running service.

EDIT: The helplibagent process the plugin itself creates, while it has a real pid that can be enumerated in other ways, still shows the owning process id as system(4) using tcpview/netstat.

blabberer
August 8th, 2013, 11:53
neat lets say the system is great every one including tom's dick's and harry's uncle nowadays lectures that everything should be system oriented
and wants to audit systems only so ms is going that way fast forward all process are system so one pid one name one system and all is well

try this toy and see what it quacks

http://www.microsoft.com/en-in/download/details.aspx?id=17148

http://support.microsoft.com/kb/832919

edit

try these toys too seems to have an x 64 version as well

http://www.nirsoft.net/utils/cports.html

Kayaker
August 8th, 2013, 19:41
Neither tool shows any additional information. PortQry (admin mode) did mention that "Port to process mapping is not supported on this system". Googling for that just referred back to things like tcpview/tcpvcon, fport or netstat -o, all of which have been tried and don't resolve the 47873 <-> helplibagent relationship.

blabberer
August 12th, 2013, 02:01
what did you say marks proc explorer returns ?

Kayaker
August 12th, 2013, 03:13
2789

For comparison, HelpViewer itself. The TCP connections close after about a minute, with only the UDP one remaining.

2790

blabberer
August 21st, 2013, 15:10
@k

compile run confirm the path in your machine and paste the output

Code:


#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <shlwapi.h>
// ASSOCF_NONE is undefined in sdk but documented in msdn as 0
#define ASSOCF_NONE 0

#pragma comment(lib, "shlwapi.lib"

int FindPathToHelpLibeAgent (void)
{
DWORD pcchOut = NULL;
LPTSTR pszOut;
HRESULT hr;
hr = AssocQueryString(ASSOCF_NONE,ASSOCSTR_EXECUTABLE,TEXT("MS-XHelp",TEXT("open",NULL,&pcchOut);
if ( (hr == S_FALSE) && pcchOut )
{
pszOut = (LPTSTR)malloc(pcchOut * sizeof(wchar_t)+0x10);
if (pszOut)
{
if (( hr = AssocQueryString(ASSOCF_NONE,ASSOCSTR_EXECUTABLE,TEXT("MS-XHelp",TEXT("open",pszOut,&pcchOut) ) == S_OK)
{
_tprintf(L"MS-Xhelp is associated with %s \n",pszOut);
}
else
{
_tprintf(L"AssocQuery Failed with %x\n",hr);
}
}
free(pszOut);
}
return 0;
}

int FindPathToDefaultBrowser (void)
{
DWORD pcchOut = NULL;
LPTSTR pszOut;
HRESULT hr;
hr = AssocQueryString(ASSOCF_NONE,ASSOCSTR_EXECUTABLE,TEXT("http",TEXT("open",NULL,&pcchOut);
if ( (hr == S_FALSE) && pcchOut )
{
pszOut = (LPTSTR)malloc(pcchOut * sizeof(wchar_t)+0x10);
if (pszOut)
{
if (( hr = AssocQueryString(ASSOCF_NONE,ASSOCSTR_EXECUTABLE,TEXT("http",TEXT("open",pszOut,&pcchOut) ) == S_OK)
{
_tprintf(L"http is associated with %s \n",pszOut);
}
else
{
_tprintf(L"AssocQuery Failed with %x\n",hr);
}
}
free(pszOut);
}
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
FindPathToHelpLibeAgent();
FindPathToDefaultBrowser();
return 0;
}

result for me in xp sp3 is

MS-Xhelp is associated with C:\Program Files\Microsoft Help Viewer\v1.0\HelpLibAgent.exe
http is associated with c:\program files\mozilla firefox\firefox.exe

Press any key to continue . . .


Kayaker
August 21st, 2013, 18:39
MS-Xhelp is associated with c:\Program Files\Microsoft Help Viewer\v1.0\HelpLibAgent.exe
http is associated with C:\Program Files (x86)\Mozilla Firefox\firefox.exe

btw, I had to change your '_tprintf %s' to 'wprintf %S'. All part of that unicode/mbcs cornfusion.