Ayumu's I/O

ねだるな勝ち取れ、さすれば与えられん

API Monitorのフックについて調べてみる

あゆむです。今回はAPI Monitorというツールのフックの動作を簡単に調べてみました。

API Monitorとは

プロセス(32bit/64bit)によるAPIコールを監視・制御できるツールです。監視対象とするAPIは、一覧から選択することで簡単に追加することができます。
配布元のサイトをみると、Version 2.0(Alpha-r13)が最新版で、2013/03/14以降はバージョンアップされていないようです。とはいえ、Version 1.5と2.0(Alpha)まで9年の開きがあることから、今後のサポートもありうると考えられます。
個人的な使用用途はマルウェア解析中のAPIコール状況の監視ですが、動的解析中にクラッシュしてしまうことが何度かあったため、解析の手始めにちょっとだけ使ってみるくらいの利用頻度です。

www.rohitab.com

基本的な使い方

簡単なプログラムを例にAPI Monitorの使い方を説明します。

環境

本記事における動作環境は次の通りです。

OS Windows 7 64bit
コンパイラ gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)
その他 API Monitor v2 Alpha-r13

プログラムの準備

MessageBoxA関数でダイアログを表示するだけのプログラムを準備します。すぐにダイアログが表示されてしまうと扱いづらいので、getchar関数でユーザ入力されるまで待機するようにします。

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

int main(void) {
	printf("Press \"Enter\" key to continue: ");
	getchar();
	MessageBoxA(NULL, "Hello world", "Message", MB_OK);
	return 0;
}

監視の手順

1. プログラムを実行します。
f:id:aes256:20180125145425p:plain
2. API Monitorの左上のパネル「API Filter」から監視対象とするAPIを選択します。ここでは、User32.dll!MessageBoxAを監視項目にいれます。
f:id:aes256:20180125145340p:plain
3. API Monitorの左下のパネル「Running Processes」で当該プロセスを選択し、モニタリングを開始します。
f:id:aes256:20180125145541p:plain
4. Enterキーを押下し、ダイアログを表示させます。
f:id:aes256:20180125145648p:plain
5. API Monitorを確認します。右上のパネル「Summary」にて引数の情報がモニタリングできていることが確認できます。
f:id:aes256:20180125145706p:plain

API Monitorのフックを調べてみる

プログラムごとに3パートに分けてAPI Monitorの動作を説明します。

Part1

まず、先ほどの「Hello world」を出力するプログラムを用いて動作を調べてみます。API Monitorによる監視前後におけるプロセスのメモリマップの変化を確認します。監視を開始すると「apimonitor-drv-x86.sys」をはじめ、いくつかのコードが注入されていることがわかります。
f:id:aes256:20180125154744p:plain
特筆すべきは、下図のコードです。命令列らしきコードですね。
f:id:aes256:20180125154645p:plain
ここで、デバッガを使って当該プロセスにアタッチし、このコードを逆アセンブルしてみます(下図)。GetProcAddress関数やMessageBoxA関数などのアドレスを引数として「apimonitor-drv-x86.sys」内をコールしていることがわかります。
これがAPI Monitorによる関数の検知の入り口です。実際にMessageBoxA関数に関してステップインで追っていくと、User32.dllがロードされているメモリではなく下図の箇所が最初に呼び出されます。
f:id:aes256:20180125155212p:plain

さて、当該プログラムのメモリロード後のIAT(Import Address Table)を見てみます。下図のメモリダンプ(Dump1)では、MessageBoxA関数のアドレスが「1C 00 DD 02」となっており、先のコード列を指していることがわかります。このようなAPIのフックは、IATフックと呼ばれます。
f:id:aes256:20180125161117p:plain

Part2

Part1で使用したプログラムでは、User32.dllを静的リンクで呼び出していましたが、Part2では動的リンクで呼び出すように書き換えてみます(次のコード参照)。これによって、MessageBoxA関数のアドレスがIATに含まれなくなります。つまり、Part2では、IATに監視対象のAPIがない場合にAPI Monitorで監視対象のAPIコールを検出できるかを確認するということです。マルウェアによっては元のIATが破壊されていることがあるため、これができるか否かで使い物になるかどうかが変わってきます。

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

int main(void) {
	printf("Press \"Enter\" key to continue: ");
	getchar();

	FARPROC mba = GetProcAddress(LoadLibraryA("user32.dll"), "MessageBoxA");
	mba(NULL, "Hello world", "Message", MB_OK);
	return 0;
}

図は省略しますが、動的リンクの場合でも監視対象を検出することができます。調べたところ、GetProcAddress関数が呼び出された場合にMessageBoxA関数検知のためのルーチン(Part1において”API Monitorによる関数の検知の入り口”と呼んだ箇所)を追加して、そのアドレスを返すようにしているようです。

Part3

最後はGetProcAddress関数を使用せずに、User32.dllのEAT(Export Address Table)からMessageBoxA関数のアドレスを直接取得するプログラムで試してみます。コードは次の通りです。

#include <stdio.h>
#include <windows.h>
#include <string.h>

int main(void) {
	printf("Press \"Enter\" key to continue: ");
	getchar();

	HMODULE hModule = LoadLibraryA("user32.dll");
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule;
	PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS) ((LPBYTE)hModule + pDosHeader->e_lfanew);
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = &pNtHeaders->OptionalHeader;
	PIMAGE_DATA_DIRECTORY pDirectory   = &pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
	PIMAGE_EXPORT_DIRECTORY pImgExport = (PIMAGE_EXPORT_DIRECTORY)((PBYTE)hModule + pDirectory->VirtualAddress);

	DWORD* functions = (DWORD*)((void*)hModule + pImgExport->AddressOfFunctions);
	DWORD* names     = (DWORD*)((void*)hModule + pImgExport->AddressOfNames);
	WORD*  ordinals  = (WORD*) ((void*)hModule + pImgExport->AddressOfNameOrdinals);

	for (int i = 0; i < pImgExport->NumberOfNames; ++i) {
		int ord = ordinals[i];
		//printf("%08X (%08x), %s\n", functions[ord], (void*)hModule+functions[ord], (void*)hModule+names[i]);

		if (strcmp((char*)hModule+names[i], "MessageBoxA") == 0) {
			FARPROC mba = ((void*)hModule + functions[ord]);
			mba(NULL, "Hello world", "Message", MB_OK);
			break;
		}
	}
	return 0;
}

結果は下図の通りです。APIMonitorではMessageBoxA関数を検出することはできませんでした。
f:id:aes256:20180126194616p:plain

Trickbot(Ver1000102)を解析してみる

マルウェア解析が趣味のあゆむです。今回はWindows7(64bit)環境下でTrickbotを解析したので、その結果をお伝えします。検体/ログ解析時の種類特定にお役立てください。モジュールの解析までは行っていませんのであしからず。

Trickbotとは

オンラインバンキングマルウェアです。Trickbotの主なターゲットは海外で日本にはあまりばら撒かれていない印象を受けます。
Trickbotは、ウェブインジェクション機能やURLリダイレクション機能などを持っています。たとえば、感染した状態でユーザが銀行のサイトなどにアクセスしようとすると、サイトに不正なコードが挿入されて入力した情報が盗まれるという被害が起こりえます。
主な感染経路はメールによるものです。メールに添付されたファイルを開いて感染するケースや脆弱な環境下で悪意のあるサイトに誘導される形で感染するケースがほとんどのようです。
Trickbotの更新はとても盛んで、バージョンアップして動作が変わったり、新たな機能を有するモジュールをダウンロードするようになったりと日々進化しています。執筆現在(12/23)の最新バージョンは1000108です。

検体情報

解析した検体は、2017年12月13日にばら撒かれたTrickbotです。メールに添付されたDocファイルを開くとTrickbotがダウンロードされて感染するという流れです。

Docfile

MD5 5a69998c829e08486b27ad16182f451e
SHA256 ee468a25416ad9f812198dbfb0f389fcdaf6326d1267625e921e7048bc37a9ee

TrickBot(Packed)

FileName nyRhdkwSD.exe / oySielwTD.exe
MD5 8b607501725d998c14f6a34eb4e8dc3e
SHA256 d27ea2a862848c82b7726584c6e66e41cb4988e3e92a42391d85d24fbe4e3d9c
Version(ver) 1000102
GroupTag(gtag) mac1

解析結果

概要

Trickbotは、パックされており本体が動作するまでに4つのステップがあります。下図をご覧ください。最初に実行されるファイルは「nyRhdkwSD.exe」です。これを実行すると、Process Hollowingという手法を用いて、新たに作成した自身のプロセスにコードを注入します(下図1-1)。次に実行元のファイルを別の場所へコピーします(下図2-1)。また、自動実行の設定をタスクスケジューラに登録します(下図2-2)。そして、再度Process Hollowing(下図3-1)とタスクの登録試行(下図4-2)を行い、正規のプログラムsvchost.exeへTrickbotを注入します(下図4-1)。以降では、これらをステップごとに解説します。
f:id:aes256:20171223190750p:plain

内容

1st Step

まず、デバッガで開くと、CreateWindowExA関数がたくさん呼び出されていることがわかります。これらは、解析妨害のために挿入されているようで、ブレイクポイントを設定しても一度も呼び出されません。引数もすべて0で呼び出す気がないです。
f:id:aes256:20171222185237p:plain:w300
f:id:aes256:20171222185251p:plain

実行すると、Process Hollowingを行います。自身をサスペンド状態でプロセス生成し、中身をアンマップ(空洞化)して、新たなコードを注入します。そして、エントリーポイントを書き換えて、スレッドを動作させます。このテクニックはよくある手法でDreamBotでも使われています。
f:id:aes256:20171223221920p:plain
f:id:aes256:20171222190133p:plain

なお、Process Hollowing実行時に呼ばれる関数のアドレスはスタック上に展開されます。
f:id:aes256:20171222190156p:plain

2nd Step

スレッドの動作が始まると注入されたコードが実行され、主にファイルのコピーとタスクスケジューラへの登録を行います。

まず、「%APPDATA%/AppData/Roaming/」に「services」というフォルダを作成します。CopyFileW関数を用いて、実行元のファイル(8b607501725d998c14f6a34eb4e8dc3e)をコピーします。当該解析環境では「oySielwTD.exe」という名前にリネームされて配置されました。
f:id:aes256:20171223162828p:plain

さらに、タスクスケジューラへ次の内容で自動実行の登録を行います。ログオン時または登録時点から3分ごとにプログラムが実行されます。
f:id:aes256:20171222190824p:plain

名前 services update
説明 Look for services monitor.
トリガー 複数のトリガーの定義(任意のユーザのログオン時。毎日hh:mmに起動、トリガーされた後、1日間の間、00:03:00ごとに繰り返す。)
操作 プログラムの開始 C:\Users\■■■\AppData\Roaming\services\oySielwTD.exe
3rd Step

タスクスケジューラによってプログラムが実行されます。このときの動作は1st Stepと同一のもので、再度Process Hollowingが行われます。
f:id:aes256:20171223222441p:plain

4th Step

スレッドの動作が始まると注入されたコードが実行され、2nd Stepと途中まで同一の動作をします。この段階では、主にタスクスケジューラへの登録試行とTrickbotの展開、svchost.exeへの注入が行われます。

詳細を解析していませんが、svchost.exeへの注入もProcess Hollowingによって行われていると思われます。CreateProcessW関数を用いてsvchost.exe(64bit)のプロセスをサスペンド状態で作成します。
f:id:aes256:20171223222504p:plain

なお、一連の動作終了後の「oySielwTD.exe」のヒープ領域には、Trickbot(64bit)が展開されています。マジックナンバー0x4D 0x5A(MZ)が確認できます。
f:id:aes256:20171222191340p:plain:w300

Trickbot

Trickbotが注入されたsvchost.exeが動作すると、まず、「%APPDATA%/AppData/Roaming/」に新たにファイルとフォルダを作成します。この動作は繰り返されるようで、削除しても当該svchost.exeが動作している間は再度生成されます。
f:id:aes256:20171222192407p:plain
Modulesフォルダには、モジュールをダウンロードして配置します。client_id、group_tagの中身は下図をご覧ください。黒塗りにしていますが、client_idは感染端末のユーザ名から始まり、Windowsバージョンと乱数で構成されています。
f:id:aes256:20171222193107p:plain

外部のサービスと通信して感染端末のグローバルIPアドレスを確認します。User-AgentがChromeとなっていますが、確認したところ感染端末にインストールされているChromeのバージョンと異なっていました。
f:id:aes256:20171223095034p:plain
f:id:aes256:20171223165157p:plain

C2サーバとのC2通信が発生します。
f:id:aes256:20171222192651p:plain
残念ながら解析時点では接続できませんでした。Trickbotは基本的にHTTPS通信を行うようです。
f:id:aes256:20171223145144p:plain

なお、おとりC2サーバを用意して通信させてみた結果、次のリクエストを投げていることがわかりました。「/5/spk/」の5はコマンドIDで、C2サーバから何かをダウンロードする際に使われるとのことです。*1
f:id:aes256:20171223163622p:plain

さて、C2サーバのIPアドレスを含むconfigは、Trickbotが注入されたsvchost.exeのメモリのヒープ領域に保持されています。
f:id:aes256:20171222193418p:plain:w300

取り出したデータは次の通りです。ここにTrickbotのバージョンやC2通信先、取得するモジュールのリストが記載されています。

<mcconf>
<ver>1000102</ver>
<gtag>mac1</gtag>
<servs>
<srv>79.106.41.9:449</srv>
<srv>185.21.149.41:449</srv>
<srv>200.111.97.235:449</srv>
<srv>67.209.219.92:449</srv>
<srv>209.205.188.238:449</srv>
<srv>73.252.252.62:449</srv>
<srv>76.16.105.16:449</srv>
<srv>82.202.236.84:443</srv>
<srv>78.155.199.124:443</srv>
<srv>179.43.160.45:443</srv>
<srv>94.250.253.142:443</srv>
<srv>5.200.55.47:443</srv>
<srv>37.60.177.19:443</srv>
<srv>94.250.255.50:443</srv>
<srv>82.146.48.44:443</srv>
<srv>194.87.93.30:443</srv>
<srv>194.87.94.225:443</srv>
<srv>195.62.53.88:443</srv>
<srv>82.146.48.241:443</srv>
<srv>195.88.209.128:443</srv>
<srv>80.87.198.204:443</srv>
<srv>194.87.146.14:443</srv>
<srv>195.133.147.140:443</srv>
<srv>92.53.66.60:443</srv>
<srv>194.87.93.84:443</srv>
<srv>82.202.226.189:443</srv>
<srv>95.154.199.136:443</srv>
</servs>
<autorun>
<module name="systeminfo" ctl="GetSystemInfo"/>
<module name="injectDll"/>
</autorun>
</mcconf>

解析結果は以上です。
今回の解析ではC2サーバと通信ができなかったため、これ以降の分析はしていません。通常は、この後に各種情報を窃取する機能を持ったモジュールがダウンロードするようです。そのうちチャレンジします。

‪42日経過 BEP治療 1、2コース目終了‬

2017年12月5日、治療開始から42日が経過しましたので、ここまでの治療の経過を綴ります。今後は1コース終了単位(21日スパン)で記事を公開していく予定です。

はじめに

2017年12月現在、性腺外胚細胞腫瘍(後腹膜原発)絨毛癌と闘病中です。確定診断当初の値や病状は、本ブログの次の記事をご覧ください。
aes256bit.hatenablog.com

現在の治療方法について

化学療法

現在の治療は、導入化学療法BEP治療と呼ばれるもので、抗がん剤であるブレオマイシン、エトポシド、シスプラチンを用いて21日間を1コースとして繰り返し行われる。ブレオマイシンは、1、8、15日目に筋肉注射で投与し、エトポシドとシスプラチンは、1-5日目に点滴で連続投与する。僕の場合は、これを手始めに3、4コース行なうこととなった。

f:id:aes256:20171208170922p:plain
図1.BEP治療の投与スケジュール

副作用

BEP治療による副作用は、骨髄抑制や、脱毛、食欲不振、味覚障害、吐き気・嘔吐、倦怠感、口内炎、便秘、下痢、肝障害、腎障害、間質性肺炎、手足のしびれなど様々なものがある。すべてが現れるかというと、そうではなく体質によるところが大きい。
治療をしていて特に警戒されているのは、骨髄抑制である。下がり過ぎると、感染症を引き起こしやすくなったり、悪化しやすくなったりと治療の妨げとなるからだ。僕の場合は週に3回採血をして、血中の白血球や血小板などの数がどの程度下がっているかを確認している。

治療中の出来事

1コース目

1-5日目(10/25-10/30)

この間が抗がん剤を1番投入している期間で、ややツラい。点滴は10時間ほど入れっぱなしで、寝ることが乗り切る最善策だった。正直、3日目までは割と平気だったが、4日目からはシスプラチンによる倦怠感を感じはじめ、身体が重く怠かった。医学の発展もあって現在は副作用を抑える薬を同時に飲むため、映画やドラマのように吐いたりはしなかった。

6-21日目(10/31-11/14)

点滴直後は、骨髄抑制を警戒して白血球数を高める皮下注射を行なった。これによって注射直後は白血球数がとても多くなる。点滴シーズンが終わると、あとは右肩上がりになってゆく。難なく調子を取り戻し、入院前よりも元気なレベルまで回復した。ただ、点滴の間はずっと寝たきりであったため、筋肉がかなり落ちていた。
15日目、ついに抗がん剤の副作用である脱毛が始まった。抗がん剤がよく効いている証拠である。髪の毛を少し引っ張るだけで容易に5本程抜け、ドライヤーで乾かすとふわっと舞った。髪は伸びきっていたので、病院内の美容室で短髪にしてもらい、散らばる髪の毛に備えた。
この6-21日間は、骨髄抑制による免疫力の低下はなく、計2回外出外泊許可が下りた。病院食は薄味で煮付けや魚が中心なので、お家に帰って食べたいものを食べた。

2コース目

2‪2-27日目(11/15-11/20)‬

‪2回目の点滴シーズンを迎えたが、ほとんど寝て過ごしたため、難なくクリアといったところ。食欲を高める薬が処方されるようになり飲んだ結果、副作用の眠気がちょうどよく現れてよく眠れた。‬

28-42日目(11/21-12/5)‬

‪点滴を終えると食欲が戻り、52.5kgまで落ちていた体重も55kgまで回復した。その一方で抗がん剤の副作用である味覚の変化を感じ始め、特に塩の味があまり感じられなくなった。そして、この頃になると頭がスースーするほどハゲあがってしまった。なお、抗がん剤による脱毛は一時的なもので、抗がん剤の投与を終えてから1ヶ月から3ヶ月ほどでまた生えてくる。
‪2回目の白血球数を高める皮下注射のおかげもあってか、2コース目も骨髄抑制による白血球数などの減少はなく、計2回の外出外泊許可が下りた。‬

治療の進捗‬

‪2コース目の終わりに、腫瘍のサイズを確認すべく、造影剤ありのCTスキャンを行った。結果は良くもなく、悪くもないといったところで、肺に転移した腫瘍はやや縮小、後腹膜腫瘍は縮小というより細胞が死につつある状態らしい。もっとも、よく効いている場合は、転移した腫瘍がCT画像に映らなくなるほど縮小してたりするそうなのだが。‬
腫瘍マーカーの値は、下図の通り。当面の目標はこの値を陰性化すること。1コース目まではとても順調に減少しているが、やはり2コース目は腫瘍マーカーの減少がやや鈍化してきている。‬この現象は他の患者さんでもよく起こるようで、情報収集した限りでは6から3までに落とすのに3コース掛かったケースも見受けられた。
‪これらを踏まえ、BEP治療3コース目終了後に、より強い抗がん剤に切り替えることが決まった。説明は受けていないが、おそらく、1次救済化学療法TIP治療になるんじゃないかと予想している。‬

f:id:aes256:20171208170536p:plain
図2.腫瘍マーカー(hCG)値の時系列変化

名前付きパイプによるプロセス間通信をやってみる

Windowsにおいてプロセス間通信(IPC)に使われる名前付きパイプについて調べました。

名前付きパイプ(Named Pipe)とは

名前付きパイプとは、プロセス間のデータ転送のためのプログラミングAPI。メールスロットと異なり、信頼される双方向通信を実現できる。ファイルのようにアクセスでき、Windows I/Oの標準関数であるCreateFile関数、ReadFile関数、WriteFile関数、CloseHandle関数を使って扱うことができる。生成時に作成するインスタンスの個数を指定できるため、1つのサーバが複数のクライアントと通信することもできる。また、サーバが自身のアカウント権限ではなくクライアント側のアカウント権限でスレッドの実行する、偽装(Impersonation)と呼ばれる機能も利用できる。名前付きパイプの関数は、kernel32.dllに実装されている。

f:id:aes256:20171204190757p:plain
図1.名前付きパイプ(PIPE_ACCESS_INBOUND)による通信のイメージ

CreateNamedPipe関数

名前付きパイプは、CreateNamedPipe関数を使ってインスタンスを作成できる。この関数の第一引数lpNameは、名前付きパイプ名へのポインタで、次の形式で指定する。

\\<サーバ>\Pipe\<パイプ名>
  • <サーバ>の部分には、名前付きパイプサーバを実行するコンピュータを指定する。Windows定義のエイリアス「\\.\」を指定した場合は、そのシステムのみで有効となる。 本記事では未検証だが、名前付きパイプはネットワークにまたがるプロセス間通信にも利用可能で、<サーバ>の部分にDNS名、IPアドレス、NetBIOS名なども指定できる。
  • <パイプ名>の部分には一意な名前を指定する。サブディレクトリを含むことができる。
例:\\.\Pipe\MyServerApp\ConnectionPipe

第二引数dwOpenModeでパイプの通信方向を指定できる。

説明
PIPE_ACCESS_DUPLEX 双方向。サーバとクライアントの両方が名前付きパイプの読み書きを行える。
PIPE_ACCESS_INBOUND 一方向。データの流れをクライアントからサーバへの方向に限定する。
PIPE_ACCESS_OUTBOUND 一方向。データの流れをサーバからクライアントへの方向に限定する。

パイプに関連する他の関数

関数名 説明
ConnectNamedPipe クライアントが名前付きパイプに接続してくるのを待機する。
CallNamedPipe 名前付きパイプがメッセージタイプの場合は当該関数で接続する。
PeekNamedPipe 名前付きパイプの状態を調べる。
WaitNamedPipe 接続可能になるかタイムアウト時間が経過まで待機する。

詳細は、下記Webページにまとめられている。
https://msdn.microsoft.com/ja-jp/library/cc429276.aspx

名前付きパイプのアクティビティの確認方法

Windows Sysinternals の PipeListツールを使う。
docs.microsoft.com

名前付きパイプの名前、それに対して作成されているインスタンス数、最大インスタンス数を表示できる。

C:\Users\Ayum>C:\Users\Ayum\Downloads\PipeList\pipelist.exe

PipeList v1.02 - Lists open named pipes
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Pipe Name                                    Instances       Max Instances
---------                                    ---------       -------------
InitShutdown                                      3               -1
lsass                                             4               -1
protected_storage                                 3               -1
ntsvcs                                            3               -1
scerpc                                            3               -1
plugplay                                          3               -1
Winsock2\CatalogChangeListener-484-0              1                1
epmapper                                          3               -1
(以下略)

プログラム

クライアント側で文字列を入力すると名前付きパイプを利用してプロセス間通信(一方向)を行い、サーバ側で文字列が表示されるというプログラム。実行する際は、名前付きパイプを作成してから読み込む必要があるのでサーバ側から先に実行する。終了する場合は、”EXIT”という文字列を入力する。

サーバ側のソースコード

サーバ側では、CreateNamedPipe関数を呼び出して名前付きパイプのインスタンスを作成し、ConnectNamedPipe関数でクライアントが接続するまで待機する。その後、名前付きパイプにクライアントからのメッセージがある場合は表示する。

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

int main() {
    HANDLE hPipe = INVALID_HANDLE_VALUE;
    hPipe = CreateNamedPipe("\\\\.\\pipe\\mypipe", //lpName
                           PIPE_ACCESS_INBOUND,            // dwOpenMode
                           PIPE_TYPE_BYTE | PIPE_WAIT,     // dwPipeMode
                           1,                              // nMaxInstances
                           0,                              // nOutBufferSize
                           0,                              // nInBufferSize
                           100,                            // nDefaultTimeOut
                           NULL);                          // lpSecurityAttributes

    if (hPipe == INVALID_HANDLE_VALUE) {
        fprintf(stderr, "Couldn't create NamedPipe.");
        return 1;
    }
    if (!ConnectNamedPipe(hPipe, NULL)) {
        fprintf(stderr, "Couldn't connect to NamedPipe.");
        CloseHandle(hPipe);
        return 1;
    }

    while (1) {
        char szBuff[256];
        DWORD dwBytesRead;
        if (!ReadFile(hPipe, szBuff, sizeof(szBuff), &dwBytesRead, NULL)) {
                fprintf(stderr, "Couldn't read NamedPipe.");
                break;
        }
        if (!strncmp("EXIT", szBuff, 4)) { // Exit Check
                break;
        }
        szBuff[dwBytesRead] = '\0';
        printf("Server : %s", szBuff);
        Sleep(1);
    }
    FlushFileBuffers(hPipe);
    DisconnectNamedPipe(hPipe);
    CloseHandle(hPipe);
    return 0;
}

クライアント側のソースコード

クライアント側では、サーバ側で作成した名前付きパイプのハンドルを取得し、ユーザによって入力された文字列をWriteFile関数で書き込む。

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

int main() {
	HANDLE hPipe = INVALID_HANDLE_VALUE;
	hPipe = CreateFile("\\\\.\\pipe\\mypipe",
                          GENERIC_WRITE,
                          0,
                          NULL,
                          OPEN_EXISTING, 
                          FILE_ATTRIBUTE_NORMAL,
                          NULL);
	
	if (hPipe == INVALID_HANDLE_VALUE) {
		fprintf(stderr, "Couldn't create NamedPipe.");
		return 1;
	}

	while (1) {
		char szBuff[255];
		DWORD dwBytesWritten;
		fgets(szBuff, sizeof(szBuff), stdin);
		if (!WriteFile(hPipe, szBuff, strlen(szBuff), &dwBytesWritten, NULL)) {
			fprintf(stderr, "Couldn't write NamedPipe.");
			break;
		}
		if (!strncmp("EXIT", szBuff, 4)) { // Exit Check
			break;
		}
	}
	CloseHandle(hPipe);
	return 0;
}

実行結果

クライアントに文字列を入力してEnterを押下すると、サーバにも同一の文字列が表示される。

f:id:aes256:20171204175406p:plain
図2.プログラムの実行イメージ
このときの名前付きパイプのアクティビティは次の通りとなった。「mypipe」が表示されていることがわかる。

C:\Users\Ayum>C:\Users\Ayum\Downloads\PipeList\pipelist.exe

PipeList v1.02 - Lists open named pipes
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

Pipe Name                                    Instances       Max Instances
---------                                    ---------       -------------
InitShutdown                                      3               -1
lsass                                             4               -1
protected_storage                                 3               -1
ntsvcs                                            3               -1
scerpc                                            3               -1
plugplay                                          3               -1
(中略)
mypipe                                            1                1

Process Hackerなどのツールを使ってクライアントのハンドル一覧を見れば、パイプのハンドルを確認できる。

f:id:aes256:20171205174120p:plain
図3.クライアントのハンドル一覧


上記コードを32bit/64bitプログラムにそれぞれコンパイルし、各プロセス間で名前付きパイプによる通信が可能か検証した。いずれも成功した。

サーバ    クライアント プロセス間通信
32bit 32bit 成功
32bit 64bit 成功
64bit 32bit 成功
64bit 64bit 成功

初期症状から治療開始までの43日間

2017年12月現在、性腺外胚細胞腫瘍(後腹膜原発)絨毛癌と闘病中です。希少がんであるがゆえ、ネット上に情報が少ないので情報を発信します。本記事では、初期症状から治療開始までの43日間について書きます。

初期症状

9/11(月) 朝、当時24歳。目が覚めると腰とお腹に痛みがあることに気が付いた。土日は家で読書やプログラミングをしていただけだったので、痛みの明確な原因がわからなかった。腰痛はいきなり起こったにしては激痛で、電車に立ち続けるのが辛いほど痛かった。一方腹痛は、お腹を下す手前のような痛みがあるものの実際に下しているわけでなく、初めて経験するタイプの痛みであった。その頃はプロジェクトが大詰めを迎えており、締め切りに追われるように仕事をしていたので、ストレスか疲れによるものと安易に判断し、湿布を腰に貼り整腸薬を飲んで様子を見ることにした。
しかし、数週間しても症状はあまりよくならなかった。通勤途中の電車で急に気持ち悪くなって途中降車したり、好きだった音楽を聴くと吐きそうになったりと、次第に体調の良し悪しの振れ幅が大きくなっていった。毎日病院の予約を入れようか悩んだが、何故か昼間は症状が落ち着いていることが多く、また、平日に仕事を休める状況でもなかったので結局病院にはいかなかった。

初診

症状発現から26日目。体重も落ち始めた僕は、平日ではなく土曜日に胃腸科内科で診てもらうことにした。紹介状なしで総合病院にいくと高くつくので、主要駅周辺にある小さな病院を選んだ。そこの医師はあまり親身ではないタイプの人で、目もあわせず、流れ作業のように僕に問診と触診をした。すると、脳がお腹の痛みを敏感に反応しているだけと告げた。そして「まだ若いから、心配しなくて大丈夫だからね。」と言い、僕に抗うつ薬を処方した。触診だけでここまでわかるものなのだろうか…。さすがにこれは誤診だろうと直感した。Googleレビューもそれなりに高かったのだが、この病院はハズレであった。ただ、今思えばこのとき医師に整腸薬や胃腸薬などそれらしき薬を処方されていたら、病気の発見はかなり遅れていたと思う。「2週間後にまた来てください。」とも言われたが、とても行く気にはなれなかった。

症状の原因

症状発現から34日目。相変わらず平日に休めない僕は、日曜日に先週とは別の消化器内科の病院へ行った。この頃の症状である、腹痛や腰痛、気持ち悪さ、吐き気、体重減少などを医師に伝え、触診で軽く痛む箇所を訴えた。これだけで終わるなら初診の医師と同じなのだが、そこの医師は「CTを撮りましょう。」と一言。日曜日にたまたま飛び込んだ小さな病院に、CTスキャンがあったことも発見が早くなった要因だった。
画像を観た医師は、あまり深刻そうな顔はせずに「腹部に7cm大の良性腫瘍がありますね。」と軽く話した。詳しく聞くと、腫瘍は良性だがサイズが大きいため、手術で摘出する可能性が高いということがわかった。手術という言葉に戸惑っていると、医師が家族に一度説明してくれるという話になり、親を呼ぶこととなった。
後日。遠方にいる母を呼んで、再度病院を訪ねた。そこでGISTという消化管間の壁にできる悪性腫瘍を疑われていることを知った。おそらく、心配させないようにするために、僕ひとりのときには敢えて言わなかったのだろう。いずれにしても、大きい病院で精密検査をする必要があるので、病院間の連結室を通じて近くの総合病院に予約を入れてもらった。

本人の知る権利

検査当日12時。親とともに総合病院へ行き、消化器科の医師と問診をした後に血液検査、尿検査、造影剤ありのCTスキャンを行った。しかし、結果が一向に出なかった。17時になった頃、看護師が僕の元へやって来て「少し別室で休憩しましょう。」と言い、中央処置室のソファーで休むこととなった。そのとき、僕は失敗したと考えていた。これはこのまま親にだけ説明されて、本当の診断結果を聞けないやつだろうなぁと。案の定、その後18時に親が迎えにやってきて「ここでは検査しきれないから、明日専門の病院で検査することになった。」と言った。

緊急入院と精密検査

翌る日の朝、専門の病院の泌尿器科を訪れた。思ったよりも空いており、診察の順番はすぐにやってきた。いざ診察室に入ると、医師は紹介状やCTスキャンの画像を見ていた。沈黙が長らく続くなか、僕は机上に置かれた紹介状の文を目で追っていた。そこには「リンパ節の転移を疑います。」「肺への転移を疑います。」と書かれていた。深刻な状態であるのは間違いなく、自分が思っていたよりも病期は進行していた。医師は多くを語らなかったが、消化器間の壁ではなく後腹膜に腫瘍があり、腫瘍の種類もいくつかあたりがついているらしかった。そして僕はそのまま緊急入院となった。
緊急入院のその日のうちに、血液検査、尿検査、心電図、CTスキャン、胸部レントゲン、生検が行われた。振り返ると生検がとても痛かったという印象しか残っていない。生検とは、腫瘍の種類を病理検査で特定するために腫瘍の一部を体内から採取することなのだが、後腹膜に腫瘍がある僕の場合は背中から針を刺してさらに腫瘍に届くまでぐーっと押し込まれる方法(針生検)で行われた。手術室のような部屋で5人もの医療スタッフに囲まれ、局所麻酔をしたにも関わらず内臓に響く鋭い痛みが計3回続いた。感覚的には筋肉注射の10倍くらいの痛みだろうか。そのときの心拍数か血圧か忘れてしまったが、180まで振れたらしい。

確定診断

病名がわかるまでの間、死ぬかもしれないという思いと戦っていた。正直に書くと、友人にLINEで病状を伝えたときは涙を堪えられなかった。本当に長い時間だったが何日か経ち、血液検査と生検の結果から確定診断を受けた。
病名は、性腺外胚細胞腫瘍(後腹膜原発)絨毛癌。10万人に1人ほどの希少がんで、好発年齢は10代から30代。後腹膜腫瘍のサイズは7.9cmほどで、リンパ節転移、最大2cmの肺転移が数十個。腫瘍マーカーであるhCGの値が82万mIU/ml(基準値:1mIU/ml以下)とかなり高く、国際的な胚細胞腫瘍のリスク分類(IGCCC分類:1997年)によれば予後不良、5年後生存率は48%とされる。予後不良という言葉は、調べると悲観的にならざるを得ない意味合いを持つが、話を聞く限りでは胚細胞腫瘍は抗がん剤による効果が高く、手堅くやれば寛解・治癒も期待できるらしい。
こうして症状発現から43日後の10/24(水)、僕は抗がん剤による治療を開始した。