2011年01月30日
アセンブリをやってみよう! 0x100
勉強が忙しかったり、家庭の事情があったりで長らく更新の間隔を
空けてしまいました。
今回からはアセンブリ言語をやってみよう!という、
文字通りひねりのない企画ですー。
「アセンブリ」とか聞くと、高級言語使用者は身構えてしまいがちですが、
実は、このアセンブリ、下手な高級言語よりはよっぽど簡単で、
しかも納得ができます。
最近はオブジェクト指向が流行のようですが、オブジェクト指向が
極端になるとコンピュータの内部詳細がまったく読めず、
非常に理解しづらい言語となってしまうことすらあります。
アセンブリはコンピュータの内部動作までしっかり把握することが
でき、体にはねかえってきやすいです。
ただ、裏を返すと内部動作をきちんと理解しないと
使いこなすことはできないわけです。
本企画では、ハードウェアの基礎を少しおさらいし、
アセンブリ言語の基本文法を抑え、C言語との対比なども
加えつつ、進めていこうと思っています。
最大の目的は「アセンブリ言語は思ったより簡単だ」ということを
実感していただくことです。
それでは、続きからどうぞ。
○アセンブリ?アセンブラ?アセンブル??
まずは超基本用語から。
アセンブリ…アセンブリ言語のこと。
アセンブラ…アセンブリ言語を機械語に翻訳する処理系のこと。
アセンブル…アセンブリ言語を機械語に翻訳すること。
現在、これらの用語の使い方が非常に曖昧になっています。
特に、アセンブラがアセンブリ言語のことを指すことがあります。
アセンブリ入門書の鉄板でもこうなっています。
これは文脈から汲み取るほかありません。
○コンピュータって?
まずは、アーキテクチャ関連の話から。
こちらの記事もご参考にどうぞ。
現在のコンピュータは音楽も再生できるしネットも見れるし、
なんだか万能のようですが、その実体はただのすごい電卓です。
しかも二進数しか扱えない電卓なのです。
というのは耳にしたことがあると思います。
なぜ二進数かというと、コンピュータから見れば中途な
電圧は判断がつけにくいからです。
それならいっそ基準の電圧を一つだけにして、
それより高い状態と低い状態だけを判断しよう、ということになりました。
そして、それぞれを1と0に割り当てて考えたのです。
つまり、極論コンピュータは二進数すら理解していないのです。
電圧の高低を二進数に見立てているだけです。
ちなみに、2進数は扱いづらいので、通常は16進数を
用います。これらのことは上のリンクから飛べる
記事に書いてありますが、ざっくり言うと「2進数では桁が大きく
なりすぎて訳がわからん」からです。
アセンブリで扱うハードウェアは主にCPUとメモリです。
・CPU
Central Processing Unitの略。
記憶装置上の記憶素子に記憶されている状態を読み取りながら、
それを忠実に回路の通りに処理します。
要するに、言われた通りの命令を実行します。
人間で脳にあたる部分です。
命令を実行するにはメモすることも必要でしょう。
そこで、CPUの内部にはレジスタという小型記憶装置が
用意されています。
ただし、このレジスタは本当に小型で、現在主流のx86系列では
一つのレジスタは32ビットしかありません。
俗にいう「32ビットCPU」の32ビットは、「レジスタが32ビット」
であるということです。
レジスタについては後ほど詳細を記します。
・メモリ
コンピュータはレジスタだけだとたった少ししか記憶できません。
音楽や動画なんてもっての他です。文書すら不可能でしょう。
ですから、外部の記憶装置が必要です。それがメモリです。
現在のパソコンは大抵2〜4GBほどメモリが積んでありますから、
レジスタに比べればものすごい量です。
これでレジスタはいらない…かと思いきや、レジスタはCPUの
内部にあるため、メモリに比べてはるかに高速にアクセス可能
なのです。
アセンブリにおいては、いかにレジスタを有効活用できるかが、
高速なプログラムが書けるかにつながります。
ちなみに、レジスタもメモリも揮発性(電源を切るとデータが消える)の
記憶装置です。
○プログラムって?
プログラムとは、CPUに与える(実行させる)命令です。
プログラムを作る(書く)ことをプログラミングと呼んでいます。
ただし、前述の通りCPUは二進数しか理解できませんから、
人間も二進数で書いてやらないといけません。
このCPUが唯一理解できる二進数の命令を
機械語(マシン語)と呼びます。
ところが、残念ながらマシン語は人間に読めた
代物ではありません。
55 89 e5
パッと見てわかりますか?
これは当方の環境で以下のアセンブリコードをマシン語に
アセンブルしたものです。
pushl %ebp
movl %esp, %ebp
アセンブリコードならまだ何となくわかりそうな気が
しますよね?(するんだって…)
%ebpというものに対してプッシュ(push)という動作を行い、
%espなるものを%ebpに動かす(mov)…のでしょうね。
このように、マシン語はあまりにもわかりづらいため、
英単語に置き換えて幾分かわかりやすくしたのが
アセンブリ言語なのです!
アセンブリは基本的にマシンコードと一対一対応です。
上の例では、「55 : pushl %ebp」「89 : movl %esp,」
「%ebp : e5」といった感じです。
しかし、アセンブリはマシン語を置き換えたにすぎないので、
各環境にベッタリと依存しています。
このようなプログラミング言語を低級言語といいます。
それに、コンピュータの仕組みを知らなければ意味不明の
文字列でしかありません。
そこで、よりわかりやすく、環境に依存しにくくした
言語を高級言語と呼びます。
現在、低級言語はほとんどマシン語とアセンブリだけだと
思っていただいて問題ありません。
ただ、いかなる言語を用いても、最終的にはマシン語に
翻訳されなければなりません。CPUは馬鹿ですから、
他の言語なんて理解できません。
それなら、マシン語に親密なアセンブリを使った方が、
コンピュータを直接操作できますよね!
そういうわけで、アセンブリに触れてみましょう。
○アセンブラ
アセンブリには大別して二種類の記述方法があります。
それぞれのアセンブラによって違っています。
・Intel記法
[ニーモニック] [操作先] [操作元]
・AT&T記法
[ニーモニック] [操作元] [操作先]
このように、若干異なっています。
ちなみに、先ほどのマシン語とアセンブリの例では、
AT&T記法になっています。
movl %esp, %ebpは、Intel記法では
mov ebp, espとなります。
(色々若干違いますがとりあえずスルーで)
本企画では、AT&T記法にします。別にIntelが嫌いとか
いうわけではなく、Linuxで使用できるツールの多く
(gdbやobjdump等)がディスアセンブル(マシン語をアセンブリ
コードに逆翻訳すること)コードをAT&T記法で吐くので、
こちらに慣れたほうが何かと便利だったりします。
通常はIntel記法から始めることがおおいようです。なぜなら、
先ほどの例でも
mov ebp, esp
⇒ebp = esp;
というように、単純にわかりやすいからでしょう。
慣れればどちらでも大した問題ではありません。
*****************************************
それでは、アセンブラを使ってみましょう。
本ブログはLinux中心ですので、Linuxのほぼ標準コンパイラと
なっているGCC付属のGASを使いましょう。
それでは、ターミナルを開きましょう。
Ubuntuの場合は、[アクセサリ]->[端末]です。
ってここまで書かなくても大丈夫ですよね^^;
作業用ディレクトリを作っておきたい方はmkdirで適当に
作ってください。
まずは、コードを書いてみましょう。
ファイル名はtest.sとでもしておいてください。
GCCアセンブリソースの拡張子はsのことが多いです。
*****************************************
.globl main
main:
movl $4, %eax
movl $1, %ebx
movl $msg, %ecx
movl $13, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
.data
msg: .asciz "Hello,world!\n"
*****************************************
そして、アセンブルします。
$ gcc -o test test.s
$ ./test
Hello,world!
アセンブリで書いた初プログラムです!無事動いていますよ!
ソースや文法の解説は次回以降になります。
Tweet
こちらよりAmazonギフト券による支援を募集しております。
受取人のEメールに hirohorse2-suplbl(アットマーク)yahoo.co.jp を設定してください
少額でも非常に助かりますので、お気に召されました記事がありましたら、何卒ご支援の方よろしくおねがいします。
トラックバックURL
この記事へのコメント
level_fourさんが解説するアセンブリ、是非読みたいですね。
自分はnasmでやっていたので、gasに変えていこうと思います…
objdump,dissyは好きです。
アゼンブリって、全然分からなかったのですが、なんとなく分かるようになってきました。
頑張って記事に追いつけるよう勉強したいと思いますw!
nasmも非常に汎用性の高いアセンブラですが、
objdump等を使うとなると、やはりGASの方が
フィットしますね。
僕もやりたいことが全て終わったらアセンブリに挑戦してみたいと思います。
同じものなのでしょうか?
勉強させて頂きます(`・ω・´)
自分本当馬鹿ですねww
test.sの作成方法とそれの編集の仕方がわからないです;
geditで編集すればいいのでしょうか?
自己解決しました。
gedit test.sでできました!
自分はMac OS Xの環境でgcc を実行したのですが、エラーが出てうまくいきませんでした。いろいろ調べてみて試したのですが実行できません。
よろしければ、教えていただきたいです。
環境はOS X 10.8.4
CPU: 1.7 GHz Intel Core i5
2012年のMac book air 11inchです。
エラーの内容は
Undefined symbols for architecture x86_64:
"_main", referenced from:
start in crt1.10.6.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
です。
gcc -o を実行したときに返ってきました。
お願いします。
どうやらMacOS環境のgcc特有の問題らしいです。
http://stackoverflow.com/questions/9308806/nasm-gcc-issue-on-64-bit-mac-os-x-lion
ほぼ同様の問題がこちらでも報告されていましたので参考になるかと思います。
ありがとうございました。
今までのエラーは直りました。
今、CTFのバイナリ問題のためにまずはアセンブリからと思い勉強しているのですが、、、。
奥が深いですね。
バイナリ解析のためにこんな事できたほうがいいみたいなアドバイスを頂けたらうれしいです。
.date
の部分でエラーが出てしまいます。どうしたらいいんでしょうか。