プラグインを使用したHTTPプロキシサーバーの作成

画像 かつて、オンラインゲームの1つで captchaのようなやっかいなものがあるというトピックが登場しました。 キャプチャを入力するためにゲームから注意をそらすことは、それ自体ではあまり良い結果ではないことがわかります。特に最初に入力しないと、敵も迷惑をかけることができます。 しかし、これはポイントではありません。 ローカルボットを使用する人にとって特に悪いことです。 これらの小さなものはキャプチャにつまずき、このためにゲームはユニットとリソースの損失で即座に罰金を科します。 言って不快なこと。

したがって、タスク:
キャプチャを入力する必要はありません。 たとえボットがあなたのために遊んでいたとしても、あなたが自分で遊んでいたとしても、あなたが眠っていれば
追加条件:40時間(船のパニックの場合)。
優先条件:Windowsのインストールファイル。
別の望ましい条件:結果は1メガバイト以下を占める必要があります。


私はゲーマーではないことをすぐに言います。逆もまた同じです。私はオンラインゲームの反対者であり、この問題を取り上げてこの業界にエントロピーを追加することにしました。 おそらく、この事件は、これに関連するキャプチャとパニックの出現を契機にいくらかの利益をもたらす可能性がありますが、特定の理由でもたらされませんでした。

それではどうしますか?

試行1

インストールされているすべてのプログラムからのHTTPリクエストとレスポンスをインターセプトするシステムツールを作成し、キャプチャを自分で入力してキャプチャをキャプチャする必要がある回答をフィルタリングします。 この問題を解決することになっていたプログラムの中で、約2か月間、2人のベラルーシのプログラマーが積み重なり、プラットフォームをCからC#に、そしてJavaに変更し、マシンにOpenSSLをインストールする必要があるかもしれないと和解しました。 タスクが不必要な詳細に囲まれるたびに。 まあ、一般的には、うまくいきませんでした。

試行2:自分、すべて自分


多くの方法がなく、SOCKSプロキシとHTTPプロキシのどちらかしか選択できないことは明らかです。 しばらくして、すべてのユーザーアプリケーションがSOCKSプロキシをサポートしているわけではないことが明らかになり、選択が明確になりました。

プラットフォームの選択

特に試行1を考慮すると、選択は難しくありませんでした。CとC#は、完全に経験不足であるため、すぐにマークされました。 重要なライブラリが豊富な次のプラットフォームが特定されました。

Javaこのような小さなユーティリティをインストールするために、ユーザーがJVMをインストールしたいと考えるのは困難です。 Javaは落ちました。

Pythonご存じのように、それはどこでも動作し、すべてが含まれています(バッテリーが含まれています)。 重量は7Mbです。 もちろん、現代の標準では少しですが、それでももっとコンパクトにしたかったのです。 私のユーティリティのインストーラーでこのインストーラーをどのように実装するかという疑問が残ります。 Pythonアプリケーションでこれがどのように行われるかはわかりませんが、はるかに簡単かもしれませんが、どういうわけかインストーラーでインストーラーを既に実行しているので、したくありません。

Ruby検索の最初に、Windows用のワンステップインストーラーはありませんでした。 完全かつ完全に。 恐ろしいユーザーをインストールする場合、MinGW、MSYSなどのインストールを意味します。 重量7Mb。
インストーラーのインストーラーについては、疑問が残ります。

Lua C ++ゲームスクリプターの間で非常に古く人気のある言語。 緩慢なコミュニティ、異なる図書館。 必要なライブラリのカスタム VM アセンブリの重量はわずか800Kbです。 インストーラーは提供されていません。luaスクリプトを実行するパラメーターとして渡されるexeファイルのセットがあります。 必要なものは、Win、MacOS、Linux用にコンパイルされており、それぞれがバージョン32と64に個別にあります。 必要なもの。

それで、私はLuaの勉強を始めました(新年の願いが叶い、新しいプログラミング言語を学びました)。
この言語には、次のような素晴らしい特性があります。
-サンドボックス(Rubyではバージョン1.8.5のパッチのみがありました):サードパーティのコードを実行できるようにし、その環境を制限します。
-コルーチン(1.9からのルビー繊維など):非常に軽量の協調マルチタスクを作成できます。
-非常に単純な(より正確には、単純な-連想配列のみ)データ構造。これは、ほとんどのデータ処理タスクを実行するのに十分であることが判明しました。
...はるかに、1つの投稿でとても難しい。

最も簡単な方法は、HTTPプロキシサーバーの要求と応答をフィルタリングするという形でこのようなシステムを作成することでした。

アイデアは単純です:TCPサーバーを切断し、クライアントが要求するものを聞き、HTTPヘッダーを解析し、HOSTを探し、HTTPヘッダー「Proxy-Connection」を削除し、目的のリクエストをリクエストに送信し、レスポンスを受信し、クライアントに送信します。

サーバーの応答をフィルタリングする必要があります。これは、サーバーがHTTPSを使用していない場合に実行できますが、幸いなことにHTTPSは使用していません。 これを行うことは非常に簡単であることが判明し、190行のLuaに削減されたRubyのアナログメカニズムを記述するだけで十分であることが判明しました。

さて、この場合、有害なreCAPTCHAを取り除く必要がありました。
-元のリクエストがトラビアンのページをクリックした(およびHTMLページがリクエストされたかどうか):
string.find(request.uri(), 'travian') and mimetype and string.find(mimetype, 'text/html')

-結果のページに「有用な」ゲームデータの代わりにキャプチャがありましたか:
local captcha, captcha_key = string.match(response.body(), '<iframe src="(http://api.recaptcha.net/noscript??(k=[%a%d_]+&lang=en))')


キャプチャ自体を解決する方法(あまりにも現代的で高性能のインド人ではなく、非常に安価な生きているインド人の助けを借りて)は、純粋に技術的なトピックを少し超えているため、他のどこかにあります。

その結果、キャプチャ画像がダウンロードされ、ヒンドゥー教徒に送信され(念のため2回)、5〜10秒後に受信した回答が比較され、同じである場合、結果はリクエストによってHTTP POSTトラビアンに送信され、被害者はページを発行して、結局のところ、私たちは人であり、多くの「有用な」ゲームデータです。 このページは、わずかな一時停止にしか気付かない疑いのないクライアントに見せます。 悲観的なミスマッチの場合、写真は再びインディアンに送信され、少なくとも2つの同一の解決策が得られるまで、残りは払い戻しのためにサポートサービスに行きました(インディアンの景品はほとんどありません)。

だから、ここに、解決策があります。

しかし、誰も予想できなかったことが起こりました。 何らかの理由で、ユーザーは他のサイトへのアクセスも必要でしたが、考えられません! それらの中には、HTTPS経由でアクセスされるサイトでさえあり、通常のプロキシのオン/オフ切り替えをせずに、これで何かをしなければなりませんでした。
それでも、いくつかの要求を同時に受け入れる必要がありました。 Googleアナリティクスが時々リクエストを数分間続けて、シングルスレッドプロキシをスタンバイモードのままにしておくのは不快でした。

さて、このために、非同期TCPサーバーを作成するための3つの異なるライブラリがありました。 つまり、着信接続を待機し、データを取得し、制御をディスパッチャーに転送し、まだ着信接続があるか、データが存在する接続を開いているかを確認し(select / kpoll / epoll)、制御を順番に転送します。

ああ、ああ、すべてのそのような接続はローカルマシンで行われるので、これはほとんど瞬時に起こります。 そして、遅い接続が発信されています。 着信接続を多重化するように設計され既存のライブラリ( copasasok )を突くのは独自のライブラリを書くよりも困難でした。 そして、私は私の小さな(272行)を書きました。 すべての着信接続と発信接続が非同期に機能するという事実に加えて、共通のサイクルで機能するプールにさらにコルーチン(私、専門教育を受けた人々)を追加できます。

まあ、すべてが並行して動作し始め、速度の点では、プロキシなしで動作する方法にわずかに遅れていました。

(ヘッダーを含む)サーバーからページを受け取ったときの驚きはどれほど素晴らしいものでしたか。
コンテンツエンコーディング:gzip
転送エンコード:チャンク
そして実際に答えの本文として完全なkrakozyabry。

最初の考えは、リクエストでAccept-Encodingを無効にして、サーバーがデータをパックしようとせず、HTTP 1.1をHTTP 1.0に変換して「チャンク」を送信しないようにすることでした。 しかし、私は速度の低下とトラフィックの増加を考え、ユーザーに同情しました。
次のようになりました。
if headers(pipe, target)['Transfer-Encoding'] == 'chunked' then
target.body = dechunk(target.body)
end

function dechunk(chunkie)
local chunk_size
local chunk
local chunks = {}
chunkie, chunk_size = readline(chunkie)

while chunk_size and tonumber(chunk_size, 16) > 0 do
chunkie, chunk = readbytes(chunkie, tonumber(chunk_size, 16))

table.insert(chunks, chunk)
chunkie, chunk_size = readline(chunkie)
if not chunk_size or chunk_size == '' then -- sometimes there's a crlf, sometimes not
chunkie, chunk_size = readline(chunkie)
end
end

return table.concat(chunks)
end


私は資料を読みに行きました。 神に感謝します、これらの資料はまともです。
「チャンク」を接着し、gzipファイルを取得します(収縮することもありますが、まだ見ていません)。 開梱します( ライブラリの David Manuraに感謝します )。
開梱がさらに簡単になりました:
if headers(pipe, target)['Content-Encoding'] == 'gzip' and #target.body > 0 then
local decoded = {}
gzip.gunzip {input=target.body, output=function(byte) table.insert(decoded, string.char(byte)) end}
target.body = table.concat(decoded)
end


少し左
-HTTPSサイトのHTTPSトンネリングを作成します(OpenSSLを使用する必要はありません。データを透過的に転送するだけです)。
if request.method() == 'CONNECT' then
local sent_to_server, err = client.send("HTTP/1.0 200 Connection established\r\nProxy-agent: BotHQ-Agent/1.2\r\n\r\n")
print('https transparent connection')
https(client, server)
return
end

local function https(client, server)
close_callback = function()
client.close()
server.close()
end

client.receive_subscribe(function(data)
server.send(data)
end, close_callback)

server.receive_subscribe(function(data)
client.send(data)
end, close_callback)
end


-インストーラーを配置します。
一般に、 Heroku7zip sfxを起動するアドベンチャーは、別の投稿の価値があります。 勝利の喜びは、困難な開発の瞬間を覆い隠します。

さて、あなたについては知りませんが、これを行うのは面白くて魅力的でした。 過ごした時間を後悔していません。

要約すると:
プロキシサーバー自体は、 ここで行71にあります
ここに272行の非同期ライブラリTCP-server-client。
190行のHTTPクライアントの類似物。
ここで150行のキャプチャを解決するためのフィルター。
サイズが1メガバイト未満のインストールファイル。

「自動」キャプチャを使用したスパムロックなど、あまり良くないものから、スクリプトでユーザートラフィックを柔軟にフィルタリングする必要がある場合まで、このことには便利なアプリケーションがたくさんあると確信しています。 プロキシを介して接続しているユーザーがvk.comに接続することを許可しない単純なスクリプトを次に示します。
module(..., package.seeall)
function filter(request, response)
response.set_body('')
end

function pre(request, response)
return string.find(request.uri(), 'vk.com')
end


lua 5.2の今後のリリースでは、コルーチンからのメタメソッドの呼び出しに関する制限が削除され、ライブラリをより美しくすることができます。たとえば、http.set_bodyメソッドはなくなります。

Source: https://habr.com/ru/post/J111569/


All Articles