2013年2月14日木曜日

Hacking to the Omni Wheel Robot

 最近、これまでやってきた研究とは異なることに取り組んでいました。詳しくは言えないのですが、その研究のプロトタイプ作成のために車輪型ロボットを使っています。この車輪型ロボットがなかなか曲者で制御ボード等の仕様書が一切ないという状況です。これをなんとか使用するためにProcessingとArduino用いてプロトタイピングを行ないました。その過程が誰かの役にたったらいいなということでそのネタで一本書いていきます。

1. どうやって利用するか考えてみた
 制御対象とする車輪型ロボットはBluetooth通信によってAndroid端末から専用アプリを利用してリモート・コントロールを行うことができることがわかっていたので今回は専用アプリから出力されるBluetoothメッセージを解析することで車輪型ロボットを使えるようにする方法を考えていくことにしました。

2. どうやって解析するか考えてみた
 解析する方法にはいくつかあると思いますが、今回私は専用アプリをソースコードまでリバース・エンジニアリングすることでBluetoothメッセージの解析を行なっていくことにしました。
 ※ソフトウエアにおけるリバース・エンジニアリングは法律的に非常に微妙な立場にあるものであると考えられます。利用は自己責任でお願いします。また、本文章は知的財産権の侵害を推奨するものではありません。

3. ソース解析の手法
 Androidアプリは通常apkという拡張子で配布されています。これをAppSender等を用いてPC上に移動します。これでapkの拡張子をzipに変更することで展開できるようになるため、apkの中身を見ることができます。apkのなかにはいろいろなものが入っていますが、今回使用するのはdexファイルのみです。
 次に、http://code.google.com/p/dex2jar/ からdex2jarというソフトウエアを入手します。これを用いてdexファイルをjarファイルにまで戻します。dex2jar本体が入っているディレクトリにdexファイルを移動してコマンドプロンプト上でdex2jar ****.dexというコマンドを入力するだけです。
 jarファイルが得られたら、後はこれをJavaのデコンパイラに通すだけで完了です。私が使用したのはjd-guiというものでした。入手は http://java.decompiler.free.fr/?q=jdgui からできます。

4. ソース解析
 あとは好きなだけソースを眺めてBluetoothメッセージを解析するだけでした。幸い難読化が行われていなかったために短時間で走行制御メッセージを解析することができました。

5. 走行制御プログラム作成
 今回はおなじみのProcessingを用いて車輪型ロボットとのBluetooth通信を行いました。プログラムの作成には建築発明工作ゼミさまの記事を参考にさせて頂きました。そして完成したProcessingの走行制御プログラムとArduinoのJoystickコントローラを統合した際の動作デモがこちらになります。


このプログラムのソースコードは以下のようになっています。
import processing.serial.*;

int joy1,joy2;
String str1,str2,move_state;
int duty;
Serial Robot,Echo;

void setup(){
  size(640,480);
  background(0);
  duty = 30;
  Robot = new Serial(this,"/dev/tty.BTCOM-SPPB-DevB",9600);
  Echo = new Serial(this,"/dev/tty.FireFly-AD1C-SPP",115200);
  Robot.clear();
  Echo.clear();
  Echo.bufferUntil(10);
}

void draw(){
  background(0);
  determinSendValue();
  delay(70);
  controller();
  controlWithJoystick(joy1/16,joy2/16);
  move_state = "Stop";
  text("Duty = " + duty,30,30);
  text("str1 = " + str1,30,50);
  text("str2 = " + str2,30,70);
  text("move state =" + move_state,30,90); 
}

void determinSendValue(){
  str1 = str(duty/10);
  str2 = str(duty/17);
}

void controlWithJoystick(int resist1,int resist2){
  if(resist1 < 10){
    //forward
    Robot.write("0100"+str1+str1+str1+"0\r");
    move_state = "Forword";
  }else if(resist1 > 54){
    //back
    Robot.write("1000"+str1+str1+str1+"0\r");
    move_state = "Back";
  }else if(resist2 < 10){
    //left
    Robot.write("1100"+str2+str2+str1+"0\r");
    move_state = "Left";
  }else if(resist2 > 54){
    //right
    Robot.write("0010"+str2+str2+str1+"0\r");
    move_state = "Right";
  }
}

void serialEvent(Serial p){
  String stringData=Echo.readStringUntil(10);
  println(stringData);
  if(stringData != null){
    stringData = trim(stringData);
    int data[] = int(split(stringData,','));
    if(data.length == 2){
      joy1 = data[0];
      joy2 = data[1];
      Echo.write(65);
    }
  }
}

void controller(){
  Robot.clear();
  if (keyPressed){
    switch(key){
      case 'w'://Forward
        Robot.write("0100"+str1+str1+str1+"0\r");
        move_state = "Forward";
        break;
      case 'a'://Left
        Robot.write("1100"+str2+str2+str1+"0\r");
        move_state = "Left";
        break;
      case 'd'://Right
        Robot.write("0010"+str2+str2+str1+"0\r");
        move_state = "Right";
        break;
      case 's'://Back
        Robot.write("1000"+str1+str1+str1+"0\r");
        move_state = "Back";
        break;
      case 'q'://Forward-Left
        Robot.write("0102"+str1+str1+str1+"0\r");
        move_state = "Forward-Left";
        break;
      case 'e'://Forward-Right
        Robot.write("0012"+str1+str1+str1+"0\r");
        move_state = "Forward-Right";
        break;
      case 'z'://Back-Left
        Robot.write("1003"+str1+str1+str1+"0\r");
        move_state = "Back-Left";
        break;
      case 'c'://Back-Right
        Robot.write("0013"+str1+str1+str1+"0\r");
        move_state = "Back-Right";
        break;
      case '1'://Turn-Left
        Robot.write("1111"+str1+str1+str1+"0\r");
        move_state = "Turn-Left";
        break;
      case '3'://Turn-Right
        Robot.write("0000"+str1+str1+str1+"0\r");
        move_state = "Turn-Right";
        break;
      case 'h'://increase Duty
        duty++;
        if(duty >=99) duty =99;
        delay(70);
        break;
      case 'l'://decrease Duty
        duty--;
        if(duty <=0) duty = 0;
        delay(70);
        break;
      default://Stop
        move_state = "Stop";
        break;
    }
  } 
}

感想など
 簡単にAndroidアプリのコピーやら解析やらができてしまったのが恐ろしいと思います。しっかりした有料アプリ等は難読化がかけられているのだと思うのですが、個人でアプリ開発をしている人は念入りにやらないとコピー(そのままの意味でも、アルゴリズム等のアイディアという意味でも)されてしまうので気を付けなければならないと感じます。

0 件のコメント:

コメントを投稿