外部プロセスのメモリを覗く方法

作成日:2002.7.14


デバッガやハッキングツールの 1種には 実行中の外部プロセスの持っているメモリを 読み書きするこのとできるものがある。 ここでは Windows / Linux / Solaris の 3 種類の OS で、 外部プロセスからのメモリの読み込みを実現する方法を説明する。

重要な点を挙げると、

Windows

Windows の場合には、 プロセス ID から OpenProccess API によって プロセスハンドラを確保し、 ReadProcessMemory/WriteProcessMemory を用いて読み書きを行う。
ただし、 Windows NT/2000系で厳密なアクセスコントロールが行われており 通常のプロセスでは ReadProcessMemory / WritePorcessMemory を 使うことができない。 これを可能にするには 監視プロセス側のプロセスのアクセス権限のうち、 debug privilege をオンにする必要がある。
bool enable_debug_privilege() {
  HANDLE           hToken;
  LUID             Value;
  TOKEN_PRIVILEGES tkp;

  if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
    return false;
  }
  if (!LookupPrivilegeValue ((LPSTR)NULL, SE_DEBUG_NAME, &Value)){
    return false; // error
  }
  tkp.PrivilegeCount           = 1;
  tkp.Privileges[0].Luid       = Value;
  tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  AdjustTokenPrivileges (hToken, FALSE, &tkp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL);
  if (GetLastError () != ERROR_SUCCESS) {
    return false; // error 
  }

  CloseHandle( hToken );	
  return true;
}

Solaris

Solaris で外部プロセスのメモリを覗く方法は 3 つある。
  1. ptrace API を用いる方法
  2. /proc を用いてプロセスの情報を取りだす方法。
  3. process service interfaces (proc_service(3PROC))
1. は監視プロセスを対象プロセスに アタッチしてから(この時点で対象プロセスが停止)、 1 ワードづつメモリを読み込む方法。
2. と 3. の手法では 実行中のプロセスを停止させずに、 ターゲットプロセスのメモリを読み書きが可能。

2. は ターゲットプロセスの ID を <pid> とすると、 /proc/<pid>/as が プロセスのメモリイメージを保持している。 このファイルを open して後、lseek、 read、write によって読み書きすることが可能。
<pid> のプロセスからメモリアドレス offset を 読みこむコードは 以下のようになる。
char proc_pid_mem[MAX_FILE_PATH];

sprintf(proc_pid_mem, "/proc/%d/as", pid);
if( (fd = open(proc_pid_mem, O_CREAT | O_RDONLY)) != -1 ){
  off_t p = lseek( fd, offset, SEEK_SET );
  if( p == offset ){
    size_t size = read(fd, &buffer, sizeof(buffer));
    if( size == sizeof(buffer) ){
      printf("%02x", buffer);
    } 
  }
}
3. は 2. と原理的同じ手法でデータを転送するが、 /proc の下のファイルを操作する代わりに 専用の API を用いる。


Linux

Linux は Solaris と同様に procfs 経由のメモリ参照が可能。
覗きたいプロセスの対象スレッドの PID を <pid> とすると、 /proc/<pid>/memという仮想ファイルを 経由することでアクセスが可能となる。
ただし、 Linux kernel の制限上 ptrace API によって、 監視対象となるプロセスにアタッチを行ってからでないと /proc/<pid>/memを 読み書きできない。 そして ptrace によるアタッチには、 ターゲットとなるプロセスの SIGSTOP シグナルによる 一時停止が伴う。
つまり、 Linux ではターゲットとなるプロセスを一時停止させないと メモリを参照することができない。

/proc/<pid>/memwrite の他に mmap によって 監視プロセスのメモリ空間内にマッピングする機能が 用意されているようだが、 kernel のバージョンによっては 問題があるようだ。
long result;
char proc_pid_mem[MAX_FILE_PATH + 1];

// attach  proccess
ptrace(PTRACE_ATTACH, pid, NULL, NULL); 

#if defined(__READ_BY_PTRACE__)

/* ptrace API を使ってメモリを読みこむ */
result = ptrace( PTRACE_PEEKDATA, pid, (void*)offset, NULL);
printf("memory: %d\n", (int)result );

#else 

/* procfs 経由でメモリを読みこむ */
sprintf(proc_pid_mem, "/proc/%d/mem", pid);

if( (fd = open(proc_pid_mem, O_RDWR)) >= 0 ){
  off_t p = lseek( fd, offset, SEEK_SET );
  if( p == offset ){
    size_t size = read(fd, buffer, 4);
    if( size == 4){
      for(int i=0 ; i<4; i++){
        printf("%02x", buffer[i]);
      }
      printf("\n");
    }
  }
}

#endif

// detach the proccess 
ptrace(PTRACE_DETACH, pid, NULL, NULL);

コメント

トラックバック   [Trackback URL: http://www.nminoru.jp/cgi-bin/tb.cgi/programming__view_process_mem]
コメントを書き込む

TOP    掲示板    戻る
Written by Nakamura Minoru