続・Unix C がちょっと書けるだけの俺による令和最新式 Ghidra による Windows C++ アプリケーション解析入門 - E-MU 1616M CardBus が刺さってる Panasonic Let's Note CF-S10E がスリープからの復帰に失敗する (その 11)
前回 IUnknown 周りのコードを復元したことで IUnknown を実装(継承)しているクラス、すなわち COM インタフェースが一目瞭然になった。
PTR_QueryInterface_020013a8 XREF[2]: FUN_0200352f:02003538(*),
FUN_020037d5:020037eb(*)
02001394 68 5f 00 02 addr IUnknown::QueryInterface
02001398 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_020013a8 XREF[2]: FUN_0200381f:02003828(*),
FUN_02003a04:02003a1a(*)
020013a8 68 5f 00 02 addr IUnknown::QueryInterface
020013ac c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_020013c4 XREF[2]: FUN_02003c67:02003c70(*),
FUN_020041d1:020041ea(*)
020013c4 68 5f 00 02 addr IUnknown::QueryInterface
020013c8 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_020013e0 XREF[2]: FUN_0200422e:02004237(*),
FUN_020047c9:020047df(*)
020013e0 68 5f 00 02 addr IUnknown::QueryInterface
020013e4 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_02001400 XREF[2]: FUN_02004813:0200481d(*),
FUN_02004d43:02004d5c(*)
02001400 68 5f 00 02 addr IUnknown::QueryInterface
02001404 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_02001428 XREF[2]: FUN_02004d9e:02004da7(*),
FUN_02005070:02005086(*)
02001428 68 5f 00 02 addr IUnknown::QueryInterface
0200142c c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_0200143c XREF[2]: FUN_020050ba:020050c3(*),
FUN_02005acd:02005ae3(*)
0200143c 68 5f 00 02 addr IUnknown::QueryInterface
02001440 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_02001474 XREF[2]: FUN_02005b17:02005b21(*),
FUN_02005eba:02005ed3(*)
02001474 68 5f 00 02 addr IUnknown::QueryInterface
02001478 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_0200148c XREF[2]: FUN_02005f23:02005f2d(*),
FUN_02006339:02006352(*)
0200148c 68 5f 00 02 addr IUnknown::QueryInterface
02001490 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_020014ac XREF[2]: FUN_02006394:0200639d(*),
FUN_02006529:0200653f(*)
020014ac 68 5f 00 02 addr IUnknown::QueryInterface
020014b0 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_020014c0 XREF[2]: FUN_02006573:0200657c(*),
FUN_02007307:0200731d(*)
020014c0 68 5f 00 02 addr IUnknown::QueryInterface
020014c4 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_02001510 XREF[2]: FUN_02007351:0200735a(*),
FUN_0200ae45:0200ae5b(*)
02001510 68 5f 00 02 addr IUnknown::QueryInterface
02001514 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_0200162c XREF[2]: FUN_0200ae8f:0200ae98(*),
FUN_0200b676:0200b68c(*)
0200162c 68 5f 00 02 addr IUnknown::QueryInterface
02001630 c6 ae 00 02 addr IUnknown::AddRef
...
PTR_QueryInterface_02001658 XREF[2]: FUN_0200ba18:0200ba22(*),
FUN_0200c147:0200c160(*)
02001658 68 5f 00 02 addr IUnknown::QueryInterface
0200165c c6 ae 00 02 addr IUnknown::AddRef
継承するとオーバーライドしてないメンバ関数は継承元のもの引き継がれるからね、つーか IUnknown::Release は必ずオーバーライドされるんだな。
PTR_FUN_<アドレス> から名前が勝手に変わってしまったが後で全部変更するから気にしない。アドレス>
ということで COM インタフェースは 14 個らしいことがわかった、とりあえず順番に Unko1 ~ Unko14 とでもしておくか。
前回の IUnknown の仮名と被ってて紛らわしいから “Unknown COM Interfache No.1” で Unci1 ににすればよかったかも、ぶりぶり。
CtHelper.exe から呼んでるのは CLSID/REFIID の組み合わせはひとつだけなので、全部復元する必要も無いっちゃ無いのだがここは全部やっとこう。
コンストラクタとデストラクタは前回と同じ手順で特定できるので説明は割愛する。
そんでメンバ変数は継承元の IUnknown が引き継がれるから Data Type であらかじめ IUnknown 構造体を Unko1~14 にコピーしておくとよい、これだけでも随分と作業が楽になるはずである。
ポチポチ名前を変更していくと Unko8 と Unko9 は Unko5 を継承しているっぽい。
VFTBL_Unko8 XREF[2]: FUN_02005b17:02005b21(*),
Unko8:02005ed3(*)
02001474 68 5f 00 02 addr IUnknown::QueryInterface
02001478 c6 ae 00 02 addr IUnknown::AddRef
0200147c f1 5c 00 02 addr Unko8::Release
02001480 3d 5d 00 02 addr Unko8::FUN_02005d3d
02001484 65 5b 00 02 addr Unko5::FUN_02005b65
02001488 58 48 00 02 addr Unko5::FUN_02004858
VFTBL_Unko9 XREF[2]: FUN_02005f23:02005f2d(*),
Unko9:02006352(*)
0200148c 68 5f 00 02 addr IUnknown::QueryInterface
02001490 c6 ae 00 02 addr IUnknown::AddRef
02001494 60 62 00 02 addr Unko9::Release
02001498 ac 62 00 02 addr Unko9::FUN_020062ac
0200149c d9 62 00 02 addr Unko9::FUN_020062d9
020014a0 65 5b 00 02 addr Unko5::FUN_02005b65
020014a4 58 48 00 02 addr Unko5::FUN_02004858
020014a8 09 63 00 02 addr Unko9::FUN_02006309
いやこれサイズ最適化が効いて Unko5 Unko8 Unko9 が同一コードになってんのかな…単一継承だと判んねえやこれ。
C++ で継承はどのように実装されるのか
C++ で書いた以下のコードは
class IHasValue {
public:
virtual int getValue() = NULL;
};
class Foo : IHasValue {
private:
int foo = 1;
public:
int getValue() {
return foo;
}
};
class Bar : IHasValue {
private:
int bar = 2;
public:
int getValue() {
return bar;
}
};
class Buzz : public Foo, public Bar {
};
int
main()
{
Buzz* buzz = new Buzz();
if (buzz != NULL) {
std::cout << ((Foo *)buzz)->getValue() << ((Bar *)buzz)->getValue() << std::endl;
delete buzz;
}
return EXIT_SUCCESS;
}
以下の C コードと同じになるはずである。
#include <stdio.h>
#include <stdlib.h>
/* メモリアロケータ */
#define operator_new malloc
#define operator_delete free
/* IHasValue 純粋仮想関数テーブル */
struct IHasValueVFTBL {
int (*getValue)(struct Foo*);
};
/* クラス Foo */
struct FooVFTBL;
struct Foo {
const struct FooVFTBL* vftbl;
int foo;
};
/* Foo 仮想関数テーブル */
struct FooVFTBL {
int (*getValue)(struct Foo*);
void (*Delete)(struct Foo*);
};
static int Foo_getValue(struct Foo*);
void Foo_Delete(struct Foo*);
static struct FooVFTBL Foo_vftbl = {
&Foo_getValue,
& Foo_Delete
};
/* Foo コンストラクタ */
struct Foo*
Foo_Foo(struct Foo* this_)
{
this_->vftbl = &Foo_vftbl;
this_->foo = 1;
return this_;
}
/* Foo メンバ関数 */
static int
Foo_getValue(struct Foo* this_)
{
return this_->foo;
}
/* Foo デストラクタ */
static void
Foo_Delete(struct Foo* this_)
{
operator_delete(this_);
}
/* クラス Bar */
struct BarVFTBL;
struct Bar {
const struct BarVFTBL* vftbl;
int bar;
};
/* Bar 仮想関数テーブル */
struct BarVFTBL {
int (*getValue)(struct Bar*);
void (*Delete)(struct Bar*);
};
static int Bar_getValue(struct Bar*);
static void Bar_Delete(struct Bar*);
static const BarVFTBL Bar_vftbl = {
&Bar_getValue,
&Bar_Delete
};
/* Bar コンストラクタ */
struct Bar*
Bar_Bar(struct Bar* this_)
{
this_->vftbl = &Bar_vftbl;
this_->bar = 2;
return this_;
}
/* Bar メンバ関数 */
static int
Bar_getValue(struct Bar* this_)
{
return this_->bar;
}
/* Bar デストラクタ */
static void
Bar_Delete(struct Bar* this_)
{
operator_delete(this_);
}
/* クラス Buzz */
struct Buzz {
struct Foo foo;
struct Bar bar;
};
/* Buzz コンストラクタ */
struct Buzz*
Buzz_Buzz(struct Buzz* this_)
{
Foo_Foo(&this_->foo);
Bar_Bar(&this_->bar);
return this_;
}
/* Buzz デストラクタ */
void
Buzz_Delete(struct Buzz* this_)
{
operator_delete(this_);
}
/* キャスト */
#define CAST_BUZZ_FOO(this_) (&(this_)->foo)
#define CAST_BUZZ_BAR(this_) (&(this_)->bar)
int
main()
{
struct Buzz* buzz;
int foo, bar;
buzz = (struct Buzz*)operator_new(sizeof(*buzz));
if (buzz != NULL) {
Buzz_Buzz(buzz);
foo = CAST_BUZZ_FOO(buzz)->vftbl->getValue(CAST_BUZZ_FOO(buzz));
bar = CAST_BUZZ_BAR(buzz)->vftbl->getValue(CAST_BUZZ_BAR(buzz));
printf("%d%d\n", foo, bar);
Buzz_Delete(buzz);
}
return EXIT_SUCCESS;
}
注目するところは CAST_BUZZ_* マクロ。
C++ には多重継承とかいう嫌なものがあるので、キャストを行う時にこのマクロと同様のオフセット調整をコンパイラが頑張ってやらないととならない。
この反省で後発の Java は多重継承を禁止し、純粋仮想関数テーブルだけ interface として独立させたのである。 しかしその後 Java8 でバイナリ互換のため interface にデフォルト実装が持たせられるという仕様変更がなされ、結果として C++ の多重継承と同じトラブルが発生するようになって大草原。
まぁ C なんかでも Unix の dlopen/dlsym や LoadLibrary/GetProcAddress を使ってプラグイン的なものを実装する時、新しい ABI の本体が古い ABI のプラグインを呼び込むとインタフェース不足するからからシム足したりするけどさ。
N の libc でも俺が弄った時 i18n module に足りない場合は libc 側でシムを足すとかやってる、
これ
とか
これ
。
このキャスト時のオフセット調整を C++ では Thunking と呼ぶ。
閑話 Thunking とは何か
いいですよね The Beatles の Abbey Road B 面のメドレーの中の一曲、ってそれは Sun King や。 元々は think の過去形 thought の赤ちゃん言葉である thunk に現在分詞 ing までくっついたもはや謎の造語である。
プログラミング界隈だと ALGOL で名前呼出や遅延評価において値の計算に使われる計算式のことをさす。うーんこれじゃ何のことかさっぱり判らん。 ギークの言語野がぶっ壊れてるのは東も西も同じやな…
C++ においては遅延評価以外にも、多重継承のために this をキャスト先の型に応じてオフセットを調整する必要があるのを上のコードで理解いただけたはずだがこれを Thunking と呼ぶのだ。
つまりは「帳尻合わせ」程度の意味だと思っておけばよろしい。
それ以外でも C/C++ では Thunking という用語は使われる、例えば 32bit なプログラムと 64bit なプログラムで通信する際に ILP32 と LLP64 の違いによって型のサイズ計算の帳尻を合わせる必要があり、これも Thunking と呼ばれる。 Unix の LP64 と違って Windows は LLP64 だから long は 32bit のままなので影響は少ないとはいえ、ポインタは 4byte から 8byte になるからしゃーない。
ちょうど今解析してる CtDPOXY.DLL もユーザ空間から DeviceIoControl システムコール経由ででカーネルドライバを叩くプロキシ DLL なので Thunking は無縁ではない。
32bit の CtDPROXY.DLL から 64bit のカーネルドライバを叩くような組み合わせだと、カーネルの 64bit アドレスをユーザー空間の 32bit で扱えるよう「帳尻を合わせる」必要があるからね。
そのため CtDPROXT.DLL は 32bit 版でも
- Binary\Drivers\wdm\common\i386\CtDPROXY.DLL … 32bit 版
- Binary\Drivers\wdm\common\i386\thunk\CtDPROXY.DLL … 32bit Thunking 版
の 2 種類が存在するのだ、今解析してるのは 32bit Thunking 版である。
閑話休題
COM インタフェースの話に戻るんだけど、デストラクタのコードを読むと COM インタフェースは単一継承でも構造をコビーしただけでなく継承元のインスタンスも持ってるっぽいのだよな。
void __thiscall Unko8::~Unko8(Unko8 *this,byte param_1)
{
FUN_02005b17(this);
if ((param_1 & 1) != 0) {
operator_delete(this);
}
return;
}
void __fastcall FUN_02005b17(Unko8 *this)
{
int *iVar1;
iVar1 = *(int **)&this->field_0x24;
this->vftbl = (IUnknownVFTBL *)&VFTBL_Unko8;
if (iVar1 != (int *)NULL) {
(**(code **)(*iVar1 + 8))(iVar1);
}
FUN_02002cf6(this->unknown2,this->unknown3,(int)this);
this->refcount = 0;
this->unknown2 = 0;
this->unknown3 = 0;
FUN_02003aa6((uint *)&this->field_0x28);
if (this->handle != (HANDLE)INVALID_HANDLE) {
FUN_02001969(this->handle);
}
return;
}
この部分に注目してほしい。
int *iVar1;
iVar1 = *(int **)&this->field_0x24;
...
if (iVar1 != (int *)NULL) {
(**(code **)(*iVar1 + 8))(iVar1);
}
このコードって実は
Unko5 *super_;
super_ = this->super_;
...
if (super_ != (Unko5 *)NULL) {
(*super_->vftbl->Release)((IUnknown *)super_);
}
なんじゃね?
この謎を解明するため、我々は Unko8 のコンストラクタへと向かった――。
Unko8 * __thiscall Unko8::Unko8(Unko8 *this,CLSID *clsid,undefined4 param_2,undefined4 param_3)
{
...
*(undefined4 *)&this->field_0x24 = NULL;
...
}
うーんコンストラクタの時点では NULL だから継承元の Unko5 であるとは判断つかねえ、でも継承以外に他の IUnknown を継承したクラスのインスタンスを持つ理由ないもんな。
とりあえずこの謎については MIDL を書くという目的からは大きく逸脱するので深追いしないでおこう、別に COM を完全に理解したいわけじゃねえし。
そもそも C++ のデストラクタって this 以外の引数とるようなオーバーロードできないはずなのも思い出した、この Object Pooling の為にインスタンスを開放するかしないかのフラグを引数に取るデストラクタはなんなんだろう。
うーん COM も Windows も C++ も詳しくないからさっぱりわからん。
あとよくわかんねえのが Unko10 である。
VFTBL_Unko10 XREF[2]: FUN_02006394:0200639d(*),
Unko10:0200653f(*)
020014ac 68 5f 00 02 addr IUnknown::QueryInterface
020014b0 c6 ae 00 02 addr IUnknown::AddRef
020014b4 aa 64 00 02 addr Unko10::Release
020014b8 f6 64 00 02 addr Unko10::FUN_020064f6
020014bc 00 ?? 00h
020014bd 00 ?? 00h
020014be 00 ?? 00h
020014bf 00 ?? 00h
020014bc ~ 020014bf がゼロつまり =0 のインスタンスを作れない純粋仮想インタフェースっぽいのだけど、誰も継承してないんだよな。
にもかかわらず後述する Factory コードの中でインスタンス作れちゃっている。
まぁどちらの謎も今すぐ解く必要はないので放置すっか。
クラスと CLSID そしてインタフェースと REFIID の紐づけ
これさえわかれば MIDL は書けるはずである。
さっきも書いた通り COM インタフェースを実装したオブジェクトは Object Pooling パターンで実装される可能性が高いことがデストラクタの実装から透けて見えている。
なのでどこかに CLSID/REFIID すなわち GUID 構造体から対応するオブジェクトのインスタンスを生成する Creator だか Factory だか Producer が存在するはずである。
探し方はとてもかんたん簡単、sizeof を調べた時と同様にコンストラクタを呼んでるコードを探せばいいだけ。
ヒットしたコードがこちら、条件文を羅列するいわゆる Simple Factory ってやつっすな。
void __fastcall
FUN_02002e86(undefined4 param_1,int param_2,int param_3,int param_4,int *param_5, undefined4 *param_6
)
...
iVar1 = 4;
bVar6 = true;
iVar4 = iVar1;
piVar2 = param_5;
piVar5 = &DAT_02001284;
do {
if (iVar4 == 0) break;
iVar4 = iVar4 + -1;
bVar6 = *piVar2 == *piVar5;
piVar2 = piVar2 + 1;
piVar5 = piVar5 + 1;
} while (bVar6);
if (bVar6) {
this = operator_new(0x28);
if (this == (Unko1 *)0x0) {
pUVar3 = (Unko8 *)0x0;
}
else {
pUVar3 = (void *)Unko1::Unko1(this,(CLSID *)param_5,param_3,param_4);
}
if (pUVar3 == (Unko8 *)0x0) goto LAB_02003474;
iVar1 = FUN_0200ba66((int)pUVar3);
if (iVar1 < 0) {
Unko1::~Unko1(pUVar3,1);
goto LAB_02003474;
}
}
else {
...
頭が腐りそうなコードだが
- GUID 構造体同士の
memcmpを Visual C/C++ が最適化でインライン展開しレジスタサイズ単位での比較に書き換えている - レジスタの型推論がガバガバで
Unko1のインスタンスをUnko8 *な変数に代入してると誤判定されている FUN_0200ba66の引数がUnko1 *でなくintと誤判定されている
ことくらいは Unix C がちょっと書けるだけのおじさんなら理解できるので
void __fastcall
FUN_02002e86(undefined4 param_1,int param_2,int param_3,int param_4,GUID *param_5, undefined4 *param_6
)
void* prod;
...
if (!memcmp(&DAT_02001284, param_5, sizeof(DAT_02001284))) {
Unko1 unko1 = new Unko1(param_5, param_3, param_4);
if (unko1 == nullptr)
goto LAB_02003474;
if (FAILED(FUN_0200ba66(unko1)) {
delete unko1;
goto LAB_02003474;
}
prod = unko1;
} else {
...
というシンプルなコードに書き直すこと秒速でできる、つまり DAT_02001284 が Unko1 に対応する GUID ということですな。
DAT_02001284 XREF[6]: FUN_02002e86:02002f21(*),
FUN_02002e86:02002f2a(R),
FUN_0200b894:0200b8d1(*),
FUN_0200b894:0200b8da(R),
FUN_0200dd91:0200dd9e(*),
FUN_0200dd91:0200dda5(R)
02001284 c1 1c 04 7d undefined4 7D041CC1h
DAT_02001288 XREF[3]: FUN_02002e86:02002f2a(R),
FUN_0200b894:0200b8da(R),
FUN_0200dd91:0200dda5(R)
02001288 5c 70 d3 11 undefined4 11D3705Ch
0200128c 82 ?? 82h
0200128d d2 ?? D2h
0200128e 00 ?? 00h
0200128f 50 ?? 50h P
02001290 da ?? DAh
02001291 0d ?? 0Dh
02001292 96 ?? 96h
02001293 ca ?? CAh
GUID は
typedef struct _GUID {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} GUID;
という構造なのだがコンパイラがさっきの memcmp のようにレジスタサイズでアクセスするので Data2 の部分にもラベルが貼られてしまっているね。
まぁ気にせず DAT_02001284 の型を GUID に変更し名前も変更する。
GUID_Unko1+4 XREF[6,3]: FUN_02002e86:02002f21(*),
GUID_Unko1 FUN_02002e86:02002f2a(R),
FUN_0200b894:0200b8d1(*),
FUN_0200b894:0200b8da(R),
FUN_0200dd91:0200dd9e(*),
FUN_0200dd91:0200dda5(R),
FUN_02002e86:02002f2a(R),
FUN_0200b894:0200b8da(R),
FUN_0200dd91:0200dda5(R)
02001284 c1 1c 04 GUID 7d041cc1-705c-11d3-82d2-0050da0d96ca
7d 5c 70
d3 11 82
ということで Unko1 に対応する GUID は {7d041cc1-705c-11d3-82d2-0050da0d96ca} である、後はこの繰り返し。
Factory から参照されてない DAT_02001274 もあってこれはレジストリから既知の Emu10KX クラスの CLSID で DllGetClassObject で参照されてる。
HRESULT DllGetClassObject(IID *rclsid,IID *riid,LPVOID *ppv)
{
int *piVar1;
HRESULT HVar2;
int iVar3;
GUID *pGVar4;
bool bVar5;
/* 0x1ac5 2 DllGetClassObject */
iVar3 = 4;
bVar5 = true;
pGVar4 = &DAT_02001274;
do {
if (iVar3 == 0) break;
iVar3 = iVar3 + -1;
bVar5 = rclsid->Data1 == *(ulong *)pGVar4;
rclsid = (IID *)&rclsid->Data2;
pGVar4 = (GUID *)&pGVar4->field_0x4;
} while (bVar5);
if (bVar5) {
rclsid = (IID *)0xffffffff;
iVar3 = FUN_02001855(&rclsid,(char *)0x0);
さらに仮想関数テーブルからぱっとみ COM インタフェースに見えなかったクラスもあったのでこれを Unko15 とする。
...
pGVar4 = &GUID_Unko15;
...
pUVar3 = Unko15::Unko15(this_04,(CLSID *)param_5,param_3,param_4);
このクラスは QeryInterface をオーバーライドしておりそっちでもうひとつ GUID 参照しているからこれは Unko15_2 とでもしとくか。
HRESULT Unko15::QueryInterface(Unko15 *this,GUID *param_2,void **param_3)
...
GUID *pGVar3;
...
pGVar3 = &GUID_020012f4;
...
QueryInterface をオーバーライドするってことは継承がらみのはずだけど、Unko8 と Unko9 はしてないんだよな。
まぁ C++ の継承と COM の継承は別なんだろうなおそらく。
さらに Unko15 は仮想関数テーブルを 2 つ持ってるのである。
VFTBL_Unko15_2 XREF[2]: FUN_0200c212:0200c224(*),
Unko15:0200d58b(*)
02001688 96 ce 00 02 addr LAB_0200ce96
0200168c b4 ce 00 02 addr LAB_0200ceb4
02001690 a5 ce 00 02 addr LAB_0200cea5
02001694 3a c9 00 02 addr FUN_0200c93a
02001698 54 c9 00 02 addr FUN_0200c954
0200169c 00 ?? 00h
0200169d 00 ?? 00h
0200169e 00 ?? 00h
0200169f 00 ?? 00h
VFTBL_Unko15 XREF[2]: FUN_0200c212:0200c21b(*),
Unko15:0200d585(*)
020016a0 90 c2 00 02 addr Unko15::QueryInterface
020016a4 f5 c2 00 02 addr Unko15::AddRef
020016a8 2a c3 00 02 addr Unko15::Release
020016ac b8 c3 00 02 addr LAB_0200c3b8
020016b0 c5 c3 00 02 addr LAB_0200c3c5
020016b4 d2 c3 00 02 addr LAB_0200c3d2
020016b8 ab c3 00 02 addr LAB_0200c3ab
020016bc b1 cf 00 02 addr FUN_0200cfb1
020016c0 e6 ce 00 02 addr FUN_0200cee6
020016c4 03 d3 00 02 addr FUN_0200d303
020016c8 49 d0 00 02 addr FUN_0200d049
020016cc 42 d4 00 02 addr FUN_0200d442
020016d0 66 c2 00 02 addr FUN_0200c266
020016d4 22 d1 00 02 addr FUN_0200d122
020016d8 69 d1 00 02 addr FUN_0200d169
020016dc b9 d1 00 02 addr FUN_0200d1b9
020016e0 c3 ce 00 02 addr FUN_0200cec3
020016e4 8b cf 00 02 addr Unko15::~Unko15
Unko15 * __thiscall
Unko15::Unko15(Unko15 *this,GUID *param_1,undefined4 param_2,undefined4 param_3)
{
ushort uVar1;
HGLOBAL hMem;
LPVOID _Dst;
this->vftbl = &VFTBL_Unko15;
this->vftbl2 = &VFTBL_Unko15_2;
this->refcount = 1;
…
うーんもうちょっと COM 勉強しないとアカンナなこれ。
あと Factory が何も Productを返さない GUID もあるので、とりあえず GUID_Empty とでもしとくか、タイプライブラリの GUID なのかなこれ。
bVar7 = true;
iVar4 = iVar2;
pGVar5 = param_5;
pGVar6 = &GUID_02001314;
do {
if (iVar4 == 0) break;
iVar4 = iVar4 + -1;
bVar7 = *(int *)pGVar5 == *(int *)pGVar6;
pGVar5 = (GUID *)&pGVar5->field_0x4;
pGVar6 = (GUID *)&pGVar6->field_0x4;
} while (bVar7);
if (!bVar7) {
...
GUID の一覧表の完成
ということで地味な作業の成果がこれ、仮想関数テーブルと並び方違うけどなんかこれも法則あるんだろうな。
GUID_Unko2 {fe45e141-70f4-11d3-82d2-0050da0d96ca}
GUID_Emu10kx {1446ef61-7128-11d3-82d2-0050da0d96ca}
GUID_Unko1 {7d041cc1-705c-11d3-82d2-0050da0d96ca}
GUID_Unko3 {3fcec1a1-7667-11d3-82d2-0050da0d96ca}
GUID_Unko13 {f3d41200-46f2-11d6-9c86-00d0b78e3bd7}
GUID_Unko4 {b25dd750-7c19-11d3-868c-0000e84e5b10}
GUID_Unko5 {b25dd751-7c19-11d3-868c-0000e84e5b10}
GUID_Unko14 {69c4ce9d-9e98-4493-89e0-8f9bd2618eec}
GUID_Unko15 {4bb1ed02-5d34-11d2-b612-006097d1fc31}
GUID_Unko15_2 {cec25ae1-4a3a-11d5-82d5-0050da0d96ca}
GUID_Unko8 {fb92dc20-b314-11d3-a66e-00a0c98e7b97}
GUID_Empty {9135427f-dac5-4c48-a179-44f76232d12f}
GUID_Unko9 {2f3f8b80-0ae1-11d4-917b-00902744961f}
GUID_Unko6 {18148141-a722-11d3-82d2-0050da0d96ca}
GUID_Unko7 {18148142-a722-11d3-82d2-0050da0d96ca}
GUID_Unko10 {990beca1-cdd5-11d3-82d2-0050da0d96ca}
GUID_Unko12 {cbc32380-0989-11d4-98f8-cfb4fcfff857}
GUID_Unko11 {cbc32381-0989-11d4-98f8-cfb4fcfff857}
GUID_IUnknown {00000000-0000-0000-c000-000000000046}
最初の 4byte に 1 足しただけとか後半 10byte は同じとか手作業で作ったのかなこの GUID は。
生成器使わなくていいんだっけ…まぁ COM インタフェースの GUID なんて公開されてるから容易に推測できたとこで意味無いか。
Factory は無意味にも IUnknown のインスタンスも作れるのでそのペアもあった、Well-Known GUID なので Ghidra もこれ IUnknown のやつや!と教えてくれる。
普通インタフェースいったら実装無しのままだろうに =0 代入せず全部にデフォルト実装用意してるからインスタンス作れてしまうのだ。
あとインタフェース名らしき文字列がちょこちょこみつかる。
s_IDrvDevice_020013b8 XREF[1]: FUN_02003856:0200385e(*)
020013b8 49 44 72 ds "IDrvDevice"
76 44 65
76 69 63
s_IDrvALDevice_02001500 XREF[1]: FUN_020065aa:020065b2(*)
02001500 49 44 72 ds "IDrvALDevice"
76 41 4c
44 65 76
s_IDrvALContext_0200161c XREF[1]: FUN_02007388:02007390(*)
0200161c 49 44 72 ds "IDrvALContext"
76 41 4c
43 6f 6e
これはそれぞれ
- IDrvDevice … Unko2
- IDrvALDevice … Unko11
- IDrvALContext … Unko12
と対応してるようだけど他の 12 個については不明、このままだとウンコまみれになってしまう…
次回
たぶん IDL 書くとこまでは進められるはず、しかし飽きた…