##プロセスメモリエディタ
まずプロセスメモリエディタとは?
動作中のプロセスのプロセスメモリ内を、
検索・編集できるエディタのことです(ざっくり)。
####なぜプロセスメモリエディタなのかというと
- Nimでのポインタやプロセス操作を学習したい
- CTFやセキュリティ関連で、うさみみハリケーン(プロセスメモリエディタとして有名)のようなツールを
CUIかつMacOSでも使いたい
からです。
とはいえ全部作るのは結構時間かかりそうなので、
①としてまずは大枠を作っていこうと思います。
####プロセスメモリエディタとして最低限の機能
・プロセスメモリ内の検索
・プロセスの停止、再開
・プロセスメモリの書き換え
本当に最低限ですがこんな感じでしょうか。
これらの関数は後で定義したいと思います。
CLIツール作成
CLIフレームワークはdocoptモジュールを使いました(cligenでも良いと思います)。
まずはCLIツールとしてのオプション引数などの定義をします。
# docopt用のコマンドライン引数定義
const doc = """
Usage:
pme_nim <action> [<content>]
Options:
<action> ToDo Action
<content> ToDo Content
"""
pmeはProcessMemoryEditorの略として付けました。
んー分かりづらいかな。まあ一旦これで。
そしてコマンドツールとして動作するようにmain関数を書いていきます。
import system, os, options, strUtils
import docopt
proc main =
# まずはここでコマンドライン引数やオプションを変数に格納
let
args = docopt(doc, version = "1.0.0")
action: string = $args["<action>"]
content: string = $args["<content>"]
# 第一コマンドライン引数によって処理を分岐
case action:
# プロセスメモリ内の検索
of "search":
# contentがnilでなければ追加を実行
# 注意:stringで定義したのでnilはstringとして渡ってきます。
if content == "nil":
echo "content is null"
else:
echo "content is: " & content
# TODO: プロセスメモリ内Search関数
of "write":
echo "overwrite memory is complete."
# TODO: 該当ポインタアドレスに上書き
of "process_list":
echo "current processes: "
# TODO: プロセス一覧を表示
else:
# コマンドが存在しないものの場合
echo "command does not exists : " & action
when isMainModule:
main()
これで一応CLIツールとしては、
コンパイルできるようになったかと思います。
ちなみにコンパイルと実行は、例によって以下になります。
# コンパイル
$ nim c pme_nim.nim
# 実行
$ ./pme_nim search "test"
##プロセスメモリとポインタ
それでは本題のポインタをゴニョゴニョする方を考えていきます。
memfilesモジュール
メモリマップへアクセスするにはmemfilesモジュールを使うようです。
試しにポインタのアドレスを取得してみたいなと思ったので、以下を書いて試しに実行。
import memfiles
when isMainModule:
try:
# ここはps aux コマンドなどで得られたプロセスが対象とするファイルでもOK
# このopenがpointerを持ったMemFilesを返却
var memMap = memfiles.open(filename = "/usr/bin/vm_stat", allowRemap = true)
var memPointer = memMap.mapMem(mappedSize = 1024)
# ここでmemPointerのアドレスが確認できます
echo repr(memPointer)
# MemFilesは
for line in lines(memMap):
echo line
except:
echo("error!!!")
raise
echo repr(memPointer)の箇所でうまく行けばアドレスが表示されます。
ああっ、自動公開の時間がきてしまった。。
構わず追記していきます笑
posix_utilsモジュール
では次にポインタの値を書き換える関数を定義してみます。
通常プロセスメモリは他プロセスから書き換えられないようにロックされているので
まずアンロックする必要がありますが、posix_utilsモジュールにその関数が定義されています。
先ほど取得したMemFilesから取得したポインタを使うとこのような感じに
# こちらは先ほどの取得
var memMap = memfiles.open(filename = "/usr/bin/vm_stat", allowRemap = true)
var memPointer = memMap.mapMem(mappedSize = 1024)
# 1と書いた引数はメモリのページサイズを記載するようです。
memPointer.memoryUnlock(1)
memoryUnlockAllもあるみたいですが、
全部アンロックするのは危険なので対象メモリだけアンロックしようと思います。
memoryUnlockの説明に
Locks pages starting from a1 for **a1** bytes and prevent them from being swapped.
と書いてあるのですが、多分 from a1 for a2 bytesの間違いだと思われます。
これをもとにポインタのメモリを書き換える関数を作ってみると
proc overwrite(p: pointer, page_size, data: string): bool =
# メモリをアンロック
p.memoryUnlock(page_size)
# メモリ上書き処理
# p.c_memset(0000000, 1)
# p.zeroMem(1)
# メモリをロック
p.memoryLock(page_size)
メモリを上書きするための処理は本当に情報が少ないですね。。。
c_memsetがsystemモジュールにあるようですが、
importしてもダメで再定義しないとでした。
どうやらzeroMemを使う方法もあるようですが、英語の記事すら全然ない。。
psutilモジュール
こちらはjohnscillieriさん作成のプロセスを扱うためのモジュールで
Pythonのpsutilライブラリを参考にしているみたいです。
Posix, Linux, Windowsと対応しているみたいなのでとてもありがたい。。
(これを自作しようと思って試行錯誤して時間が。。)
ただこちらはまだ作成途中のようでnim v1.0.2で動かしてみたら
コンパイルエラーになっちゃいますね。
次回②はこちらをフォークさせてもらって参考にしつつ、
- psutilを使ってプロセスリスト取得
- メモリ上書きの処理
をやっていこうと思います。