NtSetInformationThreadによるデバッガからの秘匿
調べてみた関数の概要(中身薄)
NtSetInformationThread というAPIは、公式では「スレッドの優先度を設定する」と記載されています。
・・・が、第2引数の「ThreadInformationClass」のパラメータの中には、「THREADINFOCLASS 列挙体」なども含まれていますが、解説にはそのパラメータを使った場合に設定すべきパラメータが書かれていません。
仕方ない、「THREADINFOCLASS 列挙体」を調べてみよう、ということでググってみます。
さすがMicros〇ftの公式だ、検索にかからないぜ!
いやいや公式さん、流石にマニュアルに書いていある定義は書いておいてもいいんじゃない!?
まあ、ドキュメント量が半端ないので、何が抜けてるかなんて把握できないか?
「THREADINFOCLASS enum」あたりでググって以下のページを発見。
ヘッダの定義あたりから調べたのかなぁ。
Geoff Chappell, Software Analyst
そもそも、「NtSetInformationThread」というAPIの名前に対し、「スレッドの優先度を設定する」という説明は違和感がありますね。
恐らく、「スレッドに対する様々な設定が可能なAPIだが、スレッドの優先度の設定以外については非公開仕様」なんじゃないかと思ったりもします。
NtSetInformationThread の第3、4引数の PVOID ThreadInformation、ULONG ThreadInformationLength も、実際は WPARAM、LPARAM (雑に言うと、Windowsユーザ定義で使うパラメータ型)なんじゃないかなーなどと Windows のプログラムをする人の目線では思っちゃいますね。
デバッガからの秘匿の実装状況
さて、Lockbit 3.0の中でのNtSetInformationThread の利用を見てみると。
NtSetInformationThread の第2引数が 0x00000011 で呼び出されています(デバッガ実行時に ecx の値を確認した結果)。
先ほどの Geoff Chappell, Software Analyst では、「ThreadHideFromDebugger」で定義されていました。
パラメータの意味は・・・まんま「デバッガからの秘匿」ですね。
これを実際に設定すると、スレッドで使われている関数内でブレークポイントを設定しても、ブレークできなくなり、デバッガでの解析ができなくなります。
回避策
これは、Lockbit 2.0でも使われていたテクニックでした。
ええ、今年の Code Blue でも、ネタが大滑りしたアレです。
私の黒歴史がまた増えた・・・って、ネタが滑ったことを黒歴史にすると、私の歴史ってほとんど真っ黒な件。
と、いうことで、回避策はみなさんお分かりでしょう。
じゃあご一緒に♪
無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄無駄ァ!!
当該領域を、nop(0x90)で埋めてしまえばOKです。
チョコラータをぶっ飛ばすイメージでいきましょう。
なお、スマートにjmpで飛ばしてもいいです。
注意事項はnopをする範囲で、この場合は関数の復帰のためにebpを戻すためのpop ebpを巻き込まないようにすることですかね。
以上が、スレッドのデバッガからの秘匿と回避策でした。
まあ、「NtSetInformationThread」でググると、このAPIの解説より、このAPIを使った今回のテクニックのデバッガ回避の解説のほうがたくさん出てるように見えるんだけどねw
PEB(fs:[0x30])のパラメータの判定
PEBのパラメータにも、デバッガを示すパラメータがあります。
公式ではPEBの BeingDebugged が分かりやすいフラグですが、実際のところデバッガを使っていると変化するパラメータが色々あります。
Lockbit 3.0でみかけたのは、以下のような判定でした。
PEBのオフセット 0x18 の値から、さらにオフセット 0x44 を参照しています。
つまり、[PEB + 0x18] の値はアドレスであることが推測されます。
では、この [PEB + 0x18] の値は何のアドレスか調べてみましょう。
Microsoftの公式のHPに定義があるので確認します。
PEB構造体
構造体の定義は、2022年11月19日現在、以下のように記載されています。
ええと、オフセット0x18だから、その位置にあたる変数が何かを計算していくと・・・。
答え:
PVOID Reserved4[1] (VOIDポインタ Reserved4[0]~[2]の真ん中)
意味:
オペレーティング システムによって内部で使用するために予約されています。
・・・・・・。( ̄ー ̄)
いやもうね。
「公式のHP」って部分で察した人もいると思うけど、やっぱりこのオチなのよwww
仕方ないので、このようなドキュメントされていない(あるいは以前ドキュメントされていた情報を残している)ような内容を調査し公開しているサイトを参照しました。
以前の記事でも紹介されたサイトです。
Terminus Project - _PEB - combined view
今回のLockbit 3.0は32ビットなので、左側の 0x0018 を参照すると、「void *ProcessHeap」となっています。
今回のシリーズの記事を読んでいる人ならピンときたかもしれませんが、これは「Lockbit 3.0で見つけたアンチデバッグテクニック(その1)」で触れた _HEAP の構造体が入っている、ということです。
ということは、_HEAP構造体のオフセット 0x44 は、同記事で既に触れた「ForceFlags」を参照しているということがわかります。
そして、当該記事で書いた通り、このパラメータで「HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)」が設定されているかどうかでデバッガを使っていることを判定することができます。
そして、デバッガと判定されたならば、その値を ror 1 でローテートすることで無効な値とし、以後の処理を失敗させる、という方法ということが分かります。
回避策は その1 でも触れた通り、ror 1 を nop に置き換えることで簡単に回避できます。
または、jz の判定を jnz や jmp 等に置き換えをしても良いでしょう。
DbgUiRemoteBreakinへの細工
DbgUiRemoteBreakin は、リモートデバッグ時に利用されるAPIといわれています。
どのようなAPIか公式で・・・
ええ、みなさんの予想通りですよ。
ググっても公式では出てきませんよ、このAPIの解説。
ググって出てくる記事は、やはりこのAPIを使ったアンチデバッグの解説記事ですw
・・・色々充実した記事があるので、この記事不要じゃね?とも思いますが。
まあ Lockbit 3.0 に絡むテクニック、ということで、Lockbit 3.0 の解析を進める人の注意点となれば、ってとこです。
以下のようなコードが見つかりました。
DbgUiRemoteBreakin の関数のアドレスを取得し、そのアクセス権限を変更して書き込みも可能とし、先頭の32バイト(0x20)を SystemFunction040 (RtlEncryptMemory) で暗号化しています。
これにより、DbgUiRemoteBreakin が呼び出されると、先頭のコードが暗号化によりおかしくなっているので、概ねクラッシュすると考えられます。
他のケースの記事をみると、先頭を retn に書き換えていきなり関数を終了させるようにする、などのトリックを利用するケースもあるようです。
このケースの回避策は、他と同様に暗号化の処理の 0x0040B4BB~0x0040B4C5までを nop にし、DbgUiRemoteBreakin のコードの暗号化を回避する方法があります。
また、このケースでは、この処理が sub_40B470 として関数化されていたため、関数の冒頭で retn するようにしたり、呼び出し元の「call sub_40B470」自体を nop で無効化してしまう方法もあります。
なお、このAPIは(他の解説を見ている限りでは)外部からプロセスにアタッチした際に呼び出されるDebugActiveProcess()から呼び出されるAPIのようなので、デバッガで直接このマルウェアを開いて調査する場合は影響しないのではと思われます。