Log in

View Full Version : InstallShield inx files again


volodya
January 28th, 2004, 15:18
Hello, everyone.
I'm curious about the format of the inx file.
I've read everything on the forum concerning IS and I'm hardly satisfied. Does anyone know the structure of the inx-file?
Should I dig the InstDev Studio or IDriver/ISRT/IScript7 etc...? Has anyone done it before?

gook
January 28th, 2004, 17:00
Hello volodya,

There is a decompiler for inx files made by a certain "Nekosuki".
It's output can be useful to patch these inx files.

volodya
January 28th, 2004, 18:05
I know. Thanks.
As I told I'm interested to go a little bit more deeply!
I need the structure of the inx file itself.
One way is o reverse NEKOSUKI tool or sid by sn00pe. It is a really BAD idea.
The another way is to reverse engineer InstDev Studio or IDriver/ISRT.dll - but they are COM-methods which complicates my life a little bit...
That's why I'm asking...

JMI
January 28th, 2004, 18:22
Ah, volodya, remember that it is such "complications" that make life interesting. You know you have been putting off learning about COMs, now's your time to jump in there with both feet. Then you can teach the rest of us, who have also been avoiding COMs. what's important, and then WE won't have to learn it from scratch. Seems like a good plan for the rest of us.

Regards,

volodya
January 28th, 2004, 18:28
Seems like a good plan for the rest of us.

It is.
But painful for me...

JMI
January 28th, 2004, 18:50
Well the only proper reply would be; "I feel your pain ..... now get to work."

Regards,

volodya
February 17th, 2004, 13:11
Well, I tried my best - useless so far.
The task is - how can one find virtual address for a given COM method. Via VTBL or anyhow. I don't care. What did I try? Well, I read Matt Pietrek's example with CoCreateInstance - tried to apply - useless. Well, then I studied some russian articles and quickly read through available Win-sources - useless too, but I'm attaching the sources anyway. May be they will be useful for someone one day...


#include <windows.h>
#include <ole2.h>
#include <iostream>
using namespace::std;

int main( int argc, LPCTSTR * argv )
{
ITypeInfo *ptInfo = 0;
TYPEATTR *lpTypeAttr = 0;
ITypeLib *pITypeLib = 0;

CoInitialize( 0 );

HRESULT hr = LoadTypeLib( L"IScript7.dll", &pITypeLib );
UINT tiCount = pITypeLib->GetTypeInfoCount();

for ( UINT i = 0; i < tiCount; i++ )
{

if ( LOWORD(pITypeLib->GetTypeInfo(i, &ptInfo)) )
{
wcerr << "Bad TypeInfo!" << endl;
}
else
{
if ( LOWORD(ptInfo->GetTypeAttr(&lpTypeAttr)) )
{
wcerr << "Bad TypeAttr!" << endl;
}
else
{
switch (lpTypeAttr->typekind)
{
case TKIND_MODULE:
case TKIND_INTERFACE:
case TKIND_DISPATCH:
FUNCDESC FAR *ptFuncDesc;
for (UINT n = 0 ; n < lpTypeAttr->cFuncs; n++)
{
if ( LOWORD(ptInfo->GetFuncDesc(n, &ptFuncDesc)) )
{
wcerr << "Bad FuncDesc!" << endl;
}
else
{
BSTR bstrName; // name of member
BSTR bstrDoc; // file string
DWORD hContext; // help context

if ( LOWORD(ptInfo->GetDocumentation(ptFuncDesc->memid, &bstrName, &bstrDoc, &hContext, NULL)) )
{
wcerr << "Bad GetDocumentation!" << endl;
}
else
{

wcout << "function name: " << bstrName << endl;

BSTR bstrDllName;
BSTR bstrEntryName;
WORD wOrdinal;

if(!LOWORD(ptInfo->GetDllEntry(ptFuncDesc->memid,
ptFuncDesc->invkind,
&bstrDllName,
&bstrEntryName,
&wOrdinal)))
{
VOID *lpVoid;
ptInfo->AddressOfMember(ptFuncDesc->memid,
ptFuncDesc->invkind,
&lpVoid);

wcout << "function address: " << lpVoid << endl;

SysFreeString(bstrDllName);
SysFreeString(bstrEntryName);
}

SysFreeString(bstrName);
SysFreeString(bstrDoc);
}
}
ptInfo->ReleaseFuncDesc(ptFuncDesc) ;
ptFuncDesc = 0;
}
break;
default:
break;
}
}
ptInfo->ReleaseTypeAttr(lpTypeAttr);
}
ptInfo->Release();
}


CoUninitialize();

return 0;
}

volodya
February 17th, 2004, 13:58
One more thing embarasses me. If you go to the IScript7.dll file, strip out TLB information from there into IDL file. Preprocess it then with MIDL compiler like
midl /header "IScript_h.h" IScript7.IDL
and then

HRESULT result = CoCreateInstance(CLSID_SetupScriptEngine, NULL, CLSCTX_ALL, IID_ISetupScriptEngine, &isse);
((ISetupScriptEngine*)isse)->Open(L"somefile.inx";

Everything is working!
WTF...

volodya
February 17th, 2004, 20:16
OK, problem is partially solved...
Now using CoCreateInstance. Sources will be here soon...

The output example:

D:\com_va\Debug>com_va IScript7.dll
****** Object name: SetupScriptEngine
{777C89DF-5C36-11D5-ABAF-00B0D02332EB}
****** Object name: ISetupScriptEngine
name: Open
VA is: 1000c0db
offset from vtbl is: c
name: Close
VA is: 1000c192
offset from vtbl is: 10
name: AttachToDebugger
VA is: 1000c261
offset from vtbl is: 14
name: Controller
VA is: 1000c6be
offset from vtbl is: 18

nikolatesla20
February 17th, 2004, 21:19
Hey volodya, I did all this work for you already

http://www.woodmann.net/forum/showthread.php?t=5046&highlight=matt


Heh, and I agree, there hasn't been much research into COM reversing. This was an area I started to open up for myself, hence the thread above. I've already reversed one COM dll before this too.

-nt20

volodya
February 17th, 2004, 21:32
No, my dear nikolatesla20. Not that easy, my friend. I also know about this utility and have extensively read Pietrek article, BUT... For general case his utility does NOT work Therefore I have to go through the same shit again. To persuade me I'm wrong, would you, please, run his utility against two files - IDriver.exe and IScript7.dll - these are main InstallShield files located in C:\Program Files\Common Files\InstallShield\Driver\7\Intel 32. Thanks in advance. If succesful - please, let me know!

nikolatesla20
February 17th, 2004, 23:30
what im most afraid of is COM objects with dual interfaces, in other words, a COM object doesn't always need to have a type library in it, it could choose to only support the GetNamesOfIDs() and Invoke() routines *blech*.

-nt20

nikolatesla20
February 17th, 2004, 23:51
It works fine for me, I get a 1K file with the interface functions just fine. Just drop the outputted DBG file in the same directory as the DLL, and then open up IDA and disasm the file. IDA will load the DBG file and now you'll get symbol information for all the interface functions (only the vtable functions tho, not dispatch interface functions)

check it - this is from IScript7.dll. Doesn't look like anything too complex.

Code:

.text:1000C0D9 ISetupScriptEngine__Close proc near ; CODE XREF: sub_1000BFD4+19p
.text:1000C0D9 ; DATA XREF: .rdata:10027CCCo ...
.text:1000C0D9 mov eax, offset loc_10024864
.text:1000C0DE call __EH_prolog
.text:1000C0E3 sub esp, 0Ch
.text:1000C0E6 push ebx
.text:1000C0E7 push esi
.text:1000C0E8 mov esi, [ebp+8]
.text:1000C0EB push edi
.text:1000C0EC xor ebx, ebx
.text:1000C0EE mov [ebp-10h], esp
.text:1000C0F1 cmp [esi+18h], ebx
.text:1000C0F4 lea edi, [esi+18h]
.text:1000C0F7 mov [ebp-4], ebx
.text:1000C0FA jz short loc_1000C133
.text:1000C0FC push ebx ; lParam
.text:1000C0FD push ebx ; wParam
.text:1000C0FE push 12h ; Msg
.text:1000C100 push dword ptr [esi+10h] ; idThread
.text:1000C103 call ds:PostThreadMessageA
.text:1000C109 mov eax, [esi+14h]
.text:1000C10C push 2710h ; dwMilliseconds
.text:1000C111 push eax ; hHandle
.text:1000C112 call ds:WaitForSingleObject
.text:1000C118 push ebx
.text:1000C119 push edi
.text:1000C11A call sub_10006922
.text:1000C11F mov [esi+10h], ebx
.text:1000C122 mov eax, [esi+14h]
.text:1000C125 cmp eax, ebx
.text:1000C127 jz short loc_1000C133
.text:1000C129 push eax ; hObject
.text:1000C12A call ds:CloseHandle
.text:1000C130 mov [esi+14h], ebx
.text:1000C133
.text:1000C133 loc_1000C133: ; CODE XREF: ISetupScriptEngine__Close+21j
.text:1000C133 ; ISetupScriptEngine__Close+4Ej
.text:1000C133



more:
Code:

.text:1000C020 leave
.text:1000C021 retn
.text:1000C021 sub_1000BFD4 endp ; sp = 4
.text:1000C021
.text:1000C021 ; ---------------------------------------------------------------------------
.text:1000C022 ISetupScriptEngine__Open db 0B8h ; DATA XREF: .rdata:10027CC8o
.text:1000C022 ; .rdata:10027D44o
.text:1000C023 ; ---------------------------------------------------------------------------
.text:1000C023 pop eax
.text:1000C024 dec eax
.text:1000C025 add dl, [eax]
.text:1000C027 call __EH_prolog
.text:1000C02C sub esp, 3Ch
.text:1000C02F and dword ptr [ebp-14h], 0
.text:1000C033 and dword ptr [ebp-4], 0
.text:1000C037 push ebx
.text:1000C038 push esi
.text:1000C039 push edi
.text:1000C03A lea eax, [ebp-18h]
.text:1000C03D mov [ebp-10h], esp
.text:1000C040 push eax
.text:1000C041 call sub_1000C958
.text:1000C046


You have to have the COM object DLL registered on the system before the tool will work.


These files are not standard anyhow, if you look at the embedded type library, it doesn't start with the "MSFT" header, instead it's some other weird text, and it looks encrypted, like it's compressed or something. I'm going to see how it looks in memory.

Also, IDriver.exe has 90% of it functions as dispatch interface only. So no vtable offsets for those. It crashes when I run the tool on it (most likely cause it's an exe and not a DLL). I'll play with it some more.


If you new code works on EXE's it would be cool if you would be kind and post if for us

-nt20

volodya
February 18th, 2004, 11:42
nikolatesla20
I'm having some more questions for you.
OK, indeed, I was able to go through your way and obtain DBG file for the DLL, but not for an exe! So, I'm going to continue my business and write my own tool. I'm half of my way on it.
Therefore, I'd like to ask two more questions to you. Sorry

You have to have the COM object DLL registered on the system before the tool will work

Also, IDriver.exe has 90% of it functions as dispatch interface only. So no vtable offsets for those.

These two phrases... I'd like to get more information! As to the first one, do you mean that there should be CLSID in registry? Therefore, should one MANUALLY go get typelib from dll/exe, check typelib GUID and check it in the registry?

The second one is about dispatch interface. Where can I read more about it? As far as I know now ANY COM object MUST inherit IUnknown. I know nothing about IDispatch so far. Would you enlight me, please?

nikolatesla20
February 18th, 2004, 14:40
well, the COM registration is only because the tool needs to do a CoCreateInstance to grab its type library (I believe it does, anyway - haven't looked at the code in a while), and to do that, the DLL has to be registered.

The second part is about IDispatch interface. Yes, an object has to support IUnknown, but this is only three methods - AddRef, Release, and QueryInterface. YOu can use QueryInterface to ask the object if it supports another interface. For example say you have a box object. The box object could support the IBox interface. This is only an "interface" in respects that it's represented by a GUID. So you can call IUnknown's QueryInterface (a common GUID that all objects support) and ask it for the the IBox interface using the GUID for the IBox interface. The GUID identifies it as the IBox interface ! If the IBox interface is supported, you'll get back a pointer to a pointer to that interface (blech pointers to pointers).

Ok, so now you have a grab of the IBox interface. Now the problem is calling methods inside it. How do you know the order of the methods in the vtable? For "early binding", in which you use the vtable, that is what the type library helps you with. It gives you the vtable offsets of each member function. When you compile code to talk to this object the compiler uses its type library info to determine the offset within the vtable to call. So on the call to QueryInterface you get "ObjectPointer". Now you dereference this to get the "Vtable Pointer" you then move an offset in the vtable (since the first three entries are the functions AddRef, Release, QueryInterface , remember, all interfaces must support IUnknown), then you call this function. SO if you have vtable entries like so, you can do what is called "early binding", you can make a direct call into the DLL, and it is very fast. This also allows you to track down the location of the code easily too, using a type library tool. You use the type library to get the function offsets. Then you create the object, get a pointer to its vtable, and then mark the address of each vtable entry as its function name, using the offsets. This is what Matt's tool does.

A problem arises when you find an object that supports a "dual interface", in other words, it also supports the IDispatch interface. IDispatch is NOT a vtable of each function on the object. It's a set of functions you can use to call object member functions by asking for them.

For example, say IBox supported Open and Close methods. Now also say the box supports the IDispatch interface as well. If I wanted to use IDispatch, instead of IBox, I would do a IUnknown QueryInterface with the GUID for IDispatch (a common GUID). Once I got that, I could then use IDispatch to call methods like so:

Code:

QueryInterface checks whether the object supports IDispatch. (As with any call to QueryInterface,
the returned pointer must be released when it is no longer needed.)

// Call QueryInterface to see if object supports IDispatch.
hresult = punk->QueryInterface(IID_IDispatch, &pdisp);
GetIDsOfNames retrieves the DISPID for the indicated method or property, in this case, szMember.

// Retrieve the dispatch identifier for the SayHello method.
// Use defaults where possible.
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szMember,
1,
LOCALE_USER_DEFAULT,
&dispid);
In the following call to Invoke, the DISPID indicates the property or method
to invoke. The SayHello method does not take any parameters, so the fifth
argument (&dispparamsNoArgs), contains a Null and 0, as initialized at
declaration.

To invoke a property or method that requires parameters, supply the
parameters in the DISPPARAMS structure.

// Invoke the method. Use defaults where possible.
hresult = pdisp->Invoke(
dispid,
IID_NULL,
LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD,
&dispparamsNoArgs,
NULL,
NULL,
NULL);


As you can see, you get the ID of the method using the method's name. YOu then call the method USING INVOKE(). Not only that, but you don't even call invoke with the name, you use a function ID. In other words, you never call the method directly. This is called "Late binding". Scripting languages use this since it allows them to not have to know anything about the object ahead of time. They can get the list of methods from the type library and use the IDispatch interface to call them by name (also passing in the approriate args). Know what a VARIANT is? A VARIANT was created for this type of thing.

So the problem is, the "magic" of calling the actual routine in code is done by Invoke(). I suppose you might be able to make some tool that could find the IDispatch -> Invoke() function, and then see which routines invoke calls based on function ID's, and map this to the function ID's found from GetIDsOfNames(). Maybe that would work. But that's the reason you just can't load up an object with this tool and gets function offsets if the object only has dispatch entries.

-nt20

volodya
February 18th, 2004, 17:28
Thanks. Thanks again.

reverser
February 18th, 2004, 20:39
Here's some stuff I wrote several years ago. I didn't get very far
Hope it will be of some help.

reverser
February 18th, 2004, 20:48
Some more stuff. I've run tlviewer.exe on IS7 and 8 engine files. Among other things, it shows vtable offsets for methods.

volodya
February 18th, 2004, 20:49
reverser

first of all - my biggest thanks to you, sir. Sources open... Good!
secondly - I'd prefer not to have the tool, honestly, I don't need it. What I'd prefer to have is a METHODOLOGY of how to obtain the structure of the given inx file and how to decompile the higher versions inx files.
That's why I'm digging COM IS files now. That's why I'm playing with InstDev now.

BTW, nikolastela, the utility has been finished. It is better then Pietrek's one For each CoClass we are enumerating through the list of methods and fulfilling them with VAs. The sources are open:


//copyright flankerx and volodya 2004

#include <windows.h>
#include <ole2.h>
#include <iostream>
using namespace::std;

char* lpFuncType[] = {
"FUNC_VIRTUAL",
"FUNC_PUREVIRTUAL",
"FUNC_NONVIRTUAL",
"FUNC_STATIC",
"FUNC_DISPATCH"
};

char* lpTypeKind[] = {
"enum",
"struct",
"module",
"interface",
"dispinterface",
"coclass",
"typedef",
"union"
};

int wmain(int argc, wchar_t *argv[ ], wchar_t *envp[ ])
{
ITypeLib *pITypeLib = 0;
ITypeLib *pTL = 0;
ITypeInfo *ptInfo = 0;
ITypeInfo *pInfo = 0;
TYPEATTR *lpTypeAttr = 0;
TYPEATTR *lpIntAttr = 0;
VARDESC *lpVarDesc = 0;
BSTR bstrGUID = 0;
BSTR bstrName = 0;
GUID clsid;
GUID iid;

CoInitialize( 0 );

if( argc != 2)
{
wcerr << "Usage: com_va filename" << endl;
return -1;
}


if ((LoadTypeLib(argv[1], &pITypeLib )) != S_OK)
{
wcerr << "LoadTypeLib failed for file: " << argv[1] << endl;
return -1;
}

unsigned tiCount = pITypeLib->GetTypeInfoCount();

/* procedure outline:
foreach typeinfo
if typekind == TKIND_COCLASS
foreach member interface
print_interface_info(clsid, iid)
end foreach
end if
end foreach
*/

for ( unsigned i = 0; i < tiCount; i++ )
{
if (!LOWORD(pITypeLib->GetTypeInfo(i, &ptInfo)))
{
if(!LOWORD(ptInfo->GetTypeAttr(&lpTypeAttr)))
{
if(lpTypeAttr->typekind != TKIND_COCLASS)
continue;

clsid = lpTypeAttr->guid;
StringFromCLSID(clsid, &bstrGUID);
pITypeLib->GetDocumentation(i, &bstrName, 0, 0, 0);

wcout << "coclass " << bstrName << " GUID: " << bstrGUID << endl;

SysFreeString(bstrName);
SysFreeString(bstrGUID);

unsigned mCount = lpTypeAttr->cImplTypes;
//foreach member interface
for(unsigned j = 0; j < mCount; j++)
{
HREFTYPE ref = 0;
ptInfo->GetRefTypeOfImplType(j, &ref);
if(!LOWORD(ptInfo->GetRefTypeInfo(ref, &pInfo)))
{
// get index in typelib
UINT index = 0;
pInfo->GetContainingTypeLib(&pTL, &index);
pTL->GetDocumentation(index, &bstrName, 0, 0, 0);
pInfo->GetTypeAttr(&lpIntAttr);
iid = lpIntAttr->guid;
StringFromCLSID(iid, &bstrGUID);

wcout << "\t" << lpTypeKind[lpIntAttr->typekind] << " " << bstrName << "GUID: " << bstrGUID << endl;

SysFreeString(bstrName);
SysFreeString(bstrGUID);

unsigned fCount = lpIntAttr->cFuncs;
for(unsigned k = 0; k < fCount; k++)
{
FUNCDESC* fdesc = 0;
pInfo->GetFuncDesc(k, &fdesc);

pInfo->GetDocumentation(fdesc->memid, &bstrName, 0, 0, 0);

LPVOID iUnk = 0;
if (!LOWORD(CoCreateInstance(clsid, 0, CLSCTX_ALL, iid, &iUnk)))
{
BYTE* pVTable = (BYTE*)*(DWORD*)(iUnk);
DWORD pFunction = *(DWORD*)(pVTable + fdesc->oVft);

wcout.unsetf( ios_base::dec );
wcout.setf( ios_base::hex );
wcout << "\t\t" << lpFuncType[fdesc->funckind] << " " << bstrName << " VA: " << pFunction << " Offset: " << fdesc->oVft << endl;
}

SysFreeString(bstrName);
pInfo->ReleaseFuncDesc(fdesc);
}
pInfo->ReleaseTypeAttr(lpIntAttr);
pInfo->Release();
}//if(!LOWORD(ptInfo->GetRefTypeInfo(ref, &pInfo)))
}//for(UINT j = 0; j < mCount; j++)
ptInfo->ReleaseTypeAttr(lpTypeAttr);
}//if(!LOWORD(ptInfo->GetTypeAttr(&lpTypeAttr)))
ptInfo->Release();
}//if (!LOWORD(pITypeLib->GetTypeInfo(i, &ptInfo)))
}//for ( UINT i = 0; i < tiCount; i++ )

CoUninitialize();
return 0;
}

reverser
February 18th, 2004, 22:07
Quote:
[Originally Posted by volodya]reverser

first of all - my biggest thanks to you, sir. Sources open... Good!
secondly - I'd prefer not to have the tool, honestly, I don't need it. What I'd prefer to have is a METHODOLOGY of how to obtain the structure of the given inx file and how to decompile the higher versions inx files.

I would gladly help you if I remembered how It was quite long ago. And you can learn from the sources about some structures... You can also try to reach sn00pee and/or nekosuki, I'm sure they know much more about inx

Solomon
February 19th, 2004, 00:45
This tool may help a little.
hxxp://www.ddevel.com

volodya
February 19th, 2004, 11:08
Solomon

Wow! I'm impressed! I'll give it a try.
Meanwhile the new pre-release version of my COM/VA tools.
Made by me and flankerx. Big thanks to nikolastela and kolam.