情熱大陸#1140 登大遊さん(サイバー技術開発集団)

2021年2月7日(日)23:00~毎日放送情熱大陸」で日本が誇るIPA未踏OB天才プログラマー登さんがピックアップされていました。本邦初公開の貴重な映像もあり、必見です。

情熱大陸#1140 登大遊IPANTT東日本/サイバー技術開発集団 統括)コロナ禍であえぐ全国の自治体を救え!天才プログラマーが挑む緊急テレワークシステム開発

放送を見逃した方は1週間ほどTVerで視聴できるので、ぜひご覧ください。
※ 2月14日(日) 22:59 終了予定 tver.jp

放送中のTwitterのまとめ togetter.com

主役は登さんはじめシンテレワークシステムの構築に貢献されたNTT東日本IPA関係者・J-LISの皆さんですが、サイバー技術研究室のモブ役として少しだけ竹迫も映っていました。

f:id:TAKESAKO:20210210000326j:plain
けしからん定例会議
本当にありがとうございました。けしからんをスローガンに挑戦し続けたいと思います。

SECCON 2019 Final write-up (Factor the flag, Bad Mouse)

SECCON 2019 Finalでは、サーバ6のJeopardy問題2つ「Factor the flag」と「Bad Mouse」を作問しましたので、出題者の立場からの想定解write-upを公開します。

今年はCTFの競技ネットワークをクラウド環境に移行チャレンジした背景もあり、私が担当する出題テーマとして、ネットワークに依存せず、軽量で小さな問題であることを意識して作問しました。(万が一ネットワークトラブルがあっても解ける、お持ち帰りでも解析できる)

Jeopardy 6-3: Factor the flag

問題文:

I hid the SECCON{} flag in a big prime number.

1401111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111112220791111111111111111
1111111111111111111111111111122207911223089031988903088023088023088920012001200
2319889030879222080230880230890319887911122318879211992120012999912120013000013
0000131008920012001199121200120022089200130000119912119911121200120011992119912
1199121199121199121200130101000012001199121200120930009200130000119921199111121
2001200119921199121199121199121199121200130010208012002318879112120929999112120
9299991212103188892001200119912230890318889199121199121200130000131007911112119
9212092091991211992119912120013010111188791222079112129999121199121199121200130
0001200119911121200120012091992119921299992120013010099991112119911112129999121
1991211991212001300001200120012001209199223198889199212001209199213010099991112
1199212001299991212001300001300001300001200120012001299991111212091991212001209
1992130100999911121199122308903198890308802308802308892001200119922308903198879
2121031988903088011992130100999911121199111111111111111111111111111111111111111
2220791111111111111111111111111111111111111111111112220791111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111111239593

出題の意図:

64bitを超える巨大な数に対して、素因数分解をパッとできるプログラム・ツールが手元に揃っているかどうかというサービス問題です。

解法:

素因数分解(factor)すると 13 x 97 x 1261桁の素数の掛け算となります。 1261桁の素数は、奇遇にもちょうど97文字で折り返すと13行になります。

1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111991111111111111111111111111111111111111111111111111991111
1199911999991199911199911199911911191119111999991199911119911199911199911999991111111999911119111
1911191911111911191911191911191991191119111911111911191119911911191911191111191111111911191119111
1911111911111911111911111911191919191119111911111911191191911911191911191111911111111911191119111
1911111911111911111911111911191911991119111999911111191191911111191191911111911999911911191119111
1199911999911911111911111911191911191991111111191111911911911111911119111119111919191999911111991
1111191911111911111911111911191911191119111111191119111911911119111191911119111919191911111119111
1111191911111911111911111911191911191119111911191191111999991191111911191191111919191911111119111
1911191911111911191911191911191911191119111911191911111111911911111911191191111919191911111119111
1199911999991199911199911199911911191119111199911999991111911999991199911191111919191911111119111
1111111111111111111111111111111111111111991111111111111111111111111111111111111111111111111991111
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111213

フォントを小さくして1と9の文字を白黒のドットに見立てるとSECCON{524287mP}のフラグが見えます。 ペイントソフトで塗りつぶししても良いです。

スクリプト:find_the_factor.pl

素因数分解をして1を.に9を#に置き換えて表示する攻略スクリプトPerlワンライナーに近い記法で書いてみます。

#!/usr/bin/perl
use bigint try => 'GMP';
use Math::Prime::Util ':all';
my $p=join"",<DATA>; $p=~s/\s//g;
print join" x\n",map{s/(\d{97})/$1\n/g;s/9/#/g;s/1/./g;$_;}factor($p);
__DATA__
1401111111111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111112220791111111111111111
1111111111111111111111111111122207911223089031988903088023088023088920012001200
2319889030879222080230880230890319887911122318879211992120012999912120013000013
0000131008920012001199121200120022089200130000119912119911121200120011992119912
1199121199121199121200130101000012001199121200120930009200130000119921199111121
2001200119921199121199121199121199121200130010208012002318879112120929999112120
9299991212103188892001200119912230890318889199121199121200130000131007911112119
9212092091991211992119912120013010111188791222079112129999121199121199121200130
0001200119911121200120012091992119921299992120013010099991112119911112129999121
1991211991212001300001200120012001209199223198889199212001209199213010099991112
1199212001299991212001300001300001300001200120012001299991111212091991212001209
1992130100999911121199122308903198890308802308802308892001200119922308903198879
2121031988903088011992130100999911121199111111111111111111111111111111111111111
2220791111111111111111111111111111111111111111111112220791111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111111239593

実行結果

$ sudo apt install libmath-prime-util-gmp-perl
$ perl find_the_factor.pl
.3 x
#7 x
.................................................................................................
........................................##.................................................##....
..###..#####..###...###...###..#...#...#...#####..###....##...###...###..#####.......####....#...
.#...#.#.....#...#.#...#.#...#.##..#...#...#.....#...#...##..#...#.#...#.....#.......#...#...#...
.#.....#.....#.....#.....#...#.#.#.#...#...#.....#...#..#.#..#...#.#...#....#........#...#...#...
.#.....#.....#.....#.....#...#.#..##...#...####......#..#.#......#..#.#.....#..####..#...#...#...
..###..####..#.....#.....#...#.#...#.##........#....#..#..#.....#....#.....#...#.#.#.####.....##.
.....#.#.....#.....#.....#...#.#...#...#.......#...#...#..#....#....#.#....#...#.#.#.#.......#...
.....#.#.....#.....#.....#...#.#...#...#...#...#..#....#####..#....#...#..#....#.#.#.#.......#...
.#...#.#.....#...#.#...#.#...#.#...#...#...#...#.#........#..#.....#...#..#....#.#.#.#.......#...
..###..#####..###...###...###..#...#...#....###..#####....#..#####..###...#....#.#.#.#.......#...
........................................##.................................................##....
..............................................................................................2.3

正解:SECCON{524287mP}

解けたチーム数:国内:15チーム、国際1314チーム

決勝に出場しても得点がゼロだと職場に戻れない、という過去の出場者の声を反映したサービス問題でした。

実際のところ出場した全チームが解けている問題で、練習問題相当として出題しました。簡単ですね。

回答者:@stereotype32さんによるwrite-up https://pub.harold.kim/ctf/SECCON-CTF-Finals-2019/chal6-challenges/factortheflag.html

Jeopardy 6-4: Bad Mouse

問題文:

Hack the USB HID device. (配布デバイスの写真)

No claim no return. Firmware dump list is as follows:

:10000000A5C1C9C1F5C1C7C194C5C5C1C4C1C3C1DA
:10001000C2C1C1C1C0C1BFC1BEC1BDC1BCC13D3EE5
:100020003F4061426344415569486B4A4B4C4D4ED9
:1000300051504F61535C5556575859606B656D6709
:100040006F69417163644579774C794E7B505D4DA2
:100050006F707172737475767778797A7B7C7D7E38
:100060004F487251434C45464748694E5B555D5772
:100070005F597158535455565758595A5B5C5D5ED9
:100080005F607D66456C476E49706D716B6C6B7D12
:100090005178537A557C777E777875417D443F4619
:1000A000414845464344414D49504B524D545152AD
:1000B0004F504D59555C575E596055615B5C5B6DA7
:(省略)
:100F90000895EE0FFF1F0590F491E02D0994FB01D9
:100FA000DC0104C08D910190801921F44150504022
:0C0FB000C8F7881B990B0895F894FFCF38
:040FBC000105FF5AD2
:00000001FF

ダウンロード先:badmouse.hex https://gist.githubusercontent.com/takesako/9171fd0111b37dcc9ee775ae23f3fad2/raw/16f10cc55bc3106f57d66f3c1cddb20d650affb6/badmouse.hex

出題の意図:

AVRアセンブリ読解、ファームウェアリバースエンジニアリング問題。

ハードウェア情報:

Digispark - USB Development Board ATtiny85マイコン搭載

プログラムメモリ: 8KByte → すごい大容量!
EEPROM: 512Byte 
SRAM: 512Byte 
内部クロック: 12.8/16.5MHz
動作電圧: 2.7V~5.5V

V-USBというライブラリを使って、AVRマイコンのGPIO端子からUSBプロトコルの信号を直接出力しているという低レイヤーハッカー向けの開発ボードです。eBayやAliExpressで中国から1個200円前後でDigispark互換機を安価に購入できます。このような素敵な開発ボードが安価に入手できる中国が本当に大好きです。私はこういった基板に触るのが好きで深センに2回訪問しました。

解法1:忍耐で突破(おススメできない)

Bad MouseをUSBポートに挿すと、マウスが勝手に動いてマウスが自動でクリックされます。マウスカーソルの動くスピードが徐々に遅くなっていくのですが、ペイントソフトを起動してBad MouseをUSBポートに挿したままずっと待てば正解のフラグが描かれます。ただ、このためには辛抱強く待つ必要があり、このアプローチだとフラグ文字列の横の長さがどこまであるのかわからないので途中大変不安になります。

回答者:@hikariumさんによるwrite-up https://hikalium.hatenablog.jp/entry/2019/12/22/232138

実はこの文字描画フォントがFactor the Flag問題と同じであることに気づくと、ビットマップデータがどういう形式で埋め込まれているかのヒントとなります(伏線)。

解法2:ビットマップデータを抽出

HEX形式の16進ダンプをリバースエンジニアリングしてビットマップデータを抽出します。

先頭の3行目以降のバイナリのASCII文字列を見るとopqrstuvwxyz{|}~のように1ずつ増えているパターンが見えます。 これはマルウェアの簡単な暗号化で見ることがあるunrollingという手法だと目grep時点で気づくことができます。

実際に逆アセンブルすると、64の剰余を計算してこの文字列テーブルから63引いた値から1ずつ減らしながら6bitの剰余を2回デコードしてY座標を12ドット描いてから右のX座標にずれて、またY座標の描画を繰り返していることがわかります。

C言語でビットパターンをデコードするプログラムを書くと以下になります。

#include <stdio.h>

const char a[] = "?@aBcDAUiHkJKLMNQPOaS\\UVWXY`kemgoiAqcdEywLyN{P]Mopqrstuvwxyz{|}~"
"OHrQCLEFGHiN[U]W_YqXSTUVWXYZ[\\]^_`}fElGnIpmqklk}QxSzU|w~wxuA}D?FAHEFCDAMIPKRMTQRO"
"PMYU\\W^Y`Ua[\\[mc`ibsdcughIjKlK}p@rBstwzY@kBeDCE?@CBERgGaHOJKLINQYS[UYQYWXyakemgo"
"iAgcdelwqys{uM}opw~KwUvO{?F{|IDQIcJUMQLGHgP]T_VaXsYSTSeg`ibkd}e_`cbedcuihkjklmt?yA"
"{C}UEwxuz}E?GAE}ECDAFIQKSMQIQOPqYc\\e^g`y^[\\UeshsjulEmghejmuowqumustS|I@KBMD_E?@?"
"QSLUNWPiQKLORqXCZ}\\[]WXQaodofqhAicdqlyqKr}uytopOxE|G~I@[A{|[DQHSJULgMGHIJ[T~]OXQR"
"STuYoZ_\\Yk]`_`gb{dEu?hojklMu?xAzC|UzwxYz[|yMa@cBCDELWQYS[Um]OPoXe\\g^i`{a[\\[mohq"
"jslEmghGp}t?vAxSystvFxHWI[|]~?@B";

int main()
{
    for (int i = 0; i < sizeof(a)-2; i += 2) {
        for (int j = 12-1; j >= 0; j--) {
            putchar((((a[i+(j/6)]-63-(i+(j/6)))%64)>>(j%6))&1 ? '#' : '.');
        }
        putchar('\n');
    }
}

実行結果:

$ gcc decode.c && ./a.out
............
......#.....
......#.....
..########..
......#...#.
......#...#.
............
............
..........#.
..#########.
..#.........
............
............
...##.......
..#..#.#....
..#..#.#....
..#..#.#....
..#####.....
............
.#..###.....
#..#...#....
#..#...#....
#..#...#....
.#######....
:
(略)

ダウンロード先: https://gist.github.com/takesako/be0abea85a75b07b3fd9b63744f736dd

AVRは初見だとハーバードアーキテクチャで16bit単位のメモリアクセスでI/O命令の扱いが少し特殊で読むのは大変なのですが、 Arduinoで使われている世界標準のマイコンなので(今やPICより主流)IoT時代に覚えておいて損はないと思います。 GhidraがAVRに対応していて便利な世の中になりましたが、プリミティブな命令が多いので、慣れないとちょっと時間がかかります。

回答者:@ptr-yudaiさんによるwrite-up https://ptr-yudai.hatenablog.com/entry/2019/12/23/001203

正攻法で頑張って解いていて本当に素晴らしいです。

解法3:

ファームウェアを書き換えてwaitの待ち時間を短縮させてフラグを表示する方法も想定解です。

リバースエンジニアリングすると、プログラムメモリの特定の番地を読み込んで0x3Dと比較して一致していなかったら待ち時間を延ばさない、といった処理が見えます。0x3Dが出現するのはプログラム中1個所のみで、HEXファイルで言うと2行目の後尾の方です。

micronucleusのビルド

Arduino IDEを使って書き込んでも良いのですが、今回はmicronucleusという軽量なコマンドラインツールを使ってDigisparkのファームウェアを書き込みます。micronucleusは、原理的にファームウェア上書き一方向のみの書き込みができて、読み込みには対応していません。

$ sudo apt install -y libusb-dev
$ git clone https://github.com/micronucleus/micronucleus.git
$ cd micronucleus/commandline
$ make

ファームウェアの更新

アセンブルの結果より、HEXファイル中の2行目の3Dを1bit書き換えます。

$ sed -i -e "s/3D3E/2D3E/" badmouse.hex
$ ./micronucleus --timeout 60 badmouse.hex
> Please plug in the device ...
> Press CTRL+C to terminate the program.
> Device is found!
connecting: 40% complete
> Device has firmware version 2.4
> Device signature: 0x1e930b
> Available space for user applications: 6522 bytes
> Suggested sleep time between sending pages: 7ms
> Whole page count: 102  page size: 64
> Erase function sleep duration: 714ms
parsing: 40% complete
parsing: 60% complete
> Erasing the memory ...
erasing: 80% complete
> Starting to upload ...
writing: 100% complete
>> Micronucleus done. Thank you!

正解:

SECCON{379eaX85bTa99c695b36855i4Ycfa5b5}

勝手に解けている動画(約10分)

解けたチーム数:国内2チーム、国際6チーム

懇親会で余った予備のBad USB欲しい人いる?と言ったところ全部すぐなくなり人気でした。 ハードウェア問題に挑戦した人は総じて楽しかったと言っていただけて出題者としては嬉しかったです。

出題者:

Perlが第二母国語で、TMTOWTDI (There's More Than One Way To Do It)「やり方はひとつじゃない」の哲学が好きです。SECCON創設者の一人で初代実行委員長です。

DAY2にNICTブースに立ち寄ったら@kamapuさんに撮影していただきました。

挑戦していただいたCTF playerの皆様、ありがとうございました。

コンパイル時計算完全に理解した

コンパイル時計算完全に理解したリクルートテクノロジーズの竹迫です。

この記事はRecruit Engineers Advent Calendar 2019の4日目(12/4)のエントリーです。先月、社内のTGIFで飛び入り発表したスライドを記事にまとめなおしました。

adventar.org

皆さんは素数を数えることが好きでしょうか?私は大好きです。

問題:MAX以下の素数の個数を数えよ。

与えられた数以下の素数の個数を予測する「ガウス素数階段」を作る簡単な問題です。素数かどうかを判別するにはどんな数字でも割り切れないことを確認する必要があるため、ナイーブに素数判別を計算するとどうしても時間がかかってしまいます。(※厳密に素数の個数を予測する魔法の「リーマンの素数公式」はとても面白いのですが、今回の記事では解説対象外です。)

f:id:TAKESAKO:20191204230408p:plain

引用元:http://tsujimotter.hatenablog.com/entry/riemann-prime-number-formula

ガウス素数階段を改良した)リーマンの素数公式に興味のある方は上記URLに日曜数学者のtsujimotterさんによる可視化アプリもあるので(ぜひ解説も含めて)お楽しみください。

そこでコンパイル時計算ですよ

C++コンパイル時計算というテクニックを使うと、コンパイル時に計算結果が定数として出力され、プログラム実行時に計算コストが一切かからないというメリットがあります。

C++03のテンプレートメタプログラミングでMAX以下の素数の個数を計算してみます。

#include <iostream>

template<int N, int n=N-1, int m=N%n> struct isPrime { 
  enum { ok = isPrime<N, n-1>::ok }; 
};
template<int N, int n> struct isPrime<N, n, 0> { 
  enum { ok = 0 }; 
};
template<int N> struct isPrime<N, 1, 0> { 
  enum { ok = 1 }; 
};

template<int N> struct countPrime { 
  enum { sum = isPrime<N>::ok + countPrime<N-1>::sum };
};
template<> struct countPrime<1> {
  enum { sum = 0 };
};

int main() { 
  std::cout << countPrime<MAX>::sum << std::endl;
}

複雑にならないよう以前herumiさんに教えてもらったenumを使うテクニックを使っています。

このプログラム prime.cpp をコンパイルして、整数MAX=100までの素数の個数を数えてみます。

$ g++ -DMAX=100 prime.cpp && ./a.out
25

100までの素数の個数25が出力されました。

objdumpでディスアセンブルしてみる

本当にコンパイル時に計算されているのかどうか、実行時に計算しているのではないかという疑惑を払拭するため、コンパイルされた結果のバイナリをobjdumpコマンドでディスアセンブルして中身を見てみます。

$ objdump -d a.out
<抜粋>
000000000000088a <main>:
 88a:   55                      push   %rbp
 88b:   48 89 e5                mov    %rsp,%rbp
 88e:   be 19 00 00 00          mov    $0x19,%esi
 893:   48 8d 3d 86 07 20 00    lea    0x200786(%rip),%rdi        # 201020 
 89a:   e8 c1 fe ff ff          callq  760 <_ZNSolsEi@plt>
 89f:   48 89 c2                mov    %rax,%rdx
 8a2:   48 8b 05 27 07 20 00    mov    0x200727(%rip),%rax        # 200fd0 
 8a9:   48 89 c6                mov    %rax,%rsi
 8ac:   48 89 d7                mov    %rdx,%rdi
 8af:   e8 8c fe ff ff          callq  740 <_ZNSolsEPFRSoS_E@plt>
 8b4:   b8 00 00 00 00          mov    $0x0,%eax
 8b9:   5d                      pop    %rbp
 8ba:   c3                      retq

ちなみにobjdumpのデフォルトはGAS形式のAT&T表記で出力されるため、私にとって読みやすいIntel表記で出力するにはobjdumpに-M intelオプションを指定します。

$ objdump -M intel -d a.out
<抜粋>
000000000000088a <main>:
 88a:   55                      push   rbp
 88b:   48 89 e5                mov    rbp,rsp
 88e:   be 19 00 00 00          mov    esi,0x19
 893:   48 8d 3d 86 07 20 00    lea    rdi,[rip+0x200786]            # 201020
 89a:   e8 c1 fe ff ff          call   760 <_ZNSolsEi@plt>
 89f:   48 89 c2                mov    rdx,rax
 8a2:   48 8b 05 27 07 20 00    mov    rax,QWORD PTR [rip+0x200727]  # 200fd0 
 8a9:   48 89 c6                mov    rsi,rax
 8ac:   48 89 d7                mov    rdi,rdx
 8af:   e8 8c fe ff ff          call   740 <_ZNSolsEPFRSoS_E@plt>
 8b4:   b8 00 00 00 00          mov    eax,0x0
 8b9:   5d                      pop    rbp
 8ba:   c3                      ret

これを見ると「mov esi,0x19」のコードがあり、100までの素数の個数(25=0x19)がコンパイル時に定数となってバイナリに埋め込まれていることがわかります。コンパイル時計算すごいですね。

線形再帰の問題

ここで、調子に乗ってMAX=1000までの素数の個数を計算してみます。

$ g++ -DMAX=1000 prime.cpp
prime.cpp: In instantiation of ‘struct isPrime<997, 101, 88>’:
prime.cpp:5:8:   recursively required from ‘struct isPrime<997, 995, 2>’
prime.cpp:15:31:   recursively required from ‘struct countPrime<999>’
prime.cpp:15:31:   required from ‘struct countPrime<1000>’
prime.cpp:60:31:   required from here
prime.cpp:5:8: fatal error: template instantiation depth exceeds maximum of 900 
                            (use -ftemplate-depth= to increase the maximum)
   enum { ok = isPrime<N, n-1>::ok };
        ^
compilation terminated.

おっと。コンパイル時にfatal errorが出て止まりました!

よくエラーメッセージを見ると「template instantiation depth exceeds maximum of 900」と書いてあり、ご丁寧にも(use -ftemplate-depth= to increase the maximum)と深さを増やすオプション指定をコンパイラが教えてくれています。

$ g++ -ftemplate-depth=1000 -DMAX=1000 prime.cpp
$  ./a.out
168

このようにC++03の古典的なテンプレートメタプログラミングにおいては、線形再帰だと探索の深さが問題になるので、わざわざ二分再帰の形に書き直して深さではなく幅を広く探索するようにプログラムを書きなおすテクニックが必要でした。テンプレートメタプログラミングが魔術と言われる所以はこういうところにもありました。

C++14 constexpr を使おう

C++11の言語仕様からconstexprが追加され、コンパイル時定数式として評価できる関数を定義できるようになりました。ただ、C+11ではconstexpr関数の中で事実上return文一個しか書くことができず、変数を使った処理やif文やforループの繰り返しを書くことが困難で、変数は引数を使って表現し直して、繰り返しのループは再帰関数呼び出しに置き換えたりする必要があり、実際のところかなり面倒でした。

そこで、C++14の仕様ではconstexpr関数の制限が大きく緩和され、変数やif文の条件分岐や繰り返しのforループなどが使えるようになりました。これで普通の人でもカジュアルにconstexprを書くことができます。

わかりやすくなるよう、いったんコンパイル速度は気にせずナイーブに、C++14 constexpr関数を使ってコンパイル時計算で素数の個数を数えるプログラムを書いてみました。

#include <iostream>

template<size_t N>
struct constexpr_Prime {
  int debug = 0x33333333;
  int isPrime[N+1];
  constexpr constexpr_Prime() : isPrime() {
    int ok = 0;
    isPrime[0] = 0;
    isPrime[1] = 0;
    for(int i = 2; i <= N; i++) {
      for (int j = 2; j <= i/2; j++) {
        if (i % j == 0) { --ok; break; }
      }
      isPrime[i] = ++ok;
    }
  }
};

int main() { 
  constexpr auto cp = constexpr_Prime<MAX>();
  std::cout << cp.isPrime[MAX] << std::endl;
}

ここではMAX以下の素数の個数をすべてコンパイル時にisPrimeの定数配列に計算して格納しています。実行時に使うときは、constexpr auto cp = constexpr_Prime<MAX>();で実体化してから

  for (int i = 0; i <= MAX; i++) {
    std::cout << i << ":" << cp.isPrime[i] << std::endl;
  }

のようにして呼び出します。実行時は定数配列を添え字で参照するだけなので超高速です。

$ g++ -DMAX=100 prime.cpp && ./a.out
25

実際にobjdumpコマンドで定数配列が生成されているかどうかを確認してみます。

$ g++ -DMAX=100 prime.cpp && ./a.out
25
$ objdump -z -S -j .rodata a.out

a.out:     file format elf64-x86-64


Disassembly of section .rodata:

0000000000000a60 <_IO_stdin_used>:
 a60:   01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
 a70:   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................

0000000000000a80 <_ZStL19piecewise_construct>:
 a80:   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
 a90:   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00     ................
 aa0:   33 33 33 33 00 00 00 00 00 00 00 00 01 00 00 00     3333............
 ab0:   02 00 00 00 02 00 00 00 03 00 00 00 03 00 00 00     ................
 ac0:   04 00 00 00 04 00 00 00 04 00 00 00 04 00 00 00     ................
 ad0:   05 00 00 00 05 00 00 00 06 00 00 00 06 00 00 00     ................
 ae0:   06 00 00 00 06 00 00 00 07 00 00 00 07 00 00 00     ................
 af0:   08 00 00 00 08 00 00 00 08 00 00 00 08 00 00 00     ................
 b00:   09 00 00 00 09 00 00 00 09 00 00 00 09 00 00 00     ................
 b10:   09 00 00 00 09 00 00 00 0a 00 00 00 0a 00 00 00     ................
 b20:   0b 00 00 00 0b 00 00 00 0b 00 00 00 0b 00 00 00     ................
 b30:   0b 00 00 00 0b 00 00 00 0c 00 00 00 0c 00 00 00     ................
 b40:   0c 00 00 00 0c 00 00 00 0d 00 00 00 0d 00 00 00     ................
 b50:   0e 00 00 00 0e 00 00 00 0e 00 00 00 0e 00 00 00     ................
 b60:   0f 00 00 00 0f 00 00 00 0f 00 00 00 0f 00 00 00     ................
 b70:   0f 00 00 00 0f 00 00 00 10 00 00 00 10 00 00 00     ................
 b80:   10 00 00 00 10 00 00 00 10 00 00 00 10 00 00 00     ................
 b90:   11 00 00 00 11 00 00 00 12 00 00 00 12 00 00 00     ................
 ba0:   12 00 00 00 12 00 00 00 12 00 00 00 12 00 00 00     ................
 bb0:   13 00 00 00 13 00 00 00 13 00 00 00 13 00 00 00     ................
 bc0:   14 00 00 00 14 00 00 00 15 00 00 00 15 00 00 00     ................
 bd0:   15 00 00 00 15 00 00 00 15 00 00 00 15 00 00 00     ................
 be0:   16 00 00 00 16 00 00 00 16 00 00 00 16 00 00 00     ................
 bf0:   17 00 00 00 17 00 00 00 17 00 00 00 17 00 00 00     ................
 c00:   17 00 00 00 17 00 00 00 18 00 00 00 18 00 00 00     ................
 c10:   18 00 00 00 18 00 00 00 18 00 00 00 18 00 00 00     ................
 c20:   18 00 00 00 18 00 00 00 19 00 00 00 19 00 00 00     ................
 c30:   19 00 00 00 19 00 00 00                             ........

_ZStL19piecewise_constructのシンボル上に100個までの素数の個数が定数展開されていることがわかります。

ここまで自力で出来たので「C++完全に理解した」Tシャツを着ることができる人権が得られました。 f:id:TAKESAKO:20191204225330p:plain 引用元:https://twitter.com/shapoco/status/958302684880109568

最近のC++20では、constexprにtry-catchブロックが書けるようになったり、インラインアセンブリ(!)が書けるようになったりしています。C++の進化はすごいですね。 C++の言語仕様とコンパイラの対応状況などは以下のページでまとめられています。 cpprefjp.github.io

ちなみに、ワタシハC++チョットデキルTシャツはこの世界に存在してはならないと個人的には思います。

実行時間は?

さきほどのコンパイル時に素数を計算する時間をシェルスクリプトを書いて計測してみました。

#!/bin/bash
n="10 100 1000 5000 10000 20000 30000 40000 50000"
for i in $n
do
  echo -n "$i, "
  /usr/bin/time -f "%U" g++ -DMAX=$i prime.cpp
done

実行結果

10, 0.20
100, 0.21
1000, 0.32
5000, 1.65
10000, 5.29
20000, 20.65
30000, 45.87
40000, 81.28
50000, 129.12

グラフにすると以下のような形に

f:id:TAKESAKO:20191204225345p:plain

素数の数え上げにアトキンのふるいアルゴリズムを実装するなどすれば、もう少しまともに早くなりますが。 コンパイル時計算は、プログラム実行時を超高速に処理するために、事前コンパイルをものすごく頑張るというポリシーです。

さいごに:素数表を持ち歩こう

私は歩いているときに視界に入ってきた数字が素数かどうかをパッと見で判別するのが趣味で、暗黒通信団素数表をいつもカバンの中に入れて持ち歩いています。

f:id:TAKESAKO:20191204225253j:plain

引用元:https://www.amazon.co.jp/dp/4873101565/

本体価格は税別で357円とお買い求めしやすい素数となっております。(いや、3で割り切れるので違いますね) 計算機資源を使わない最高のキャッシュとして、紙を使った素数表、一家に一冊いかがでしょうか?

f:id:TAKESAKO:20191204225240j:plain

おわり

alpine-iot演習環境の構築手順

alpine-iot環境の構築手順について解説します。Arduino互換機Digispark開発ボードを使ってBadUSBを作る演習で必要となります。

1. VirtualBox本体のインストール

Downloads – Oracle VM VirtualBoxから自分のOS環境にあった最新のVirtualBoxをダウンロードしてインストールしてください。

f:id:TAKESAKO:20190827161225p:plain

セットアップ途中のCustom Setup画面で「VirtualBox USB Support」が選択されていることを確認してください。

f:id:TAKESAKO:20190827152449p:plain

初めてインストールするときに「このデバイス ソフトウェアをインストールしますか?」(名前:Oracle Corporation ユニバーサル シリアル バス コントローラ)という確認画面が表示されたら「インストール」をクリックしてOracle社のUSBコントローラを必ずインストールするようにしてください。

f:id:TAKESAKO:20190827153026p:plain

このUSBコントローラのインストールが完了できていないと、ホスト側に接続されたUSBデバイスをゲストOSから認識できなくなります。

2. Extension Pack のインストール

VirtualBox本体のインストールが終わった後、上記と同じVirtualBoxのダウンロードページから「VirtualBox x.y.z Oracle VM VirtualBox Extension Pack」の下にあるリンク「All supported platforms」をクリックして、Extension Packをダウンロードしてファイルをダブルクリックしてインストールしてください。ダブルクリック後にインストール画面が表示されるまで少し時間がかかります。このとき、必ずVirtualBox本体のバージョンと一致するExtension Packをインストールしてください。

3. Vagrant本体のインストール

Download - Vagrant by HashiCorpから最新のVagrantをインストールしてください。

f:id:TAKESAKO:20190827163953p:plain

初回インストール時はファイルの展開にものすごい時間がかかりますが、気長に待っていれば終わります。 SSDではなくHDDの場合、インストールが完了するまで数分から十数分ぐらい待つ必要があります。 Vagrantのインストールが終わったらOSの再起動が必要となります。

# 4. alpine-iot.zip の解凍 勉強会会場にてUSBメモリで配布している alpine-iot.zip を解凍してください。 ※日本語を含むフォルダ名がフルパスの途中にあるとVagrantssh鍵生成に失敗する不具合が報告されているので、英語名のみのパス上で解凍してください。

5. コマンドプロンプトの起動

cmd.batをダブルクリックして、解凍フォルダ上でコマンドプロンプトを起動します。 (macOSの場合は、コマンドを開いて解凍ディレクトリに cd alpine-iot してください。)

4. alpine-iot 作業ディレクトリの作成

mkdir alpine-iot
cd alpine-iot

※日本語を含むフォルダ名がフルパスの途中にあるとVagrantssh鍵生成に失敗する不具合が報告されているので、英語名のみのパス上で解凍してください。

5. 演習で使う Vagrant box のインストール

コマンドプロンプトから以下のコマンドを実行してください。

vagrant init takesako/alpine-iot

テンプレートのVagrantfileファイルが自動で作成されます。

【実行結果】

C:\alpine-iot>vagrant init takesako/alpine-iot
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

6. VMの起動

Vagrantfileのあるディレクトリで以下のコマンドを実行してください。

vagrant up

Vagrantfileに書かれた該当のboxイメージからVMが自動で起動します。

【実行結果】

C:\alpine-iot>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'takesako/alpine-iot' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Successfully added box 'takesako/alpine-iot' (v3.9.4) for 'virtualbox'!
==> default: Importing base box 'takesako/alpine-iot'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'takesako/alpine-iot' version '3.9.4' is up to date...
==> default: Setting the name of the VM: alpine-iot_default_1566895136480_97807
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => C:/alpine-iot

7. VMの接続

以下のコマンドを実行してsshログインします。

vagrant ssh

コンソールに「Welcome to Alpine Linux!」が表示されたらログインに成功です。 ログインに成功したらexitと入力して、ゲストOSのLinuxからログアウトします。

exit

【実行結果】

C:\alpine-iot>vagrant ssh
Welcome to Alpine Linux!
alpine:~$ exit
Connection to 127.0.0.1 closed.

ゲストOSとホストOS間でのファイル共有の確認(任意:この項目は作業しなくて良いです)

ゲストOS上の /vagrant フォルダは、ホストOSでVagrantfileが存在するフォルダとファイルが共有されています。 以下のコマンドを実行してゲストOSのLinuxからファイルを作成し、ホストOSからそのファイルを読み込めることを確認してください。

echo host > host.txt
vagrant ssh
cd /vagrant
ls
cat host.txt
echo guest > guest.txt
exit
type guest.txt

【実行結果】

C:\alpine-iot>echo host > host.txt
C:\alpine-iot>vagrant ssh
Welcome to Alpine Linux!
alpine:~$ cd /vagrant
alpine:/vagrant$ ls
Vagrantfile  host.txt
alpine:/vagrant$ cat host.txt
host
alpine:/vagrant$ echo guest > guest.txt
alpine:/vagrant$ exit
Connection to 127.0.0.1 closed.

C:\alpine-iot>type guest.txt
guest

8. VMの停止(ここまで実施する)

以下のコマンドを入力してゲストOSを安全にシャットダウンします。

vagrant halt

【実行結果】

C:\alpine-iot>vagrant halt
==> default: Attempting graceful shutdown of VM...

→これで演習環境の確認は終了です。お疲れ様でした。

9. VMイメージの削除(これは実行しないでください)

もしも、VMイメージを削除したいときは以下のコマンドを実行してください。

vagrant destroy
vagrant box remove takesako/alpine-iot

→これは演習環境を完全に消去したいときだけ実行してください。

EMFMのpodcastに出演してギルドマスターについてアツく語ってきました

生まれて初めてpodcastに出演しました。EMFMという、Engineering ManagerによるEngineering ManagerのためのPodcastです。

EMFM ep13. 人事制度の制約は悪なのか? ゲスト: 竹迫良範(@takesako)さん

https://anchor.fm/em-fm/episodes/ep13----takesako-e2v1v2

f:id:TAKESAKO:20190117224659p:plain
EMFM ep13. 人事制度の制約は悪なのか? ゲスト: 竹迫良範(@takesako)さん

Shibuya.pm、リリースマネージャーとしての活動、
セキュリティ・キャンプの卒業生はどこに行くのか、
Engineering Manager 10人登用、
人事制度とプログラムの関係、
評価制度の正しい使い方とは、
これからあるべきマネージメント組織とは、
ゲームで学ぶマネージメント、
などについて語ります。

anchor.fm

ep13.では、幻のハッカー甲子園の炎上事件と事業仕分けについても語っていて、私が3年前に前職のサイボウズ・ラボを辞めて、リクルートに転職した経緯・思いを語っています。 マネジメントについてはあまり表で語られない内容だと思いますので、エンジニアリングマネージャの人たちがこうして普段やっていること意識している暗黙知言語化して形式化いくことは非常に価値があることだと思っています。 今回は約3回分の収録をしましたので、残り2回ep14.とas.7で次のネタがまた配信されると思います。

メインパーソナリティのひろき(@hiroki_daichi)さん、ゆのん(@yunon_phys) さん、呼んでいただいてありがとうございました!

※2019/01/27追記 ep14. が配信されました。

ep14. スペシャリストとプロフェッショナリストの違いとは ゲスト: 竹迫良範(@takesako)さん anchor.fm

エンジニア35歳定年説、竹迫さんのキャリア形成のやり方、アウトプットとは、
スペシャリストとプロフェッショナリストの違いとは、
ニュートンの万有引力の法則、新しいパラダイムが出たときの対処方法、
理論を作る人と言語化する人は同じ人であるべきか、
メタ認知とニューラルネットワーク、ハードウェアとソフトウェア、
クラウド型言語がやってくる?、これから生き残れるエンジニア、
について語ります。

ニコ技深圳観察会用自己紹介文(竹迫良範)

1. 自己紹介

竹迫良範です。2017年4月のニコ技深圳観察会に参加した平野さんの会社の上司の上司にあたる管理職40才です。平野さんには会社のブログで長編の5つの記事を書いていただき圧倒的感謝です。

tech.recruit-mp.co.jp

元々は大量のトラフィックを捌く職業Perlプログラマーでしたが、セキュリティ技術のR&Dと新規事業開発を経験し、現在は日本の大企業における内製開発の組織作りを担当しています。現代社会は高度に専門分化された職務がありますが、ブラックボックスを次々と開けていく低レイヤー技術を身に付けつつも、雑多な仕事も何でも一度は自分の手でやってみて経験してみることをモットーに職務の深さと幅を広げてきました。

2. 何を仕事にしてるか

普段は上場企業R社のIHD担当(大企業におけるインハウス・内製開発組織構築)の専門役員として、100名規模のエンジニア・デザイナー・プロダクトマネージャーの組織作りとマネージメントを担当しています。兼業先の一つとして、IPAで未踏IT人材発掘・育成事業のプロジェクトマネージャーも兼務しており、25歳未満のアントレプレナーシップを持った若いITエンジニアの個人プロジェクトの立ち上げ支援を行なっています。担当分野はセキュリティと低レイヤー案件です。新規事業の0→1においてハードウェアのプロトタイプやモックアップを気軽に作れる環境は日本でも整ってきましたが、1→10のフェーズで事業拡大する段階になったときにどうやって量産するのかといったノウハウの有無に大きなギャップを感じています。全部を一国一企業の自前主義にこだわることなく、それぞれが得意な領域を持つパートナー同士でコラボレーションしていくようなやり方を深センで学びたいと思っています。

3. 自分の時間で何を作ってるか

2006年にセキュリティキャンプで講師をすることになったことがきっかけで、若者向けのセキュリティ演習教材を趣味の余暇の時間で作っています。国立高等専門学校機構 高知工業高等専門学校(高知高専)の客員准教授も兼務しており、セキュリティ素養を持ったモノづくり人材を育成するため、高専生向けのセキュリティSTEM教材を試作しています。今年度は、全国の高専を巡るキャラバン隊の一員として、高知高専石川高専、阿南高専鈴鹿高専、弓削商船高専、都立産技高専などでIoTセキュリティ演習を実施しています。

tech.recruit-mp.co.jp

私が自作した教材の中では、5Vで動作する50円のAVRマイコンのGPIO端子をON/OFFし、高速にLチカすることで、10kHzでPS/2の疑似キーボード信号を生成し、自分オリジナルのBadUSBを製作してみるという自作演習が一番人気です。当初、第1世代のキットでは純正のAVRライターAVRISP mkIIを使っていましたが、1個3,200円と高価だったため、演習後に機材一式を持ち帰って自己学習することができませんでした。そこで、格安のAVRライターを低コストで自作することを決意し、第2世代のキットとして、FTDI社FT232RLチップを搭載した1個250円のUSBシリアルの格安公板をeBayやAliExpressで中国から大量輸入してavrdudeが動作するようにlibftdiのパッチを書いて使っていました。演習で必須だったAVRライターのコストが1/10以下になり、部品リストの低価格化を実現することができ、演習後に各自ライターを持ち帰って自己学習を継続することができるようになりました。ただ、中国から輸入したUSBシリアルの格安公板は30個のうち1~3個ほど通電するが実際に通信できない不良品のチップに当たることが多く、演習時にハード不良によるトラブルシュートに時間をとられるようになってしまいました。純正のWindowsドライバでも動かないケースがあったため、パッチを当てたドライバやチップの各種設定、クロスコンパイラを同梱したAlpine LinuxのAPKBUILDパッケージを自作して公開しています。

GitHub - takesako/alpine-iot: Alpine Linux packages for IoT tools

現在は、更なる部品リストの低価格化を進めるべく、各社USBシリアルの格安公板を買い漁り、安定した品質と低価格が実現できるチップと公板を探しました。試行錯誤の結果、第3世代として、Silicon Labs社のCP210xチップを搭載した1個150円の格安公板が良さそうということがわかったのですが、avrdudeで使うためには基板のカスタマイズが必要で、半田付けの作業が必要なため、アセンブリの作業を頑張って内製でやるか、コストをかけて外製でやるか、腹決めするために、深センの様々な現場に行って空気を感じたいと思っています。2017年11月にPicoDuinoが欲しくなり、初めてtindieでオンラインの買い物をした際、Slovakia在住の売り手の人とメッセージのやり取りをした経験がきっかけで、自作ハードウェアの国家間の距離は急速に縮まり、変わってきていると実感しました。

gigazine.net

4. 中国経験など

台湾×5回、上海×2回、香港×1回の渡航経験があります。深センは未経験です。中国ではガイドブック片手に一人で行動ができます。大学では第二外国語として中国語を7年間勉強しました。私自身、語学が非常に苦手で、単位がとれるまで繰り返し何年も同じ先生の中国語の授業を受けていたというのが実態ですので、ボキャブラリーは豊富ではありません。発音については先生に厳しく指導されたため、ピンイン表記があれば中国語の四声を正しく発音できます。北京の標準語の教科書で勉強していたため、簡体字繁体字に読み替えることができます。日常会話は、英語も中国語も日本語も聞き取りが非常に苦手で…、何度も聞き直すことがあります。

台湾のカンファレンスでは、セキュリティ技術に関する国際発表を3回ほど行ないました。

  • 2010年 OSDC.tw 2010「Polyglot Programming and Web Security」
  • 2011年 OSDC.tw 2011「x86 ASCII Programming (16bit/32bit)」
  • 2011年 HITCON 2011 Hacks in Taiwan Conference「Disassemble Flash Lite 3.0 SWF File」

5. メイカーズのエコシステム ハードウェアのシリコンバレー深圳感想

日本の大企業の中で新規事業開発の部署をどのように作るかといった苦労が垣間見れる「つくる~む新横」を立ち上げたリコーの井内さん寄稿の記事が非常に心に刺さりました。ブルーカラーは製造原価算入、ホワイトカラーは販売管理費と、中国の会計基準に関する記述についても惜しみなく藤岡さんが触れているのが興味深かったです。アントレプレナーシップを持ちながらも、オーナーシップを持ち、業務プロセスの末端までコンプリート出来る経営者は私のロールモデルです。

単純作業を自動化して人類の無駄な時間をなくそう

このエントリーは Recruit Engineers Advent Calendar の 5日目の記事です。

www.adventar.org

目次

  1. 自己紹介
  2. 時間は有限、大切に使おう
  3. sshで30台のサーバをセットアップする
  4. expect コマンドを使おう
  5. StrictHostKeyChecking no
  6. chpasswdでパスワード設定
  7. chmod u+sとSUID
  8. seqコマンドとbash拡張構文{1..9}
  9. Excel業務を自動化しよう
  10. Win32::GuiTest でキーボード入力を自動化
  11. MouseMoveAbsPix でマウス操作を自動化
  12. マインスイーパーの自動化
  13. BadUSBによるキーボード入力の自動化
  14. まとめ

1. 自己紹介

@takesakoです。最近、まつもとゆきひろさんと岸川克己さんが技術フェローと技術顧問に就任した会社で専門役員を担当しています。 会社ではホールディングス兼務10%の傍ら、事業会社のプロダクト開発部部長、デザイングループと開発支援グループの2つのグループマネージャを兼務し、現在約70名の内製開発組織の体制構築と運営を行なっています。 会社では兼業申請により副業も認められていて、今年度は、独立行政法人2社、NPO法人1社、一般社団法人1社、公立大学法人1社、2省庁、1協議会の事業を掛け持ちしながら複数プロジェクトの進行と通常業務を並行して行なっています。現代人にとって、時間の管理と有効活用は非常に重要な課題です。

2. 時間は有限、大切に使おう

私はエンジニアですので、単純な作業はできるだけプログラムで自動化するコードを書いて、時間を生み出すようにしています。 このエントリーでは、新しくはないが、古くから使われているUNIXの素朴な技を使うことによって、地味な作業を自動化する温故知新なTIPSを紹介してみます。

3. sshで30台のサーバをセットアップする

さて、いきなり30台のLinuxサーバをセットアップする仕事が来たとします。最近はイメージのコピーで複数のサーバをセットアップできるクラウド環境が整っているため、コピー元の基本となるサーバのイメージをベースに30台のサーバに環境をコピーして作業するのが良いのですが、まとめて複数台のサーバへのコマンド実行をしたいことがあります。

まずはコピー元のオリジナルのサーバを用意し、ssh-keygenでssh秘密鍵と公開鍵のペアを作成し、公開鍵を~/.ssh/authorized_keysに追記し、ファイルのパーミッションを600にします。このユーザー名をvi /etc/sudoers で追記し、sudoできる権限にしておきます。コントローラーのサーバには秘密鍵を置き、コピー元のサーバには公開鍵のみを配置します。そして、コントローラーのサーバからsshでログインして各種セットアップを遠隔から行ないます。

4. expect コマンドを使おう

ssh で初回接続時に、~/.ssh/known_hosts に接続先sshサーバのフィンガープリントを記録するかどうか yes/no を答える場面がでてきます。

$ ssh 10.0.0.xx
The authenticity of host '10.0.0.xx (10.0.0.xx)' can't be established.
RSA key fingerprint is ff:ff:ff:ff:37:83:d8:c5:7a:56:72:c6:48:30:b3:7c.
Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added '10.0.0.xx' (RSA) to the list of known hosts.

この yes の入力を手で毎回やるのは面倒なので、expect コマンドで自動化してしまいましょう。

#!/bin/sh
for xx in `seq 10 39`; do
  expect -c "
    set timeout 3
    spawn ssh 10.0.0.$xx
    expect \"yes/no\" { send \"yes\n\" }
    expect \":~\" { send \"exit\n\" }
    exit
  "
done

IPアドレス、10.0.0.10~39の30台のサーバに対して実行した様子です:

5. StrictHostKeyChecking no

実は、sshクライアントの接続オプションで -o 'StrictHostKeyChecking no' を指定すると、yes/noの判断を無視して常に接続することができます。

$ ssh -o 'StrictHostKeyChecking no' 10.0.0.xx

また、~/.ssh/config で「StrictHostKeyChecking no」の行を追加すると常にその設定を有効にすることができます。

$ vi ~/.ssh/config

StrictHostKeyChecking no

ただ、DNSスプーフィングなどで違うサーバに誘導されたりしたときにリスクがありますので、この設定は正直あまりお勧めできません。自己責任でご利用ください。

6. chpasswdでパスワード設定

まとめて30台のサーバのログインパスワードを設定して各ユーザに配布するシチュエーションに遭遇した場合、chpasswdコマンドを活用しましょう。

echo 'username:passw0rd' | sudo chpasswd

タブ区切りで用意した各ログイン配布情報を元にssh経由で各サーバで実行するシェルスクリプトPerlで自動生成します。

#!/usr/bin/perl
print<<EOF;
#!/bin/sh
EOF
$_ = <DATA>;
while(<DATA>) {
  chomp;
  my ($NO, $IP, $USER, $PASS) = split /\t/, $_;
  print "ssh $IP echo '$USER:\Q$PASS\E' \\| sudo chpasswd\n";
}
__DATA__
No IPアドレス  ユーザー名 パスワード
1  10.0.0.10   username    P@ssw0rd111
2  10.0.0.11   username    p!ssw1rd222
3  10.0.0.12   username    p#ssw2rd333

上記プログラムを実行すると、以下のシェルスクリプトが生成されます。Perlの文字列中に\Qと\Eで変数をはさむことにより、変数中の記号が自動でシェルエスケープされます。

#!/bin/sh
ssh 10.0.0.10 echo 'username:P\@ssw0rd111' \| sudo chpasswd
ssh 10.0.0.11 echo 'username:p\!ssw1rd222' \| sudo chpasswd
ssh 10.0.0.12 echo 'username:p\#ssw2rd333' \| sudo chpasswd

sudoの前の'|'は、sshログイン後に実行したいため、'\|'としておく必要があります。\でエスケープしないと手元のsshコマンドを実行しているサーバ上で実行されてしまいます。エスケープを注意するのはUNIXらしいですね。

ただし、今回の手法ではechoコマンドで生のパスワードを表示しているため、.bash_historyにパスワードの履歴が残りますので、sshターミナルからコピペできる手軽さの反面、セキュリティは低下しますので、ご注意ください。

7. chmod u+sとSUID

30台のサーバでsudo chmod u+s /bin/dateを実行して、dateコマンドにもSUID(SET UID)ビットを付けたいシチュエーションがあるとします。

SUIDはUNIXのファイルアクセス権限の一つで、ファイルのオーナー権限で一般ユーザがコマンドを実行できる仕組みです。SUIDがデフォルトでついているコマンドはpasswdが代表例で、root権限がなくてもユーザ権限でパスワード変更できるようになっています。

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 23420 Aug 11  2010 /usr/bin/passwd

dateコマンドにもSUIDビットをつけて、時刻の変更をユーザ権限で行ないたいというシチュエーションです。

8. seqコマンドとbash拡張構文{1..9}

シェルで繰り返し実行するfor in文ではスペース区切りで繰り返し変数の値を指定します。

#!/bin/sh
for xx in 10 11 12 13 14; do ssh 10.0.0.$xx sudo chmod u+s /bin/date ; done

連番の数字を全部手で入力するのは面倒なので、seqコマンドの結果をバッククォートでinに渡す方法が古くから知られています。

#!/bin/sh
for xx in `seq 10 39`; do ssh 10.0.0.$xx sudo chmod u+s /bin/date ; done

最近のbashでは、`seq 10 39`相当のことが、{10..39}で書けるので、地味に便利です。bashの拡張記法わかりやすくて良いですね。

#!/bin/bash
for xx in {10..39}; do ssh 10.0.0.$xx sudo chmod u+s /bin/date ; done

9. Excel業務を自動化しよう

今度は、ちょっとテイストが変わって、外部の方とExcel方眼紙のファイルをやりとりする場合、以下のような定型項目の入力は自動化しておきます。

f:id:TAKESAKO:20161206013030p:plain

WindowsにActivePerlをインストールしておくとWin32::OLEが使えるので、現在Excelで開いているシートの項目に現在の日付や任意の文字列を自動で入力することができます。

#!/usr/bin/perl
use Win32::OLE qw();

my $Excel = Win32::OLE->GetActiveObject('Excel.Application');
my $Book  = $Excel->ActiveWorkBook;
my $Sheet = $Book->ActiveSheet;

$Sheet->Range("V3")->{Value} = @{[localtime]}[5] + 1900; # YYYY
$Sheet->Range("Z3")->{Value} = @{[localtime]}[4] + 1;    # MM
$Sheet->Range("AB3")->{Value} = @{[localtime]}[3];       # DD
$Sheet->Range("D4")->{Value} = "まなび事業本部 OL推進室";# 部署
$Sheet->Range("L4")->{Value} = "竹迫 良範";    # 氏名
$Sheet->Range("V4")->{Value} = "XXXXXXXX";     # 社員番号
$Sheet->Range("D7")->{Value} = "WXX+XXXXX";    # 内線
$Sheet->Range("L7")->{Value} = "03-XXXX-XXXX"; # 外線
$Sheet->Range("V7")->{Value} = "テプコ";       # 拠点

exit;

プログラム中でファイル名を指定して開くよりも、実はこの方が便利だったりします。

このようなExcel業務が発生する場合、プログラムで入力を自動化して時間を効率よく使うことが必須です。

10. Win32::GuiTest でキーボード入力を自動化

さらに、Win32::GuiTestモジュールをインストールすると、SendKeysでキーボード入力を自動化することができます。

use Win32::GuiTest qw(SendKeys);

system("start notepad.exe");
sleep(1);
SendKeys("foo{ENTER}");
SendKeys("bar{ENTER}");
SendKeys("bar{BS}z{ENTER}");

上記は、メモ帳(notepad.exe)を起動して、1秒待ってから、f o o 改行、b a r 改行、b a rと打って一文字削除してzを入力して改行するプログラムです。

11. MouseMoveAbsPix でマウス操作を自動化

Win32::GuiTestには、マウスを操作するAPIがあり、MouseMoveAbsPix($x, $y)で任意の座標にカーソルを移動することができます。

use Win32::GuiTest qw(:ALL);

for ($x = 0; $x < 900; $x += 2) {
  $y = sin($x / 60) * 300 + 400;
  MouseMoveAbsPix($x, $y);
}

12. マインスイーパーの自動化

これらの技術を応用してできたのが、5年前のYAPC::Asia 2011で発表したAcme::MineChanです。

yapcasia.org

ここでは、ゲームの攻略を題材にしたAIを作成していますが、実務の仕事にも応用できることは明白です。私が実際にどんな業務を自動化しているかは…内緒とさせてください。:)

13. BadUSBによるキーボード入力の自動化

BadUSBによるキーボード入力の自動化については、昨年のAdvent Calendarで記事にしましたので、IoTや電子工作に興味のある方はぜひご覧ください。

tech.recruit-mp.co.jp

14. まとめ

人類に与えられている時間は皆平等なため、どのように有限の時間を使いこなすかは、人それぞれです。 今回紹介したテクニックをさらに発展させ、ボットの開発や、機械学習による単純作業の自動化によって、人類全体の無駄な時間をなくし、エンジニア個々人のQoL(Quality of Life)を向上させましょう。