2021年4月7日水曜日

ゼロから作るOS自作入門3日目番外編:きれいな模様セルフコンテスト

永遠に時間が潰せる、と思った。 

平日は大体仕事してるので、というかまぁ忙しいので定時じゃ上がれないわけですね。

でもOS作りたいなって思ってるんだけどガッツリはできないからその代わりに遊んでる。

シンプルに

彩度をいじる



れいんぼー


もっとれいんぼー




もやもや
X軸:明度をへらす
Y軸:彩度をへらす



WindowsとかのGUIプログラミングではあんまりこういうのやったこと無いし、
正直できる気がしない。

ベアメタルのプログラミングでならメモリにカラーコード書き込むだけでできて直感的なので割とこういう事が簡単にできて良いよね。

複雑な図形をかけって言われると微妙だけどね。

箸休め終わり。


本をゆるーく進めて、何か得るものがあって、まだ情報が出てなさそうなものは書いていくスタンスにしよう。

本を読んで、毎日書きましょう だと続かない人間だし。

2021年4月5日月曜日

ゼロからのOS自作入門をRustで書いていく3章~UEFIライブラリがしんどい件について

最新のリポジトリはこちら。(作業完了時のコミット→Day3のコミット)


 マジでこの苦しみを分かち合いたい。

この辺の人達もだいたい同じところで苦戦してる。

【ゼロからのOS自作入門】MikanOSをRustに移植する 1章・2章

MikanOSをRustでやってますが、しんどい。

Rustの人たち3人よれば文殊の知恵になったらいいな。
3人目の俺がポンコツなのでならないかもしれないけど。

というわけでさらなる4人目のRust使いがこのあたりしんどい・・・ってなったときの道標として↑の記事読んでも詰まった部分を書いていく。

何度も言うけど、UEFIライブラリ(uefi-rs)で詰まった。

詰まりどころじゃない部分のソースもgithubに載せておくので、コピペして使ってOK

3章(3日目)は1~2日目とは違って、カーネルファイルをロードするというそれっぽいことをします。が、Rust使いには茨の道だったという話を書いていくつもり。

ファイル読み込みは苦行

事前に言っておくと、uefi-rsを使う以上、カーネル側のkernel_main関数もEFI_API呼び出し規約で合わせて置かないと、うまくカーネル側で受け取れないので結構な時間ハマることになる。普通にハマった。呼び出し規約については「MikanOSをRustでやってますが、しんどい。」の記事に書いてあるので省略する。


みかん本でもUEFIのファイル開くところは深く触れられてないので、正直ここを理解しつつ移植するのは骨が折れた。
ファイル読み込み関数(open_file)でやっていること
このあたりはUEFI規格書に書いてあるが、ライブラリのほうと整合させるのに苦労した。簡潔すぎてドキュメントの読み方がわからん。
  1. LoadedImage ProtocolをBootServicesのhandle_protocolメソッドで取得する
  2. 取得したLoadedImage Protocolのインスタンスのdeviceメソッドを叩くとデバイスオブジェクトが取得できる(どのデバイスを開くか)
  3. SimpleFileSystem Protocolの引数に2で取得したデバイスオブジェクトを引数にすると、ファイルシステムが開ける
  4. SimpleFileSystemオブジェクトのopen_volumeを叩くとディレクトリオブジェクトが取れる
  5. ディレクトリオブジェクトを使って「ファイルハンドル」オブジェクトを取得する
    1. なお、ここで取得している「ファイルハンドル」はファイルだけでなく「ディレクトリ」である可能性もあるので本当はオブジェクトの種別をちゃんとチェックしないといけない。現状は決め打ちでやっている。
  6. ファイルオブジェクトをRegularFileオブジェクトに変換する
  7. RegularFileオブジェクトを返却する
というのが、open_file関数でやっていること。
かつ、その上位にあるFileReaderWriterはRegularFileオブジェクトをラップするオブジェクトになっている。

殆どは、RegularFileオブジェクトに存在するメソッドを叩いているだけだが、ファイルサイズ取得メソッド(get_size)だけは少し毛色が違うので大きめになっている
具体的にやっていること
  1. get_infoメソッドを使い、FileInfoを取得する。(公式ドキュメントがわかりづらすぎる件。)
    1. このメソッドは、確保しているバッファが少ないと、失敗したときにerrorオブジェクト内に必要なバッファサイズを返却するという仕様。
    2. とりあえず動的にバッファを確保してFileInfoをしまうバッファを作った。
      1. そして書いてる途中に気づいたけど、確保したメモリを開放してないからメモリリークしてる。だめじゃん
  2. 取得したFileInfoオブジェクトのfile_sizeメソッドを呼び出してファイルサイズを取得している。

これでようやく、カーネルをロードできるようになった。

あとは「MikanOSをRustでやってますが、しんどい。」の記事に習って、ロードするだけ。

で、色々やりたいことをやればよろしい。

UEFIで動くHSV色空間からRGBに変換する部品は2年くらい前に作ってあったので、それを動かしてみた。

モジュール分割してみる

とりあえずローダとカーネルでファイルが分かれているので分割したい。
でも分割するのはファイルだけで、共通部分は共通で使いたい。
なので以下の分割方針を取ることにした
  • ブートローダ:バイナリクレート
  • カーネル:バイナリクレート
  • その他共通部品:ライブラリクレート
    • アセンブラ関数やフレームバッファと言ったブートローダやカーネルで共通に使うものを入れておくライブラリ
というわけで方針が決まったのでざっと分割すると今のリポジトリみたいな感じになる。

苦行だけど、できると楽しい。
神書籍だと思う。

おまけ

ライブラリクレートは、バイナリクレート経由でビルドされることによって、バイナリクレート側のビルド設定ファイルが適用されてビルドされる。
ただし、ビルドスクリプトはライブラリクレート固有のものになる。

つまりこういう事
ビルド設定:バイナリクレートの設定(build-stdなどのライブラリ設定)が継承される
ビルドスクリプト:ライブラリクレートのものが使われる

ライブラリクレートはUEFI側とカーネル側、つまるところ、PEファイルとELFファイル両方にリンクできる形式でビルドされなければならない。
とりあえず、PEファイルとELFファイルに対してコンパイラとリンカを個別に使って解決することにした。

ゼロからのOS自作入門をRustに書き換えながら作っていく1章~2章

ゼロからOSを作る本(通称:みかん本)、某30日本のモダンOS版ですね。

day3はこちら

なんとなくPDF版を買いました。

余剰のモニター使えば顔上げたまま読めるし^Fの検索も効くので。

とりあえずRustでやるからには環境を整えないといけない。
Day1はバイナリ打ち込もうぜ。っていう話でPEバイナリをひたすらバイナリエディタで作るだけなのでただやるだけ。ひたすらにやるだけ。なので記事は書いてない。

ビルドについて

Rustのデフォルトビルドコマンドである、 cargo build コマンドでビルドができるようにしたので、この界隈でよく使われるxargoとかxbuildは使っていない。
Rustでやるならここらはしっかりこだわりたい。
Makefileとか使わなくてええんやで。という強い推しをしていきたいんだけど
それ以外の部分がだいぶ苦行だからちょっと推せない気がしてきてる。


ちょっと早いけど、こちらがリポジトリ(Day2終了時点のコミット)

最新のリポジトリ本体

  1. 環境構築(WSL2)
    1. qemu-system-x86_64のインストール
    2. ovmf.fdを導入(みかん本では、ovmf_code.fd ovmf_vars.fdを使っているが、特に分ける理由もないので、ovmf.fdを使っていく)
  2. X-Serverのインストール(これはみかん本の最後の方に書いてあった)
  3. WSL2にRust(nightly版の最新)をインストールする
これが終わってようやく開発が始められる。
というわけで続き。

C++ではEDK-IIですが、Rustではuefi-rsを使って、ブートローダを書きます。
みかん本通りに進めているなら自然とEDK-IIは使えているわけですが
いかんせんこのuefi-rsはみかん本のスコープ外なので自力で調べる必要があります。
ここが難所です。マジわかりにくいのなんの。
大体コード書く時間を1割とすると、ライブラリを調べる時間とデバッグする時間が9割くらい。

とはいえ、Day2はDay3に比べるとそんなに分かりにくいライブラリは使わずに終えたのでウォーミングアップとしてはそれなり。
でも難しいものは難しいので勘所を書いておこうかと。
特にファイル入出力あたりの書き方が分からなさすぎて辛い。

なので、ファイル入出力のサンプルコードとして、リポジトリの当該コードにリンクを貼っておく。

で、確かDay2の目的はとりあえずメモリマップを吐ければOKだったので、吐いてみた。

とりあえず、吐けたのでDay2は完了
ただ、たまによくわからないバイナリがボコッと入ってくるのはなんでだろう。
多分メモリ上のゴミだと思うんだけど、原因不明なので類似事象が発生した方の報告まち。

Day2はおわり。

2019年7月15日月曜日

Kansa(Powershell incident response framework)をWORKGROUPで動かす方法。

はじめに 
フォレンジックの証跡収集で使うツールの話です。  
前提として収集するマシン・されるマシンはWindows 10 Proを想定しています。  
Win 10 Homeは手元に環境がないため動作未確認です。

 何をするの?
Kansa(読み:カンザ)というフレームワークを使って、証跡の収集を行います。
Kansaのコマンド例(.\kansa.ps1 -Target $env:COMPUTERNAME -ModulePath .\Modules -Verbose)では、AD環境前提なので、ADの無い一般のご家庭やADをもそも使ってない企業ではそのまま使うことはできません。 

じゃあ、どうすればいいの?
このフレームワークはWinRM(リモート管理サービス)を利用しています。
 なので、まず普通の家庭のマシンはWinRMは無効になっているはずです。
また、接続されているネットワークを信頼していないとWinRMが使え無いのでネットワークの信頼設定も行う必要があります。
さらに、接続先が信頼されていないとWinRMで接続できないので、「接続先のマシン」についても信頼しなければなりません。  

まとめると、 
1.ネットワークを信頼するようにWindowsに設定する(接続する側・接続される側)  
2.WinRMを有効化する(接続する側・接続される側)  
 3.接続先マシンも信頼するようにWindowsに設定する(接続する側) カッコ内は、設定するマシンを示しています。 (3は接続する側だけ設定するよう示していますが、できなかったら両方設定してみてください)そのための設定を以下に記載します。  (引用元:How to setup WinRM in a WorkGroup Non Domain Environment)  

1.ネットワークを信頼するようにWindowsに設定する
 →WinRMサービスを動作させるNICを「プライベートネットワーク」にします。
Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Private   

2.WinRMを有効化する
 →WinRMを有効化します。
 Enable-PSRemoting -force  

3.接続先マシンも信頼するようにWindowsに設定する  
→10.10.10.12は接続先(信頼するべきホスト)です。ここは実際の環境に合わせて設定を行います。
winrm set winrm/config/client '@{TrustedHosts="11.10.10.12"}'  

ここまでがWinRMの設定です。 
これが済んだら、Kansaを実行し証跡を取得していきます。  
以下のようなコマンドで実行可能です。  

4. Kansaの実行  
Kansa公式のコマンドに”-Authentication Default”オプションを付与することで、設定のめんどくさいケルベロス認証をせずにログインさせることができます。(わざわざSSLを使うとかの設定にしなくても良くなる)   

コマンドテンプレート
.\kansa.ps1 -Target <コンピュータ名> -Credential <アカウント名> -ModulePath .\Modules -Verbose -Authentication Default  
コンピュータ名:接続するIPアドレスやホスト名、ただし、「localhost」は使えない。  
アカウント名:収集したいコンピュータ上にある管理権限を持つアカウントを設定します。
 パスワードはプロぷとが立ち上がるので、そこで入力します。  

自分自身に接続して収集する例(アカウント名が user1 の場合)
 .\kansa.ps1 -Target $Env:COMPUTERNAME -Credential user1 -ModulePath .\Modules -Verbose -Authentication Default  

192.168.1.2に接続して収集する例(アカウント名が user1 の場合)
 .\kansa.ps1 -Target 192.168.1.2 -Credential user1 -ModulePath .\Modules -Verbose -Authentication Default 


 TARGET_HOST(NetBIOS名)に接続して収集する例(アカウント名が user1 の場合)
 .\kansa.ps1 -Target TARGET_HOST -Credential user1 -ModulePath .\Modules -Verbose -Authentication Default   

2017年4月11日火曜日

C言語で相互参照する構造体の書き方メモ

最近D言語に色んな意味でドハマリしていて久しくC言語書いてなかったので忘れていた事をメモする。



こんな感じで、定義の前にタグ名だけのstructを書いておく。
多分3年ぶりくらいにC言語でプログラム書いたのでどうやったっけなーとか思い出すのに時間かかった、ていうかググった。

2017年3月25日土曜日

UEFI AppをD言語で作れるようになるまでに。

さて、色々試しているD言語。
UEFI Appを作りたいってあれ。

任意のデータ構造とメソッドを持った任意の構造体を定義できた。
ワンチャンコレで作り始めることができそうなのでサンプルソースを置いておく。

ただ、RTTIとか、例外機構そういうD言語っぽい何かを使うことはできないので、
完全にメモリとか色々な管理をサボれる(scopeが使えるって意味で、メモリ管理機構等を作らなくていいという話ではない)CとDの間の何かっぽいアレができた。




下手したら、D言語のコミッターとかに見つかったらぶっころされそうな感じのハックをしているので、出来ればいい感じにobject.dとか実装していきたいけど
いい感じに出来ないからガムテープでぐるぐる巻きにした感じのものが出来た。

マングルされた名前のシンボルをただエクスポートしてるだけ。
基本的にRTTIのものばっかり。でもRTTI使わないしいいよねって感じ。

ただ、_d_assert_msgとかがどういうふうに使われてるのかが理解できるいい機会になった。

いじょー

追記
uefi-dライブラリを使ってUEFIを叩いている所。(クリックで拡大)
一般的な保護機構を持つOS上では動作しない命令(cliとhlt命令)がちゃんと実行されていることが確認できて楽しい。(build.ps1でqemuを-nographicモードで動かしてる。)

2017年3月12日日曜日

/SUBSYSTEM:UEFI_APPLICATIONなバイナリをD言語で

はい。
何もしないアプリケーションを作ってみたいと思います。
たぶん動かないんじゃないかなぁ。

動機:
1.頑張ればUEFI App作れる。
2.頑張れば→構造体とか使うと、object.dを定義してTypeInfo_Structとか実装しなきゃダメ。←これ誰か一緒にやって欲しい感じある。D言語の仕様を一緒に読んでほしい。
3.でも頑張る前にコンパイルして、実際にバイナリに落ちるか試したかった。

プロジェクトはね、ここにあるから。適当に色々やってみてって感じ。
dub -b=release でビルドできる
https://github.com/segfo/D_MinimalUEFI/tree/master


で、これがビルドスクリプト。MS製のリンカに色々とオプション渡してるだけ。
参考文献:MSDN:リンカー オプション
(注意点はdub -b=releaseオプションで必ずリリースビルドをすること。あと32ビットでビルドしようとしたら強制的に64bitビルドに切り替わるように、"dflags-x86"で、-m64を渡して切り替えるところくらいかな)

まぁ、アプリケーション本体のソースはこんな感じ本当に何もしない。
ていうか、何もできないんだよね。
D言語の場合C言語と違って、object.dを実装しないと構造体が使えないから。
本当に何もできない悲しい。
日本のD言語erの皆さん、ヒープがない環境でのobject.dの実装についてアドバイスください。

以上

2017年1月29日日曜日

CTF4bのバイナリwriteup(復習問題除く)

と言うわけで、Writeupをサクッと書いてしまおうと思います。
(時間無いし、あとでやるとかやってるとだいたいモチベが低下するので)
解いた問題だけやる形でいきます。
と言うわけでスコアボード。
表示はなんか恥ずかしいから、susonoにしてた、バイナリ(非実在バイナリ=テキスト形式で渡されたやつはやってません)を今回は攻めてみた。



スコアボード、スコア全く気にしてなかったけど48位だった。
やったぜ?




2017年1月21日土曜日

Raspberry pi Zeroを個人輸入してみた

というわけでですね、今回はラズパイZeroを個人輸入してみた。

目的:
 Androidデバイスから、物理スイッチのみのスピーカー・照明器具の電源制御をしたい

と言う感じなので、とりあえず全部入りのスターターキットを買ってみた。

一番安い送料で送ってもらった。のでこんな感じの小包の状態で届いた


さらに袋に入ってた。なんか海外って感じがするよね。


ひらいた


右下の小包はラズパイZeroの専用ケース。(要組立て)


本体、GPIOのピンは未実装。
ただし、スターターキットに全て付属しているので、はんだ付けする。


GPIOピンを付けてケースに収納した所。


なお、OS(Raspbian)自体はmicro SDに焼かれていて、そのままでも使える。



・・・が、個人的にこの使い方だとGUIいらないので、Raspbian Jessie Liteを突っ込む。
Windows10なので、Win32DiskImagerを使ってSDカードにイメージを焼いていく。

で、続いてOTGを有効にしてUSB繋いだだけでパソコンとSSHできるようにする。

0.焼いたイメージの設定ファイルを少し弄る
 Windows 10なら、マウントしたmicro SDカードの直下のconfig.txtとcmdline.txtを少し修正して保存する。

・config.txt
 dtoverlay=dwc2
上記1行を最終行に追記する。

・cmdline.txt
 modules-load=dwc2,g_ether
上記1文を rootwait の直後に追記する。

1.OTGドライバをインストール
 Raspberry pi ZeroをEthernetアダプタとして認識させるためのドライバを入れる。
 わりかし便利な機能だけど、ラズパイZeroしかハード的に対応してないみたい。
 RPI Driver OTG

2.mDNSをWindowsで使えるようにBonjourを入れる
 プリントサービスを入れる理由は、その中にmDNSを喋ってくれるクライアントがあるから。他のところではiTunesでも入れればいいんじゃね?って言われてるけど、iTunes使わないし。どうせ起動しないなら小さいアプリのほうがいいじゃん。
※2017年1月21日現在Windows 10ではmDNSは非対応。対応しているのは類似技術のLLMNRであり、互換性はないのでいくらやってもBonjour無しでは引けない。

あとは、ラズパイゼロをパソコンのUSBに繋いで
raspberrypi.local
でpingが通るか確認すればOK。
なんか、mDNSはIPv6優先で引いてくれるらしい。
だから最悪ラズパイのipv4アドレスが同一ネットワーク内になくてもアクセスできる。
最高。

2016年12月28日水曜日

D言語の演算子評価順序


Twitterでこんな記事を見た。
paiza開発日誌 - ツイッターで出題した未定義問題のお詫びと調査と解説について

最近のpaiza割りと酷くて、C言語の環境依存系の問題バカスカ出してる印象がある。
(ポインタの長さ系の問題とか、sizeof(char*)==4が正解みたいなアレ)
まぁそれは置いといて、では我らがD言語さんは、言語仕様で演算子の評価順序が定義されているのか確認してみた。

公式の文書(Expressions)によれば、

Order Of Evaluation

Binary expressions and function arguments are evaluated in strictly left-to-right order. This is similar to Java but different to C and C++, where the evaluation order is unspecified. Thus, the following code is valid and well defined. 
というわけで、左から右へ評価される と定義されているらしい。
では実際にコードを書いて、生成されたバイナリを見てみよう。

こんなコードを書く。
そんで、次にバイナリを読む。



もっとわかりやすくしてみる。


お分かりいただけただろうか。
とにかく左から順に計算していく。
ただし、インクリメントは後置と前置では全く違うのでこのあたりを考慮してやる必要がある。

では、Paizaの問題をD言語で評価するとどうなるか。

int i=0;
i = i++ + i++;
機械語を読むとこう評価されている模様。(後置のインクリメントは1度だけ実行)
i = 1+(0 + 0);
というわけで、Javaだけじゃなくて、D言語も対象言語に入るよ~
Paizaさん、D言語ちゃんも仲間に入れてあげて!

2016年12月23日金曜日

fsbでELFファイルを引っこ抜いてみる

fsbでできることをとりあえず書いていく
まとめではないので、いつかはまとめたい。

SharifCTFのpwnで学んだことが一つあるのでそれについて。
Sharif CTF pwn 150点の問題の概要
「fsbが有って、それを使ってELFを引っこ抜いてpwnする!以上だ!」
みたいな感じだった(だった気がする、細かいことは気にしない)

個人的には、スタックのリークとアドレスの上書き(GOT Overwrite等)くらいかなと思ってたけど、ELFの引っこ抜きは盲点だった。
ってわけで、やってみる。

ELFファイルが実行されるときは、大体仮想アドレス上の0x400000に固定されて走る
ELFファイルもココに全部マッピングされる。

つまり、fsbで0x00400000から0x00410000くらいまでをリークすればELFファイル
を復元できる
(厳密には完全に復元できるわけじゃなく特定のセクション、今回は.textのみ復元できる)
.textが復元できれば、プログラムの構造がだいたいどんなふうに成っているか分かるし
pwnしやすくなる。

と言うわけでやっていきましょう。

2016年12月13日火曜日

Binary ninjaで遊んでみる

Binary ninja買いました 今のレートだと1万2000円位じゃないでしょうか(ざっくり)

端的にいうと、Binary ninjaはいいぞ。

2016年10月1日土曜日

D言語でOpenSSLをstatic linkする方法

訳あってOpenSSLのDLLがない環境でも動くものが必要になった。
そこで、D言語でOpenSSLを使った暗号化ラッパーライブラリを作って静的リンクさせたかったので書く。

dubビルドスクリプト(json)
これが今回一番大事で、これちゃんと作れないとx64版ビルドしかできない。
x86版が作れない。
ほんとうに大事。

dubに何も引数指定しない場合:x86版がビルドされる
dubに--arch=x86_64を指定した場合:x64版がビルドされる
  • sourceFiles-x86_64ディレクティブ
    • x64版のstatic linkライブラリを指定する
    • dub --arch=x86_64 を指定した場合のみここが走る
  • sourceFiles-x86ディレクティブ
    • x86版のstatic linkライブラリを指定する
  • sourceFilesディレクティブ
    • どのような場合でもOpenSSLが使うインポートライブラリをリンクする
  • dflags-x86ディレクティブ
    • x86版をビルドする場合に、 -m32mscoff オプションを付ける
    • これを付けることで、MSCOFF形式のライブラリ/オブジェクトファイルに対応してくれる
    • ていうかMS謹製のlink.exeが今のところ立ち上がる。(検証済み)
    • デフォルトだとOMF形式しかリンカが認識しないためその処置
    • なんでMSCOFFとOMFで2つも規格あるんだよどっちかに統一してくれ


MSCOFFとOMFには割りと因縁があり、7年前C言語の勉強始めた頃に、VisualStudioでビルドされたライブラリをBorland C Compilerについてるリンカでリンクしようとしたら怒られまくって心折れた記憶が復活したよね。
今回はなんとか倒した。(他の人に色々聞いた、優しい世界)

原因は、x64ビルドだとVisualStudio付属のlink.exeが立ち上がってビルドされるから問題ないけど、x86だとDigital Mars製のOptlinkが立ち上がってビルドする。このOptlinkはデフォルトだと読まれるライブラリをOMFと仮定して処理しようとする。
つまりデフォルトでは、MSCOFF形式のライブラリだとエラーを吐く。クソかよ。


メイン関数
特に説明はない。
ハッシュ値の生成とランダム値の生成をやってるだけ

標準ライブラリのハッシュクラスを更に使いやすく(Pythonっぽく)ラッピングしたクラステンプレート
これ一つで文字列・ファイル・バイト列からSHA1でもSHA224でもSHA256でもMD5などD言語が対応しているハッシュなら生成できる (MD5の使いみちとしたら衝突攻撃の検証とかかな?)

OpenSSLリンクチェック用のランダム値生成関数


というわけで、なんとか静的リンクさせることができた。
あと、D言語のテンプレートクラスと標準ライブラリの豊富さに無限の可能性を感じた。
終わり。

2016年9月2日金曜日

D言語のコンパイル時に決定する静的な文字列の扱い

ここにOSによって、SEGVするコードがある。
その理由を突き詰めてみる。

なお、元ネタ。


OS(というかバイナリの形式、Windowsの場合はPE、Linuxの場合はELF)によって
配置されるセグメントが違うから、SEGVが起こると考えられる。
実際どこに置かれるか、逆アセンブルして見てみる。

Linux版バイナリははWindows Subsystem for Linuxのbash(x86-64)でビルド
Windows版バイナリはWindows 10 Professional(x86-64)でビルド

Linux
c:\D_Codes\test>bash
segfo@SEGFODESKTOP:/mnt/c/D_Codes/test$ uname -a
Linux SEGFODESKTOP 3.4.0+ #1 PREEMPT Thu Aug 1 17:06:05 CST 2013 x86_64 x86_64 x86_64 GNU/Linux 

Windows
c:\D_Codes\test>ver
Microsoft Windows [Version 10.0.14393]

dmdのバージョンは以下のとおり

Linux
segfo@SEGFODESKTOP:/mnt/c/D_Codes/test$ dmd --version
DMD64 D Compiler v2.071.1                                                           
Copyright (c) 1999-2015 by Digital Mars written by Walter Bright
segfo@SEGFODESKTOP:/mnt/c/D_Codes/test$ exit

Windows
c:\D_Codes\test>dmd --version
DMD32 D Compiler v2.071.1                 
Copyright (c) 1999-2015 by Digital Mars written by Walter Bright


なお、コンパイルは以下のコマンドで行った。(共通)
※WSLでは現状32bitバイナリが実行できないため64bitバイナリを生成する。

dmd -m64 SEGV_OS_Dependent.d

実行結果は以下のとおり。

Linux版バイナリ
segfo@SEGFODESKTOP:/mnt/c/D_Codes/test$ ./test                                                           
Segmentation fault (コアダンプ)

Windows版バイナリ
c:\D_Codes\test>test                                                                                     
hAgehoge 

Linux版はSEGV
Windows版は最後まで実行できた。

この違いは何か、バイナリレベルで見てみる。
今回は、x64バイナリ、IDA Freeでは見られないので
Linuxはgdb、Windowsはx64Dbgで書き換えられる部分のメモリの属性を見る。

まずはLinux版

RBX: 0x7ffffe14c7f0 --> 0x7ffffe14c810 --> 0x0
RCX: 0x468491 --> 0x65676f6865676f ('ogehoge')   
RDX: 0x1  
[---------------------code------------------------------]
   0x438c12 <_Dmain+50>:        call   0x4390d0 <_D3app7__arrayZ>
   0x438c17 <_Dmain+55>:        inc    rcx
   0x438c1a <_Dmain+58>:        mov    QWORD PTR [rbp-0x10],rcx
=> 0x438c1e <_Dmain+62>:        mov    BYTE PTR [rcx],0x41
[-------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0000000000438c1e in D main () at source/app.d:5                                                                                     
5               ubyte[] sb=cast(ubyte[])s;       

おもむろに、gdbを立ち上げて、runするだけ。

codeペインのこの行は
=> 0x438c1e <_Dmain+62>:        mov    BYTE PTR [rcx],0x41                        

D言語のソースの7行目を指している。
(gdbの画面では5行目がでているが、恐らくバグ)
なお、
Legend: code, data, rodata, value 
で、rodata(読み取り専用セグメント)に代入していることがわかる。

vmmapで確認する

gdb-peda$ vmmap                                              
Start       End        Perm  Name
0x00400000  0x00481000 r-x-  /mnt/c/D_Codes/test/SEGV_OS_Dependent
0x00680000  0x00681000 r---  /mnt/c/D_Codes/test/SEGV_OS_Dependent
0x00681000  0x0068c000 rw--  /mnt/c/D_Codes/test/SEGV_OS_Dependent 

RCXの値は、
RCX: 0x468491 --> 0x65676f6865676f ('ogehoge') 

(cast(ubyte[])"hogehoge")[1] の2文字目の o を指しているポインタであることがわかる。
値は0x468491

vmmapでは、このメモリ領域のパーミッションは、r-x-となっている。
だからSEGVが発生する。

ではWindowsではどうか。

00007FF7229D1039の命令が7行目の命令。
RCXの値は 00007FF722A1EA41

00007FF722A1EA41のメモリ領域は、-RW-- となっており、読み書き自由。


以上のことから、コンパイル先のバイナリやOS?によってDコンパイラはデータを配置するセグメントを変えていると思われる。

Windowsの場合は、明示的にstringをimmutableにしてもキャストしたら実行できてしまった。
このことから、コンパイル時に確定している文字列は暗黙的にimmutable(またはconst)の扱いとなると考えられる。


D言語でも吸収できない闇を垣間見てしまった気がする。
おわり。

2016年8月4日木曜日

Windows subsystem for Linuxを試してみた。

WindowsでUbuntu bash使えるようになってたので
いろいろ動かしてみた。

動いたもの
Pythonライブラリ:pwntools / binjitsu
Python
gdb/gcc
gdb-peda

動かなかったもの
ping(icmpソケットが開けない的な理由)

で、gdb-pedaの動作確認をしていた時の事。
とてもエモい感じだったのでメモ。



a.outの.textのベースアドレスがWindowsっぽく、0x00400000から始まってるのと
各.textセクションのパーミッションが、r-x-のはずがrwx-になっていて非常にエモい。
多分、命令をエミュレート(Windows syscallに動的変換?)する時に書き込んだりしてるのかもね。


それだけ。

2016年7月10日日曜日

D言語でPEファイルヘッダをパースしてみた

今日もD言語やっていこうかと。

と言うわけで、作るもののネタが無いのでPEファイルのヘッダを解析してみる。
アルゴリズム的には、構造体作って、構造体にそのまま取り込んで、表示するだけなので
構造体をコピペすれば完成する代物。

ちょっとだけエイリアスを宣言してやる必要があるけど、やることはそれだけ。
とりあえず、x86バイナリと、x64バイナリに対応できればいいかなって感じ。

ちゃんと解析できてるかどうかの確認はIDA Freeあたりで試す。

D言語の std.File.rawReadは配列しか渡せないので、構造体をC言語みたいにポインタで渡すとか言う直感的な方法でなくて、戸惑ったけど、書き方が微妙に違うだけでほぼ同じだった。
D言語はいいぞ。ランタイムあたりが厄介者だけど、それはどうにかすればいい。

dmd -m32 PEHeaderAnalyzer.d
PEHeaderAnalyzer.exe PEHeaderAnalyzer.exe

実行結果こんな感じ
Machine: i386
applicationBits: 32
Subsystem: Windows CUI
ImageBase: 0x400000
BaseOfCode: 0x2000
SizeOFCode: 183296
AddressOfEntryPoint: 2565c
Absolute entry point: 42565c
SizeOfImage: 274432
IDAで答え合わせ。

※ 画像クリックで拡大可能

Machine: i386 と
ImageBase: 0x400000 はIDAでみると、問題なさそう。

BaseOfCode: 0x2000 も取り出せてる



エントリーポイントのアドレス(Absolute entry point) = ImageBase + AddressOfEntryPoint
ImageBase: 0x400000
AddressOfEntryPoint: 2565c
Absolute entry point: 42565c
合ってるっぽい。



かんたんでよい、きょうはここまで。

2016年7月9日土曜日

D言語ローレイヤ入門

D言語(ローレイヤ)入門していきます。

ぼく「D言語の最小ランタイムほしいよ~」
ぼく「uefi-dとか使ってみたけど構造体宣言できないし色々ダメ」
ぼく「UEFI AppをDでつくりたい、あわよくば・・・OSカーネルも」

というつぶやきをしたらプロから返事が帰ってきた。
Twitterつよい。


というわけで、minimal.zipを落として makeしてみた

minimal-d/object.d:1248: `_deh_beg' に対する定義されていない参照です
minimal-d/object.d:1249: `_deh_end' に対する定義されていない参照です
無いってさ!
ちなみに、object.dの1248行目と1249行目はこんな感じ。

        extern __gshared
        {
            /* Symbols created by the compiler and inserted into the object file
             * that 'bracket' the __deh_eh segment
             */
            void* _deh_beg;
            void* _deh_end;
        }

※object.d:すべてのクラスや構造体のベースになるクラスが入ったファイル。これがないとD言語の機能はもちろん、C言語で普通にできてた構造体の宣言すらできない(RTTI:実行時型情報が必須な言語の宿命(TypeInfo_Struct等が宣言されてないとリンカエラー))ので、大事。


見たとおり外部に宣言してある_deh_begと_deh_endを参照する ってな感じになってるし
もちろんコンパイラはそんなシンボルを生成してくれないしで、きっとD言語の仕様が微妙に変わったのかもしれない。

リンカスクリプトでEHテーブルセクションを定義した時にシンボルも定義すれば良いんだけど、それをやらなくてもできる方法があるので今回はそっちでやる。
ちなみに、EHテーブルセクション(セグメント)については、ここに書いてあった。
http://www.kmonos.net/alang/d/abi.html

例外ハンドラがたくさん書いてあるところ(GOTみたいな関数テーブル)っていう感じ。

先ず、CompilerDSOData構造体と_d_dso_registryって関数をみてみると
CompilerDSOData構造体には、_deh_beg、_deh_endの2つが居るけど
でもその構造体を引数にとってる、_d_dso_registry関数はそれらメンバに対して何も操作してない。
でも、引数をとってるからには、何かが入ってるはずなので、下のソースみたいに
もともと有った外部参照するところをコメントアウトする。
次に、普通にコメントアウトした変数と同名の変数の実体を宣言する。
さらに、宣言した変数に引数からもらった CompilerDSOData構造体の同名の変数の値を代入する。

これでリンカエラーは出なくなる。

じゃあ本当に直した部分は動くの?動きそうなの?
と言う疑問が湧いてきたので、一旦これでmakeコマンド叩いてみて、バイナリを生成して逆アセンブルしてみる。

動きそう、動かなそうの判断は、意図したデータ(EHテーブルセクションの開始位置と終了位置)
が入ってるか入ってないかで判断する。

各メモリアドレスの詳細


一応メモリアドレスがちゃんと有効なセクションを指してるのと、
構造体の順番と同じようにスタックにレイアウトされてるから問題なさそう。

C言語っぽい擬似コード




てわけで、きょうはこんなところで。

2015年11月22日日曜日

AVTokyo2015のバッジを作った。


AVTokyo2015に今年も参加したわけですが、そこでもらったこれ、参加証のバッジ。



これな

もらった時は
なんだこれ????基盤????
パーツねーぞ????んん?????
どうするんだこれ、どうせなら遊びたいぞ
って思いましたが、一緒にいた人達に聞いてみたところ・・・


「あ、このそこでパーツ売ってるよ、買ってきたら?」

ぼく「まじっすか!買ってきますっ!!!!!」
売店のイケメンおにーちゃん「後で呼ぶから、そしたら来て1000円払ってね!」
ぼく「はーい」

1時間経ったぼく「呼ばれないぞ・・・」
2時間経ったぼく「これ無理じゃね」
3時間たったぼく「あ、後30分で終わっちゃう」

ついに、AVTokyo2015が終わってしまった。
最後まで、呼ばれなかった。
・・・ので、売店のおねーさんに問い合わせてみた

ぼく「部品ってここで買えません?」
おねーさん「ちょっと待って下さいね」
隣りにいたおにーさん「あーごめんなさい売り切れちゃいました、
              後でAVTokyoのページに部品全部のせますよ―」
ぼく「まじっすか!!!やったーーーーあざます!!!!」


というわけで、約1週間後の今日、AVTokyo2015のバッジを完成させてみました。
Arduino互換なので、色々と楽でした。

持っててよかったArduino(詳細は後述)


1.パーツを集める
このページに載ってる情報を元にパーツを集める
http://ja.avtokyo.org/avtokyo2015/badge

秋月電子さんで全部買いました。
AVTokyoのページの情報だと、Arduino互換のものが出来上がるらしい。
ちゃんとUARTもついててそれっぽい。

Arduino互換ってことはプログラムは、スケッチじゃろ
あと、Arduino IDEが必要。ライターどうしようかな・・・(後述)


2.ハンダ付けをする
はんだづけして、IC乗っけて終わりーーーーと思った。

でも現実はそう甘くはなく
「そういえばこれプログラムってどうやって載せんの??」
当日に完成させたみなさん、一定のパターンとかで光ってたよね。スイッチとかで切り替えてたよね
プログラム載せる必要あるじゃん

3.つまづく
初心者なので、このあたりのことわからないから、
「ICなんてどこで買っても同じだろwwwww」
と思ってた。そんなことはなかった。

ブートローダーが入ってない奴だったよね。

4.ブートローダーを書く
この辺りを参考にして
https://www.arduino.cc/en/Tutorial/ArduinoISP

こうする。

これでブートローダーが書けるんじゃ・・・便利じゃろ?
高いライターを買わなくてもええんや・・・

んで、問題はスケッチを書くあれだけど、
Arduino互換なら、ブートローダも書いたし、ArduinoのICと付け替えてやれば書けるだろ。

というわけで、Arduinoのもともと有ったICを外して、AVTokyoバッジ用ICを付けてやる。

んで、普通にArduinoに以下のURLのスケッチをブチ込むと書ける。
https://github.com/mikodayo/AVT15/blob/master/AVT15.ino

便利ですなぁ。
持っててよかったArduino。

5. 完成


6.動作確認

無事に動きました。
来年もこういう楽しいのが有ったらいいなー(他力本願)

以上!

2015年10月11日日曜日

C++のインスタンス生成処理

今日はC++です。
正直苦手としていた言語ですが、ひょんなことから始めました。

で、オブジェクトの生成とかどうしてるのか気になりますね。
気になるんです。

具体的には下記1点

  • 配置newしたときにデータと実行コードはどこに置かれるのか
気になりますねー

OS作るときは、データやコードがメモリのどこに配置されるかすごく気を使うので知っておきたいです。

どこに置かれるか、機械語を見てみましょう。
機械語を見ればわかるのです。バイナリです。
とりあえずソースを置いときます。




こんな感じにMain.cppからTestClass.cppをnewするだけです。
じゃあ機械語レベルではどーなってるのって話ですね。

みてみましょ。(画像はすべてクリックで拡大可能)

まずはMain.cppのオブジェクト生成部


TestClass.cppの TestClass::TestClass(char *s);メソッド(コンストラクタ)


見てみた結果

  • 実行コードはメモリ上に1セットしかない。
  • データ領域だけオブジェクトの数分配置される。
ということでした。

すっきり。

2015年6月20日土曜日

UEFIでOSつくるぞー(ブートローダ・カーネルローダー編)

OS作ってますよ


さて、全壊前回の記事からちょっと時間が経過しましたが
今の所飽きずに開発してますよ。面白いですねー

リポジトリはここです

UEFI使って独自OS作ってるって記事がネット上にあんまり転がってなくて、割りと大変。
Linuxとか起動すること出来るよーみたいなの載ってるけどね。

というわけで、はりぼてOSの64ビット版、UEFIデビューを目指して頑張って行きたいと思うですねー
(いつ飽きるかわからんですが・・・w)

開発環境
OS:Ubuntu 14.04
コンパイラ:gcc 4.8.2
リンカ: ld 2.24

作ったもの

  • ブートローダー(解説は、おるみんさんブログの「ブートローダーは4行で実装される」で読んでくだしあ。こっちのほうが詳しいです)
    • 将来的にはブートオプションとかをカーネルローダに渡すことができたら付けられたらいいなー!みたいな。
  • カーネルローダー(この記事解説)
    • OSにブートローダーからもらった情報で初期化したデータを渡すとかしている
    • 現状は、GOP(Graphics Output Protocol)でグラフィックの初期化とフレームバッファ(VRAMの先頭メモリアドレス)の取得。(なんと24ビットカラー!強い!!
  • カーネル(OS、解説は後日)
    • OS本体!
    • メモリマップの取得とUEFIのブートサービスを切る(ExitBootServicesの呼び出し)
    • (ココでやっている理由は、メモリマップを取得した直後じゃないとExitBootServicesが動いてくれない仕様なのでとりあえず、という感じ。)
    • なんかCentOSもそんな感じでやっているっぽい(どこで見たかは忘れた)ので真似してみました。
    • 現状、メモリマップ取得とExitBootServicesを呼ぶソースとカーネル本処理のソースは分けてありますが、モジュール的には1つです
みたいな感じです。

なかなかそれっぽいでしょ?
カタチから入るのもモチベーションだと思うわけですよ。

もちろん、
「俺のOSは3段階じゃなくて2段階でいいよねー 」
「ブートローダーにカーネルロードさせて、カーネルでカーネルローダーが行っている初期化の役割をやってやるぜ!」

とかそういうのも有りじゃねーのかなーと思うので、そこのあたりは思想にお任せします。

現状、UEFIが使えなくなると、画面に変数を表示できなくなるので
newlibを使って、sprintfとOSのグラフィック関数を組み合わせて表示することにします。

で、フォントもUEFIから引き継げない(?)ので、とりあえず今の所は
『30日でできる!OS自作入門』のメモの「C言語で直接フォントを表現」にある素敵ツールを使わせていただきました。(助かってます!!)

(もし、UEFIからフォントデータとかを持ってこれるよ!って情報有ったら教えてくださいなー)


では長くなりましたが、ブートローダーからソースをぺたぺた貼っていきたいと。

このブートローダーは「ブートローダーは4行で実装される」
からパクってきました!(おるみんさんありがとうございます。フル活用させて頂いてます)
後々、テキストファイルにカーネルとカーネルローダーの場所を書いておいて、
そこからロードしてくるように直そうかと。優先度はかなり低め。

ではお次はカーネルローダー。
量的にはボリューム有るように見えたり見えなかったりしますが、やってることは簡単。
GOPを初期化して、ピクセル情報や解像度情報、フレームバッファを取得しているだけです。


大きく分けて、処理は4つ
131行目:ブートオプション(カーネルの起動パス)とイメージハンドルを取得している。
136行目:カーネルの起動情報を格納するメモリを確保。
142行目:GOPの初期化と画面の色々な情報を136で確保したメモリに入れる。
 最大で、1024x768の解像度で初期化してほしい
 という要求をchangeGraphicModeの第2引数・第3引数に渡している。
 最大なので、800x600で起動されても問題ないけど、
 GOPの仕様上1024x768以上の解像度は保証されたハズ

150行目:カーネルを起動する

というような流れになっとります。
「この関数の解説をもっと詳しく!」とか要望があれば、もうチョット書きますが無いですよね。

では!