What is a way to lookup a COM method offset with an image, just based on the module name and method name.
For example want to find "Exec" method in "WScript.Shell" In this scenario we know the method is in wshom.ocx. In this case it is relatively easy to find with the public symbols.
We can find the vtable :
.text:7B471070 ; const CWshShell::`vftable'{for `IWshShell3'}
.text:7B471070 ??_7CWshShell@@6BIWshShell3@@@ dd offset ?QueryInterface@CWshExec@@UAGJABU_GUID@@PAPAX@Z
.text:7B471070 ; DATA XREF: CWshShell::Create(IUnknown *)+74↓o
.text:7B471070 ; CWshShell::`scalar deleting destructor'(uint)+B↓o
.text:7B471070 ; CWshExec::QueryInterface(_GUID const &,void * *)
.text:7B471074 dd offset ?AddRef@CWebPreviewDispatch@@EAGKXZ ; CWebPreviewDispatch::AddRef(void)
.text:7B471078 dd offset ?Release@?$CAggregatedUnknown@$02@@UAGKXZ ; CAggregatedUnknown<3>::Release(void)
.text:7B47107C dd offset ?GetTypeInfoCount@CWshExec@@UAGJPAI@Z ; CWshExec::GetTypeInfoCount(uint *)
.text:7B471080 dd offset ?GetTypeInfo@CWshShell@@UAGJIKPAPAUITypeInfo@@@Z ; CWshShell::GetTypeInfo(uint,ulong,ITypeInfo * *)
.text:7B471084 dd offset ?GetIDsOfNames@CWshShell@@UAGJABU_GUID@@PAPAGIKPAJ@Z ; CWshShell::GetIDsOfNames(_GUID const &,ushort * *,uint,ulong,long *)
.text:7B471088 dd offset ?Invoke@CWshShell@@UAGJJABU_GUID@@KGPAUtagDISPPARAMS@@PAUtagVARIANT@@PAUtagEXCEPINFO@@PAI@Z ; CWshShell::Invoke(long,_GUID const &,ulong,ushort,tagDISPPARAMS *,tagVARIANT *,tagEXCEPINFO *,uint *)
.text:7B47108C dd offset ?get_SpecialFolders@CWshShell@@UAGJPAPAUIWshCollection@@@Z ; CWshShell::get_SpecialFolders(IWshCollection * *)
.text:7B471090 dd offset ?get_Environment@CWshShell@@UAGJPAUtagVARIANT@@PAPAUIWshEnvironment@@@Z ; CWshShell::get_Environment(tagVARIANT *,IWshEnvironment * *)
.text:7B471094 dd offset ?Run@CWshShell@@UAGJPAGPAUtagVARIANT@@1PAH@Z ; CWshShell::Run(ushort *,tagVARIANT *,tagVARIANT *,int *)
.text:7B471098 dd offset ?Popup@CWshShell@@UAGJPAGPAUtagVARIANT@@11PAH@Z ; CWshShell::Popup(ushort *,tagVARIANT *,tagVARIANT *,tagVARIANT *,int *)
.text:7B47109C dd offset ?CreateShortcut@CWshShell@@UAGJPAGPAPAUIDispatch@@@Z ; CWshShell::CreateShortcut(ushort *,IDispatch * *)
.text:7B4710A0 dd offset ?ExpandEnvironmentStringsA@CWshShell@@UAGJPAGPAPAG@Z ; CWshShell::ExpandEnvironmentStringsA(ushort *,ushort * *)
.text:7B4710A4 dd offset ?RegRead@CWshShell@@UAGJPAGPAUtagVARIANT@@@Z ; CWshShell::RegRead(ushort *,tagVARIANT *)
.text:7B4710A8 dd offset ?RegWrite@CWshShell@@UAGJPAGPAUtagVARIANT@@1@Z ; CWshShell::RegWrite(ushort *,tagVARIANT *,tagVARIANT *)
.text:7B4710AC dd offset ?RegDelete@CWshShell@@UAGJPAG@Z ; CWshShell::RegDelete(ushort *)
.text:7B4710B0 dd offset ?LogEvent@CWshShell@@UAGJPAUtagVARIANT@@PAG1PAF@Z ; CWshShell::LogEvent(tagVARIANT *,ushort *,ushort *,short *)
.text:7B4710B4 dd offset ?AppActivate@CWshShell@@UAGJPAUtagVARIANT@@0PAF@Z ; CWshShell::AppActivate(tagVARIANT *,tagVARIANT *,short *)
.text:7B4710B8 dd offset ?SendKeys@CWshShell@@UAGJPAGPAUtagVARIANT@@@Z ; CWshShell::SendKeys(ushort *,tagVARIANT *)
.text:7B4710BC dd offset ?Exec@CWshShell@@UAGJPAGPAPAUIWshExec@@@Z ; CWshShell::Exec(ushort *,IWshExec * *)
.text:7B4710C0 dd offset ?get_CurrentDirectory@CWshShell@@UAGJPAPAG@Z ; CWshShell::get_CurrentDirectory(ushort * *)
.text:7B4710C4 dd offset ?put_CurrentDirectory@CWshShell@@UAGJPAG@Z ; CWshShell::put_CurrentDirectory(ushort *)
In this case offset at 7B4710BC points to the method I'm looking into. However I'm trying to more generally resolve this when there are no symbols available.
Analyzing the following with WinDbg:
"C:\WINDOWS\syswow64\WindowsPowerShell\v1.0\powershell.exe" -Command "$obj = New-Object -ComObject WScript.Shell;$obj.Exec('notepad')"
Found that C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll has a method System.Management.Automation.ComInterop.ComTypeDesc.TryGetFunc(System.String, System.Management.Automation.ComInterop.ComMethodDesc ByRef)
Which is taking "Exec" as the first parameter, and seems to internally use a hash table to find the method, but couldn't see how the hash table is initially populated.
Eventually the function is called via Oleaut32!DispCallFunc which seems to have a function definition like:
HRESULT __stdcall DispCallFunc(
void *pvInstance,
ULONG_PTR oVft,
CALLCONV cc,
VARTYPE vtReturn,
UINT cActuals,
VARTYPE *prgvt,
VARIANTARG **prgpvarg,
VARIANT *pvargResult)
In this case pvInstance is pointing to the vfTable in the COM object, and oVft is the offset for the specific target method.
Essentially I am trying to work out how do I calculate the vftable offset for "WScript.Shell" (with the module name already known)(, and oVft offset for method i.e. "Exec", even without symbols for a COM method.
ITypeLib
interface usingLoadTypeLibEx
.ITypeInfo::AddressOfMember
might do what you need?