拾い物のコンパス

まともに書いたメモ

PEMUを動かしてみる

Dynamic Binary Instrumentation(DBI)を行うフレームワークの一つ,PEMUを使ってみた.githubの説明が簡潔すぎて動くまでに時間がかかったから,手順を書き残す.
pemu自体はqemuをベースに作成されているから,作業の所々にQEMU関連の操作が入る.

OSの準備

Linux上作業を行う.今回はVirtualboxUbuntu仮想マシンを作成し,その上でPEMUを動かした.
仮想マシン(VirtualBox)上で仮想マシン(QEMU)を動かすことになるから,動作が重かった.
以降,ホストOSは仮想マシン上のUbuntu 12.04 32bit.ゲストにも同じバージョンのUbuntuを使った.下手に新しいバージョンのUbuntuを使うとPEMUのコンパイルエラーが起きて厄介.

ホストOSのインストー

http://releases.ubuntu.com/12.04/辺りからISOファイルをダウンロードして,仮想マシンを作成する.Desktop版がおすすめ.

$ uname -a
Linux pemu-VirtualBox 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014 i686 i686 i386 GNU/Linux

ホスト側での作業

$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt-get install p7zip-full qemu-utils git qemu

QEMUで動かすゲストをダウンロード.
一から作成するもの手間だったから,出来合いのものを使った.
https://virtualboxes.org/images/ubuntu-server/から適当なイメージを取ってくる.バージョン違いによる諸問題を避けるため,12.04(32bit)を使った.他のバージョンは試していない.Desktop版だと重すぎるからServer版を使用.

$ wget http://sourceforge.net/projects/virtualboximage/files/UbuntuServer/12.04/ubuntu-12.04-server-i386.7z
$ 7z x ubuntu-12.04-server-i386.7z
VBox向けのイメージをQEMU用に変換.
$ qemu-img convert ubuntu-12.04-server-i386.vdi -O qcow2 ubuntu32_1204.qcow2
$ qemu-system-i386 -m 512 ubuntu32_1204.qcow2 -monitor stdio # ゲストOSの起動

ゲストのログインパスワードは

Username: ubuntu
Password: reverse

PEMUの準備

PEMUのREADME.mdの一番最初に書いてあることをやっていく.task-infoのカーネルモジュールを実行して,結果をlinux.cへと移植する.
移植を忘れるとプロセスの解析が上手く行かない.

ゲストOS上での作業

<guest>$ sudo loadkeys us # キー配列がおかしくなっていたときのみ
<guest>$ sudo apt-get update
<guest>$ sudo apt-get -y install git gcc make linux-generic
この部分は時間がかかるから,CPU数を増やすかのんびり待つ.
<guest>$ sudo reboot
<guest>$ git clone https://github.com/utds3lab/pemu
<guest>$ cd pemu/task-info/task-info.c && make
<guest>$ sudo /sbin/insmod task-info.ko

メッセージとして

insmod: error inserting 'task-info.ko': -1 Operation not permitted

と出れば成功のようだ.

<guest>$ dmesg | tail -n10

出力の最後にカーネルモジュールからのメッセージが入っている.今回は

    0xC1820FE0, /* task struct root */
    444, /* offset of task_struct list */
    472, /* offset of mm */
    44, /* offset of pgd in mm */
    748, /* offset of comm */
  }

これをメモして,一旦ゲストを終了.メッセージの値はゲストによって違うようだ.
以降,ホストに戻る.

ホスト上でPEMUのコンパイル

$ git clone https://github.com/utds3lab/pemu
$ cd pemu
$ vim ./target-i386/PEMU/linux.c

出力に合わせて./target-i386/PEMU/linux.cの25行目以降を書き換える. 次にPEMUをコンパイルしていく

$ sudo apt-get -y install autoconf dh-autoreconf # READMEに書いてはないが,いれないとエラーが出る
$ sudo apt-get -y build-dep qemu
$ mkdir build && cd build
$ ../myconfig && make install
$ cd ../plugins && make
$ cd ../build/bin && ./qemu-system-i386 -m 512 ubuntu32_1204.qcow2 -monitor stdio
(qemu)> pemu ls strace.so # qemuのプロンプトで実行
<guest>$ ls

qemuを動かしているターミナル上で,PEMU_startから始まる何らかの出力は得られる.これで一応動いたことになる.

ソース解析

出力のPEMU_startはmonitor.cに書かれている.その後の数字の出力はplugins/strace.cの

VOID SysBefore(ADDRINT ip, ADDRINT num) {
    fprintf(stdout,"0x%lx: %ld\n",
                (unsigned long)ip, (long)num);
}

のようだ.strace.c の main は

int main(int argc, char*argv[]) {
    PIN_AddSyscallEntryFunction(SyscallEntry, 0);
    PIN_AddFiniFunction(Fini, 0);
    PIN_StartProgram();
    return 0;
}

のように,いつ関数を呼び出すかを指定している.
出力のip, numについて詳しく見ると,値PIN_GetSyscallReturnPIN_GetSyscallNumberの戻り値が入っている.

VOID SyscallEntry(THREADID threadIndex,
                        CONTEXT*ctxt, SYSCALL_STANDARD std, VOID*v) {
        SysBefore(PIN_GetSyscallReturn(ctxt, std),
                        PIN_GetSyscallNumber(ctxt, std));
}

実装はsystemcall_api.cにある.出力しているのはシステムコールの戻り値(PIN_GetSyscallReturn: systemcall_api.cでreturn EAXしてる)と,システムコール番号(正確にはLinux/Win/Macのどれかを調べた後,Linuxなら呼び出し時のEAXを返している.まあ,システムコール番号)のようだ.
Linuxで実行する限り,二つの関数で得られる値は同じになるんじゃ・・・.

pemu ls <plugin>によって,plugins/のプログラムを呼び出していて,自作も簡単そうだ. 実装としては,monitor.cからplugins以下のプログラムが呼び出される仕組み.

反省

論文に書かれた検証環境のOSバージョンはしっかり守る

所感

とりあえず動くようにはなったが,PEMUならではな機能がいまいち理解できていない.
気が向いたら色々試してみたい.

参考

PinからPEMUへ | 一生あとで読んでろ

GitHub - utds3lab/pemu

[SOLVED] insmod error "operation not permitted" as root

第278回 Ubuntuカーネルとの付き合い方:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社

radare2によるバイナリ編集

radare2はデバッガであり,バイナリエディタでもある.バイナリエディタとして使えると何かと便利だと思い,調べてみた.
基本的なことを書き残す.

環境

$ uname -a
Linux poppycompass 4.11.3-1-ARCH #1 SMP PREEMPT Sun May 28 10:40:17 CEST 2017 x86_64 GNU/Linux

$ r2 -v
radare2 1.6.0-git 15021 @ linux-x86-64 git.1.4.0-472-g7512396ab commit: 7512396ab5ae5f7292f41a80dc0b4dca09c4d6f2 build: 2017-06-11__02:50:45

準備

radare2がインストールされていればOK.

# Ubuntu
$ sudo apt install radare2

# Arch Linux
$ sudo pacman -S radare2

# 最新好きの人
$ git clone https://github.com/radare/radare2 && cd radare2 && sys/install.sh
$ r2 -v

でそれっぽいのが出れば良い. 次に,適当なバイナリファイルを用意する.

$ echo -en "\xaa\xbb\xcc\xdd" > test.bin

基本

write modeでファイルを開いて編集していく

$ r2 -w ./test.bin
 -- It's working! Look at the door!
シェルを既に起動した場合は
[0x00000000]> o+ <file>

[0x00000000]> px 4 # ファイルの内容を確認
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  aabb ccdd                                ....


[0x00000000]> w? # ヘルプ
|Usage: w[x] [str] [<file] [<<EOF] [@addr]
| w[1248][+-][n]       increment/decrement byte,word..
| w foobar             write string 'foobar'
| w0 [len]             write 'len' bytes with value 0x00
| w6[de] base64/hex    write base64 [d]ecoded or [e]ncoded string
| wa[?] push ebp       write opcode, separated by ';' (use '"' around the command)
| waf file             assemble file and write bytes
| wao[?] op            modify opcode (change conditional of jump. nop, etc)
| wA[?] r 0            alter/modify opcode at current seek (see wA?)
| wb 010203            fill current block with cyclic hexpairs
| wB[-]0xVALUE         set or unset bits with given value
| wc                   list all write changes
| wc[?][ir*?]          write cache undo/commit/reset/list (io.cache)
| wd [off] [n]         duplicate N bytes from offset at current seek (memcpy) (see y?)
| we[?] [nNsxX] [arg]  extend write operations (insert instead of replace)
| wf -|file            write contents of file at current offset
| wh r2                whereis/which shell command
| wm f0ff              set binary mask hexpair to be used as cyclic write mask
| wo[?] hex            write in block with operation. 'wo?' fmi
| wp[?] -|file         apply radare patch file. See wp? fmi
| wr 10                write 10 random bytes
| ws pstring           write 1 byte for length and then the string
| wt[f][?] file [sz]   write to file (from current seek, blocksize or sz bytes)
| wts host:port [sz]   send data to remote host:port via tcp://
| ww foobar            write wide string 'f\x00o\x00o\x00b\x00a\x00r\x00'
| wx[?][fs] 9090       write two intel nops (from wxfile or wxseek)
| wv[?] eip+34         write 32-64 bit value
| wz string            write zero terminated string (like w + \x00)

以降,編集していく.

16進数の書き込み

[0x00000000]> wx eeff # 最初の2バイトを"eeff"に書き換え
[0x00000000]> px 4
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  eeff ccdd                                ....

[0x00000000]> wx 1122 @ 0x2 # オフセットを指定し,0x2(3バイト目)から0x1122を書き込み
[0x00000000]> px 4
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  eeff 1122                                ...

書き込みはオフセットを指定しない限り,現在のアドレスからの書き込みになる.
アドレスを変更したいときは> s <addr>で移動できる.

文字列の書き込み

[0x00000000]> w poppycompass # "poppycompass"という文字列を書き込み
[0x00000000]> px 20
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00000000  706f 7070 7963 6f6d 7061 7373 ffff ffff  poppycompass....
0x00000010  ffff ffff                                ....

どうやら,ファイルサイズより大きく出力するとffが入るようだ.

Visual modeで常に結果を見ながら編集する

実際に編集するときは,変更を加えてから毎回px 20みたいなコマンドを打つのは手間だ.この問題は簡単に解決できる,そうVisual modeならね!

[0x00000000]> V # Visual modeに移行
[0x00000000 0% 504 ./bin]> xc
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF  comment
0x00000000  aabb ccdd ffff ffff ffff ffff ffff ffff  ................

この状態で:を押して,通常通りコマンドを入力するだけ.

Press <enter> to return to Visual mode.
:px 11223344<enter><enter>
[0x00000000 0% 392 ./bin]> xc
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF  comment
0x00000000  1122 3344 ffff ffff ffff ffff ffff ffff  ."3D............

トルエンディアンで書き込みたい!

> wv 0xaabbccdd         # \xdd\xcc\xbb\xaaとして格納
> wv 0xaabbccddeeff0102 # \x02\x01\xff...として格納

1-4bytesの数値は32ビットとして,5-8bytesの数値は64ビットの値として取り扱われ,少ないバイトには00が書き込まれる(0xaabbだと\xbb\xaa\x00\x00となる).
9bytes以上の値を書き込もうとすると,失敗して全桁がffになる.

アセンブルして書き込みたい

以下のファイルを用意する.
$ echo "push eax\npop eax" > asm.txt
$ r2 -w ./test.bin
[0x00000000]> waf ./asm.txt
もしくは
[0x00000000]> "wa pop eax;push eax"

注意点は,> wa "pop eax;push eax"じゃないこと.

Visualmodeで直接編集

Vimっぽく編集する方法がある.Stirlingなどに慣れた人はこっちの方が親しみやすいかも.

[0x00000000]> V

cを押すと,16進ダンプが囲まれるはず.ここからはvim
移動はhklj,編集場所にカーソルが来たらiでインサートモード.
数値をいれたら上書きされる.終わったらEscを押す.

おまけ

Visual modeの編集機能を使うと,通常のエディタっぽく使えなくもない. 以下はCでHello, Worldプログラムを書いた例.使いみちは無さそうだ.

$ r2 -- # ファイルを指定しないでシェルを立ち上げる
> o+ hello.c # 書き込みモードで hello.c を開く
> V # ヴィジュアルモードに移行して,書き込みを逐次見られるようにする

cを押すと,16進ダンプが囲まれる.ここでタブを押すと,ASCIIの方が囲まれる.この状態でiを押して,書くだけ. 改行(\x0a)とダブルクウォート(\x22)は16進からしか書けないのが難点.

[0x00000000 + 30> * INSERT MODE *
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F |0123456789ABCDEF| comment
0x00000000  2369 6e63 6c75 6465 203c 7374 6469 6f2e |#include <stdio.|
0x00000010  683e 0a69 6e74 206d 6169 6e28 766f 6964 |h>.int main(void|
0x00000020  2920 7b20 7072 696e 7466 2822 4865 6c6c |) { printf("Hell|
0x00000030  6f2c 2057 6f72 6c64 2122 293b 207d ffff |o, World!"); }..|
$ cat hello.c
#include <stdio.h>
int main(void) { printf("Hello, World!"); }

volatilityにプロファイルを追加する方法

メモリダンプ解析用ツールvolatilityはデフォルトではWindowsのメモリダンプのみが解析できる.LinuxOSXで作成されたメモリダンプを解析するためにはプロファイルを追加してやれば良い.公式から用意されているものを使うこともできるし,自作することもできる.

概要

プロファイルの追加は定義ファイルを追加すればよい.いくつかは公式から用意されており,基本的にはGithubからcloneするだけで使える.

$ git clone https://github.com/volatilityfoundation/profiles
$ cd profiles
$ find -type d -maxdepth 2
... snip ...
./Mac
./Mac/10.10
./Mac/10.5
./Mac/10.8
./Mac/10.9
./Mac/10.6
./Mac/10.12
./Mac/10.7
./Mac/10.11
./Linux
./Linux/Fedora
./Linux/Ubuntu
./Linux/Debian
./Linux/CentOS
./Linux/RedHat
./Linux/OpenSUSE
... snip ...

以上のように複数のディストリのプロファイルが用意されている.各ファイルにはx86x64がある.

使い方

使い方はvol.pyまたはvolatiltyが参照しているpluginsというディレクトリにZIPのままコピーする.
以下はUbuntu14043x64版のプロファイルを使えるようにする場合.

$ pwd
profiles
# ローカルなvolatilityを使っている場合
$ cp Linux/Ubuntu/x64/Ubuntu14043.zip volatility/plugins/Linux
# apt/pacmanなどでインストールしたvolatilityを使っている場合
$ cp Linux/Ubuntu/x64/Ubuntu14043.zip /usr/lib/python2.7/site-packages/volatility/plugins/linux/

確認

以下は,/usr/lib/python2.7/site-packages/volatility/plugins/linux/にコピーした場合のコマンド.

$ volatility --info
Profiles                                                                                                         
--------                                                                                                         
LinuxUbuntu14043x64 - A Profile for Linux Ubuntu14043 x64
VistaSP0x64         - A Profile for Windows Vista SP0 x64                                    
VistaSP0x86         - A Profile for Windows Vista SP0 x86                                    
VistaSP1x64         - A Profile for Windows Vista SP1 x64                             
VistaSP1x86         - A Profile for Windows Vista SP1 x86                                                                         
... snip ...

後は通常通り
$ volatility -f <dump_file> --profile=LinuxUbuntu14043x64 linux_lsof
といった感じで解析できる.

プロファイルを自作する

IIJがいい感じでまとめていた.自作したい人はこちらを参照.ツールがvolatilityに用意されているから,作成したいディストリ上でmakeしてzipするだけのようだ.特定のバージョンを作りたいときはカーネルrpmで持ってくる必要あり.

Internet Infrastructure Review(IIR)Vol.32 | IIJの技術/セキュリティレポート | IIJ

おまけ

ZIPファイルはplugins以下に置けば認識してくれるから,

$ cp profile.zip /usr/lib/python2.7/site-packages/volatility/plugins/self_made

といった目的別に分けることもできる.便利.

サイボウズの開発インターンに参加してきた

サイボウズで行われている開発インターンに3週間行ってきた.何をしたのかを含めて振り返りを書き残す.

きっかけ

就職が近くなってきて,どこか行かなきゃとは思うけどどこにいったらいいのかわからない.
周りの人は有名企業のエリートコースを狙って頑張っているが,それらの職業は上流過ぎて今持っている技術力は全然活かせそうにない.(でも稼ぎたい)
技術を活かしたり,磨くのが好きだから,これができるであろうエンジニアとして働くことを考え,職業としてのエンジニアを深く見たいからちょっと長めで現場で働けるインターンを探した.
いくつか候補は見つけた中で,IPAのセキュリティイベントでよく聞く川合秀実さんのいるサイボウズに興味を持ち,行ってみた.因みに,行く前のサイボウズに対する印象は

開発インターンは通年で募集している.kintoneというWebサービスの開発チームと一緒に機能の拡張や実装を行っていくインターン
デフォルトは10日間だが,実装に心残りが出ると思ったため,もう一週間長くやりたいと言うだけ言ってみた.向こうも考えてくれた上で受け入れてくれた.有り難い.
勤務地はサイボウズの本社支社がある,東京・大阪・松山のどれかを希望できる.比較的近い大阪支社に行かせてもらった.離れた場所に住んでいても,部屋を手配してもらうことは可能らしい.
余談だが,インターン期間中は1日1万円の報酬がついている.本職エンジニアの技や生活を学べる上にお金をもらえるなんて素晴らしい.

大阪支社の場所

阪急梅田駅から直進徒歩5分もかからない場所にある阪急梅田ビルの35階.屋上駐車場を見下ろすことができる高さで,見晴らしはムスカ大佐.雨で霧がかかっている日は街が3割増し幻想的に見えた.画像は夕方. f:id:poppycompass:20170310020017j:plain

kintoneについて

f:id:poppycompass:20170310013715g:plain kintoneというサービスについて軽い説明をする.(インターン前はkintoneの名前・有用性がよくわかっていなかった)
サイボウズが提供しているサービスは「Office」「Live」「Garoon」「kintone」などがある.それぞれグループを作って会話したり,予定管理をまとめることができたりする.
kintoneはSlackみたいなチャット機能に加えて,テーマなどをまとめるスレッド・スペースを管理できる.大きな特徴として,kintoneでは既存のパーツなどを使って目的に応じたアプリを作成することができる.例えば,製品開発の進捗を行単位のレコードで表すアプリを作成したいときには,任意の数の列や選択を設定することができる. 使い込む時間はなかったが,データ管理については柔軟性が非常に大きいと思う.仕事向きに作られているから,学生の間では利点が伝わりにくいのが残念.

主に使うのはチャット機能で,色んな人の投稿を眺めることができる.数時間見ないでいると50件以上溜まったりするから驚くが,一覧にすると見やすいから問題はない.
インターン中は自分のつぶやきスペースで思いついたことをつぶやいたり,わからないことを質問すると誰かが答えてくれた.
サイボウズのアメニティグッズにキリンやゴリラがプリントされることがあるから,てっきりイメージキャラクターだと思っていたが,現場のエンジニア曰く公式キャラはいないらしい.黄色いからキリンがキャラだと思っていた.

職場の雰囲気

メンター(主に教えてくれる人)は非常に丁寧に対応してくれる人で,こちらの細かな質問にも一つ一つ答えてくれた.
周りの社員の方々がそれぞれの就職に対する考え方についても教えてくれたので,非常に参考になった. 経営理念として,「チームワークあふれる社会を創る」とあり,それを実現していることを実感した.

社長挨拶 | サイボウズ株式会社

kintoneでつぶやくと,近くの席の人が直接話しかけてくることもあって楽しかった.東京のエンジニアから返事が来ることもあり,自社製品で全社をしっかり繋いでいる印象だった.
概ねいい場所だが,難点も当然ある.それは質問する時.メンターの人が在宅勤務や出張などでいないときは,チャット機能を使って質問することになる.直接話すときと違い,タイプや考えることでレスポンスが遅くなるため,ストレスを感じることもあった.これを解決するためにテレビ電話があるが,インターン中は使い方がよくわからず放置してしまった.早めに使えばよかった・・・.
在宅勤務は仕事に集中できるのかという質問をしてみたところ,「在宅だとリミッターが外れて出社するよりも仕事が進んで止められない.そんな人間を採用しているから大丈夫」といった返答をいただいた.理由がとってもサイボウズ

メンターはこちらの質問の本質を探して,その問題を解決するためにより効率的なアプローチを提案してくることが多かったように思う.質問した内容にそのまま答えるよりも良い返答だと感じた.本質を探す真似をしてみようと思ったが,上手く行かなかった.まだまだ未熟.
当然社員の方々はバリバリの実装マンで実力主義な雰囲気を感じた.

やったこと

これを繰り返す感じ.途中で寿司を食べながら自分の好きなことについて話すイベントもあった.スクラム開発は最近サイボウズでやり始めた,短期的に進捗を確認しながら仕様などを見直していく開発工程のこと.語源はラグビー
一週間に一回kintone開発チーム全体に進捗を発表する時間を設けてもらって,そこで何をやっているかを発表していく.本職の人にコメントをもらえるので,非常に励みになった.

出来事

サイボウズでの日々をもう少し詳しく書いておく.

1週目

  • 環境の準備
  • kintoneをローカルで立ち上げる
  • kintoneを触りながら,改善できそうな部分を探す
  • テーマ決め
  • スクラム開発の基礎を勉強

5日間のインターンだと社員さんが用意している課題を勧められることが多いらしいが,時間があるとのことでじっくり考えることができた.
スクラムでやることについては,スクラムマスターという資格を持った社員さんが自発的に教えてくれた.有り難い.
覚えることが多かったり,慣れない職場で帰るとすぐ寝ていた.

2週目

  • PBL/SBLの作成
  • 実装・コードレビュー・リファクタリング 社員さんに自分の書いたコードがゴリゴリ綺麗な構文になっていくのは気持ちが良かった.コードの行数が少ないって素晴らしいって改めて思う.

まだ一週間あるため,実装に集中できる一番平和な期間だった.周りを見渡す余裕が出てきて,会社の日常を味わえるようになってくる.長めのインターンの醍醐味はここだと思う.

3週目

  • 実装
  • ローカル環境が壊れる
  • Gitをrebaseしようとして,山ほどの衝突が起きてつらい
  • テスト用に作成したデータが検証の途中で初期化される
  • 資料の作成が終わらない

先週のペースなら余裕と思っていたら,インターン終了前日にローカルkintoneクラッシュから始まる悪夢の行進.最終成果発表1時間前に必死に資料を作っていた.
大切な発表は,2日前までに資料を作成し,前日は発表練習に充てましょう.
最後は送別会まで開いてもらった.

小話

  • インターン生はランチ代の補助が出る日がある.ランチパスポートがあると安く抑えられそう
  • 資料作成用にMacを貸してもらっていたが,"kintone"といれると,ことえりの自動変換で"intone" or “kenton"になる

    • この変換をバイパスするには"KIntone" or “kinTone"がおすすめ(そもそも変換をGoogleのやつに切り替えれば解決する)
  • スタンディングデスクが来た! f:id:poppycompass:20170310015838j:plain

    • 画像はインターン期間中に来たスタンディングデスク.立ったまま使うための机.椅子を持ってきて,わざわざ椅子に座ってスタンディングデスクを使う猛者もいた.スタンディングデスクで立ったままプログラムを組んでみたが,足に力を入れながらタイプする経験があまりなかったから新鮮だった.レベルの高い立ち読みという印象.マウスを落とすと高さ2倍で威力も大きいというkintoneへの投稿もあった.
  • サイボウズ内では「マッスル部」という部活があるほど筋トレや運動に対して積極的だった.ずっと座っているから体の衰えが気になってくる(切実).就業中に流れてくるつぶやきもマッスル部が多かった印象がある.内容はどのプロテインが美味しかったとか,自分の筋トレメニューを解説していたりしていた.コード調査に疲れたときに元気をもらっていた.

筋トレ用具を常備している人もいる. f:id:poppycompass:20170310020149j:plain

  • カフェ部というものもあり,屋上駐車場よりも高い眺めを一望できるラウンジで弁当と一緒にコーヒーや紅茶を嗜む.
    • バレンタインや最終日に開いていただいて美味しいコーヒー・紅茶を味わった

恩恵

  • サイボウズでエンジニアの生活を実感できる(働きやすい!)
  • 一週間以上行くことで,会社の普段の雰囲気をつかめてくる
  • 大規模プログラムを触ることができる
  • 報酬あり(1万/日)

興味が出た人へ

言語はJava, Javascript, CSS, HTML, SQLといったWebの基礎知識があったほうが良い.無いならないでその場で聞くなり,触って感じればいい.

インターンの面接は実力をよく見られた印象がある.今までに何を作ってきたのかを説明したり見せることができると良さそう.
でも,興味があるのなら実力がついてから応募しようなんて難しいことを考えずにとりあえず応募してみると良いと思う.その後はなるようになる.

インターンシップ | サイボウズ 採用情報(新卒・キャリア)

最後に

働くと起きている時間の殆どを仕事に使うことになる.仕事を好きになれないと辛いことになりそうだから,興味を持って働ける場所に行きたいと思う.
インターンでは,研究室にこもっているだけでは到底出会えない経験をさせてもらった.言葉では言い表せない感謝を忘れないようにしたい.

サイボウズ

  • エンジニアが過ごしやすい雰囲気
  • 在宅勤務などの新しい働き方を積極的に取り入れている
  • 社員さんのレベルが高くて尊敬できる!
  • 筋肉に興味があるならマッスル部

な会社です.

最後にサイボウズで関わってくださった皆様,3週間ありがとうございました!

findしてlessする動作を自動化するシェル関数

基本的にfindコマンドで探したファイルはlessなどで閲覧することが多い.大きなプログラムだと,ディレクトリごとに名前が同じファイルがあったりするから,findして目grepしてパスをコピーしてlessするって動作を繰り返しているとうんざりしてくる.そこで自動化するシェル関数を作成した..zshrcに追記すれば使える.
やりたいことはless $(find -name "hoge").複数ヒットしたときもカバーしたいと思うと複雑になってくる.

code

zsh上では動作が確認できている.bashは知らない.

# findで見つけたファイルを表示し,lessする
function fl() {
  if [[ $# -gt 0 ]]; then
    list=`find -type f -name $1 2>/dev/null`
    if [[ $list = "" ]]; then
      line="0"
    else
      line=`echo $list | wc -l`
    fi
    case "$line" in
      "0") echo "No file" ;;
      "1") less $list ;;
      *)   echo -en $list | nl
           arr=(`echo $list | tr -s '\n', ' '`)
           echo -en "file: "
           read num
           less $arr[$num] 2>/dev/null ;;
    esac
  else
    echo "fn \"<file_name>\""
  fi
}

使用例

$ fl "*zshrc"
1  ./git/zsh/StartupFiles/zshrc
2  ./dotfiles/.zshrc
flie: 2 <Enter>

検索する対象はカレントディレクトリ以下.ダブルクウォートで囲めばワイルドカードでの検索もできる.候補が一つしか無いときはそれをlessする.
とりあえずこれを使って,不便なら繰り返し処理を加えるなりしてみようかな.
そもそもIntelIJみたいなIDE使えという話もあるが,手軽に探す分にはこれで十分だろう.コードが冗長な気がするから,気が向いたら書き直す.

参考

bash でカンマ区切り文字を split して変数に代入

既出の値を判定するコードについて思いついたこと(ビンゴゲームをネタに考える)

重複を許さないランダムな数値を一定数生成したいという機会は割りと遭遇する.このときの既出判定についてふと思いついたことを書き残す.
「そんなの一般的に使われている」というツッコミは置いておく.実装についての答えを示すものではなく,「簡単な用途であればこんな実装のほうが綺麗だな」って話.

きっかけ

去年開催された忘年会において,ビンゴゲームが開催された.その際に「白紙を渡すから,1-16の数字の中から,9個選んで3x3に適当に書け」ということになった.
自分で選ぶのも一興だが,時間がないときにはプログラムに任せて一瞬で作成してしまいたい.乱数を生成する事自体はAPIを使えば非常に簡単に生成できる.しかし,既出判定の部分が面倒臭いから簡単にできないものかと考えた.

以降のコードの目的は,ビンゴと同じ1-16の数値を被らないように9個選ぶこととする.変数や分岐などはC言語で考えている.

最初に思いつくやつ

出てきた数字を配列に格納し,生成した数字が出ているかを確認する.実装としては以下のような感じか.

  int buf[9] = {0};            
  int i, j, t, c=0;            
                               
  srand(time(NULL));                  
  for (i=0; i<9; i++) {               
    while (1) {                            // 生成    
      t = rand() % 16 + 1;            
      for (j = 0; j < 9; j++) {            // 既出チェック     
        if (buf[j] == t) c = 1;       
      }         
      if (c == 1) { c = 0; continue; }     // c=1なら,既出でもう一回
      break;                          
    }                        
    c = 0;                            
    buf[i] = t;
    // 出力処理
  }

というように,どうにも入り組んだものになりがちである.他の処理があったりすると,読みたくないコードになってくる.既出判定のオーダーは,配列長nの半分程度だから,O(n/2)かな?
このコードだと取り扱う数字・個数が大きくなっても使えるが,毎回線形探索するのはオーバヘッドばかりかかる.かといって配列を毎回ソートし,二分探索みたいなことするのも複雑だ.ビンゴ生成にそこまでやるかという問いはさておき.

楽そうなコード(フラグ管理してみる, 配列)

フラグ管理してしまえば簡単だなと思った.乱数を添字にして,フラグ配列に1が入っていれば既出,0なら未出扱い.

  int flag[16] = {0};              // フラグ管理

  for (i=0; i<9; i++) {
    tmp = rand() % 16 + 1;        // 1-16の値をランダム生成
    if (flag[i-1] == 1) continue; // 既出だから,もう一回
    flag[i-1] = 1;
    // 出力処理
  }

大分スッキリした.既出判定は一回しか参照しないから,オーダーはO(1)のはず.問題は,取り扱う数値・個数が多くなったときに使用するメモリ量が大きくなりがちなこと.
もうちょい改良してみる.

楽そうなコード(フラグ管理してみる, 省メモリ)

フラグは0/1しかないのに,何もわざわざintの配列を確保する必要はない.変数一個だけでも行けるなと気づいた.ブール変数の配列とどっちがいいんだろうか.

  unsigned int flag = 0;   // 64ビットOSだと,変数の大きさは64ビット
  int i, t;
 
  srand(time(NULL));                                                                                                    
  for (i=0; i<9; i++) {                                                                                                     
    while (1) {                                                                                                             
      t = rand() % 16 + 1;                                                                                                  
      if ( (flag & (1 << (t-1) )) == 0 ) break;  // t-1ビット目に1が立っていれば既出扱い,0なら未出                                                                       
    }                                                                                                                     
    flag |= (1 << (t-1));
    // 出力処理
  }

初見だと少し読みにくい印象.
これもオーダ的にはO(1)か.64個までしか数字を取り扱わないのなら,これでも十分にできる.しかし,64以上の整数や個数を取扱いにくいのが難点. 多倍長的なものを実装するのが良いが,これは実装コストが一気に上がる(といっても配列のどの要素を見るかの処理を加えるくらいだが).GMPを使えば楽にできそうだが,GMP使うと速度が下がりがちだから,要注意か.

完成版ビンゴジェネレータ

現実で使う機会が無さそうだから,ここでソースを挙げておく.

/* bingo.c */
#include <stdio.h>                                                                                                          
#include <stdlib.h>                                                                                                         
#include <time.h>                                                                                                           
                                                                                                                          
int main(void)                                                                                          
{                                                                                                                
  unsigned int flag = 0;                                                                                                
  int i, t=0;                                                                                                               
                                                                                                                            
  printf("=== Bingo Card Generator ===\n"                                                                                 
         "================\n|");
  srand(time(NULL));                                                                                                    
  for (i=0; i<9; i++) {                                                                                                     
    while (1) {                                                                                                             
      t = rand() % 16 + 1;                                                                                                  
      if ( (flag & (1 << (t-1) )) == 0 ) break;                                                                             
    }                                                                                                                     
    flag |= (1 << (t-1));                                                                               
                                                                                                                 
    if ( ((i % 3) == 0) && (i != 0) ) printf("\n|");                                                                    
    if (t >= 10) printf(" %d |", t);                                                                                        
    else         printf("  %d |", t);                                                                                       
  }                                                                                                                       
  printf("\n================\n");
  printf("Good Luck!\n");                                                                                                   
  return 0;                                                                                                                 
}                                                                                                                         
/* E.O.F. */
$ gcc bingo.c -o bingo
$ ./bingo
=== Bingo Card Generator ===                                                                                                
================                                                                                                            
|  2 | 16 |  8 |                                                                                                            
|  1 | 15 | 14 |                                                                                                            
| 13 |  5 |  4 |                                                                                                            
================                                                                                                          
Good Luck!

おわり

そこまで大きな数字・個数を取り扱わないものであれば,既出判定はフラグ管理するのがスッキリしててわかりやすそう.

Linuxで突然startxが起動しなくなったときの対処

startxが上手くいかないときの対処はググると色々出てくるが,ややこしいやつばかり出てきた.そして,それを試しても成功しなかった.最終的に上手くいったことを書き残す.

症状

それまで普通に使えたUbuntuを再起動した途端にGUIで起動しなくなった.$ startxしてみると,
waiting for X server to shut down error setting MTRR (base = 0xd0000000, size = 0x04000000, type = 1) Invalid argument(22)
とでて,終了してしまう.
$ sudo Xorg -configure
してみると,セグフォが起こっていると言われた.

対処

いくつか試してみたが,結局
$ cp /etc/X11/xinit/xinitrc ~/.xinitrc
した後に$ startxしたら動いた.
下手にビデオカードやら何やら入れていないなら,.xinitrcはいじらないほうが良いようだ.