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 からも建造が可能ですね。今度、ストアアプリで試してみましょう。