「はじめて読むPentium マシン語入門編」をVisual Studio 2017で学習する

最近はじめてCTF(Capture The Flag)にチャレンジしたので、アセンブラやバイナリに興味が湧いてきてしまいました。ということで、初心者向けとして非常に良書だと思っている、「はじめて読むPentium マシン語入門編」を再読していました。

はじめて読むPentium マシン語入門編

はじめて読むPentium マシン語入門編

ところがこの本は2004年出版で、かなり昔の本ということもあって、学習環境がVisual C++ 2003を前提として書かれています。

ではこの学習環境を今は作れないのかというと……2017年現在は、無償で利用できるVisual Studio 2017 Community Editionがあります。これでほぼ同書内と同じ学習環境が構築できるので、その方法を紹介しておこうと思います。(というか、忘れないように自分用のメモという意味合いが強かったり。)

前置きが長くなりましたが、この記事は、「マシン語アセンブラにちょっと触れてみたい/学習を始めてみたいけど、何をどうしたらいいかさっぱり分からない……」という人向けです。

Visual Studio 2017のインストール

2017年現在、無償で利用できるVisual Stduioで、マシン語の学習に利用できるモノとしては、「Visual Studio Community 2017」があります。

なお企業でCommunity Editionを使う際にはいくつかライセンスの問題がありますが、個人が学習用に使うならば何も問題ありません。具体的なダウンロード手順等は省略します。MSのサイトに行けば普通に見つかりますね。

ただし、インストールの際に、インストールするオプションに注意が必要なので以下で説明します。

マシン語学習のための初期インストール

Community Editionでは、マシン語学習用の環境であるWin32コンソールアプリケーションを作るモジュール(CLIサポート)がデフォルトではインストールされません。このため、インストール時にオプションを選択する必要があります。

具体的には、Visual Studioインストーラを起動すると、下図のインストールするコンポーネントの選択画面に移ります。

ここでまず、「C++によるデスクトップ開発」のチェックが外れているので、クリックして選択します。

さらに上図で、右側の「C++/CLIサポート」にもチェックを入れておきます。これをチェックしておかないと、Win32コンソールアプリケーションが作成できません。

これで「インストール」を押せば、必要なモジュールが入った状態でインストールできます。なおインストールには6GBちょっと必要と、なんとまぁ富豪的マシン語の学習環境です(笑。

インストール完了後に、「ファイル」→「新規作成」→「プロジェクト」で、Visual C++で「Win32 コンソール アプリケーション」が選べるようになっていることを確認してください。

「はじめて読むPentium」の学習環境は、これでひとまずインストール完了です。

「はじめて読むPentium」のサンプルコードの書き方と実行

ここでは、93ページからのリスト5-1「プロジェクト名findzero」の例を、現在のVisual Studio 2017で追うやり方を紹介します。なお、「はじめて読むPentium マシン語入門編」を持っていることを前提としているので、持っていない人はまず買ってください。

ソースコードの書き方

リスト5-1は、以下のようなコードです。これを実際にVisual C++で書いてみましょう。なお、Visual C++Intel記法を用いるため、mov等の引数は「mov dst src」です。

データ:
  char Title[] = "Hello World!";

プログラム:
  mov   ecx, 100h
  mov   edi, offset Title
Loop1:
  cmp   [edi], 0
  je    end
  inc   edi
  loop  Loop1
end:

(引用:「はじめて読むPentium マシン語入門編」リスト5-1)

まずデータ部は、グローバル変数となるためmain関数の外で定義します。実際のプログラム(アセンブリ言語)は、Visual C++インラインアセンブラの機能を利用して記述します。具体的には、main関数の中に、以下のように __asm{} というブロックを作ります。

   __asm {
      (ここにアセンブリ言語を書く)
   }

インラインアセンブラでリスト5-1を記述してみたのが以下の図です。本の指示通りにブレークポイントも設定してあります。

なおブレークポイントやステップイン実行は、Visual C++ 2003の頃から操作に変わりは無いので、本の通りに設定できます。

デバッグウィンドウ

メニューの「デバッグ」→「デバッグの開始」後、「デバッグ」→「ウィンドウ」→「レジスタ」(Ctrl+Alt+G)でレジスタの内容が確認できます。なおレジスタの子ウィンドウは自由に移動できるので、タイトルバーをクリックして掴んで、しっくりくる場所に移動してください。

上の図は、「はじめて読むPentium」 の通りに、「自動変数」のウィンドウに入れた例です。

メモリの内容も確認してみましょう。下図のとおり、今、変数Title(Hello, World!)のアドレスは0x00a79000です。

ここで、「デバッグ」→「ウィンドウ」→「メモリ」→「メモリ1」を選択し、0x00a79000を確認してみると、下の図のとおり該当のアドレスには確かに「Hello World!」が格納されています。

ここで、「!」のASCIIコードは0x21であることを思い出してください。アドレス0x00a79000から12バイトめの「21」の後ろは0x00となっており、文字列が終端されていることが分かります。(この例では、他のアドレスも0x00が多いのでちょっと分かりにくいけど)。

混合モードと逆アセンブル

「はじめて読むPentium」 の本の中では、「混合モード」でマシン語を確認しようと書かれています。しかしこれは古い呼び名で、現在のVisual Studio 2017では「逆アセンブル」で確認することができます。

ここでは、以下のようなシンプルなC言語のソースを書きます。

#include "stdafx.h"

int main()
{
    char ch = 'A';

    printf("ch=%c\n", ch);

    return 0;
}

ブレークポイントを設定してデバッグを開始したら、「デバッグ」→「ウィンドウ」→「逆アセンブル」(Ctrl+Alt+D)としてみてください。すると下図のように、C言語のソースがアセンブラ言語に展開された混合モードで表示されます。

さらにこの状態で、逆アセンブルウィンドウで右クリックして「コードバイトの表示(Y)」を選択すると、次のように各行のニーモニックに対応するマシン語が表示されます。

まとめ

「はじめて読むPentium マシン語入門編」を、現在のVisual Studio 2017(Visual C++ 2017)で学習する環境について解説しました。ひとまず操作がこれだけ分かっていれば、同書を読み進めることができる(はず)です。

なお「はじめて読む」シリーズは、実は「はじめて読む486―32ビットコンピュータをやさしく語る」が、もっとも内容が濃く優れた良書、という評判が多いです。

はじめて読む486―32ビットコンピュータをやさしく語る

はじめて読む486―32ビットコンピュータをやさしく語る

しかし、マシン語に全くの初心者ならば、これよりもまずは「はじめて読むPentium マシン語入門編」がいいかなというのが私の意見です。同書はタイトル通りまさに「マシン語入門」であって、「Pentium入門」ではありません。そのため中級者以上からは「内容が簡単すぎる」とあまり評判がよろしく無いようですが、逆に初心者向けとしては非常にシンプルで分かりやすい本です。
また本記事で解説したとおり、2017年現在でも、無償でVisual Studio 2017で手を動かして色々試すことができますから、マシン語への入り口へはぴったりだと私は思います。

nmapのクリスマススキャン(-xS)をもっとキラキラさせよう

あまりに長いあいだこのブログを放置していましたが、大丈夫です。生きています。

ただ、ちょっとメンタル的に色々ありすぎて放置していました。。。さすがに1年以上も更新がないとツラいので、ちょうどクリスマスということで、ポートスキャナツールNmapの、クリスマススキャンについて軽く書いてみることにしました。

Nmapとクリスマス

今日は12月25、クリスマスです。セキュリティ診断の業界では有名ですが、12月25日にnmapで-vオプションを付けてポートスキャンをすると、nmapが、

"Nmap wishes you a merry Christmas!"

というメッセージを出してクリスマスを祝ってくれます。

診断業界の皆は、クリスマスに脆弱性診断をするとき、このメッセージを見るとみんなほんわかとした気分になったものです……(んなわけねーだろ! という誰かの声が聞こえる)

この際Nmapは、ついでに、"Specify -sX for Xmas Scan"と、クリスマススキャンのやり方も教えてくれます。クリスマススキャンとは、通常のSYNスキャンと違い、TCP FlagsのURG/PSH/FINビットを立ててスキャンするものです。一応nmapのマニュアルには、こういう特殊なスキャンをすることで一部のファイアウォールを通過できる場合がある……ということが書いてありますが、まぁこじつけで、実践でこのスキャンを使うことはまず無い。それよりも、ビットがいっぱい立っていてピカピカしているからクリスマスツリーのようだ……ということでクリスマススキャンと呼ばれる説が有名です。

クリスマススキャンをもっとピカピカさせる

さて、こうなると、「たったの3bitじゃもの足りん、TCP Flagsの全bitを立ててもっとピカピカさせよう」と思うのが人情というものです。nmapでは--scanflagsオプションを付けるとこれを実現できます。

やり方は2つあって、一つはTCP Flagsは6bitなので、「--scanflags 63」と指定すれば全ビット起立したパケットが飛びます。もう一つは文字列指定で、「--scanflags URGACKPSHRSTSYNFIN」と、URGからFINまで全部並べると、これも全TCP Flagsのビットが立ったパケットが飛びます。

# nmap -v --scanflags 63 -p 22 10.5.17.208
または
# nmap -v --scanflags URGACKPSHRSTSYNFIN -p 22 10.5.17.208

これで、6bit立ちました。キラキラしてますね。

TCP Flagsは9bit

と、ここまで書いておや? と思った方もいるでしょう。そう、実はTCP Flagsは後年さらに拡張されており、現在は6bitではありません(私が大学で学んだときは、まだ6bitだったんだけどな〜)。

元々予備に用意されていた予約ビットを使う形で、まずRFC 3168で、ECN-Echo(ECE)とCongestion Window Reduced(CWR)の2bitが追加されました。さらにRFC 3540で、ECN-nonce(NS)の1bitが追加されました。というわけでこれでもっとピカピカキラキラしたスキャンができるぞ! というわけですが、ちょっと試した限り、--scanflagsは255までしか指定できないので9bitめをキラキラさせることができませんでした。本当はできるのかもしれませんけど、私の知識不足&時間切れです。

# nmap -v --scanflags 255 -p 22 192.168.2.9
または
# nmap -v --scanflags CWRECEURGACKPSHRSTSYNFIN -p 22 192.168.2.9

これで、8bitキラキラさせることができました! これならようやくクリスマスツリーと言えるでしょう(たぶん)。なお、このスキャンの実用性は、まぁ、自己満足だけなのでゼロですね……。

補足すると、scanflagsオプションは奥が深く、「この組み合わせでOS検出ができるぞ!」とか、「クリスマススキャンで特定のACLが回避できるぞ!」とか色々と議論・研究がされているのですが、ちょっとそこまで詳しく調べてない&よく知らないのでここではつっこまないことにします。

ということで中途半端ですが、おしまい。

参考リンク

  • Nmapの真実
    • abendさんによるNmap面白ネタ、クリスマススキャンの話も出ています。おもしろ。

sshのポートをデフォルトの22/tcpから変えるべきか論争に、終止符を打ちました

また間が開きましたが、すみだセキュリティ勉強会2015#2を開催しました。発表していただいた@inaz2さん、@yasulibさん、ありがとうございました。当日の発表資料は上記の勉強会ブログからリンクしています。

今回の私の発表は、「攻撃を『隠す』・攻撃から『隠れる』」。ポートスキャンをするとsshが100個現れる「ssh分身の術」がメイン(?)です。

当初は、パケットヘッダやプロトコルのすき間にメッセージを隠したり、ファイルを隠すなども考えていたのですが……。あまりに盛りだくさんになりそうだったので、「ポートスキャンをいかに隠れて実行するか・ポートスキャンからどうやって隠れるか」と、ポートスキャンとnmapに絞って発表しました。

発表資料

私の発表資料は以下です。

発表ノート付きなのでPDFです。以下、落穂ひろいなど。

スキャンするポート数とカバレッジ

勉強会ではカバレッジという言葉を使いましたが、元ネタのnmap公式ガイドブック(目ン玉本)では、"Effectiveness"と書かれています。ちょっと分かりにくいので、勉強会ではCoverageという言葉にしました。

10ポートスキャンすればEffectivenessが50%というのは、初めて読んだときには「たったの10ポートでそんなにカバーできるの!?」とちょっとビックリしましたが、冷静に考えてみれば感覚的にはまぁそんなもんかなという印象です。

ただ、この値はインターネット越しにスキャンを行った場合ですね。データセンターなどで同一セグメントからポートスキャンを行う場合は、もっとたくさんの開放ポートが見えるので話は違ってくると思います。

sshのポートは、やっぱり22/tcpから変えましょう

sshのポートをデフォルトの22/tcpから変えても意味ないよ」というブログ記事は、はてなブックマークあたりで定期的に炎上するテーマです。以前から、「ピンポンダッシュが激減するんだから、ゴチャゴチャ言ってないで変えるべき」派だった私は、変えなくていいよ派の「ポートスキャンすれば一発で分かるじゃん」という意見に懐疑的でした。今回のネタはその辺が出発点になっています。

今回、こうして具体的にポートスキャン一発では分からない手法がいっぱいあるよと示すことで、「sshのポートをデフォルトの22/tcpから変えたほうがいいか?」の議論を終結させることができたので満足です(たぶん)。

デコイについて

当日はC-130のフレアだけ示しましたが、第二次世界大戦でのハリボテ戦車のデコイなど、面白い話は色々転がっています。お近くの軍事オタに聞いてみると良いでしょう。

kippoのバナーについて

当日にwakatonoさんから、「kippoはpythonで書かれているので、返すバナーをランダム化すればいい」とコメントをもらいました。確かに簡単ですし、面白いと思います。

こういうイメージ。↓

root@kali:~# nmap -sV -p0-65535 192.168.2.66
....(snip)....
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 5.3 (protocol 2.0)
2200/tcp open  ssh     OpenSSH 5.1p1 Debian 5 (protocol 2.0)
2201/tcp open  ssh     Sun_SSH 1.1 (protocol 2.0)
2202/tcp open  ssh     OpenSSH 5.3 (protocol 2.0)
2203/tcp open  ssh     OpenSSH 5.1p1 Debian 5 (protocol 2.0)
2204/tcp open  ssh     OpenSSH 4.6 (protocol 2.0)
2205/tcp open  ssh     OpenSSH 5.3p1 Debian-3ubuntu7 (protocol 2.0)
2206/tcp open  ssh     OpenSSH 5.1p1 FreeBSD-20080901 (protocol 2.0)
2207/tcp open  ssh     Sun_SSH 1.1 (protocol 2.0)
2208/tcp open  ssh     OpenSSH 5.5p1 Debian-6+squeeze2 (protocol 2.0)
2209/tcp open  ssh     OpenSSH 5.3 (protocol 2.0)
....(snip)....

これなら、どれがホンモノのsshかの推定は困難だし、面倒でもありません。

Port Knockingについて

当日質問もいただきましたが、事前に特定のポートにアクセスしないと対象ポートに到達できないようにするPort Knockingというアプローチもあります。

私はknockdはきちんと運用したことが無いので(rsyncとかでの連携ホストがある場合、運用がメンドくさそうだなぁ〜、と思っていた)、すっぽり話題として抜けちゃいました。ちょっとマジメに触ってみて、何かあれば後日書くかも。

宣伝

6月にLinuxの入門書を出しました。Linux初心者にいい本だなぁと我ながら思っているので、ぜひポチっとしてお買い求めください!

新しいLinuxの教科書

新しいLinuxの教科書

Linux入門書を出しました:「新しいLinuxの教科書」

えらく長いことかかってしまいましたが、Linuxの入門書を書きました。「新しいLinuxの教科書」というタイトルで、友人の三宅氏(id:mollifier)との共著です。

新しいLinuxの教科書

新しいLinuxの教科書

Linuxの入門書にも色々な切り口がありますが、本書は「CLIコマンドラインインタフェース)」と「シェル(bash)」を中心としました。というか、ほぼそれしか書いていません。後半は、ページを多く割いてシェルスクリプトについて入念に解説しています。マウスを使う場面はほとんど出てきませんから、本書を読む際にはマウスを窓から投げ捨ててしまっても問題ありません。

目次については、SBクリエイティブのページに掲載されていますので参照ください。

mollifier氏はバリバリのプログラマで、私はサーバ構築やネットワークセキュリティなどのいわゆるインフラエンジニアなため、2人で色々もみ合うことでイイ感じになったかなと思っています。

対象読者について

想定する読者は以下のような方です。

おすすめする読者
  • とりあえずLinuxを学習するためにインストールしてみたが、マウスでちょっとつっつくくらいしかできておらず、CLIの基礎知識をしっかり身につけたい
  • Linuxコマンドラインを基礎からきちんと学習したい
  • 今までなんとなく自己流でLinuxを学んでいたので、一度体系的に基礎を固めたい
  • Gitの基本操作を身につけたい

第19章で、mollifierが渾身の「Git入門」を書いてくれたので、ここだけでも一読の価値があると思います。

おすすめしない読者

本書はシェルの操作を中心したLinux入門書であるため、以下のような目的の方は読んでみても期待外れとなるかもしれません。

  • GUIを利用して、「ここをクリックして設定」などのスクリーンショットを多用した解説書が欲しい
  • ApacheTomcatなど、サーバ構築の入門のためのLinux解説書を求めている
  • Linuxの各種設定(ネットワーク、ユーザ管理、セキュリティ等)についての入門書を求めている
  • Linuxで、RubyPHPを利用したプログラミングを行いたい
  • Linuxカーネルに対する知識を得たい

シェルスクリプトbashで書く意味

本書ではシェルスクリプトに多くのページを割いていますが、そこではすべて「#!/bin/bash」として、bashスクリプトで記述しました。なぜシェルスクリプトを書く際にshではなくbashを使うのかは、本書内でも詳しく述べています。しかし炎上しやすいテーマなのでここにも少し理由を書いておきます。

私がbashスクリプトを勧めるようになったきっかけは、昔に関根達夫氏の「UNIXシェルスクリプトハンドブック」を読んだことに由来しています。

この本はすべてのシェルスクリプトbashで記載しており、冒頭でその理由として、「移植性に気を使ってshで書いても、結局はLinux/Solaris/BSD間の実装の差に苦労する。しかしbashは、GNUによる実装ひとつしか無い。だから機種間の実装による差に悩まされず移植性に優れている。シェルスクリプトはshではなく、むしろbashで書くべき」と述べられています(昔に読んだので細かい表現が若干違うかもしれません)。

当時の私は、DebianRedHatSolarisHP-UXとAlphaのDigital UNIX/bin/shの差に苦労しており、この主張に全面的に賛同しました。最近はSolarisにも普通にbashが入っていますから(それどころか最近のSolariszshすらデフォルトで入っています)、bashをわざわざ避ける理由はもうありません。このような主張をされている方は非常に少ないのですが、言われてみればその通りで、どうして今まで思いつかなかったのだろう? という感じです。(なお、bashスクリプトなのにshebangを「#!/bin/sh」にするのは死罪に値します。決してやってはいけません)。

これ以降、私はシェルスクリプトをshスクリプトではなくbashスクリプトで書くようになりました。ただし、たとえばFreeBSDではbashは/usr/local/bin/bashにインストールされるので、「本当に/bin/bashでいいのか」問題はまだ残っているのですが……。

書き足りなかったこと

まず、ネットワーク周りの設定については全くと言っていいほど書いていません。pingコマンドがほんのちょっと登場するだけです。これにはいくつか理由はあるのですが、単純に分量が多くなりすぎてムリということと、本書はシェルの使い方を基本とした本ですから、分野からしてそもそも対象外だな、と判断しました。

また、Linuxの運用管理に関わる部分もほとんど触れていません。起動・終了とパッケージのインストールについては触れましたが、cronの設定やサービスの起動など、一般的なLinuxサーバ入門的な部分はありません。またカーネルパラメータなども出てきません。これらは、この本で基礎を身につけたあと、それぞれ興味のある方向に進んでくださいというスタンスです。

細かい点で言うと、bashのhelpコマンドをそういえば紹介していなかったな……というのが心残りです。例えばbashの組み込みコマンドsetのヘルプを読むには「man bash」としてから「SHELL BUILTIN COMMANDS」のところのsetコマンドを読めばいいのですが、bashのmanは巨大なため、これはいささか不便です。この場合、次のようにhelpコマンドを使えば簡単に組み込みコマンドのヘルプを参照できます。

$ help set

参考文献

今回の本を書くために様々な書籍を参考にしましたが、私が特に意識したのは以下の2冊です。

たのしいUNIX―UNIXへの招待 (Ascii books)

たのしいUNIX―UNIXへの招待 (Ascii books)

The Linux Command Line: A Complete Introduction

The Linux Command Line: A Complete Introduction

特に坂本文さんの「たのしいUNIX」は、私がUNIXを学び始めた15年ほど前に読み、当時とても面白く読んだことを覚えています。今読み返してみても楽しめますし、2015年現在でも役に立つ素晴らしい良書とは思うのですが、やはりどうしても時代の流れは感じてしまいます(Linuxは全く登場しませんし、端末エミュレータは使わず物理端末を使う前提で書かれていますしね……)。非常におこがましいとは思いますが、「たのしいUNIX」の現代版とでもいうようなコマンドライン中心の本が書きたいな……と長いこともやもやしていたので、その野望はちょっぴり達成できたかな、と思います。

また、「The Linux Command Line」については、以下のURLでPDF版が無料で公開されています。("Download it here."からダウンロードできます)

非常に優れたテキストですから、英語に抵抗のない方はぜひ読まれることをおすすめします。この本は「ほとんど」どころか「一切」マウスを使う操作が出てこない、今どき硬派なLinux入門書ですが、その内容は非常に丁寧で読みやすいものになっています。

おわりに

mollifier氏による書籍紹介も合わせてお読みください:

「あの、ふだんはzshのことしか語らないmollifier氏がbashを語る」という点でも、本書は貴重です。