HttpClient を使って、艦これの「建造」を偽装してみます。画面的には「工廠」のところから、ぽちぽちと燃料や弾薬を設定するのですが、これを一発で設定して建造まで行かせます。マクロを作る気はないので、送信の偽装部分の試してみましょう、ということで。
■送信にトークンが必要
建造の通信は、POST通信を使って行われています。Cookieを使わず、単純なトークンのやり取りでログインチェックをしているようなので、なんらかの形でトークンが取れれば、別のツールから送信することが可能です。現在のトークン自体は、FeddlerCore でトラップすれば OK です。
送信データは、以下のようにフォーム型です。
api%5Fitem4=30&api%5Fitem3=500&api%5Fitem2=251&api%5Fitem1=200&api%5Fkdock%5Fid=1&api%5Fhighspeed=0&api%5Fverno=1&api%5Ftoken=e41e63da9c8985ec5a319c065fdb0d3be30f34f7
これを適当に分解してクラスを作っておきます。
public class Kenso
{
public int nenryo { get; set; }
public int danyaku { get; set; }
public int kouzai { get; set; }
public int baux { get; set; }
public int dock { get; set; }
public bool speed { get; set; }
public string token { get; set; }
public string ToContent()
{
/*
* api%5Fitem4=30& // ボーキサイト
* api%5Fitem3=500& // 鋼材
* api%5Fitem2=251& // 弾薬
* api%5Fitem1=200& // 燃料
* api%5Fkdock%5Fid=1& // 建造ドック
* api%5Fhighspeed=0& // 高速化 0:なし
* api%5Fverno=1& // バージョン
* api%5Ftoken=e41e63da9c8985ec5a319c065fdb0d3be30f34f7
*/
string body = "";
body += string.Format("api%5Fitem1={0}&", nenryo);
body += string.Format("api%5Fitem2={0}&", danyaku);
body += string.Format("api%5Fitem3={0}&", kouzai);
body += string.Format("api%5Fitem4={0}&", baux);
body += string.Format("api%5Fkdock%5Fid={0}&", dock);
body += string.Format("api%5Fhighspeed={0}&", speed? "1":"0");
body += string.Format("api%5Fverno={0}&", 1);
body += string.Format("api%5Ftoken={0}", token);
return body;
}
}
艦これに接続している間はトークンは一定なので、なんらかの形でトークンが取得できれば、それを使いまわせます。
■PostAsyncの内容を偽装する
private async void button1_Click(object sender, EventArgs e)
{
var kn = new Kenso()
{
nenryo = int.Parse(textBox2.Text),
danyaku = int.Parse(textBox3.Text),
kouzai = int.Parse(textBox4.Text),
baux = int.Parse(textBox5.Text),
dock = 1,
speed = false,
token = _token
};
var cl = new HttpClient();
var url = "http://" + _HOST + "/kcsapi/api_req_kousyou/createship";
var s = kn.ToContent();
var cont = new StringContent(s);
cl.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:23.0) Gecko/20100101 Firefox/23.0");
cl.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
cl.DefaultRequestHeaders.Add("Accept-Language", "ja,en-us;q=0.7,en;q=0.3");
cl.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
cl.DefaultRequestHeaders.Add("Referer", "http://" + _HOST + "/kcs/port.swf?version=1.4.0");
cont.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
cont.Headers.ContentLength = s.Length;
var res = await cl.PostAsync(url, cont);
MessageBox.Show("建造を開始しました");
}
POST するデータを作った後は、HttpClient を使って送信します。コンテンツ部分は、StringContentを使って HttpContent オブジェクトを作ります。ヘッダ部の偽装はできるだけ行っていますが、こんなにやる必要はないかもしれまん。これはターゲットにするサーバーによりけりですね。
あと、Content-Type を変更しないといけないのですが、これが曲者でした。どうやら、ContentType プロパティに MediaTypeHeaderValue オブジェクトで設定しないといけないようです。DefaultRequestHeaders や、Headers に Add すると実行時にエラーがでます。また、User-Agent の場合は、Headers に対して設定すると実行エラーになります。このあたり、stack overflow でも間違った回答がついているので注意が必要です。
■これで建造はできます
無事、建造はできているようなのですが、いくつか問題点あります。
- 建造の設定後、成功のレスポンスを受け取ってやる必要がある。
- 建造の後に、通常はクライアントからいくつかのリクエストを送っているが、これのは場合はしていない。
- 艦娘が収容量いっぱいでも、建造できてしまう。
あらかじめ、わかっている建造レシピをプリセットして、投入するのは可能なのですが...まあ、そのあたりは少しずつ。ちなみに、これはデスクトップアプリから試していますが、トークンさえわかってしまえば、他のアプリでも可能なので、ストアアプリとか iPhone からも建造が可能ですね。今度、ストアアプリで試してみましょう。