🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

2回答

3679閲覧

アセンブリ言語 write システムコール write 関数 違いについて

kazuyakazuya

総合スコア193

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

1クリップ

投稿2019/10/25 04:18

編集2019/10/25 05:05

"Hello World!"出力するシェルコードを作成するには
アセンブリ言語が必要だということでいろいろ調べているのですが

結局、

アセンブリ言語で関数のwriteを呼び出すパターン
(ヘッダーファイルから定義を参照して最終的にシステムコールを使う場合)

s

1レジスタ設定など・・・(ここから上) 2call _write

と、

アセンブリ言語で syscall関数を使って間接的に
writeシステムコールを使う場合。引用元

s

1.intel_syntax noprefix # コードの書式 2.global main # 実行開始地点のラベル 3 4main: 5# 前処理 6 push rbp 7 mov rbp, rsp 8 9# 文字列'Hi!'をスタックに格納する 10 push 0xA216948 11 12# ステップ1 system call number で'1'(=writeに対応)を指定する 13 mov rax, 1 14 15# ステップ2 writeのシステムコールに渡す引数をレジスタにセットする 16 mov rdi, 1 # 書き出し対象のファイルディスクリプタで1(標準出力)をセットする 17 mov rsi, rsp # スタックの先頭アドレス(文字`H`が入っている)をセットする 18 mov rdx, 4 # 出力する文字列のサイズをセットする 19 20# ステップ3 システムコールを発行する 21 syscall 22 23# 後処理 24 mov rsp, rbp 25 pop rbp 26 ret

これら2つは何が違うのですか?

勝手な考察

一つ目は、
call _write より<unistd.h>
という名前のヘッダーファイルを参照し、
最終的に機械語のwriteシステムコールを発行する。

二つ目は

・・・
syscall

より、syscall関数が定義されている
<sys/syscall.h>という名前のファイルを参照し、
最終的に機械語のwriteシステムコールを発行する。

・・・何が違うんですか?

レジスタの設定、スタックに引数を積むかどうか

など、設定に違いはありますが

やっていることは結局同じな気がしますが

これら2つは何が違うんでしょうか?

分からないので教えてください。(環境自体の相違性?)

(今回も結構な勘違いをしているような・・・)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2019/10/25 04:45

タグに「Windows」がついていますが、Windows環境での話ですか?Linux上での話の間違いではないですか?
kazuyakazuya

2019/10/25 04:49

ん、質問を質問で返して悪いのですが syscall はLinux限定の関数なんですか?
y_waiwai

2019/10/25 04:59

そうです Windowsはまた別のシステムコールとなります
dodox86

2019/10/25 05:00

話がややこしくなりそうです。既に回答をいただいていますので、そちらで解決するのであれば、まずはそれで良いと思います。
dodox86

2019/10/25 05:03 編集

話をこちらから切るのも無責任に思いましたので補足しますと、syscall関数そのものの話と言うよりは、「system call number で'1'(=writeに対応)を指定する」の時点で既にWindowsの話ではないはず、との指摘、確認でした。
kazuyakazuya

2019/10/25 05:04

誤解をしていたようです。すみません。 タグを変更します。 _write関数自体にも誤解ありました。 ただ・・・ write関数を使わず、わざわざsyscall関数を使うメリット (シェルコード的な意味で) はまだ解決していません・・・。
asm

2019/10/25 05:06

Windowsにはシステムコール writeはありません。 そもそも、狭義のシステムコールを公開せずラッパー関数のみを公開しています。
kazuyakazuya

2019/10/25 05:08

そ、そうなんですか! でも、今回はLinux前提でお願いします。 (Linuxの話をwindowsと勘違いしていたわけか・・・)
kazuyakazuya

2019/10/25 05:14

windowsでは int ox80 を syscall の代わりに入れてあげればいいみたいですね。
dodox86

2019/10/25 05:20

いや、違いますって。int 0x80は、32ビットLinuxで使うシステムコール(ソフトウェア割込み)です。syscall関数はそのラッパーでしかありません。
dodox86

2019/10/25 05:35

そちらのURLの記事は、Windows をホストとしたVMWareの仮想マシンCentOS(linux)上での動作の話です。つまりはLinuxです。
dodox86

2019/10/25 05:36

記事の冒頭に書いてあります。環境:CentOS7 on Windows(VMWare)
kazuyakazuya

2019/10/25 05:36

そうだったんですね。 わかりました。
dodox86

2019/10/25 05:40 編集

まぁ、記事を拝読するといきなりx86アセンブリの話になっているので、初心者の方には誤解しても仕方が無い部分があるかもしれませんね。(私も一瞬、を??と思いました)
kazuyakazuya

2019/10/25 05:59

ありがとうございます。 その記事は事前に拝見させていただきました。 また勘違いかもしれないのですが この記事の、syscall関数を自作してみるというところ・・・ syscall関数を定義(?)しているなかでsyscall関数を使っている気が するのですが・・・ (だって、windowsにはsyscall関数がないから 自作してしまおうという話だと思うのですが そのsyscall関数を定義している中でsyscall関数を使っているということは syscall関数を使えないことに矛盾するのでは・・・)
y_waiwai

2019/10/25 06:05

syscall関数とシステムコールという言葉を混同するんじゃねえw 日本語の講釈からせんといかんのかw
asm

2019/10/25 06:06

Microsoftが公表していないだけで、syscall命令自体は使えます。 Linuxのシステムコールとの互換性はおろか ドキュメント化されていないので、サービスパックレベルで挙動や番号等が変わる可能性がありますし 解析の結果信じられている動作が正しいとも限りません。 呼称も、とりあえずラッパー関数と同名とみなしている感じです。
dodox86

2019/10/25 06:50

あっ、私自身のコメントにもsyscall"関数"と書いてしまっていました。混乱を呼んでしまったかもしれない誤記でした。大変失礼しました。
kazuyakazuya

2019/10/25 07:48 編集

〇 syscall命令 × 関数syscall でした・・・。 参考リンクをよく見ると確かにそのように書かれていました。 ん、となると windowsではsyscall命令はできるけど 関数の定義はされていない・・・(でも関数ではないんですよね?) 提示いただいたリンク先では syscall関数を定義すると書かれていたのですが・・・ (つまり・・・どういうことなのでしょうか?)
dodox86

2019/10/25 08:17 編集

疑問が疑問を呼んで、このままでは質問が閉じれなくなるのでは。 syscallと言う単語について、まとめてみます。 (1) syscallと言う「関数」は存在します。 https://linuxjm.osdn.jp/html/LDP_man-pages/man2/syscall.2.html これは、UNIX系OSの「システムコール」を間接的に呼び出す為に用意されたC言語ベースの関数です。 (2) syscallと言う「CPU命令」も、今のx86CPUには存在します。 https://www.wdic.org/w/SCI/syscall これはCPU命令です。関数ではなく、直接、OSのシステムコールを呼べるものです。int 0x80でのソフトウェア割込み/システムコールより直接的に使えます。Windowsでsyscall命令を使える、と言うのはこの話です。CPU命令自体は実行できる、と言っても良いかもしれません。ですが、システムコールとして使うにはWindowsではファンクション番号などの使い方が全く違うので、実質使い物になりません。SaitoAtsushiさんの回答は、syscall CPU命令をlinux上で使う限りにおいて、共有ライブラリ*.soを経由しないので、シェルコードとして使う場合には影響がないでしょう、と言われています。 > WindowsだけでLinuxみたいにsyscallしてみる の記事は、syscallと言う名前の「関数」を、記事の著者様自身がアセンブリ言語で実装(=定義)しています。レジスタにファンクション番号などをセットするように設計しているので、「Linuxみたいにsyscallしてみる」と表現しています。つまり、この著者さんがsyscall関数の定義をして、自分の範囲で使っています。 WindowsのOSとAPI体系下ではsyscallと言う関数の定義がされて、プログラマーに提供しているわけではない、と言うことです。 話が拡がってしまって質問が閉まらなくなるので、syscallの話は後でよいのでは? そんな訳にはいきませんか?
dodox86

2019/10/25 08:34 編集

> WindowsだけでLinuxみたいにsyscallしてみる あ、いや、記事では結局、中でsyscall CPU命令を使っていますね。syscallと言うシンボル名の関数を定義して、syscall CPU命令を使っている。また混乱を呼んだかもしれません。すみません。(これ以上は黙っています。間違いのご指摘は歓迎します)
dodox86

2019/10/25 09:50

> 質問が閉まらなくなるので、syscallの話は後でよいのでは? そんな訳にはいきませんか? いや、せっかくの興味と好奇心の芽を摘む悪いコメントだったかもしれません。すみません、少し反省しました。
kazuyakazuya

2019/10/25 16:16

解説ありがとうございます。 確かにタイトルと内容が変わってきちゃっているので 分からなければ別に質問を立てようとおもいます。 ありがとうございました。
guest

回答2

0

ベストアンサー

関数かどうかが違います。

関数ではないシステムコールはC言語からはそのまま呼び出すことができません。
そのため、同名のラッパー関数が作られているわけです。

シェルコードの場合のメリットですが
関数を呼び出すためには、関数先頭のアドレスが必要です。
広大なメモリのどこに関数があるのか分かるのならばwrite関数の呼び出しでも問題ありません。
対してシステムコールの場合は、OSがwriteに対して割り当てた番号さえ知っていれば呼び出すことができます。

投稿2019/10/25 06:00

編集2019/10/25 07:14
asm

総合スコア15149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rubato6809

2019/10/25 06:59

重箱の隅ではあるけど、日本語が正しくないと誤解されちゃうので ×関数先頭へのアドレスが必要です ◯関数先頭のアドレスが必要です write関数のアドレスがわからないと call 命令では呼びようが無い、ということを理解してもらいましょう。
asm

2019/10/25 07:15

ご意見ありがとうございます。修正しておきました
kazuyakazuya

2019/10/25 07:44 編集

回答ありがとうございます。 その先頭アドレスについてなのですが・・・ 例えばprintf関数を使ったときにヘッダーファイルは 外部にあるから ヘッダーファイル(stdio.h)のアドレスを書き込まなければいけない というのを聞いたのですが その(printfを定義している場所のアドレス)アドレスのことですか?
kazuyakazuya

2019/10/25 07:49

またそれとは別で、ソースコード自体が置かれているアドレスということでしょうか?
SaitoAtsushi

2019/10/25 08:14

実行ファイル中のシンボルの実体が外部の共有オブジェクトにある場合、たとえば write は libc.so にあるわけですが、普通は実行ファイルをロードしたときに実行ファイル中に格納されているシンボルテーブルを元に OS が libc.so をロードしてその中の write のアドレスに置き換えるという手順を取ります。 ですが、シェルコードというのは通常の実行ファイルのような手順を取るわけではありません。 実行中の別のプログラムの中にうまいことシェルコードを差し込むという形が普通ですから、シンボルを実体に置き換えるという工程を経由しないのです。 もちろん OS がやってくれるロード処理を自分でやるならできなくもないかもしれませんが、しなくて済むならその方が楽ですね。
rubato6809

2019/10/25 14:14

> write は libc.so にある そうなのでしょうね。そして call write 命令はアセンブルしてみるとわかるけど、E8 xx xx xx xx という機械語になるようだから、xx xx xx xx はwrite 関数までの「距離」です。だから、write 関数のアドレス(とこのcall命令があるアドレス)がわからないと、call write の機械語は確定しません。
asm

2019/10/25 14:50

> だから、write 関数のアドレス(とこのcall命令があるアドレス)がわからないと、call write の機械語は確定しません。 E8の相対callならばそうですが、FFの絶対間接callを用いる事で現在のEIPに関わらず呼び出せます。
kazuyakazuya

2019/10/25 16:07

ありがとうございます。
guest

0

OS 内部の情報にアクセスしたり各種デバイスにアクセスするには特別な「権限」が必要なようになっていて、アプリケーション側ではできないことを OS に要求するのがシステムコールです。 しかし C から利用するには型が付いた関数があった方が良いですし、システムコールであることを意識せずに利用できた方が良いのでシステムコールのラッパーとして関数が用意されています。

write システムコールのラッパー関数が write であるというだけの話です。

投稿2019/10/25 04:50

SaitoAtsushi

総合スコア5684

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuyakazuya

2019/10/25 05:00

回答ありがとうございます。 あぁ・・・つまり、そもそもwrite関数自体 アセンブリ言語から使うもんじゃない (C言語から使うことを前提とした関数) ということですね。 うーん、そういった意味でwrite関数自体の意味はわかりました。 なら・・・ write関数を使わず、わざわざsyscall関数を使うメリット (シェルコード的な意味で) は何ですか?
y_waiwai

2019/10/25 05:06

write関数を使わずに、同じことを実装してみればわかります がんばってください
kazuyakazuya

2019/10/25 05:15

やるだけやってみます。
SaitoAtsushi

2019/10/25 05:26

write 関数は一般的には共有オブジェクト libc.so にあり、 /lib や /usr/lib に置かれているのが普通です。 しかしディストリビューションによっては変な場所にある可能性もありますし、あるいはそもそも無いのかもしれません。 攻撃対象がどのような構成になっているのか調べるよりは余計なものを経由せずに直接的にシステムコールをした方が確実性が高いかもしれませんね。
kazuyakazuya

2019/10/25 05:41

(Linux前提で) syscall関数はwrite関数のように変な場所に置かれることはあるんでしょうか? 関数を表面的に使う回数は同じだから・・・ (内部的にはちょっとわからない)
SaitoAtsushi

2019/10/25 06:19

質問文中における syscall は関数呼び出しではなくアセンブリ言語の命令そのものです。 なので call 命令で呼び出すのではなく直接書かれています。 OS の世界に移行するための直接的な命令であり、関数を経由しません。
kazuyakazuya

2019/10/25 07:40

ありがとうございます。 あ、関数ではなく命令なんですね。
rubato6809

2019/10/25 14:07

write() 関数の中で syscall 命令を実行してるはずです。 hello world が表示されるまでの経路は 1. プログラム => write() => syscall命令 => OS => 画面表示 2. プログラム => syscall命令 => OS => 画面表示 という2通りがある。write() 関数を通るかどうかの違いだから、同じように見えるわけです。 この場合、syscall命令を実行できればシステムコールできる=hello worldを表示できる。言い方を変えれば syscall 命令を実行することが肝心であって、write() を通るかどうかは些細な違い。 syscall命令が何をするものか、カズヤ君は調べてみたかな?動作をイメージできるかな?syscall命令と call 命令の動作は、似ていると同時に、大きな違いがある。 ・・・と聞いている私自身、漠然とした説明しかできそうにないけど、大事なポイントは押さえているつもり。
kazuyakazuya

2019/10/25 15:48

ありがとうございます。 確かに2番のほうが手間が省けているわけですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問