LoginSignup
3

More than 3 years have passed since last update.

マルウェアのバイナリコードの静的解析には,ツールで完全に自動で行う方法もありますが,コードを人間が読んで理解する方法もあります.

私もたくさん経験していますが,逆アセンブルしたアセンブリコードの処理や意図を理解するのはかなり骨が折れる作業です.アセンブリコードがちょっと複雑になったりちょっと長くなったりすると,とたんにわからなくなります.時間もかかります.

逆アセンブルして得られたアセンブリコードを読んで理解するよりも,デコンパイラ(逆コンパイラ)でデコンパイル(逆コンパイル)して得られたCプログラムのコードを読んで理解するほうが,はるかに楽だし短時間で終わります.

現在入手可能なデコンパイラですぐ思いつくものを挙げておきます.

C言語ではこんなに単純なコードも

factarray.c
#include <stdio.h>
#include <stdlib.h>

int fact(int n)
{
  if (n <= 1) {
    return 1;
  } else {
    return n * fact(n - 1);
  }
}

int main(void)
{
  int i, ans;
  for (i = 1; i <= 10; i++) {
    ans = fact(i);
    printf("fact(%d) = %d\n", i, ans);
  }
  return 0;
}

アセンブリ言語レベルでは処理の流れや意図をつかむのすら大変で時間がかかる.以下はobjdump -dでの逆アセンブル結果.バイナリは32ビットでビルドした.

0000051d <fact>:
 51d:   55                      push   %ebp
 51e:   89 e5                   mov    %esp,%ebp
 520:   83 ec 08                sub    $0x8,%esp
 523:   e8 92 00 00 00          call   5ba <__x86.get_pc_thunk.ax>
 528:   05 b0 1a 00 00          add    $0x1ab0,%eax
 52d:   83 7d 08 01             cmpl   $0x1,0x8(%ebp)
 531:   7f 07                   jg     53a <fact+0x1d>
 533:   b8 01 00 00 00          mov    $0x1,%eax
 538:   eb 16                   jmp    550 <fact+0x33>
 53a:   8b 45 08                mov    0x8(%ebp),%eax
 53d:   83 e8 01                sub    $0x1,%eax
 540:   83 ec 0c                sub    $0xc,%esp
 543:   50                      push   %eax
 544:   e8 d4 ff ff ff          call   51d <fact>
 549:   83 c4 10                add    $0x10,%esp
 54c:   0f af 45 08             imul   0x8(%ebp),%eax
 550:   c9                      leave
 551:   c3                      ret

00000552 <main>:
 552:   8d 4c 24 04             lea    0x4(%esp),%ecx
 556:   83 e4 f0                and    $0xfffffff0,%esp
 559:   ff 71 fc                pushl  -0x4(%ecx)
 55c:   55                      push   %ebp
 55d:   89 e5                   mov    %esp,%ebp
 55f:   53                      push   %ebx
 560:   51                      push   %ecx
 561:   83 ec 10                sub    $0x10,%esp
 564:   e8 b7 fe ff ff          call   420 <__x86.get_pc_thunk.bx>
 569:   81 c3 6f 1a 00 00       add    $0x1a6f,%ebx
 56f:   c7 45 f0 01 00 00 00    movl   $0x1,-0x10(%ebp)
 576:   eb 2d                   jmp    5a5 <main+0x53>
 578:   83 ec 0c                sub    $0xc,%esp
 57b:   ff 75 f0                pushl  -0x10(%ebp)
 57e:   e8 9a ff ff ff          call   51d <fact>
 583:   83 c4 10                add    $0x10,%esp
 586:   89 45 f4                mov    %eax,-0xc(%ebp)
 589:   83 ec 04                sub    $0x4,%esp
 58c:   ff 75 f4                pushl  -0xc(%ebp)
 58f:   ff 75 f0                pushl  -0x10(%ebp)
 592:   8d 83 68 e6 ff ff       lea    -0x1998(%ebx),%eax
 598:   50                      push   %eax
 599:   e8 12 fe ff ff          call   3b0 <printf@plt>
 59e:   83 c4 10                add    $0x10,%esp
 5a1:   83 45 f0 01             addl   $0x1,-0x10(%ebp)
 5a5:   83 7d f0 0a             cmpl   $0xa,-0x10(%ebp)
 5a9:   7e cd                   jle    578 <main+0x26>
 5ab:   b8 00 00 00 00          mov    $0x0,%eax
 5b0:   8d 65 f8                lea    -0x8(%ebp),%esp
 5b3:   59                      pop    %ecx
 5b4:   5b                      pop    %ebx
 5b5:   5d                      pop    %ebp
 5b6:   8d 61 fc                lea    -0x4(%ecx),%esp
 5b9:   c3                      ret

以下はGhidraでのデコンパイル結果.はるかにわかりやすくなった.元のCプログラムを理解する作業とほとんど変わらない.forループはwhileループに変わった.

int fact(int param_1)

{
  int iVar1;

  __x86.get_pc_thunk.ax();
  if (param_1 < 2) {
    iVar1 = 1;
  }
  else {
    iVar1 = fact(param_1 + -1);
    iVar1 = iVar1 * param_1;
  }
  return iVar1;
}
undefined4 main(void)

{
  undefined4 uVar1;
  int local_18;

  local_18 = 1;
  while (local_18 < 0xb) {
    uVar1 = fact(local_18);
    printf("fact(%d) = %d\n",local_18,uVar1);
    local_18 = local_18 + 1;
  }
  return 0;
}

以下はHex-Rays Decompilerでのデコンパイル結果.こちらもわかりやすい.変数名がiだし.if文のthen節とelse節は逆転.

int __cdecl fact(int a1)
{
  int result; // eax

  if ( a1 > 1 )
    result = a1 * fact(a1 - 1);
  else
    result = 1;
  return result;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [esp+0h] [ebp-10h]
  int v5; // [esp+4h] [ebp-Ch]

  for ( i = 1; i <= 10; ++i )
  {
    v5 = fact(i);
    printf("fact(%d) = %d\n", i, v5);
  }
  return 0;
}

デコンパイラによって,10分かかっていた静的解析の作業が1分で終わるようになったと感じます.

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3