見出し画像

Pythonでアプリを作ったらウィルスと誤認されたので対策した話

Pythonを使ってtkinterでGUIアプリを作ったものの、Githubにアップロードしたアーカイブファイルをダウンロードするとウィルスと判定されてたのでその対策手順をまとめてみた


PythonでGUIアプリを作った!

本内容とはあまり関係ないですが、このようなアプリを作りました。
かなりニッチな用途のアプリです。
総Python製で、GUIのライブラリにtkinterを選択しました。
このtkinterを選択したことが後々面倒なことになった一因かも
環境はPython3.10.10/pyinstaller 6.5.0

GitHubに登録した!

GitHubにソースコードとreadme.mdを配置。
実行ファイルはPyInstallerで作成し、リリースの所から登録

実行ファイルがウィルスと誤認された!

使おうとしてくれた人が、ダウンロードしようとしたらウィルスと誤認されてダウンロード出来ないと教えてくれました。まじか。
調べてみたらPyInstallerとtkinterの組み合わせはマルウェアなどの有害アプリでも使われることが多く、ウィルスと誤判定されやすいらしい。

何故ウィルスと誤認されるのか?

Copilot先生によると、PyInstallerが既にコンパイルされたブートローダーを使って実行ファイルを作るので、そのブートローダーがウイルス対策ソフトウェアにウィルスだ!と見做されるらしい。
なので自分でブートローダーをコンパイルしたら良いらしい。

多分こんな感じで実行ファイルが作られているんじゃないかな?

推測の話だけど、多分PyInstallerはマシン語の純粋な実行アプリを作っているわけでは無いんだと思います。
多分、スクリプトを実行する実行ファイル(ブートローダーの事)があり、それにスクリプトを合体させて一つのアプリにしてるんじゃないかと。
実行環境と作ったコードをひとまとめにしてるわけです。
その実行環境がウィルス判定になってると推測
なのでそのウィルス判定の部分を作り直せば良いって寸法

こんな感じでウィルス誤判定に対処した!

先ずは必要なソフトをダウンロードしておく!

ブートローダを作り直す必要があるので、作り直すのに必要なソフトウェアをダウンロードします。
MinGW-W64ってのをダウンロードしたら良いようです。
ブートローダーはPythonではなくてC言語かC++言語で書かれてるようで、それを実行ファイルにコンパイルするのがMinGW-W64です。
Windows用のファイルはここから最新版をダウンロードするようです。
どれが良いのかは良く分かりませんが、その時最新のx86_64-13.2.0-release-win32-seh-msvcrt-rt_v11-rev1.7zをダウンロードしました。
ダウンロードしたらテキトーなところに展開してください。

※他の人のやり方では環境変数を書き換える方法が殆どですが、MinGW-W64を使うのは今回1回だけです。
環境変数でいつでもどこでも使えるのは便利な反面セキュリティ的にはどうかと思いますので、一時的に変更する方法を採用しています。なのでMinGW-W64を展開したフォルダのパスは覚えておいてください
次の工程[2.]で使用します。

PyInstallerのブートローダーを作り替える!

  1. コマンドプロンプトを開き、カレントフォルダをPyInstallerをインストール場所に変えます。
    cd ”PyInstallerをインストールしたい場所” のような感じで
    ドライブが違うときはドライブレターも変えましょう。
    カレントフォルダがc:\User\hogeでPyInstallerをD:¥Appsにインストールしたい場合は
    C:¥User¥ほげ>D: [Enter]
    D:\>cd .¥Apps  [Enter]

  2. MinGW-W64が使えるように一時的に環境変数を設定
    set path=%path%;MinGW-W64を展開したフォルダのパス\bin
    ※gcc.exeがあるフォルダです。
    MinGW-W64をD:\Apps\MinGW-W64に展開した場合は
    set path=%path%;D:\Apps\MinGW-W64\bin
    になります。エラーが出る場合はパスが正しいか、binフォルダ内にgcc.exeがあるかチェックしてください。

  3. でPyInstallerのリポジトリをGitでクローンします
    git clone https://github.com/pyinstaller/pyinstaller

  4. pyinstallerフォルダに移動
    cd .\pyinstaller

  5. venvで仮想環境を構築
    python -m venv venv

  6. 仮想環境に入る
    venv\Scripts\Activate

  7. PyInstaller内のbootloaderディレクトリに入ります。
    cd .\bootloader

  8. 再構築します。成功なら表示が結構長いです。次の項で再構築の様子のスクリーンショット画像貼ってますので参考に。
    python .\waf distclean all

  9. pyinstallerフォルダに戻ります
    cd ..\

  10. 作ったpyinstallerをインストールします※python setup.py install はpython3.10.10ではダメでした
    pip install . ←ピリオドも要ります

  11. 再構築されたPyInstallerを使用して、Pythonスクリプトを再度実行可能なファイルに変換します。
    pyinstaller スクリプトファイル -–onefile -–noconsole

きちんと実行できていればウィルスと誤判定された実行ファイルとは違う、誤判定されない実行ファイルが出来ていると思います。
但しこれで100%誤判定されないって訳でもないようです。

おまけ:[8.]の再構築の際の様子

こんな感じだと失敗。Cのコンパイラが無いぞって言われてます。
上記の設定[2.]の環境変数の設定を見直しましょう。

'distclean' finished successfully (0.027s)
'all' finished successfully (0.000s)
'distclean' finished successfully (0.000s)
Setting top to                           : D:\AI\pyinstaller\bootloader
Setting out to                           : D:\AI\pyinstaller\bootloader\build
Python Version                           : 3.10.10 (tags/v3.10.10:aad5f6a, Feb  7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)]
MSVC target(s)                           : ['x64', 'x86_amd64']
Checking for 'msvc' (C compiler)         : not found
Checking for 'gcc' (C compiler)          : not found
Checking for 'clang' (C compiler)        : not found
could not configure a C compiler!
(complete log in D:\AI\pyinstaller\bootloader\build\config.log)

成功例はこんな感じ

'distclean' finished successfully (0.000s)
'all' finished successfully (0.000s)
'distclean' finished successfully (0.000s)
Setting top to                           : D:\AI\pyinstaller\bootloader
Setting out to                           : D:\AI\pyinstaller\bootloader\build
Python Version                           : 3.10.10 (tags/v3.10.10:aad5f6a, Feb  7 2023, 17:20:36) [MSC v.1929 64 bit (AMD64)]
MSVC target(s)                           : ['x64', 'x86_amd64']
Checking for 'msvc' (C compiler)         : not found
Checking for 'gcc' (C compiler)          : D:\Apps\mingw64\bin\gcc.exe
Checking size of pointer                 : 8
Platform                                 : Windows-64bit-intel detected based on compiler
Checking for compiler flags -m64         : yes
Checking for linker flags -m64           : yes
Checking for compiler flags -Wno-error=unused-but-set-variable : yes
Checking for library user32                                    : yes
Checking for library comctl32                                  : yes
Checking for library kernel32                                  : yes
Checking for library advapi32                                  : yes
Checking for library gdi32                                     : yes
Build tests                                                    : disabled
Checking for function unsetenv                                 : no
Checking for function mkdtemp                                  : no
Checking for function dirname                                  : yes
Checking for function basename                                 : yes
Checking for function strnlen                                  : yes
Checking for program 'strip'                                   : D:\Apps\mingw64\bin\strip.exe
Checking for program 'strip'                                   : D:\Apps\mingw64\bin\strip.exe
'configure' finished successfully (1.470s)
'make_all' finished successfully (0.034s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\debug'
[ 1/23] Compiling zlib\zutil.c
[ 2/23] Compiling zlib\inffast.c
[ 3/23] Compiling zlib\adler32.c
[ 4/23] Compiling zlib\inftrees.c
[ 5/23] Compiling zlib\crc32.c
[ 6/23] Compiling zlib\inflate.c
[ 7/23] Compiling src\pyi_python.c
[ 8/23] Compiling src\pyi_apple_events.c
[ 9/23] Compiling src\pyi_global.c
[10/23] Compiling src\pyi_pyconfig.c
[11/23] Compiling src\pyi_pythonlib.c
[12/23] Compiling src\pyi_path.c
[13/23] Compiling src\pyi_main.c
[14/23] Compiling src\pyi_archive.c
[15/23] Compiling src\pyi_win32_utils.c
[16/23] Compiling src\pyi_splashlib.c
[17/23] Compiling src\pyi_splash.c
[18/23] Compiling src\pyi_launch.c
[19/23] Compiling src\pyi_utils.c
[20/23] Compiling src\pyi_exception_dialog.c
[21/23] Compiling src\main.c
[22/23] Linking build\debug\run_d.exe
[23/23] Processing build\debug\run_d.exe
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\debug'
'build_debug' finished successfully (3.893s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\release'
[ 1/23] Compiling zlib\zutil.c
[ 2/23] Compiling zlib\inffast.c
[ 3/23] Compiling zlib\adler32.c
[ 4/23] Compiling zlib\inftrees.c
[ 5/23] Compiling zlib\crc32.c
[ 6/23] Compiling zlib\inflate.c
[ 7/23] Compiling src\pyi_python.c
[ 8/23] Compiling src\pyi_apple_events.c
[ 9/23] Compiling src\pyi_global.c
[10/23] Compiling src\pyi_pyconfig.c
[11/23] Compiling src\pyi_pythonlib.c
[12/23] Compiling src\pyi_path.c
[13/23] Compiling src\pyi_main.c
[14/23] Compiling src\pyi_win32_utils.c
[15/23] Compiling src\pyi_utils.c
[16/23] Compiling src\pyi_launch.c
[17/23] Compiling src\pyi_exception_dialog.c
[18/23] Compiling src\pyi_archive.c
[19/23] Compiling src\pyi_splashlib.c
[20/23] Compiling src\pyi_splash.c
[21/23] Compiling src\main.c
[22/23] Linking build\release\run.exe
[23/23] Processing build\release\run.exe
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\release'
'build_release' finished successfully (2.585s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\debugw'
[ 1/23] Compiling zlib\zutil.c
[ 2/23] Compiling zlib\inffast.c
[ 3/23] Compiling zlib\adler32.c
[ 4/23] Compiling zlib\inftrees.c
[ 5/23] Compiling zlib\crc32.c
[ 6/23] Compiling zlib\inflate.c
[ 7/23] Compiling src\pyi_python.c
[ 8/23] Compiling src\pyi_apple_events.c
[ 9/23] Compiling src\pyi_global.c
[10/23] Compiling src\pyi_pyconfig.c
[11/23] Compiling src\pyi_pythonlib.c
[12/23] Compiling src\pyi_path.c
[13/23] Compiling src\pyi_archive.c
[14/23] Compiling src\pyi_splash.c
[15/23] Compiling src\pyi_utils.c
[16/23] Compiling src\pyi_launch.c
[17/23] Compiling src\pyi_exception_dialog.c
[18/23] Compiling src\pyi_main.c
[19/23] Compiling src\pyi_win32_utils.c
[20/23] Compiling src\pyi_splashlib.c
[21/23] Compiling src\main.c
[22/23] Linking build\debugw\runw_d.exe
[23/23] Processing build\debugw\runw_d.exe
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\debugw'
'build_debugw' finished successfully (2.681s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\releasew'
[ 1/23] Compiling zlib\zutil.c
[ 2/23] Compiling zlib\inffast.c
[ 3/23] Compiling zlib\adler32.c
[ 4/23] Compiling zlib\inftrees.c
[ 5/23] Compiling zlib\crc32.c
[ 6/23] Compiling zlib\inflate.c
[ 7/23] Compiling src\pyi_python.c
[ 8/23] Compiling src\pyi_apple_events.c
[ 9/23] Compiling src\pyi_global.c
[10/23] Compiling src\pyi_pyconfig.c
[11/23] Compiling src\pyi_pythonlib.c
[12/23] Compiling src\pyi_path.c
[13/23] Compiling src\pyi_main.c
[14/23] Compiling src\pyi_archive.c
[15/23] Compiling src\pyi_splashlib.c
[16/23] Compiling src\pyi_exception_dialog.c
[17/23] Compiling src\pyi_utils.c
[18/23] Compiling src\pyi_win32_utils.c
[19/23] Compiling src\pyi_splash.c
[20/23] Compiling src\pyi_launch.c
[21/23] Compiling src\main.c
[22/23] Linking build\releasew\runw.exe
[23/23] Processing build\releasew\runw.exe
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\releasew'
'build_releasew' finished successfully (2.608s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\debug'
[23/24] Processing build\debug\run_d.exe
+ install D:\AI\pyinstaller\PyInstaller\bootloader\Windows-64bit-intel\run_d.exe (from build\debug\run_d.exe)
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\debug'
'install_debug' finished successfully (0.029s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\release'
[23/24] Processing build\release\run.exe
+ install D:\AI\pyinstaller\PyInstaller\bootloader\Windows-64bit-intel\run.exe (from build\release\run.exe)
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\release'
'install_release' finished successfully (0.030s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\debugw'
[24/24] Processing build\debugw\runw_d.exe
+ install D:\AI\pyinstaller\PyInstaller\bootloader\Windows-64bit-intel\runw_d.exe (from build\debugw\runw_d.exe)
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\debugw'
'install_debugw' finished successfully (0.029s)
Waf: Entering directory `D:\AI\pyinstaller\bootloader\build\releasew'
[23/24] Processing build\releasew\runw.exe
+ install D:\AI\pyinstaller\PyInstaller\bootloader\Windows-64bit-intel\runw.exe (from build\releasew\runw.exe)
Waf: Leaving directory `D:\AI\pyinstaller\bootloader\build\releasew'
'install_releasew' finished successfully (0.028s)

おまけ:[10.]の再インストールの様子

pyinstallerの環境にpyinstallerをインストールしています
この環境を使って作ったスクリプトを実行ファイルにします。

Processing d:\ai\pyinstaller
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
  Preparing metadata (pyproject.toml) ... done
Collecting pyinstaller-hooks-contrib>=2024.3
  Using cached pyinstaller_hooks_contrib-2024.3-py2.py3-none-any.whl (329 kB)
Collecting packaging>=22.0
  Using cached packaging-24.0-py3-none-any.whl (53 kB)
Collecting pefile>=2022.5.30
  Using cached pefile-2023.2.7-py3-none-any.whl (71 kB)
Requirement already satisfied: setuptools>=42.0.0 in d:\ai\pyinstaller\venv\lib\site-packages (from pyinstaller==6.5.0) (65.5.0)
Collecting altgraph
  Using cached altgraph-0.17.4-py2.py3-none-any.whl (21 kB)
Collecting pywin32-ctypes>=0.2.1
  Using cached pywin32_ctypes-0.2.2-py3-none-any.whl (30 kB)
Building wheels for collected packages: pyinstaller
  Building wheel for pyinstaller (pyproject.toml) ... done
  Created wheel for pyinstaller: filename=pyinstaller-6.5.0-py3-none-any.whl size=1942427 sha256=6f03809fd091962795f48834dc481d29ced1be0bba817a3cb0f343d13fa93930
  Stored in directory: C:\Users\Neko\AppData\Local\Temp\pip-ephem-wheel-cache-8_mm55x8\wheels\2d\97\e8\30398140700f0102cd729f9ee90023978cc404acf2c52da4c6
Successfully built pyinstaller
Installing collected packages: altgraph, pywin32-ctypes, pefile, packaging, pyinstaller-hooks-contrib, pyinstaller
Successfully installed altgraph-0.17.4 packaging-24.0 pefile-2023.2.7 pyinstaller-6.5.0 pyinstaller-hooks-contrib-2024.3 pywin32-ctypes-0.2.2

実際に対策版をGitHubにアップロードした!

ウィルス誤判定対策版の実行ファイルをGitHubにアップロードしてみました。一旦プレリリースでダウンロードを試してみたところ、ウィルス判定なくちゃんとダウンロード出来ました。
そして本リリース。

一度環境を作れば後はそれを使えば良い

今回作ったpyinstallerを使えば、今後もPythonアプリを配布するときにウィルス誤判定される可能性は低いと思います。
一度作っておけば今後も使えるので安心です。

いいなと思ったら応援しよう!