☆バイナリファイルを書き換えてUSB無線LANクライアントを認識させる

このエントリに記載している方法は危険かもしれません。実施される方は自己責任で行ってください。

2010/06/01 追記
USB_DEVICEマクロを使ってIdVendor,IdProductの指定をしている
("__mod_usb_device_table"のmatch_flagsが0x03)
場合はこの方法で問題ないとおもわれますが、match_flagsの値がそれ以外の場合は十分でないようです。
hitoさんによる此方の投稿を参照してください。
https://forums.ubuntulinux.jp/viewtopic.php?pid=60947#p60947

Ubuntuでインストール後にすぐ使用できるコマンドだけでカーネルモジュールを直接書き換えて
USB無線LANクライアントを認識させる方法を下記に記述します。
(readelf,od,strings,perlおよびテキストエディタを使用)

成功例が一つ(rt2870sta)だけであるため、別のモジュールで有効であるか不明ですが、
どうしても有線LANの接続が確保できない場合やビルド環境の導入が困難である場合に
試してみる価値はあると思います。

使用するUSB無線LANクラアント(GW-USMicroN)がLucid,Karmicで
デフォルトで認識されてしまうので例はJauntyで実施しました。
カーネルは2.6.28-11-generic。GW-USMicroNのIDは2019:ed14です。
これだけでは心もとないので、Lucidで2019:ed14を取り除いたモジュールをビルドし、同じ要領を実施しました。
これも認識、接続に成功しています。
実はRalinkのドライバ(RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2)で試そうと思ったのですが、なぜかソースに2019:ed14を追加してビルドした場合でもGW-USMicroNで接続が出来ませんでした。(Lucid,Karmicとも)

前提条件は対象のUSB無線LANクライアントが使用しているチップを駆動するカーネルモジュールが既知で
あること。(そしてデフォルトでモジュールが存在するが、子機が認識されないこと。)
USB無線LAN子機のidVendor,idProductのペアがmodules.alias,modules.usbmapに
存在しない場合、下記の方法で接続できる可能性があります。

目次

●1.前置き(きっかけ)

●2.readelf,od,stringsで収集する情報

●3.書き換え作業

●4.モジュールの認識



●1.前置き(きっかけ)

きっかけはdepmodコマンドで更新される[/lib/modules/$(uname-r)/]のmodules.* にlsbusbで出力されるidVendor,idProductと同じ値を見つけたことに始まります。

当該の値が含まれているファイルはmodules.aliasとmodules.usbmapで下記のようなものでした。

■modules.aliasの内容
(先頭行と一部抜粋)
# Aliases extracted from modules themselves.
alias rt3070sta rt2870sta
alias usb:v0411p015Dd*dc*dsc*dp*ic*isc*ip* rt2870sta
alias usb:v1737p0077d*dc*dsc*dp*ic*isc*ip* rt2870sta
■modules.usbmapの内容
(先頭行と一部抜粋)
# usb module         match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info
rt2870sta            0x0003      0x148f   0x2770    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x1737   0x0071    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x1737   0x0070    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0
rt2870sta            0x0003      0x148f   0x2870    0x0000       0x0000       0x00         0x00            0x00            0x00            0x00               0x00               0x0

これらの値はそれぞれdepmodコマンドがモジュールのファイルから読み出したものです。
depmodのソースから調べてみると下記の部分から取得していました。

■modules.aliasの元情報

モジュールの ".modinfo" セクションを元に作成されています。

$ readelf -p .modinfo モジュールファイルのパス
で簡単に見ることができます。

モジュールのソースにおていこの情報は
linux/module.hで定義されているMODULE_ALIASマクロを使って宣言されているようです。

#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)

#define MODULE_DEVICE_TABLE(type,name)  \
  MODULE_GENERIC_TABLE(type##_device,name)

■modules.usbmapの元情報

モジュールの"__mod_usb_device_table"シンボルに格納されています。
バイナリなので容易には見ることができません。
depmodを含むmodule-init-toolsのソースパッケージではtables.hで
その構造とサイズを定義しています。
(usb_device_id構造体、サイズはUSB_DEVICE_SIZE32,USB_DEVICE_SIZE64)

モジュールのソースにおていこの情報は
linux/usb.hで定義されている下記のマクロを使って宣言されているようです。

#define USB_DEVICE(vend,prod) \
 .match_flags = USB_DEVICE_ID_MATCH_DEVICE, \
 .idVendor = (vend), \
 .idProduct = (prod)

Ralinkのドライバ(RT2870_LinuxSTA_V2.3.0.0.tar.tar.bz2)では
USB_DEVICEを使用しているソースからMODULE_ALIASを使用するソースを
自動で生成しているようです。

●2.readelf,od,stringsで収集する情報

書き換え対象のidVendor,idProductとそのモジュール上の位置を知る必要があります。 ここでは"__mod_usb_device_table"シンボルの先頭のIDを対象にします。 そして対応する".modinfo"のエントリのオフセットを探します。

■モジュールの退避、バックアップの作成

対象モジュールを作業ディレクトリにコピーし、念のためバックアップを作っておきます。

$ sudo cp -p /lib/modules/$(uname -r)/kernel/drivers/staging/rt2870/rt2870sta.ko ./
$ TRG_MOD=./rt2870sta.ko
$ sudo cp -p $TRG_MOD ${TRG_MOD}.org
■"__mod_usb_device_table"の最初のエントリの位置と値の取得

まずreadelfコマンドで"__mod_usb_device_table"シンボルがが格納されている
セクションとその先頭からのオフセット、サイズを取得します。
コマンドの実行結果から25番目のセクションに格納されていて、
先頭からのオフセットが16進で0x4140、サイズが1220Byteであることが分かります。
(先頭だけを対象にするのでサイズは特に必要ありません。)

$ TRG_MOD=/lib/modules/2.6.28-11-generic/kernel/drivers/staging/rt2870/rt2870sta.ko
$ readelf -s $TRG_MOD | sed -ne '1,3p;/__mod_usb_device_table/p;'

Symbol table '.symtab' contains 1267 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
   555: 00004140  1220 OBJECT  GLOBAL DEFAULT   25 __mod_usb_device_table

25番目のセクションを探します。".data"セクションです。
ファイルの先頭から16進で0x6c980で始まっていることが分かります。
(別のモジュールで試してみると番号[Nr]はモジュールによって違いますが、
セクション名は".data"でした。基本的に".data"セクションを探せばよさそうです。)

$ readelf -S $TRG_MOD | sed -ne '1,4p;/\[25\]/p;'
There are 35 section headers, starting at offset 0x73830:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [25] .data             PROGBITS        00000000 06c980 004694 00  WA  0   0 32

開始アドレスを計算してodコマンドで内容を表示します。
1レコードが20Byteで先頭5項目はunsigned shortであることが分かっているので
(depmodのソースtable.h参照)
odのオプションに -N 20 --width=20 -t x2 を指定します。

ここで -t x1 とすると、x86はリトルエンディアンなのでunsigned shortの
上位バイトと下位バイトが逆になって表示されます。
出力から書き換え箇所はファイル先頭からのオフセットが十進で(461504 + 2) Byte
そこからunsigned shortを2つ分(計4byte)を書き換え対象にします。

$ perl -e 'print 0x4140 + 0x6c980 ,"\n"'
461504
$ od -j 461504 -N 20 -v -A d -t x2 --width=20 $TRG_MOD
0461504 0003 148f 2770 0000 0000 0000 0000 0000 0000 0000
■modules.aliasの元になる情報の書き換え位置

次にmodules.aliasの元になる情報の書き換え位置を調べます。
これは文字列なのでstringsコマンドに-tdオプションを指定することで
比較的簡単に調べることができます。
十進でファイルの先頭からのオフセット438048Byte,長さ20Byteを書き換えます。
("alias=usb:v148Fp2770" を "alias=usb:v2019pED14"へ)

$ strings -td $TRG_MOD | grep -i -e 148f.2770
 438048 alias=usb:v148Fp2770d*dc*dsc*dp*ic*isc*ip*

●3.書き換え作業

perlを使って書き換えを行ないます。

■"__mod_usb_device_table"の先頭書き換え

write_vp.plというスクリプトを書きました。
ネット接続不可の状況でも、その場ですぐにかけるような内容です。
書換え後のidVendor,idProductは直書きです。
引数は対象ファイルとオフセットです。

### スクリプト内容の表示
$ cat ./write_vp.pl
#!/usr/bin/perl

if($#ARGV != 1){
    print STDERR "usage $0 filepath offset\n";
}

my($file) = shift;
my($offset) = shift;

open FH, "+< " . $file or die "$!:$file";
seek(FH, $offset, 0);
my($buf) = pack "SS", 0x2019, 0xed14;
syswrite(FH, $buf, 4) or die "$!";

### 実行
$ sudo ./write_vp.pl $TRG_MOD 461506
### 確認
$ od -j 461504 -N 20 -v -A d -t x2 --width=20 $TRG_MOD
0461504 0003 2019 ed14 0000 0000 0000 0000 0000 0000 0000
■".modinfo"セクションの書き換え

write_alias.plというスクリプトを書きました。
write_vp.plと同じく短いものです。

### スクリプト内容の表示
$ cat ./write_alias.pl
#!/usr/bin/perl

if($#ARGV != 1){
    print STDERR "usage $0 filepath offset\n";
}

my($file) = shift;
my($offset) = shift;

open FH, "+< " . $file or die "$!:$file";
seek(FH, $offset, 0);
syswrite(FH,"alias=usb:v2019pED14",20);
seek(FH, $offset, 0);
sysread(FH,$buf,20);
print $buf,"\n";

### 実行
$ sudo ./write_alias.pl $TRG_MOD 438048
alias=usb:v2019pED14
### 確認
$ strings -td $TRG_MOD | grep -e 2019.ED14
 438048 alias=usb:v2019pED14d*dc*dsc*dp*ic*isc*ip*

●4.モジュールの認識

モジュールを正式な位置に書き戻してdepmodで依存情報を更新し、modprobeでモジュールを読み込みます。

$ sudo cp $TRG_MOD /lib/modules/$(uname -r)/kernel/drivers/staging/rt2870/
$ sudo depmod -a $(uname -r)
$ sudo modprobe rt2870sta

あとはNetworkManagerで認証関係の設定を行えば接続できるはずです。

0 件のコメント:

コメントを投稿