質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.06%

socketでsendするときに引数の変換に失敗してしまいます。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 100

t4gforce

score 4

前提・実現したいこと

python3でsocketのsendの引数に、bytes型を指定したいのですが、変換に失敗します。

発生している問題・エラーメッセージ

'utf-8' codec can't decode byte 0x83 in position 1: invalid start byte

該当のソースコード

エラーが起きているソースコードを追記させていただきます。
以下のリンクのコードを参考にしています。
https://github.com/AllGloryToTheHypnotoad/Black-Hat-Python/blob/master/BHP-Code/Chapter2/bhnet.py
一つのファイルの実行時に指定されたオプションで、サーバ側とクライアント側の動作が変わる様になっています。
サーバとして動作させる場合は、python ./bhnet.py -l -p 9999 -c とオプションを指定し、
そのサーバに接続する際は、python ./bhnet.py -t localhost -p 9999 とオプションを指定して起動しています。

・encoding='utf-8'を追加し、再度更新しました

# coding:utf-8

# filename : bhnet.py

import sys
import socket
import getopt
import threading
import subprocess

import traceback

# global
listen = False
command = False
upload = ""
execute = ""
target = ""
upload_destination = ""
port = 0


# コマンドを実行し出力を返す
def run_command(command):
    command = command.rstrip()

    try:
        output = subprocess.check_output(
            command, stderr=subprocess.STDOUT, shell=True)
        output = output.decode(encoding="shift-jis")
        # output = output.decode(encoding="utf-8")  # 'utf-8' codec can't decode byte 0x83 in position 1: invalid start byte
    except Exception as e:
        output = str(e)
        output += "\r\n"
        output += "Failed to execute command.\r\n"

    return output


# クライアントが送ったデータを処理する
def client_handler(client_socket):
    global upload
    global execute
    global command

    # uploadが指定されたときの動作
    if len(upload_destination):
        file_buffer = ""

        while True:
            data = client_socket.recv(1024).decode(encoding='utf-8')

            if not data:
                break
            else:
                file_buffer += data

        try:
            file_descriptor = open(upload_destination, "wb")
            file_descriptor.write(file_buffer)
            file_descriptor.close()

            client_socket.send(
                b"Successfully saved file to %s\r\n" % upload_destination)
        except Exception:
            client_socket.send(
                b"Failed saved file to %s\r\n" % upload_destination)

    # コマンドの実行(execute)が指定されたときの動作
    if len(execute):
        output = run_command(execute)
        client_socket.send(output.encode(encoding='utf-8'))

    # commandが指定された場合はシェルとして動作する
    if command:
        while True:
            propmt = "<BHP:#> "
            client_socket.send(propmt.encode(encoding='utf-8'))

            cmd_buffer = ""
            while "\n" not in cmd_buffer:
                cmd_buffer += client_socket.recv(1024).decode(encoding='utf-8')
            response = run_command(cmd_buffer)

            client_socket.send(response.encode(encoding='utf-8'))


# 受信用の関数
def server_loop():
    global target
    global port

    # ターゲットの指定がなければすべてのアクセスを受け付ける
    if not len(target):
        target = "0.0.0.0"

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((target, port))

    server.listen(5)

    while True:
        client_socket, addr = server.accept()

        # クライアントの処理を行うスレッドの作成
        client_thread = threading.Thread(
            target=client_handler, args=(client_socket,))
        client_thread.start()


# クライアントとしてデータを送信する関数
def client_sender(buffer):

    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        client.connect((target, port))
        print("connected!")

        if len(buffer):
            client.send(buffer.encode(encoding='utf-8'))

        while True:
            recv_len = 1
            response = ""

            while recv_len:
                data = client.recv(4096).decode(encoding='utf-8')
                recv_len = len(data)
                response += data

                if recv_len < 4096:
                    break

            print(response)

            buffer = input("")
            buffer += "\n"

            client.send(buffer.encode(encoding='utf-8'))
    except Exception as e:
        print(e)
        print(traceback.format_exc())
        print("[*] Exception! Exiting.")
        client.close()


def main():
    global listen
    global port
    global execute
    global command
    global upload_destination
    global target

    if not len(sys.argv[1:]):
        sys.exit(0)

    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "hle:t:p:cu",
            ["help", "listen", "execute=", "target=",
             "port=", "command", "upload="]
        )
    except getopt.GetoptError as err:
        print(str(err))
    for o, a in opts:
        if o in ("-l", "--listen"):
            listen = True
        elif o in ("-e", "--execute"):
            execute = a
        elif o in ("-c", "--commandshell"):
            command = True
        elif o in ("-u", "--upload"):
            upload_destination = a
        elif o in ("-t", "--target"):
            target = a
        elif o in ("-p", "--port"):
            port = int(a)
        else:
            assert False, "Unhandled Option"

    # クライアントとして動作
    if not listen and len(target) and port > 0:
        buffer = sys.stdin.read()
        client_sender(buffer)

    # サーバとして動作
    if listen:
        print("...")
        server_loop()


main()

試したこと

プログラムのコードすべてとTraceback全文を追記しました。
長くなってしまいますが、よろしくお願いいたします。

再度更新いたしました
重ねてお願いとなってしまいますが、よろしくお願いいたします。

補足情報(FW/ツールのバージョンなど)

Python 3.8.1

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • can110

    2020/07/30 22:20

    どの部分で発生したか追えるように、エラーは全文(Traceback)を記載ください。
    また、できればクライアント側の完全なコードも提示ください。

    キャンセル

  • t4gforce

    2020/07/31 00:25

    クライアント側とサーバ側のコードと、Tracebackをすべて追記しました。
    引き続きよろしくお願いいいたします。

    キャンセル

  • bsdfan

    2020/08/01 22:33

    subprocess で実行しているコマンドの出力のエンコードはUTF-8なのでしょうか?
    それがSJISなだけでは?

    キャンセル

  • t4gforce

    2020/08/02 01:48

    教えていただきありがとうございます。
    コマンドの出力がshift-jisなだけのようですので、subprocess.check_output()の戻り値の文字コードによって処理を変更するようにしようと思います。

    キャンセル

回答 2

checkベストアンサー

+1

サーバ側のエラーについてですがcmd_buffer変数がbyte型なのにstr型を足しているため提示エラーが発生しています。
どのように修正すべきかはコードの意図が不明なので分かりませんが、型を統一しないといけません。

クライアント側のエラーは再現しませんでしたが、サーバとクライアントでのエンコーディングの違いが原因だと思われます。
コード上の.encode().decode()関数にてencoding='utf-8'と明示的にエンコーディングを指定すると解消すると思われます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/01 13:26

    教えていただきありがとうございます。
    サーバ側エラーはすべてstr型で統一することで動作しました。
    他の箇所も.encode()/.decode()にencoding='utf-8'とすることで動作したのですが、
    ソースコードの30行目だけencoding="shift-jis"としなければ、
    'utf-8' codec can't decode byte 0x83 in position 1: invalid start byte
    というエラーが出てしまいます。
    重ねてお願いとなってしまいますが、ソースコードを更新しましたので、このエラーについて詳しく教えていただけないでしょうか。

    キャンセル

  • 2020/08/01 21:55

    >ソースコードの30行目だけencoding="shift-jis"としなければ、
    それで正常に動作するのであれば、そのままでよいです。
    subprocess.check_output関数の戻り値の標準出力結果の文字列(実際はバイト列)の文字コードは
    環境依存だったはずなので、その部分の.decode()は環境にあわせる必要があります。

    キャンセル

  • 2020/08/02 01:47

    詳しく教えていただきありがとうございました。
    subprocess.check_output()の戻り値の文字コードによってdecode()の文字コード指定を変更するようにしようと思います。

    キャンセル

+1

encoding="shift-jis" ではどうでしょうか

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.06%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る