Compare commits

...
412 Commits
Author SHA1 Message Date
87owo a04d74a949 Add files via upload 2026-05-10 17:54:55 +08:00
87owo b93a7ebd94 Add files via upload 2026-05-10 17:45:08 +08:00
87owo cbc7b0340f Add files via upload 2026-05-10 17:38:28 +08:00
87owo ea7a197848 Add files via upload 2026-05-10 17:31:14 +08:00
87owo 82d4df889e Add files via upload 2026-05-10 13:06:24 +08:00
87owo df3a2a1010 Add files via upload 2026-05-10 13:06:05 +08:00
87owo 67cd6c2f59 Add files via upload 2026-05-10 02:30:07 +08:00
87owo b1788e7df9 Add files via upload 2026-05-10 02:28:27 +08:00
87owo 1cb8a81518 Add files via upload 2026-05-10 00:06:45 +08:00
87owo e1b61f2675 Update README.md 2026-05-09 22:53:01 +08:00
87owo 834d44856c Add files via upload 2026-05-09 22:49:48 +08:00
87owo c088eeb5bc Add files via upload 2026-05-09 22:47:09 +08:00
87owo 921510b7b7 Add files via upload 2026-05-09 22:27:36 +08:00
87owo 17a54f7a8a Add files via upload 2026-05-08 20:02:34 +08:00
87owo 43ff924efc Add files via upload 2026-05-08 18:24:36 +08:00
87owo f9bd4b6628 Add files via upload 2026-05-08 18:20:52 +08:00
87owo 702aa22139 Delete Interface/static/js/style.css 2026-05-08 18:20:37 +08:00
87owo 57d53f63e5 Add files via upload 2026-05-08 18:17:20 +08:00
87owo 835ada4a53 Add files via upload 2026-05-08 13:51:08 +08:00
87owo 7f204f3e1b Add files via upload 2026-05-08 10:53:11 +08:00
87owo 70e13b8414 Delete Engine/Properties/train_server_lfp.py 2026-05-07 14:13:19 +08:00
87owo df9bb8cbb8 Add files via upload 2026-05-07 14:12:56 +08:00
87owo cebdb32502 Add files via upload 2026-05-07 13:58:44 +08:00
87owo 58bd61343a Add files via upload 2026-05-06 14:27:54 +08:00
87owo 90faf1007e Add files via upload 2026-05-06 14:14:48 +08:00
87owo f8dbd12b69 Add files via upload 2026-05-06 12:54:54 +08:00
87owo cf6ba66dd2 Add files via upload 2026-05-04 23:29:36 +08:00
87owo 2080b5d10a Add files via upload 2026-05-04 23:29:14 +08:00
87owo dd180977d9 Add files via upload 2026-05-04 23:28:48 +08:00
87owo 221dcbaf1b Add files via upload 2026-05-04 23:28:13 +08:00
87owo 1a2098f4fb Add files via upload 2026-05-04 23:27:50 +08:00
87owo 73eab17110 Add files via upload 2026-05-03 11:34:05 +08:00
87owo 978a336358 Update README.md 2026-05-03 03:27:57 +08:00
87owo 6a0fd4e427 Update README.md 2026-05-03 03:23:58 +08:00
87owo 5fd4efd920 Add files via upload 2026-05-03 03:20:31 +08:00
87owo 43be2987f5 Add files via upload 2026-05-02 17:47:38 +08:00
87owo 06cc048fdc Add files via upload 2026-05-02 17:47:03 +08:00
87owo 5752471420 Add files via upload 2026-05-02 16:16:46 +08:00
87owo 3dfe12b90a Add files via upload 2026-05-02 15:48:47 +08:00
87owo ad1e3501b1 Add files via upload 2026-05-02 15:33:52 +08:00
87owo 1b46572e4c Add files via upload 2026-05-02 15:09:42 +08:00
87owo 9d8e42d34c Add files via upload 2026-05-02 15:09:28 +08:00
87owo 3f6e9dab3e Add files via upload 2026-05-02 15:09:06 +08:00
87owo ad85f11875 Add files via upload 2026-05-02 01:49:17 +08:00
87owo 9ae31834cc Add files via upload 2026-05-02 01:16:32 +08:00
87owo 49a4dc6942 Add files via upload 2026-05-02 01:16:05 +08:00
87owo a9f58cc30f Add files via upload 2026-05-02 01:15:51 +08:00
87owo bdb2198582 Add files via upload 2026-05-02 01:15:28 +08:00
87owo f32a77ca8b Add files via upload 2026-05-01 23:16:30 +08:00
87owo 121d3fb0ae Add files via upload 2026-05-01 23:15:59 +08:00
87owo 1dd72f7600 Add files via upload 2026-05-01 23:15:22 +08:00
87owo c7c168b007 Add files via upload 2026-05-01 23:14:45 +08:00
87owo 441b41c6af Add files via upload 2026-05-01 23:13:56 +08:00
87owo 49a907283a Add files via upload 2026-05-01 15:41:33 +08:00
87owo 0ffc52f9ba Add files via upload 2026-05-01 13:10:33 +08:00
87owo 4e96ec49e7 Update README.md 2026-04-29 21:04:53 +08:00
87owo dfb4646f51 Delete Embed directory 2026-04-29 21:04:30 +08:00
87owo a8e82c38c9 Add files via upload 2026-04-29 18:09:24 +08:00
87owo dba23289fd Add files via upload 2026-04-29 17:41:00 +08:00
87owo c2c1eb98d9 Add files via upload 2026-04-29 16:19:44 +08:00
87owo 364176912d Add files via upload 2026-04-28 14:38:35 +08:00
87owo 202245baad Add files via upload 2026-04-28 14:10:07 +08:00
87owo c3fa99640c Add files via upload 2026-04-27 15:03:53 +08:00
87owo a7c34b5a6a Add files via upload 2026-04-27 10:19:47 +08:00
87owo 05de092642 Update README.md 2026-04-26 22:35:28 +08:00
87owo 1bfa40c841 Add files via upload 2026-04-26 21:13:42 +08:00
87owo c325435ae8 Add files via upload 2026-04-26 15:41:47 +08:00
87owo b978bd41c3 Add files via upload 2026-04-26 14:31:23 +08:00
87owo f03d8733b0 Add files via upload 2026-04-26 14:30:49 +08:00
87owo a624b3b759 Update README.md 2026-04-26 14:29:57 +08:00
87owo d683398ba9 Add files via upload 2026-04-26 14:28:24 +08:00
87owo 42f692b82d Add files via upload 2026-04-26 14:27:33 +08:00
87owo 855fe9115e Delete Engine/Pattern directory 2026-04-26 14:26:30 +08:00
87owo d6bd383822 Add files via upload 2026-04-26 02:13:58 +08:00
87owo b1b77b73ac Add files via upload 2026-04-25 21:45:46 +08:00
87owo 8d87adcc50 Add files via upload 2026-04-25 21:45:32 +08:00
87owo 49bf6c54e7 Delete Interface/main.py 2026-04-25 20:53:22 +08:00
87owo 7e92e0b1d9 Add files via upload 2026-04-25 20:52:55 +08:00
87owo f0805d320c Add files via upload 2026-04-25 19:19:36 +08:00
87owo 364f48a6f0 Update README.md 2026-04-25 16:55:34 +08:00
87owo cfebf6df0e Update README.md 2026-04-25 16:40:38 +08:00
87owo 688855b72d Update README.md 2026-04-25 16:38:08 +08:00
87owo 77b275a04b Update README.md 2026-04-25 16:30:11 +08:00
87owo 98b142df12 Update README.md 2026-04-25 16:01:11 +08:00
87owo c5fe845f18 Delete PYAS_Resource.py 2026-04-25 15:59:56 +08:00
87owo e5e9629d31 Delete PYAS_Interface.py 2026-04-25 15:59:48 +08:00
87owo b65b9e6bfb Delete PYAS_Config.py 2026-04-25 15:59:26 +08:00
87owo 93ae8b02e3 Add files via upload 2026-04-25 15:55:56 +08:00
87owo 5c183ea200 Add files via upload 2026-04-25 15:54:58 +08:00
87owo 661765f08c Add files via upload 2026-04-25 15:54:33 +08:00
87owo 60fa19c681 Add files via upload 2026-04-25 15:53:27 +08:00
87owo 606eddba53 Add files via upload 2026-04-21 14:56:52 +08:00
87owo 71605ade31 Add files via upload 2026-04-19 02:21:41 +08:00
87owo ee409e4872 Add files via upload 2026-04-18 01:41:13 +08:00
87owo 91024abc4a Add files via upload 2026-04-15 19:59:41 +08:00
87owo 99de36a6cf Add files via upload 2026-04-15 19:57:35 +08:00
87owo ac1e48dc30 Add files via upload 2026-04-12 13:49:40 +08:00
87owo 494bab3380 Add files via upload 2026-04-12 13:42:38 +08:00
87owo 6696ca93ce Add files via upload 2026-04-11 16:50:33 +08:00
87owo b7e80fb8ed Add files via upload 2026-04-10 17:47:07 +08:00
87owo ce5b642d3e Add files via upload 2026-04-10 15:50:15 +08:00
87owo 76d414164a Update PYAS.py 2026-04-10 15:16:07 +08:00
87owo b6d13047ba Merge pull request #40 from MichaelMVS/fix-bare-except-clauses
Fix bare except clauses to catch only Exception types
2026-04-10 15:05:22 +08:00
87owo f1ba295796 Update README.md 2026-04-10 14:45:49 +08:00
zocomputer 415abef4fc Fix bare except clauses to catch only Exception types
Replace bare 'except:' with 'except Exception:' in three locations:
- check_process_survival (line ~1632): when accessing pe.szExeFile
- get_process_name (line ~1782): when getting process name from snapshot
- pipe_server_thread (line ~2340): when terminating malicious processes

Bare except clauses also catch SystemExit and KeyboardInterrupt,
which can prevent proper shutdown and mask serious issues.
Using 'except Exception:' allows the code to properly handle
expected errors while letting system-exiting events propagate.
2026-04-10 06:40:26 +00:00
87owo 95af2014d8 Update README.md 2026-04-10 14:39:38 +08:00
87owo f75b039daf Add files via upload 2026-04-10 14:35:02 +08:00
87owo 65b1ac78f3 Rename train_vt.py to train_server_lfp.py 2026-04-10 14:27:44 +08:00
87owo b067591882 Add files via upload 2026-04-10 14:24:58 +08:00
87owo 7941d76fd7 Delete Engine/Properties/train.py 2026-04-10 14:13:24 +08:00
87owo 7bff601beb Add files via upload 2026-04-10 14:12:27 +08:00
87owo 7b4da32bee Update README.md 2026-04-10 14:00:42 +08:00
87owo bbf2974703 Update README.md 2026-04-10 13:50:28 +08:00
87owo f5abaf06de Add files via upload 2026-04-08 17:57:14 +08:00
87owo 88023dd09e Add files via upload 2026-04-08 01:20:52 +08:00
87owo eec2996fab Add files via upload 2026-04-07 23:53:35 +08:00
87owo 97ca200c29 Add files via upload 2026-04-07 23:52:55 +08:00
87owo f0600ea3a3 Add files via upload 2026-04-07 23:52:20 +08:00
87owo 19b3eb51d7 Add files via upload 2026-04-04 16:02:36 +08:00
87owo 871dabf142 Add files via upload 2026-04-02 01:30:02 +08:00
87owo 58795b9349 Add files via upload 2026-04-01 23:42:12 +08:00
87owo 1ef769842b Add files via upload 2026-04-01 22:45:57 +08:00
87owo 6051570414 Add files via upload 2026-04-01 22:45:18 +08:00
87owo da5d5d0dc7 Add files via upload 2026-03-31 19:39:18 +08:00
87owo 782aa22d4d Add files via upload 2026-03-31 19:14:08 +08:00
87owo 0084867f38 Add files via upload 2026-03-29 21:14:20 +08:00
87owo 5a27858f86 Add files via upload 2026-03-27 02:17:54 +08:00
87owo 7905d8375f Add files via upload 2026-03-25 15:59:26 +08:00
87owo ba8aa63d75 Add files via upload 2026-03-16 15:10:16 +08:00
87owo ac82778e26 Add files via upload 2026-03-11 17:55:19 +08:00
87owo 55d2661e38 Add files via upload 2026-03-11 17:37:54 +08:00
87owo 4803b89294 Update train.py 2026-03-11 16:05:26 +08:00
87owo e48adbd87d Add files via upload 2026-03-11 16:00:07 +08:00
87owo 227314d83c Add files via upload 2026-03-08 03:53:52 +08:00
87owo 3c0e21eb02 Add files via upload 2026-03-08 03:42:55 +08:00
87owo 86d6153ef1 Add files via upload 2026-03-08 02:56:01 +08:00
87owo 78cf8bded3 Add files via upload 2026-03-07 12:50:56 +08:00
87owo 26d12181cd Add files via upload 2026-03-07 12:49:07 +08:00
87owo 8d184b011c Add files via upload 2026-03-01 18:40:56 +08:00
87owo ed3a1444ec Add files via upload 2026-03-01 18:19:34 +08:00
87owo d3e70a47cd Add files via upload 2026-03-01 18:19:08 +08:00
87owo 3171151219 Add files via upload 2026-03-01 18:16:04 +08:00
87owo e407867635 Add files via upload 2026-03-01 17:46:39 +08:00
87owo 6d9cfab55f Add files via upload 2026-02-26 02:50:09 +08:00
87owo f0bce029f0 Add files via upload 2026-02-26 01:21:41 +08:00
87owo d47960a0cf Add files via upload 2026-02-26 01:20:03 +08:00
87owo 8416018afe Add files via upload 2026-02-23 17:48:40 +08:00
87owo e93fc70169 Add files via upload 2026-02-23 17:47:50 +08:00
87owo 5b3474c781 Add files via upload 2026-02-23 01:04:21 +08:00
87owo 8d2a9f3b2b Add files via upload 2026-02-20 14:14:19 +08:00
87owo 73d09f9b09 Add files via upload 2026-02-20 13:49:18 +08:00
87owo 4e7c6efa2a Add files via upload 2026-02-20 13:46:13 +08:00
87owo 81a884bc61 Update PYAS_Engine.py 2026-02-18 23:56:27 +08:00
87owo ac4f960352 Add files via upload 2026-02-10 17:45:23 +08:00
87owo e4b3e4e438 Add files via upload 2026-02-10 17:44:48 +08:00
87owo d6ee476296 Add files via upload 2026-02-10 17:44:15 +08:00
87owo c23741ba9a Add files via upload 2026-02-10 17:43:47 +08:00
87owo 7bac5b4740 Add files via upload 2026-02-10 17:43:04 +08:00
87owo 1fe440168e Add files via upload 2026-02-04 23:11:56 +08:00
87owo 26ad57138b Add files via upload 2026-02-04 23:11:14 +08:00
87owo 92e1fdaf0f Add files via upload 2026-02-04 23:09:59 +08:00
87owo 357540d0ad Update README.md 2026-01-24 01:19:33 +08:00
87owo 405622a91e Update README.md 2026-01-24 01:14:02 +08:00
87owo 5686c73c56 Update README.md 2026-01-24 01:11:38 +08:00
87owo 76e2f787ac Update README.md 2026-01-24 01:10:28 +08:00
87owo 02b67f4e6a Update README.md 2026-01-24 01:06:59 +08:00
87owo a2ab527761 Update README.md 2026-01-24 01:05:17 +08:00
87owo f1142ae742 Add files via upload 2026-01-23 14:48:01 +08:00
87owo decb222dac Add files via upload 2026-01-23 14:25:34 +08:00
87owo 00c598e6f7 Add files via upload 2026-01-23 14:25:11 +08:00
87owo e9843dd3a5 Add files via upload 2026-01-23 14:24:27 +08:00
87owo 37f390742b Add files via upload 2026-01-23 14:23:14 +08:00
87owo 533e7dfd15 Add files via upload 2026-01-23 13:49:06 +08:00
87owo ddcbe1cd51 Delete Engine directory 2026-01-23 13:46:15 +08:00
87owo 4eabd04bc6 Add files via upload 2026-01-23 13:44:24 +08:00
87owo c8f2da6ac9 Add files via upload 2026-01-23 13:43:17 +08:00
87owo 8820ac4bf3 Add files via upload 2026-01-23 13:42:37 +08:00
87owo 236b200f39 Add files via upload 2026-01-12 14:28:24 +08:00
87owo 6789e32674 Add files via upload 2026-01-12 14:27:37 +08:00
87owo 9eb55deedf Add files via upload 2026-01-12 13:29:30 +08:00
87owo 6e76b5b8eb Add files via upload 2026-01-12 13:28:04 +08:00
87owo 491b0da83d Add files via upload 2026-01-10 22:48:17 +08:00
87owo 5d2c17e940 Add files via upload 2026-01-10 22:47:35 +08:00
87owo 0497c4a9e7 Add files via upload 2026-01-10 20:05:44 +08:00
87owo 6d0765848b Add files via upload 2026-01-10 20:03:39 +08:00
87owo 40c4ae71bd Merge pull request #35 from secretlay3r/main
refactor: replace ifs with dicts/sets + simplify exception handling
2026-01-10 19:37:25 +08:00
secretlay3r 8edba10dff Update PYAS.py
backup_mbr():
- Shorten except blocks logic into one
- Add len(mbr) == 512 check  
- Add MBR_SIGNATURE instead of just b"\x55\xAA"

pipe_server_thread():
- Replace whole ifs chain with RULES dict
- Replace [4001, 5001] with KILL_CODES set (for readability)

get_window_info()  and  get_process_name():
- Extract process name logic into private _get_process_name()
2026-01-10 14:17:27 +03:00
87owo 85aa7bc652 Add files via upload 2026-01-09 14:05:47 +08:00
87owo 854ad1db07 Add files via upload 2026-01-09 00:11:30 +08:00
87owo 955600a1e1 Add files via upload 2026-01-07 22:19:13 +08:00
87owo b739134d85 Add files via upload 2026-01-07 22:18:36 +08:00
87owo 723152566d Add files via upload 2026-01-07 21:10:34 +08:00
87owo 678b045e0e Update README.md 2026-01-07 17:47:13 +08:00
87owo 681f4df49a Add files via upload 2026-01-07 17:44:23 +08:00
87owo b19f7d7098 Add files via upload 2026-01-06 15:11:42 +08:00
87owo daaae3ec10 Add files via upload 2026-01-06 14:43:06 +08:00
87owo c14957afff Update and rename convert_full.py to convert.py 2026-01-04 22:34:35 +08:00
87owo 843a4998f1 Delete Engine/Model/convert.py 2026-01-04 20:45:08 +08:00
87owo 9637e74edc Add files via upload 2026-01-04 20:17:23 +08:00
87owo b1bdf4f671 Add files via upload 2026-01-04 18:55:43 +08:00
87owo 238ed8e364 Add files via upload 2026-01-04 18:55:04 +08:00
87owo 8aa1509863 Add files via upload 2026-01-04 13:41:45 +08:00
87owo aac182e30b Add files via upload 2026-01-04 13:40:19 +08:00
87owo ac7e953acc Add files via upload 2026-01-04 12:28:25 +08:00
87owo 6ae0f27e05 Add files via upload 2026-01-04 11:46:45 +08:00
87owo f6e5a92400 Add files via upload 2026-01-02 22:27:59 +08:00
87owo 5e5ff3428c Add files via upload 2026-01-02 22:26:42 +08:00
87owo 1cdbb9c402 Add files via upload 2026-01-02 22:25:22 +08:00
87owo 8caacd1870 Update README.md 2026-01-01 21:38:30 +08:00
87owo cfbc4c9c20 Update README.md 2026-01-01 21:29:21 +08:00
87owo 76dbf1c5d0 Add files via upload 2026-01-01 20:38:03 +08:00
87owo c0e7c4afa5 Add files via upload 2026-01-01 18:31:39 +08:00
87owo 9da0970d85 Delete ProtectRules.cpp 2026-01-01 18:31:01 +08:00
87owo d6f2ad32a7 Delete ProtectRegistry.cpp 2026-01-01 18:30:54 +08:00
87owo 8dbc87a331 Delete ProtectProcess.cpp 2026-01-01 18:30:45 +08:00
87owo 77eb1ce44e Delete ProtectFile.cpp 2026-01-01 18:30:37 +08:00
87owo e12f4ae61a Delete ProtectBoot.cpp 2026-01-01 18:30:29 +08:00
87owo c23a73625f Delete DriverEntry.cpp 2026-01-01 18:30:20 +08:00
87owo 3a66946686 Delete DriverCommon.h 2026-01-01 18:30:13 +08:00
87owo 7fef43701f Add files via upload 2026-01-01 18:29:52 +08:00
87owo 7f5eb34ac6 Add files via upload 2026-01-01 17:41:40 +08:00
87owo ba4369f829 Add files via upload 2026-01-01 03:39:49 +08:00
87owo 81af2e51fc Delete Plugins/Filter/ProtectScreen.c 2026-01-01 03:37:53 +08:00
87owo dff09064c8 Delete Plugins/Filter/ProtectRules.h 2026-01-01 03:37:45 +08:00
87owo ee0bb32cce Delete Plugins/Filter/ProtectRules.c 2026-01-01 03:37:35 +08:00
87owo 4a215c308b Delete Plugins/Filter/ProtectRegistry.c 2026-01-01 03:37:18 +08:00
87owo 79493a96ea Delete Plugins/Filter/ProtectRemote.c 2026-01-01 03:37:07 +08:00
87owo 3cca1c4671 Delete Plugins/Filter/ProtectProcess.c 2026-01-01 03:36:54 +08:00
87owo 72e9923d08 Delete Plugins/Filter/ProtectMatch.h 2026-01-01 03:36:43 +08:00
87owo 406e152891 Delete Plugins/Filter/ProtectMatch.c 2026-01-01 03:36:27 +08:00
87owo e9eb569d50 Delete Plugins/Filter/ProtectInject.c 2026-01-01 03:36:17 +08:00
87owo 640b389e44 Delete Plugins/Filter/ProtectImage.c 2026-01-01 03:36:07 +08:00
87owo 314e3349d6 Delete Plugins/Filter/ProtectFile.c 2026-01-01 03:35:56 +08:00
87owo 0ae4d72bc9 Delete Plugins/Filter/ProtectBoot.c 2026-01-01 03:35:45 +08:00
87owo 8920a5fb21 Delete Plugins/Filter/DriverPipe.c 2026-01-01 03:35:34 +08:00
87owo b338ff5dd9 Delete Plugins/Filter/DriverEntry.h 2026-01-01 03:35:25 +08:00
87owo 87e2fcb37c Delete Plugins/Filter/DriverEntry.c 2026-01-01 03:35:05 +08:00
87owo bab43e3b17 Add files via upload 2026-01-01 03:34:44 +08:00
87owo e602088330 Add files via upload 2025-12-29 20:14:49 +08:00
87owo 2f6db92077 Add files via upload 2025-12-29 20:14:14 +08:00
87owo cee33c0f77 Add files via upload 2025-12-28 14:58:49 +08:00
87owo 4fcdaa10b6 Add files via upload 2025-12-28 14:54:52 +08:00
87owo 949f74ab04 Add files via upload 2025-12-28 14:21:07 +08:00
87owo ef68a89b45 Add files via upload 2025-12-28 14:20:35 +08:00
87owo 10ecb35cca Add files via upload 2025-12-28 14:12:29 +08:00
87owo 696ac22f77 Add files via upload 2025-12-27 15:17:13 +08:00
87owo 8d13f98030 Add files via upload 2025-12-27 02:09:29 +08:00
87owo 1dddf13a9d Add files via upload 2025-12-27 02:08:10 +08:00
87owo 855472239a Delete Engine/Model/train_torch.py 2025-12-27 01:42:33 +08:00
87owo dc5d317623 Delete Engine/Model/train_tensorflow.py 2025-12-27 01:42:24 +08:00
87owo 91011e9e2a Add files via upload 2025-12-27 01:42:07 +08:00
87owo bb09b75d3b Add files via upload 2025-12-27 01:41:45 +08:00
87owo b2f9adad66 Add files via upload 2025-12-27 01:41:03 +08:00
87owo 0c2c84d2a9 Add files via upload 2025-12-26 15:13:42 +08:00
87owo 1733e7cc86 Add files via upload 2025-12-25 23:49:05 +08:00
87owo 831d9a9895 Add files via upload 2025-12-25 18:53:53 +08:00
87owo 70ba3a629d Update README.md 2025-12-25 03:14:28 +08:00
87owo 29e9702d21 Update README.md 2025-12-25 03:12:28 +08:00
87owo f8ef756bd4 Add files via upload 2025-12-25 01:23:41 +08:00
87owo 233b28e8a5 Add files via upload 2025-12-24 22:34:19 +08:00
87owo da77c17be5 Add files via upload 2025-12-24 14:33:17 +08:00
87owo 8dfa76822e Delete Engine/Model/train.py 2025-12-24 14:32:38 +08:00
87owo 4163991b66 Add files via upload 2025-12-24 14:32:18 +08:00
87owo 78323c9a08 Add files via upload 2025-12-24 14:31:06 +08:00
87owo eb517e86f3 Add files via upload 2025-12-24 14:30:13 +08:00
87owo e24f3823cb Add files via upload 2025-12-24 11:15:31 +08:00
87owo fd3e8ee28a Update README.md 2025-12-21 23:27:08 +08:00
87owo a980ea7bd6 Update README.md 2025-12-21 23:26:00 +08:00
87owo 20ce73c54f Update README.md 2025-12-21 23:17:32 +08:00
87owo d8f22f7f28 Update README.md 2025-12-21 23:16:03 +08:00
87owo 591055edaf Update README.md 2025-12-21 23:13:09 +08:00
87owo fbf0e06c04 Update LICENSE.txt 2025-12-19 22:46:40 +08:00
87owo 9daf327e80 Update README.md 2025-12-19 22:42:21 +08:00
87owo 4441978e10 Update LICENSE.txt 2025-12-19 22:38:14 +08:00
87owo d8eb770735 Update LICENSE.txt 2025-12-18 20:12:03 +08:00
87owo cefbf79c6e Update LICENSE.txt 2025-12-11 20:01:08 +08:00
87owo fc8762094e Update LICENSE.txt 2025-12-11 20:00:42 +08:00
87owo 801c3aadcc Update LICENSE.txt 2025-12-09 11:43:08 +08:00
87owo 4296f1ec43 Add files via upload 2025-11-27 15:28:31 +08:00
87owo a7f469747c Add files via upload 2025-11-27 14:57:17 +08:00
87owo 68d0352343 Add files via upload 2025-11-27 14:56:17 +08:00
87owo 01e6d09f7c Update train.py 2025-11-26 17:56:08 +08:00
87owo 6582abfd4a Update train.py 2025-11-26 17:26:31 +08:00
87owo f8f4b303d7 Update LICENSE.txt 2025-11-01 17:08:11 +08:00
87owo 79f05a23a8 Add files via upload 2025-10-08 11:51:18 +08:00
87owo 95845c5c85 Add files via upload 2025-10-08 11:05:43 +08:00
87owo cd1a24290e Add files via upload 2025-10-07 23:08:14 +08:00
87owo 21261bfb17 Add files via upload 2025-10-07 22:48:17 +08:00
87owo 79abf51cb8 Add files via upload 2025-10-07 22:25:51 +08:00
87owo e538b837d5 Add files via upload 2025-10-07 21:53:12 +08:00
87owo 7b995e1223 Add files via upload 2025-09-16 20:12:31 +08:00
87owo c7826b58a3 Add files via upload 2025-09-16 20:04:32 +08:00
87owo 5786dbdb75 Add files via upload 2025-09-16 20:03:15 +08:00
87owo 0f42c19ec7 Delete README_zh_TW.md 2025-09-10 13:43:50 +08:00
87owo 87d44a64f3 Delete README_zh_CN.md 2025-09-10 13:43:40 +08:00
87owo 77dd4ace33 Update README.md 2025-09-10 13:43:14 +08:00
87owo 0907d71957 Update README.md 2025-09-10 13:36:40 +08:00
87owo e7a30646e3 Update README.md 2025-09-10 13:35:25 +08:00
87owo 98d21b3391 Update LICENSE.txt 2025-09-09 09:45:56 +08:00
87owo 35d9203b9c Update LICENSE.txt 2025-09-09 09:32:39 +08:00
87owo e6e384a7f7 Rename LICENSE.md to LICENSE.txt 2025-09-09 08:45:38 +08:00
87owo 7d954c14ae Rename LICENSE.txt to LICENSE.md 2025-09-09 08:45:19 +08:00
PYDT 41388962e2 Add files via upload 2025-08-29 17:59:44 +08:00
PYDT 6d51f6b6a9 Add files via upload 2025-08-29 16:42:14 +08:00
PYDT c7af9a3702 Add files via upload 2025-08-29 16:41:38 +08:00
PYDT da7c61202c Add files via upload 2025-08-28 13:36:16 +08:00
PYDT c24e872a33 Add files via upload 2025-08-28 10:28:15 +08:00
PYDT d0809ec313 Add files via upload 2025-08-28 08:58:42 +08:00
PYDT c1a796da38 Add files via upload 2025-08-27 17:53:10 +08:00
PYDT 98c27800e0 Add files via upload 2025-08-26 10:11:36 +08:00
PYDT 7a18eadd13 Add files via upload 2025-08-25 23:50:38 +08:00
PYDT b07adb7b30 Add files via upload 2025-08-25 23:23:42 +08:00
PYDT 4d7c3cce4d Add files via upload 2025-08-25 20:28:36 +08:00
PYDT 4ff0ab9cd8 Add files via upload 2025-08-25 09:42:51 +08:00
PYDT 264dd50969 Add files via upload 2025-08-25 08:49:53 +08:00
PYDT 26d0ad1a6e Add files via upload 2025-08-24 22:06:31 +08:00
PYDT 90284018e5 Add files via upload 2025-08-24 22:06:02 +08:00
PYDT 46eeec16d4 Add files via upload 2025-08-24 22:05:30 +08:00
PYDT 083fb0dccc Add files via upload 2025-08-23 22:07:39 +08:00
PYDT d46f145806 Delete Plugins/Filter/ProtectReg.c 2025-08-23 22:04:35 +08:00
PYDT 868eddfd2b Add files via upload 2025-08-23 22:04:11 +08:00
PYDT 8295e6bb5b Add files via upload 2025-08-22 23:13:46 +08:00
PYDT 424914b842 Add files via upload 2025-08-22 22:16:20 +08:00
PYDT 8c5027df0f Delete Plugins/Filter/ProtectBoot.c 2025-08-22 21:16:55 +08:00
PYDT 0b7817d992 Add files via upload 2025-08-22 21:16:36 +08:00
PYDT ca6c9bfd05 Add files via upload 2025-08-22 05:31:46 +08:00
PYDT 0a1abd8040 Add files via upload 2025-08-22 05:27:25 +08:00
PYDT ed69a4fdf6 Add files via upload 2025-08-22 05:20:50 +08:00
PYDT 2ae19a6d4e Add files via upload 2025-08-22 05:08:52 +08:00
PYDT 404bc50dd4 Add files via upload 2025-08-22 04:56:09 +08:00
PYDT cada5eeeaf Add files via upload 2025-08-22 04:16:55 +08:00
PYDT 5e9bd40e8b Add files via upload 2025-08-22 03:22:40 +08:00
PYDT 0788bab0dd Add files via upload 2025-08-22 03:21:51 +08:00
PYDT 4e8e0dbea7 Add files via upload 2025-08-19 08:27:20 +08:00
PYDT b8c5279137 Add files via upload 2025-08-19 07:52:24 +08:00
PYDT a3255b1681 Add files via upload 2025-08-19 07:43:35 +08:00
PYDT f151379f8f Add files via upload 2025-08-18 22:49:35 +08:00
PYDT 7784544583 Add files via upload 2025-08-18 15:34:45 +08:00
PYDT f742937296 Add files via upload 2025-08-18 00:29:36 +08:00
PYDT 5fd9242291 Add files via upload 2025-08-17 15:30:24 +08:00
PYDT 9f343196a9 Add files via upload 2025-08-17 00:04:25 +08:00
PYDT 5468eded04 Add files via upload 2025-08-17 00:01:22 +08:00
PYDT 1b97758fc9 Add files via upload 2025-08-16 23:57:22 +08:00
PYDT b2e8e49447 Add files via upload 2025-08-16 23:56:03 +08:00
PYDT 3eec781139 Add files via upload 2025-08-16 15:37:39 +08:00
PYDT b6f1977fa0 Add files via upload 2025-08-16 00:02:23 +08:00
PYDT e0aa080f00 Add files via upload 2025-08-15 23:56:43 +08:00
PYDT 66aa76c836 Add files via upload 2025-08-15 23:55:49 +08:00
PYDT bb9444e1ae Update README_zh_CN.md 2025-08-15 19:19:35 +08:00
PYDT 6c9bfa7755 Update README_zh_TW.md 2025-08-15 19:18:26 +08:00
PYDT c4fa849290 Update README_zh_TW.md 2025-08-15 19:17:58 +08:00
PYDT 4807e2c410 Update README.md 2025-08-15 19:15:51 +08:00
PYDT 5388efdebc Update README.md 2025-08-15 19:13:15 +08:00
PYDT 1d656de7ba Add files via upload 2025-08-15 18:31:42 +08:00
PYDT eaa1d725a9 Update README_zh_TW.md 2025-08-15 18:01:43 +08:00
PYDT dea2150d63 Update README_zh_CN.md 2025-08-15 18:00:55 +08:00
PYDT 80f9d6cf75 Update README.md 2025-08-15 17:56:38 +08:00
PYDT 7430d38594 Update README.md 2025-08-15 17:54:45 +08:00
PYDT 6804e4af9f Update README_zh_CN.md 2025-08-15 16:38:23 +08:00
PYDT 138756c31d Update README_zh_TW.md 2025-08-15 16:37:50 +08:00
PYDT 349bc293c6 Update README.md 2025-08-15 16:37:17 +08:00
PYDT 2f87cd10f2 Update README.md 2025-08-15 16:30:41 +08:00
PYDT fc50eafcca Add files via upload 2025-08-15 16:13:36 +08:00
PYDT b48e20a727 Update README_zh_CN.md 2025-08-15 15:58:20 +08:00
PYDT ce7ed2e6eb Update README_zh_TW.md 2025-08-15 15:58:04 +08:00
PYDT 043d71e0ff Update README.md 2025-08-15 15:57:32 +08:00
PYDT 1855ae0e1d Delete Plugins/Filter/PYAS_Driver.sys 2025-08-15 15:48:29 +08:00
PYDT 77323a50df Add files via upload 2025-08-15 15:48:14 +08:00
PYDT 1c2f6b85ca Add files via upload 2025-08-15 15:47:36 +08:00
PYDT 2b5c478e4b Update and rename LICENSE.md to LICENSE.txt 2025-08-15 15:39:17 +08:00
PYDT 90fd83a81f Rename LICENSE.proprietary to LICENSE.md 2025-08-15 15:38:29 +08:00
PYDT 8fa2a166f3 Delete LICENSE 2025-08-15 15:36:54 +08:00
PYDT e8cc45ebe7 Update LICENSE.proprietary 2025-08-15 15:36:00 +08:00
PYDT eb87117b91 Rename LICENSE.md to LICENSE 2025-08-15 14:51:50 +08:00
PYDT 3799dfa1e8 Rename LICENSE.proprietary.md to LICENSE.proprietary 2025-08-15 14:51:37 +08:00
PYDT 163c7fd680 Rename LICENSE.proprietary to LICENSE.proprietary.md 2025-08-15 14:51:22 +08:00
PYDT 44ca762f69 Create LICENSE.proprietary 2025-08-15 14:50:15 +08:00
PYDT 8db311ba33 Add files via upload 2025-08-15 14:21:22 +08:00
PYDT 99d8604db4 Add files via upload 2025-08-15 11:14:46 +08:00
PYDT 90aa88daeb Update README.md 2025-08-14 20:31:29 +08:00
PYDT ae6eebad4d Update README_zh_CN.md 2025-08-14 20:30:39 +08:00
PYDT cd31ce1c38 Update README_zh_TW.md 2025-08-14 20:30:00 +08:00
PYDT ed3846e451 Update README_zh_TW.md 2025-08-14 20:28:02 +08:00
PYDT 0c79cf8e37 Update README_zh_CN.md 2025-08-14 20:25:36 +08:00
PYDT 81c7e16c0e Update README_zh_CN.md 2025-08-14 20:23:44 +08:00
PYDT 16c03095b8 Update README.md 2025-08-14 20:22:36 +08:00
PYDT b5dab3aa22 Update README_zh_CN.md 2025-08-14 20:21:11 +08:00
PYDT e4a3c871f8 Update README.md 2025-08-14 20:15:46 +08:00
PYDT a2fe947b08 Update README.md 2025-08-14 20:13:26 +08:00
PYDT 8df49ecfc1 Update README.md 2025-08-14 20:03:11 +08:00
PYDT 526b9bd4c7 Update README.md 2025-08-14 20:00:42 +08:00
PYDT c67ff3d936 Add files via upload 2025-08-14 14:21:47 +08:00
PYDT 417b17da98 Add files via upload 2025-08-13 16:35:12 +08:00
PYDT 4ca19a4eb0 Update README_zh_CN.md 2025-08-13 16:00:20 +08:00
PYDT e834dfb7ee Update README_zh_TW.md 2025-08-13 16:00:05 +08:00
PYDT 5e1ff74fb3 Update README.md 2025-08-13 15:59:50 +08:00
PYDT 7a1d0f2ca7 Delete Engine/Model/PYAS_Model_A1.onnx 2025-08-13 15:53:33 +08:00
PYDT b305522106 Delete Engine/Model/PYAS_Model.json 2025-08-13 15:53:15 +08:00
PYDT ff3495645a Add files via upload 2025-08-13 15:52:58 +08:00
PYDT ddc000321f Add files via upload 2025-08-13 15:41:32 +08:00
PYDT fe4e6af3b9 Add files via upload 2025-08-13 15:40:43 +08:00
PYDT a1a83cc32c Add files via upload 2025-08-13 15:39:56 +08:00
PYDT e37665218b Update README_zh_CN.md 2025-08-12 17:25:31 +08:00
PYDT 2a876f4136 Update README_zh_TW.md 2025-08-12 17:20:27 +08:00
PYDT 290d8e8a64 Update README_zh_TW.md 2025-08-12 17:19:09 +08:00
PYDT 121bce529f Update README_zh_TW.md 2025-08-12 17:17:17 +08:00
PYDT 1232d98573 Update README.md 2025-08-12 17:16:46 +08:00
PYDT 208d9150b0 Update README.md 2025-08-12 17:10:23 +08:00
PYDT 351bf7725e Update README.md 2025-08-12 17:07:56 +08:00
PYDT a547776766 Add files via upload 2025-08-12 17:06:02 +08:00
PYDT 86a8d20757 Add files via upload 2025-08-11 11:26:26 +08:00
PYDT 760c420f95 Delete Plugins/Filter directory 2025-08-11 11:26:15 +08:00
37 changed files with 10529 additions and 287275 deletions

No files matched your search

File renamed without changes.
-11
View File
@@ -1,11 +0,0 @@
{
"Suffix": [".com", ".exe", ".dll", ".sys", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".wsf"],
"Labels": ["Pefile/White", "Script/White", "Pefile/General", "Script/General"],
"Detect": ["Pefile/General", "Script/General"],
"Resize": [224, 224, 1, "nearest"],
"Values": 95
}
Binary file not shown.
+441
View File
@@ -0,0 +1,441 @@
import os, sys, math, time, datetime, pefile, hashlib
import ctypes, ctypes.wintypes, multiprocessing
try:
import orjson
JSON_DUMPS = lambda x: orjson.dumps(x).decode('utf-8')
JSON_LOADS = orjson.loads
HAS_ORJSON = True
except ImportError:
import json
JSON_DUMPS = json.dumps
JSON_LOADS = json.loads
HAS_ORJSON = False
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
BATCH_SIZE = 1000
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
MAX_WORKERS = os.cpu_count() - 2
TARGET_EXTENSIONS = {
'.exe', '.dll', '.sys', '.ocx', '.scr', '.efi', '.acm', '.ax', '.cpl', '.drv', '.com', '.mui', '.pyd'
}
####################################################################################################
class WINTRUST_FILE_INFO(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
("hFile", ctypes.wintypes.HANDLE),
("pgKnownSubject", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA_UNION(ctypes.Union):
_fields_ = [
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
("pCatalog", ctypes.wintypes.LPVOID),
("pBlob", ctypes.wintypes.LPVOID),
("pSgnr", ctypes.wintypes.LPVOID),
("pCert", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
("pSIPClientData", ctypes.wintypes.LPVOID),
("dwUIChoice", ctypes.wintypes.DWORD),
("fdwRevocationChecks", ctypes.wintypes.DWORD),
("dwUnionChoice", ctypes.wintypes.DWORD),
("u", WINTRUST_DATA_UNION),
("dwStateAction", ctypes.wintypes.DWORD),
("hWVTStateData", ctypes.wintypes.HANDLE),
("pwszURLReference", ctypes.wintypes.LPCWSTR),
("dwProvFlags", ctypes.wintypes.DWORD),
("dwUIContext", ctypes.wintypes.DWORD),
("pSignatureSettings", ctypes.wintypes.LPVOID)
]
class GUID(ctypes.Structure):
_fields_ = [
("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.c_ubyte * 8)
]
V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
####################################################################################################
def verify_signature(file_path):
if os.name != 'nt': return 0
try:
wintrust = ctypes.windll.wintrust
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
wintrust.WinVerifyTrust.restype = ctypes.wintypes.LONG
abs_path = os.path.abspath(file_path)
fi = WINTRUST_FILE_INFO()
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
fi.pcwszFilePath = abs_path
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
td.pSIPClientData = None
td.dwUIChoice = 2
td.fdwRevocationChecks = 0
td.dwUnionChoice = 1
td.u = wt_union
td.dwStateAction = 0
td.hWVTStateData = None
td.pwszURLReference = None
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return 1 if wintrust.WinVerifyTrust(None, ctypes.byref(V2_GUID), ctypes.byref(td)) == 0 else 0
except Exception:
return 0
####################################################################################################
class FeatureExtractor:
@staticmethod
def _safe_float(val):
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data: return 0.0
import numpy as np
arr = np.frombuffer(data, dtype=np.uint8)
counts = np.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-np.sum(probs * np.log2(probs)))
@classmethod
def extract(cls, file_bytes, file_path, fsize):
if fsize == 0: return None
base, dlls, apis, pe = {}, set(), set(), None
try:
pe = pefile.PE(data=file_bytes, fast_load=True)
base['TrustSigned'] = verify_signature(file_path)
base['FileEntropy'] = cls._calc_entropy(file_bytes)
base['FileSize'] = fsize
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
base['NumberOfSections'] = getattr(fh, 'NumberOfSections', 0)
base['TimeDateStamp'] = getattr(fh, 'TimeDateStamp', 0)
base['PointerToSymbolTable'] = getattr(fh, 'PointerToSymbolTable', 0)
base['NumberOfSymbols'] = getattr(fh, 'NumberOfSymbols', 0)
base['SizeOfOptionalHeader'] = getattr(fh, 'SizeOfOptionalHeader', 0)
base['Characteristics'] = getattr(fh, 'Characteristics', 0)
curr_ts = datetime.datetime.utcnow().timestamp()
base['HasInvalidTimestamp'] = 1.0 if (base['TimeDateStamp'] < 631152000 or base['TimeDateStamp'] > curr_ts + 2592000) else 0.0
base['FileTimeException'] = 1.0 if base['TimeDateStamp'] == 0 else 0.0
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fields = ['Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode', 'SizeOfInitializedData', 'SizeOfUninitializedData', 'AddressOfEntryPoint', 'BaseOfCode', 'ImageBase', 'SectionAlignment', 'FileAlignment', 'MajorOperatingSystemVersion', 'MinorOperatingSystemVersion', 'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion', 'MinorSubsystemVersion', 'SizeOfImage', 'SizeOfHeaders', 'CheckSum', 'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit', 'SizeOfHeapReserve', 'SizeOfHeapCommit', 'LoaderFlags', 'NumberOfRvaAndSizes']
for f in fields: base[f] = getattr(op, f, 0)
base['IsDriver'] = 1.0 if base.get('Subsystem') == 1 else 0.0
if hasattr(op, 'DATA_DIRECTORY'):
for i, directory in enumerate(op.DATA_DIRECTORY):
base[f'DataDirectory_{i}_Size'] = directory.Size
base[f'DataDirectory_{i}_VA'] = directory.VirtualAddress
exc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_EXCEPTION', 3)
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies, raw_sizes, v_sizes = [], [], []
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
except Exception:
s_entropy = 0.0
entropies.append(s_entropy)
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
for flag in char_flags:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
base['SectionMeanEntropy'] = sum(entropies) / len(entropies) if entropies else 0.0
base['SectionMaxRawSize'] = max(raw_sizes) if raw_sizes else 0.0
base['SectionMinRawSize'] = min(raw_sizes) if raw_sizes else 0.0
base['SectionMeanRawSize'] = sum(raw_sizes) / len(raw_sizes) if raw_sizes else 0.0
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = exec_sec
base['WritableSections'] = write_sec
base['ReadableSections'] = read_sec
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
base[f'{key}Length'] = 0.0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
try:
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS']
])
except Exception: pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
base['IsDebug'] = 1.0 if flags & 0x1 else 0.0
base['IsPreRelease'] = 1.0 if flags & 0x2 else 0.0
base['IsPatched'] = 1.0 if flags & 0x4 else 0.0
base['IsPrivateBuild'] = 1.0 if flags & 0x8 else 0.0
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = len(pe.DIRECTORY_ENTRY_IMPORT)
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception: pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try: apis.add(imp.name.decode('ascii', 'ignore'))
except Exception: pass
base['ImportFunctionCount'] = func_count
base['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = icon_count
else:
base['IconCount'] = 0.0
base['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
for k in base: base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception: return None
finally:
if pe: pe.close()
####################################################################################################
def load_existing_hashes():
hashes = set()
if os.path.exists(JSONL_PATH):
with open(JSONL_PATH, 'r', encoding='utf-8') as f:
for line in f:
try:
data = JSON_LOADS(line)
if 'FileHash' in data: hashes.add(data['FileHash'])
except Exception: pass
return hashes
####################################################################################################
GLOBAL_HASHES = set()
def worker_init(shared_hashes):
global GLOBAL_HASHES
GLOBAL_HASHES = shared_hashes
def _extract_worker_wrapper(args):
return _extract_worker(*args)
def _extract_worker(file_path, label):
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > MAX_FILE_SIZE:
return 'error', None
with open(file_path, "rb") as f:
file_bytes = f.read()
sha256 = hashlib.sha256(file_bytes).hexdigest()
if sha256 in GLOBAL_HASHES:
return 'skipped', None
res = FeatureExtractor.extract(file_bytes, file_path, fsize)
if res:
final_res = {"Label": label, "FileHash": sha256, **res}
return 'success', (sha256, JSON_DUMPS(final_res) + "\n")
return 'error', None
except Exception:
return 'error', None
####################################################################################################
def scan_and_save(path, label, existing_hashes):
print(f"\n[*] Scanning path: {path} (Label={label})")
files = [path] if os.path.isfile(path) else [os.path.join(r, f) for r, _, fs in os.walk(path) for f in fs if os.path.splitext(f)[1].lower() in TARGET_EXTENSIONS]
total = len(files)
if total == 0:
print("[-] No valid PE files found.")
return
print(f"[*] Found {total} files. Starting {MAX_WORKERS}-process extraction...\n")
if HAS_ORJSON:
print("[+] orjson acceleration enabled.")
count, errors, skipped, processed_files = 0, 0, 0, 0
start_time = time.time()
batch_buffer = []
pool = multiprocessing.Pool(processes=MAX_WORKERS, initializer=worker_init, initargs=(existing_hashes,))
tasks = [(fpath, label) for fpath in files]
try:
for status, data in pool.imap_unordered(_extract_worker_wrapper, tasks):
processed_files += 1
if status == 'error':
errors += 1
elif status == 'skipped':
skipped += 1
elif status == 'success':
sha256, json_str = data
if sha256 not in existing_hashes:
existing_hashes.add(sha256)
batch_buffer.append(json_str)
count += 1
else:
skipped += 1
if len(batch_buffer) >= BATCH_SIZE:
with open(JSONL_PATH, 'a', encoding='utf-8') as f_out:
f_out.writelines(batch_buffer)
batch_buffer.clear()
elapsed = time.time() - start_time
rate = processed_files / elapsed if elapsed > 0 else 0
sys.stdout.write(f"\r[{processed_files}/{total}] Added: {count} | Skipped: {skipped} | Errors: {errors} | Speed: {rate:.1f} files/s")
sys.stdout.flush()
pool.close()
pool.join()
except KeyboardInterrupt:
print("\n\n[-] User aborted. Force terminating all child processes...")
pool.terminate()
pool.join()
except Exception as e:
print(f"\n[-] Critical error: {e}")
pool.terminate()
pool.join()
finally:
if batch_buffer:
try:
with open(JSONL_PATH, 'a', encoding='utf-8') as f_out:
f_out.writelines(batch_buffer)
except Exception as save_err:
print(f"\n[-] Failed to save remaining buffer: {save_err}")
print(f"\n\n[+] Scan complete. Total added: {count}, Total skipped: {skipped}")
####################################################################################################
if __name__ == "__main__":
multiprocessing.freeze_support()
print("\n---------------- PE Dataset Builder v4.0 ----------------\n")
existing_hashes = load_existing_hashes()
print(f"[+] Found {len(existing_hashes)} existing hashes.\n")
try:
while True:
print("-" * 57)
target_path = input("\n[*] Enter path (file or directory): ").strip().strip('"').strip("'")
if target_path.lower() in ['exit', 'q']: break
if not target_path or not os.path.exists(target_path):
print("[-] Invalid path.")
continue
while True:
label_input = input("[*] Enter label (0=Safe, 1=Malware): ").strip()
if label_input.lower() in ['exit', 'q']: sys.exit()
if label_input in ['0', '1']: break
print("[-] Please enter 0 or 1.")
scan_and_save(target_path, int(label_input), existing_hashes)
except KeyboardInterrupt:
print("\n\n[-] Aborted.")
+249
View File
@@ -0,0 +1,249 @@
import os, sys, time, json, onnxmltools
import numpy as np
import pandas as pd
import lightgbm as lgb
import scipy.sparse as sp
from collections import defaultdict
from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 256,
'feature_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 20,
'lambda_l1': 0.01,
'lambda_l2': 0.01,
}
####################################################################################################
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
dll_counts = defaultdict(int)
api_counts = defaultdict(int)
num_lines = 0
with open(jsonl_path, 'r') as f:
for line in f:
try:
data = json.loads(line)
base_keys.update(data.get('Base', {}).keys())
for d in data.get('DLLs', []):
dll_counts[d] += 1
for a in data.get('APIs', []):
api_counts[a] += 1
num_lines += 1
except Exception:
continue
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = sorted([f"Dll_{k}" for k, v in dll_counts.items() if v >= 100])
api_schema = sorted([f"Api_{k}" for k, v in api_counts.items() if v >= 100])
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {num_lines})...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
feature_idx = {feat: i for i, feat in enumerate(final_features)}
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
with open(jsonl_path, 'r') as f:
row_idx = 0
for line in f:
try:
data = json.loads(line)
y[row_idx] = data.get('Label', 0)
for k, v in data.get('Base', {}).items():
idx = feature_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
for d in data.get('DLLs', []):
idx = feature_idx.get(f"Dll_{d}")
if idx is not None:
rows.append(row_idx)
cols.append(idx)
vals.append(1.0)
for a in data.get('APIs', []):
idx = feature_idx.get(f"Api_{a}")
if idx is not None:
rows.append(row_idx)
cols.append(idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
return X, pd.Series(y), final_features
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.005
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={final_pos_weight:.4f}")
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
print("\n[*] Starting LightGBM training...")
start_time = time.time()
model = lgb.train(
params,
train_data,
valid_sets=[valid_data],
num_boost_round=500,
callbacks=[
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
####################################################################################################
def export_onnx_model(model, feature_names):
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
try:
initial_type = [('float_input', FloatTensorType([None, len(feature_names)]))]
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type)
onnxmltools.utils.save_model(onnx_model, ONNX_FILE)
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export successful. Feature order saved to {FEATURE_FILE}")
except Exception as e:
print(f"[-] ONNX export failed: {e}")
def evaluate_and_save(model, X_test, y_test):
print("\n------------------- Evaluation Report -------------------\n")
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
y_pred = (y_prob > 0.5).astype(int)
acc = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
feature_imp = pd.DataFrame(sorted(zip(importance, feature_names), reverse=True), columns=['Value', 'Feature'])
print(feature_imp.head(20).to_string(index=False))
print("\n---------------------------------------------------------\n")
model.save_model(MODEL_FILE)
print(f"[+] Native model saved to: {os.path.abspath(MODEL_FILE)}")
export_onnx_model(model, feature_names)
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Filtered DLL features: {len(dll_schema)}")
print(f"[*] Filtered API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] System exception: {e}")
+249
View File
@@ -0,0 +1,249 @@
import os, sys, time, json, onnxmltools
import numpy as np
import pandas as pd
import lightgbm as lgb
import scipy.sparse as sp
from collections import defaultdict
from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_T1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
EXCLUDE_FEATURES = {
'label', 'filehash',
}
LGBM_PARAMS = {
'objective': 'binary',
'metric': ['binary_logloss', 'auc'],
'boosting_type': 'gbdt',
'num_leaves': 64,
'feature_fraction': 0.8,
'bagging_fraction': 1.0,
'bagging_freq': 5,
'verbose': -1,
'seed': RANDOM_SEED,
'n_jobs': -1,
'max_depth': -1,
'min_data_in_leaf': 20,
'lambda_l1': 0.01,
'lambda_l2': 0.01,
}
####################################################################################################
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
dll_counts = defaultdict(int)
api_counts = defaultdict(int)
num_lines = 0
with open(jsonl_path, 'r') as f:
for line in f:
try:
data = json.loads(line)
base_keys.update(data.get('Base', {}).keys())
for d in data.get('DLLs', []):
dll_counts[d] += 1
for a in data.get('APIs', []):
api_counts[a] += 1
num_lines += 1
except Exception:
continue
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = sorted([f"Dll_{k}" for k, v in dll_counts.items() if v >= 100])
api_schema = sorted([f"Api_{k}" for k, v in api_counts.items() if v >= 100])
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {num_lines})...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
feature_idx = {feat: i for i, feat in enumerate(final_features)}
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
with open(jsonl_path, 'r') as f:
row_idx = 0
for line in f:
try:
data = json.loads(line)
y[row_idx] = data.get('Label', 0)
for k, v in data.get('Base', {}).items():
idx = feature_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
for d in data.get('DLLs', []):
idx = feature_idx.get(f"Dll_{d}")
if idx is not None:
rows.append(row_idx)
cols.append(idx)
vals.append(1.0)
for a in data.get('APIs', []):
idx = feature_idx.get(f"Api_{a}")
if idx is not None:
rows.append(row_idx)
cols.append(idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
return X, pd.Series(y), final_features
####################################################################################################
def _calculate_dynamic_lr(current_iter: int) -> float:
return max(0.01, 0.1 - (current_iter // 80) * 0.01)
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.001
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
print(f"[*] Weight correction: Base={base_weight:.4f}, Final={final_pos_weight:.4f}")
train_data = lgb.Dataset(X_train, label=y_train, feature_name=feature_names)
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data, feature_name=feature_names)
params = LGBM_PARAMS.copy()
params['scale_pos_weight'] = final_pos_weight
print("\n[*] Starting LightGBM training...")
start_time = time.time()
model = lgb.train(
params,
train_data,
valid_sets=[valid_data],
num_boost_round=500,
callbacks=[
lgb.log_evaluation(period=50),
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
]
)
elapsed = time.time() - start_time
print(f"\n[+] Training complete, time elapsed {elapsed:.2f} seconds")
return model, X_test, y_test
####################################################################################################
def export_onnx_model(model, feature_names):
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
try:
initial_type = [('float_input', FloatTensorType([None, len(feature_names)]))]
onnx_model = onnxmltools.convert_lightgbm(model, initial_types=initial_type)
onnxmltools.utils.save_model(onnx_model, ONNX_FILE)
with open(FEATURE_FILE, 'w') as f:
json.dump(feature_names, f)
print(f"[+] ONNX export successful. Feature order saved to {FEATURE_FILE}")
except Exception as e:
print(f"[-] ONNX export failed: {e}")
def evaluate_and_save(model, X_test, y_test):
print("\n------------------- Evaluation Report -------------------\n")
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
y_pred = (y_prob > 0.5).astype(int)
acc = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_prob)
tp = np.sum((y_test == 1) & (y_pred == 1))
fn = np.sum((y_test == 1) & (y_pred == 0))
tn = np.sum((y_test == 0) & (y_pred == 0))
fp = np.sum((y_test == 0) & (y_pred == 1))
total_malware = tp + fn
total_safe = tn + fp
detection_rate = (tp / total_malware * 100) if total_malware > 0 else 0.0
fpr = (fp / total_safe * 100) if total_safe > 0 else 0.0
print(f"Accuracy : {acc:.6f}")
print(f"ROC AUC : {auc:.6f}\n")
print(f"True Positive (Class 1) : {detection_rate:.3f}% ({tp}/{total_malware})")
print(f"False Positive (Class 0) : {fpr:.3f}% ({fp}/{total_safe})")
print("\n---------------------------------------------------------\n")
print("[*] Top 20 Features:")
feature_names = model.feature_name()
importance = model.feature_importance(importance_type='gain')
feature_imp = pd.DataFrame(sorted(zip(importance, feature_names), reverse=True), columns=['Value', 'Feature'])
print(feature_imp.head(20).to_string(index=False))
print("\n---------------------------------------------------------\n")
model.save_model(MODEL_FILE)
print(f"[+] Native model saved to: {os.path.abspath(MODEL_FILE)}")
export_onnx_model(model, feature_names)
####################################################################################################
if __name__ == "__main__":
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
print(f"[-] Error: Feature file {JSONL_PATH} not found.")
sys.exit(1)
base_schema, dll_schema, api_schema, num_lines = analyze_schema(JSONL_PATH)
if num_lines == 0:
print("[-] Error: Feature file is empty.")
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Filtered DLL features: {len(dll_schema)}")
print(f"[*] Filtered API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
print(f"\n[-] System exception: {e}")
File diff suppressed because it is too large. Load diff
+841
View File
@@ -0,0 +1,841 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
outline: none;
}
:root {
--bg-window: rgb(245, 245, 247);
--bg-nav: rgb(255, 255, 255);
--bg-panel: rgb(255, 255, 255);
--bg-hover: rgba(0, 0, 0, 0.05);
--text-primary: rgb(29, 29, 31);
--text-secondary: rgb(134, 134, 139);
--border-color: rgb(210, 210, 215);
--accent-color: rgb(0, 122, 255);
--accent-alpha: rgba(0, 122, 255, 0.1);
--accent-shadow: rgba(0, 122, 255, 0.2);
--accent-hover: rgba(0, 122, 255, 0.3);
--shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.04);
}
body {
background-color: transparent;
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-primary);
}
.app-container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
background-color: var(--bg-window);
}
.navigate-window {
height: 44px;
background-color: var(--bg-nav);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px 0 20px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.navigate-title {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.2px;
}
.window-controls {
display: flex;
gap: 4px;
-webkit-app-region: no-drag;
}
.ctrl-btn {
width: 46px;
height: 32px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.15s;
color: var(--text-secondary);
}
.ctrl-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
#close_button:hover { background-color: #ff3b30; color: white; }
.theme-svg {
width: 16px;
height: 16px;
}
.main-layout {
display: flex;
height: calc(100% - 44px);
}
.options-window {
width: 64px;
background-color: var(--bg-nav);
display: flex;
flex-direction: column;
align-items: center;
padding: 16px 0;
gap: 12px;
border-right: 1px solid var(--border-color);
transition: background-color 0.3s;
}
.nav-btn {
width: 44px;
height: 44px;
border: none;
background: transparent;
border-radius: 10px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
color: var(--text-secondary);
transition: all 0.2s;
position: relative;
}
.nav-btn svg { width: 22px; height: 22px; }
.nav-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.nav-btn.active { background-color: var(--accent-alpha); color: var(--accent-color); }
.spacer { flex-grow: 1; }
.content-window {
flex-grow: 1;
position: relative;
overflow: hidden;
}
.page {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 28px 32px;
opacity: 0;
visibility: hidden;
transform: translateY(8px);
transition: opacity 0.25s ease, transform 0.25s ease;
display: flex;
flex-direction: column;
}
.page.active {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.state-widget {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 16px;
color: var(--accent-color);
}
.state-title {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.5px;
}
.log-widget {
flex-grow: 1;
margin-top: 20px;
}
.license-widget {
flex-grow: 1;
}
.log-text, .license-text {
width: 100%;
height: 100%;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
color: var(--text-primary);
resize: none;
padding: 16px;
font-size: 13px;
line-height: 1.6;
white-space: pre;
overflow-x: auto;
}
.progress-widget {
height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
gap: 16px;
}
.header-group {
flex: 1;
min-width: 0;
}
.header-group h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.header-group p {
font-size: 14px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
overflow-x: auto;
padding-bottom: 4px;
}
.modern-action-group {
display: flex;
gap: 12px;
align-items: center;
flex-shrink: 0;
}
.modern-select {
appearance: none;
}
.modern-select:hover, .modern-select:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.2);
}
.modern-btn {
padding: 8px 20px;
border: 1px solid var(--border-color);
border-radius: 10px;
background-color: transparent;
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.modern-btn:hover { background-color: var(--bg-hover); }
.primary-btn {
background-color: var(--accent-color);
color: white;
border: none;
box-shadow: 0 2px 4px var(--accent-shadow);
}
.primary-btn:hover {
box-shadow: 0 4px 8px var(--accent-hover);
transform: translateY(-1px);
background-color: var(--accent-color);
opacity: 0.95;
}
.primary-btn:active {
transform: translateY(0);
opacity: 0.8;
}
.danger-btn { color: #ff3b30; border-color: #ff3b30; }
.danger-btn:hover { background-color: rgba(255, 59, 48, 0.1); }
.hidden { display: none !important; }
.scan-content {
flex-grow: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.virus-widget {
width: 100%;
flex-grow: 1;
min-height: 0;
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.virus-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.list-widget {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
padding-right: 12px;
gap: 12px;
}
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 18px 24px;
border-radius: 16px;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
transition: background-color 0.2s;
gap: 16px;
}
.item-info {
flex: 1;
min-width: 0;
overflow-x: auto;
padding-bottom: 2px;
}
.list-item[data-target]:hover, .list-item[data-action]:hover {
background-color: var(--bg-hover);
}
.item-info h2 {
font-size: 16px;
font-weight: 600;
letter-spacing: -0.2px;
white-space: nowrap;
}
.item-info p {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
white-space: nowrap;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: var(--border-color);
transition: .3s;
border-radius: 28px;
}
.slider:before {
position: absolute;
content: "";
height: 24px; width: 24px;
left: 2px; bottom: 2px;
background-color: white;
transition: .3s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.toggle-switch input:checked + .slider {
background-color: var(--accent-color);
}
.toggle-switch input:checked + .slider:before {
transform: translateX(22px);
}
.icon-action-btn {
width: 32px; height: 32px;
border-radius: 8px;
border: none;
background-color: transparent;
color: var(--text-secondary);
cursor: pointer;
display: flex; justify-content: center; align-items: center;
transition: background-color 0.2s;
}
.icon-action-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-action-btn svg { width: 20px; height: 20px; }
.sub-header-toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
width: 100%;
}
.sub-header-toolbar h2 {
font-size: 24px;
font-weight: 600;
letter-spacing: -0.5px;
white-space: nowrap;
}
.icon-btn {
background: transparent;
border: none;
color: var(--text-secondary);
cursor: pointer;
padding: 6px;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
transition: background-color 0.2s;
}
.icon-btn:hover { background-color: var(--bg-hover); color: var(--text-primary); }
.icon-btn svg { width: 24px; height: 24px; }
.search-box {
display: flex;
align-items: center;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
padding: 0 14px;
width: 200px;
transition: border-color 0.2s, box-shadow 0.2s;
}
.search-box:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.search-box svg {
width: 18px; height: 18px;
color: var(--text-secondary);
}
.search-box input {
border: none;
background: transparent;
color: var(--text-primary);
padding: 10px;
outline: none;
width: 100%;
font-size: 14px;
}
.flex-grow {
flex-grow: 1;
min-height: 0;
}
.manage-widget {
border: 1px solid var(--border-color);
border-radius: 12px;
background-color: var(--bg-panel);
overflow-y: auto;
overflow-x: auto;
}
.manage-list {
list-style: none;
padding: 0;
margin: 0;
min-width: max-content;
}
.manage-list-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
transition: background-color 0.2s;
}
.manage-list-item:last-child {
border-bottom: none;
}
.manage-list-item:hover {
background-color: var(--bg-hover);
}
.manage-list-item input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.manage-list-item-content {
flex-grow: 1;
min-width: 0;
font-size: 14px;
color: var(--text-primary);
white-space: nowrap;
}
.custom-select-wrapper {
position: relative;
min-width: 100px;
display: inline-grid;
vertical-align: middle;
user-select: none;
}
select.hidden + .custom-select-wrapper {
display: none !important;
}
.custom-select-trigger {
grid-area: 1 / 1;
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
border-radius: 10px;
border: 1px solid var(--border-color);
background-color: var(--bg-panel);
color: var(--text-primary);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
gap: 12px;
box-sizing: border-box;
}
.custom-select-phantom {
grid-area: 1 / 1;
visibility: hidden;
pointer-events: none;
display: grid;
padding: 8px 16px;
font-size: 14px;
font-weight: 500;
box-sizing: border-box;
}
.custom-select-phantom span {
grid-area: 1 / 1;
white-space: nowrap;
padding-right: 28px;
}
.custom-select-wrapper.open .custom-select-trigger,
.custom-select-trigger:hover {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px var(--accent-shadow);
}
.custom-select-icon {
display: flex;
align-items: center;
color: var(--text-secondary);
transition: transform 0.3s ease;
}
.custom-select-wrapper.open .custom-select-icon {
transform: rotate(180deg);
}
.custom-select-icon svg { width: 16px; height: 16px; }
.custom-select-options {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 100%;
width: max-content;
white-space: nowrap;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 10px;
box-shadow: var(--shadow-sm);
z-index: 100;
list-style: none;
margin: 0;
max-height: 0;
padding: 0 6px;
border-width: 0;
overflow: hidden;
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0.2s, max-height 0s 0.2s, padding 0s 0.2s, border-width 0s 0.2s, box-shadow 0s 0.2s;
}
.custom-select-wrapper.open .custom-select-options {
max-height: 260px;
padding: 6px;
border-width: 1px;
overflow-y: auto;
opacity: 1;
visibility: visible;
transform: translateY(0);
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s 0s, max-height 0s 0s, padding 0s 0s, border-width 0s 0s, box-shadow 0s 0s;
}
.custom-select-option {
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
color: var(--text-primary);
transition: background-color 0.2s, color 0.2s;
text-align: center;
}
.custom-select-option:hover {
background-color: var(--bg-hover);
}
.custom-select-option.selected {
background-color: var(--accent-alpha);
color: var(--accent-color);
font-weight: 600;
}
.home-top-widget {
display: flex;
align-items: center;
height: 200px;
gap: 24px;
width: 100%;
}
.quick-actions {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
height: 100%;
justify-content: center;
}
#loading_overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #e0e0e0;
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
transition: opacity 0.4s ease, visibility 0.4s ease;
}
#loading_overlay.fade-out {
opacity: 0;
visibility: hidden;
}
.spinner {
animation: rotate 2s linear infinite;
width: 50px;
height: 50px;
}
.spinner .path {
stroke: #86868b;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
@keyframes dash {
0% { stroke-dasharray: 1, 150; stroke-dashoffset: 0; }
50% { stroke-dasharray: 90, 150; stroke-dashoffset: -35; }
100% { stroke-dasharray: 90, 150; stroke-dashoffset: -124; }
}
.app-container.fade-in {
opacity: 1 !important;
}
.manage-list-header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-panel);
font-weight: 600;
font-size: 13px;
color: var(--text-secondary);
position: sticky;
top: 0;
z-index: 10;
}
.manage-list-header input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--accent-color);
cursor: pointer;
flex-shrink: 0;
}
.header-cols-container, .manage-list-item-content {
display: flex;
flex-grow: 1;
min-width: 0;
gap: 16px;
align-items: center;
}
.col-header {
position: relative;
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
border-right: 1px solid var(--border-color);
background-color: var(--bg-panel);
user-select: none;
-webkit-user-select: none;
}
.col-header:last-child {
border-right: none;
}
.header-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 1;
}
.manage-list-header, .col-header, .col-resizer {
user-select: none;
-webkit-user-select: none;
}
.col-resizer {
position: absolute;
right: -5px;
top: 0;
bottom: 0;
width: 10px;
cursor: col-resize;
z-index: 10;
background-color: transparent !important;
}
.col-resizer::after {
content: "";
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background-color: transparent;
transition: background-color 0.2s;
}
.col-resizer:hover::after,
.col-resizer.resizing::after {
background-color: var(--text-secondary) !important;
opacity: 0.6;
}
body.resizing-active,
body.resizing-active * {
user-select: none !important;
-webkit-user-select: none !important;
cursor: col-resize !important;
}
.col-resizer:hover {
background-color: var(--accent-color);
opacity: 0.5;
}
.col-header:hover {
color: var(--text-primary);
}
.sort-icon {
font-size: 10px;
margin-left: 4px;
color: var(--accent-color);
width: 12px;
}
.row-col {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
color: var(--text-primary);
}
.custom-context-menu {
position: absolute;
z-index: 10000;
background-color: var(--bg-panel);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
padding: 4px;
display: none;
min-width: 160px;
}
.custom-context-menu.show {
display: block;
}
.custom-context-menu-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
color: var(--text-primary);
transition: background-color 0.2s;
}
.custom-context-menu-item:hover {
background-color: var(--bg-hover);
}
.custom-context-menu-item svg {
width: 16px;
height: 16px;
color: var(--text-secondary);
}
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); }
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

File diff suppressed because it is too large. Load diff
+909
View File
@@ -0,0 +1,909 @@
document.addEventListener('DOMContentLoaded', () => {
const { dict, langMap } = window.AppI18n;
const appState = {
lang: "english_switch",
scanning: false,
firstLaunch: false,
taskmgrTimer: null,
taskmgrActive: false,
virusMap: new Map(),
virusResults: [],
junkList: []
};
const reverseI18nMap = Object.entries(dict["traditional_switch"]).reduce((acc, [key, val]) => {
acc[val] = key;
return acc;
}, {});
const initI18nKeys = () => {
document.querySelectorAll("[data-i18n]").forEach(el => {
let key = el.getAttribute('data-i18n');
if (!key) {
const text = el.textContent.trim();
key = reverseI18nMap[text] || text;
el.setAttribute('data-i18n', key);
}
el.dataset.originText = el.textContent.trim();
});
document.querySelectorAll("[data-i18n-placeholder]").forEach(el => {
let key = el.getAttribute('data-i18n-placeholder');
if (reverseI18nMap[key]) {
el.setAttribute('data-i18n-placeholder', reverseI18nMap[key]);
}
el.dataset.originPlaceholder = el.getAttribute('placeholder');
});
document.querySelectorAll("option[data-i18n]").forEach(el => {
let key = el.getAttribute('data-i18n');
if (!key) {
const text = el.textContent.trim();
key = reverseI18nMap[text] || text;
el.setAttribute('data-i18n', key);
}
});
};
document.addEventListener('click', () => {
document.querySelectorAll('.custom-select-wrapper').forEach(w => w.classList.remove('open'));
});
const getMsg = (key) => (dict[appState.lang] || dict["english_switch"])[key] || key;
const formatMsg = (key, ...args) => {
let msg = getMsg(key);
args.forEach((arg, i) => msg = msg.split(`{${i}}`).join(arg));
return msg;
};
const escapeHtml = (unsafe) => {
if (typeof unsafe !== 'string') return unsafe;
return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
};
const themes = {
"white_switch": { "--bg-window": "245, 245, 247", "--bg-nav": "255, 255, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,0,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"210, 210, 215", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" },
"black_switch": { "--bg-window": "0, 0, 0", "--bg-nav": "28, 28, 30", "--bg-panel": "28, 28, 30", "--bg-hover": "rgba(255,255,255,0.1)", "--text-primary": "245, 245, 247", "--text-secondary": "134, 134, 139", "--border-color":"44, 44, 46", "--accent-color":"10, 132, 255", "--accent-alpha":"rgba(10, 132, 255, 0.1)", "--accent-shadow":"rgba(10, 132, 255, 0.2)", "--accent-hover":"rgba(10, 132, 255, 0.3)" },
"red_switch": { "--bg-window": "253, 246, 246", "--bg-nav": "255, 240, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,59,48,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 220, 220", "--accent-color":"255, 59, 48", "--accent-alpha":"rgba(255, 59, 48, 0.1)", "--accent-shadow":"rgba(255, 59, 48, 0.2)", "--accent-hover":"rgba(255, 59, 48, 0.3)" },
"yellow_switch": { "--bg-window": "254, 251, 243", "--bg-nav": "255, 248, 230", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(255,149,0,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"250, 240, 215", "--accent-color":"255, 149, 0", "--accent-alpha":"rgba(255, 149, 0, 0.1)", "--accent-shadow":"rgba(255, 149, 0, 0.2)", "--accent-hover":"rgba(255, 149, 0, 0.3)" },
"green_switch": { "--bg-window": "246, 253, 248", "--bg-nav": "235, 250, 240", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(52,199,89,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 240, 225", "--accent-color":"52, 199, 89", "--accent-alpha":"rgba(52, 199, 89, 0.1)", "--accent-shadow":"rgba(52, 199, 89, 0.2)", "--accent-hover":"rgba(52, 199, 89, 0.3)" },
"blue_switch": { "--bg-window": "246, 249, 253", "--bg-nav": "235, 244, 255", "--bg-panel": "255, 255, 255", "--bg-hover": "rgba(0,122,255,0.05)", "--text-primary": "29, 29, 31", "--text-secondary": "134, 134, 139", "--border-color":"220, 230, 250", "--accent-color":"0, 122, 255", "--accent-alpha":"rgba(0, 122, 255, 0.1)", "--accent-shadow":"rgba(0, 122, 255, 0.2)", "--accent-hover":"rgba(0, 122, 255, 0.3)" }
};
const cols = {
log: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'level', label: 'col_type', flex: 0.8}, {key: 'action', label: 'col_func', flex: 1.2, isI18n: true}, {key: 'source', label: 'col_path', flex: 3}],
process: [{key: 'name', label: 'col_name', flex: 1.5}, {key: 'pid', label: 'col_pid', flex: 0.5}, {key: 'path', label: 'col_path', flex: 3}],
virus: [{key: 'label', label: 'col_type', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
pathOnly: [{key: 'path', label: 'col_path', flex: 1}],
junk: [{key: 'path', label: 'col_path', flex: 3}, {key: 'sizeStr', label: 'col_size', flex: 1}],
popup: [{key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}]
};
const buildCustomSelectElement = (select) => {
select.style.display = 'none';
const wrapper = document.createElement('div');
wrapper.className = 'custom-select-wrapper';
const selectedOpt = select.options[select.selectedIndex];
const triggerKey = selectedOpt ? selectedOpt.getAttribute('data-i18n') : '';
const triggerTextContent = selectedOpt ? getMsg(triggerKey || selectedOpt.textContent) : '';
const triggerHTML = `
<div class="custom-select-trigger">
<span class="custom-select-text" ${triggerKey ? `data-i18n="${triggerKey}"` : ''}>${triggerTextContent}</span>
<div class="custom-select-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></div>
</div>`;
wrapper.innerHTML = triggerHTML;
const optionsContainer = document.createElement('ul');
optionsContainer.className = 'custom-select-options';
const phantom = document.createElement('div');
phantom.className = 'custom-select-phantom';
phantom.setAttribute('aria-hidden', 'true');
Array.from(select.options).forEach(option => {
if (option.disabled) return;
const optKey = option.getAttribute('data-i18n');
const optText = optKey ? getMsg(optKey) : option.textContent;
const li = document.createElement('li');
li.className = `custom-select-option ${option.selected ? 'selected' : ''}`;
li.textContent = optText;
li.dataset.value = option.value;
if (optKey) li.setAttribute('data-i18n', optKey);
li.addEventListener('click', (e) => {
e.stopPropagation();
select.value = option.value;
const triggerText = wrapper.querySelector('.custom-select-text');
triggerText.textContent = li.textContent;
if (optKey) triggerText.setAttribute('data-i18n', optKey);
wrapper.classList.remove('open');
optionsContainer.querySelectorAll('.custom-select-option').forEach(el => el.classList.remove('selected'));
li.classList.add('selected');
select.dispatchEvent(new Event('change'));
});
optionsContainer.appendChild(li);
const phantomSpan = document.createElement('span');
phantomSpan.textContent = optText;
if (optKey) phantomSpan.setAttribute('data-i18n', optKey);
phantom.appendChild(phantomSpan);
});
wrapper.appendChild(phantom);
wrapper.appendChild(optionsContainer);
select.parentNode.insertBefore(wrapper, select.nextSibling);
const trigger = wrapper.querySelector('.custom-select-trigger');
trigger.addEventListener('click', (e) => {
e.stopPropagation();
const isOpen = wrapper.classList.contains('open');
document.querySelectorAll('.custom-select-wrapper').forEach(w => w.classList.remove('open'));
if (!isOpen) wrapper.classList.add('open');
});
};
const updateCustomSelectUI = (selectId, val) => {
const select = document.getElementById(selectId);
if (!select) return;
select.value = val;
const wrapper = select.nextElementSibling;
if (wrapper && wrapper.classList.contains('custom-select-wrapper')) {
const triggerText = wrapper.querySelector('.custom-select-text');
const selectedOpt = select.options[select.selectedIndex];
if (selectedOpt) {
const optKey = selectedOpt.getAttribute('data-i18n');
triggerText.textContent = optKey ? getMsg(optKey) : selectedOpt.textContent;
if (optKey) triggerText.setAttribute('data-i18n', optKey);
}
wrapper.querySelectorAll('.custom-select-option').forEach(el => {
el.classList.toggle('selected', el.dataset.value === val);
});
}
};
const translateText = (lang) => {
appState.lang = lang;
document.querySelectorAll("[data-i18n]").forEach(el => {
const key = el.getAttribute('data-i18n');
if (key) el.textContent = getMsg(key);
});
document.querySelectorAll("[data-i18n-placeholder]").forEach(el => {
const key = el.getAttribute('data-i18n-placeholder');
if (key) el.setAttribute('placeholder', getMsg(key));
});
document.querySelectorAll('.manage-widget, .virus-widget').forEach(widget => {
if (widget.buildHeader) widget.buildHeader();
if (widget.renderData) widget.renderData();
});
document.documentElement.lang = langMap[lang] || "en";
const progressText = document.getElementById('progress_text');
if (progressText && progressText.dataset.dynamicMsg) {
try {
const msgs = JSON.parse(progressText.dataset.dynamicMsg);
if (msgs[lang]) progressText.textContent = msgs[lang];
} catch (e) {}
}
};
const applyTheme = (theme) => {
const themeVars = themes[theme];
if (themeVars) {
for (const [key, value] of Object.entries(themeVars)) {
document.documentElement.style.setProperty(key, value.startsWith("rgba") ? value : `rgb(${value})`);
}
}
};
const renderDataGrid = (containerSelector, dataList, columns, valKey, defaultChecked = false) => {
const widget = document.querySelector(`${containerSelector} .manage-widget`) || document.querySelector(`${containerSelector} .virus-widget`);
const listUl = document.querySelector(`${containerSelector} .manage-list`) || document.querySelector(`${containerSelector} .virus-list`);
const searchInput = document.querySelector(`${containerSelector} .search-box input`);
if (!widget || !listUl) return;
const isInit = !widget.gridState;
if (isInit) {
const containerWidth = widget.clientWidth || 800;
const availableWidth = Math.max(containerWidth - 60 - (columns.length * 16), 400);
const totalFlex = columns.reduce((sum, c) => sum + (c.flex || 1), 0);
widget.gridState = {
sortKey: columns[0]?.key || '', sortAsc: true, filterText: "",
colWidths: columns.map(c => Math.max(((c.flex || 1) / totalFlex) * availableWidth, 60)),
checkedSet: new Set()
};
widget.gridState.colWidths.forEach((w, idx) => widget.style.setProperty(`--col-${idx}`, `${w}px`));
const header = document.createElement('div');
header.className = 'manage-list-header';
widget.insertBefore(header, widget.firstChild);
widget.buildHeader = () => {
const headerColsHtml = columns.map((col, idx) => `
<div class="col-header" data-key="${col.key}" data-idx="${idx}" style="width: var(--col-${idx}); flex: 0 0 auto;">
<span class="header-text" data-i18n="${col.label}">${getMsg(col.label)}</span>
<span class="sort-icon">${widget.gridState.sortKey === col.key ? (widget.gridState.sortAsc ? '▲' : '▼') : ''}</span>
<div class="col-resizer"></div>
</div>`).join('');
header.innerHTML = `<input type="checkbox" class="select-all-cb" title="${getMsg('btn_select_all')}"><div class="header-cols-container">${headerColsHtml}</div>`;
header.querySelectorAll('.col-header').forEach(el => {
el.addEventListener('click', () => {
const key = el.dataset.key;
widget.gridState.sortAsc = widget.gridState.sortKey === key ? !widget.gridState.sortAsc : true;
widget.gridState.sortKey = key;
widget.buildHeader();
widget.renderData(widget.gridState.filterText, false);
});
});
header.querySelectorAll('.col-resizer').forEach(resizer => {
resizer.addEventListener('mousedown', (e) => {
e.stopPropagation();
const idx = parseInt(resizer.parentElement.dataset.idx);
const startX = e.clientX;
const startWidth = widget.gridState.colWidths[idx];
const onMouseMove = (me) => {
const newWidth = Math.max(startWidth + (me.clientX - startX), 40);
widget.gridState.colWidths[idx] = newWidth;
widget.style.setProperty(`--col-${idx}`, `${newWidth}px`);
};
const onMouseUp = () => {
document.body.classList.remove('resizing-active');
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.body.classList.add('resizing-active');
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
resizer.addEventListener('click', (e) => e.stopPropagation());
});
const selectAllCb = header.querySelector('.select-all-cb');
if (selectAllCb) {
selectAllCb.addEventListener('change', (e) => {
const isChecked = e.target.checked;
listUl.querySelectorAll('.manage-list-item input[type="checkbox"]').forEach(cb => cb.checked = isChecked);
(widget.gridState.lastDisplayData || []).forEach(item => {
const valStr = String(item[valKey]);
isChecked ? widget.gridState.checkedSet.add(valStr) : widget.gridState.checkedSet.delete(valStr);
if (containerSelector === '#scan_window') appState.virusMap.set(valStr, isChecked);
});
if (containerSelector === '#scan_window') checkVirusListEmpty();
});
}
};
widget.buildHeader();
listUl.addEventListener('click', (e) => {
const li = e.target.closest('.manage-list-item');
if (!li) return;
const cb = li.querySelector('input[type="checkbox"]');
if (e.target.tagName !== 'INPUT') cb.checked = !cb.checked;
cb.checked ? widget.gridState.checkedSet.add(cb.value) : widget.gridState.checkedSet.delete(cb.value);
if (containerSelector === '#scan_window') {
appState.virusMap.set(cb.value, cb.checked);
checkVirusListEmpty();
}
if (widget.updateSelectAllState) widget.updateSelectAllState();
});
if (searchInput) searchInput.addEventListener('input', (e) => widget.renderData(e.target.value, false));
}
const state = widget.gridState;
widget.renderData = (filterText = state.filterText, resetDataCheck = false) => {
state.filterText = filterText;
if (resetDataCheck) {
state.checkedSet.clear();
if (defaultChecked) dataList.forEach(item => state.checkedSet.add(String(item[valKey])));
} else {
const validKeys = new Set(dataList.map(item => String(item[valKey])));
for (const val of state.checkedSet) if (!validKeys.has(val)) state.checkedSet.delete(val);
if (state.checkedSet.size === 0 && defaultChecked && dataList.length > 0) {
if (!listUl.querySelector('input[type="checkbox"]:checked')) {
dataList.forEach(item => state.checkedSet.add(String(item[valKey])));
}
}
}
const header = widget.querySelector('.manage-list-header');
const selectAllCb = header ? header.querySelector('.select-all-cb') : null;
if (selectAllCb) selectAllCb.disabled = dataList.length === 0;
const ft = filterText.toLowerCase();
let displayData = dataList.filter(item => !filterText || columns.some(col => String(item[col.key] || '').toLowerCase().includes(ft)));
displayData.sort((a, b) => {
const valA = a[state.sortKey] ?? '', valB = b[state.sortKey] ?? '';
if (state.sortKey === 'sizeStr' && 'size' in a && 'size' in b) return state.sortAsc ? a.size - b.size : b.size - a.size;
const numA = parseFloat(valA), numB = parseFloat(valB);
if (!isNaN(numA) && !isNaN(numB) && String(valA).trim() !== '' && String(valB).trim() !== '') return state.sortAsc ? numA - numB : numB - numA;
return state.sortAsc ? String(valA).localeCompare(String(valB)) : String(valB).localeCompare(String(valA));
});
widget.gridState.lastDisplayData = displayData;
widget.updateSelectAllState = () => {
if (!selectAllCb) return;
const cData = widget.gridState.lastDisplayData || [];
if (cData.length === 0) { selectAllCb.checked = false; selectAllCb.indeterminate = false; return; }
const checkedCount = cData.filter(item => containerSelector === '#scan_window' ? appState.virusMap.get(String(item[valKey])) : widget.gridState.checkedSet.has(String(item[valKey]))).length;
selectAllCb.checked = (checkedCount > 0 && checkedCount === cData.length);
selectAllCb.indeterminate = (checkedCount > 0 && checkedCount < cData.length);
};
const MAX_ITEMS = 2000;
const limitData = displayData.slice(0, MAX_ITEMS);
let htmlContent = limitData.map(item => {
const itemValStr = String(item[valKey]);
let isChecked = containerSelector === '#scan_window' ? (appState.virusMap.get(itemValStr) ?? true) : state.checkedSet.has(itemValStr);
if (containerSelector === '#scan_window') appState.virusMap.set(itemValStr, isChecked);
const rowColsHtml = columns.map((col, idx) => {
const rawVal = String(item[col.key] || '');
let valStr = col.isI18n ? getMsg(rawVal) : rawVal;
if (rawVal === '未知路徑' || rawVal === 'path_unknown') valStr = getMsg('path_unknown');
return `<div class="row-col" style="width: var(--col-${idx}); flex: 0 0 auto;" title="${escapeHtml(valStr)}">${escapeHtml(valStr)}</div>`;
}).join('');
return `<li class="manage-list-item"><input type="checkbox" value="${escapeHtml(itemValStr)}" ${isChecked ? 'checked' : ''} data-path="${escapeHtml(item.path || '')}"><div class="manage-list-item-content">${rowColsHtml}</div></li>`;
}).join('');
if (displayData.length > MAX_ITEMS) {
htmlContent += `<li class="manage-list-item" style="pointer-events: none; justify-content: center;"><div style="color: var(--text-secondary); padding: 10px;">${escapeHtml(formatMsg("msg_too_many", MAX_ITEMS))}</div></li>`;
}
const scrollContainer = listUl.parentElement;
const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0;
listUl.innerHTML = htmlContent;
if (scrollContainer) scrollContainer.scrollTop = scrollTop;
widget.updateSelectAllState();
};
widget.renderData(state.filterText, isInit);
};
const getCheckedValues = (selector) => Array.from(document.querySelectorAll(`${selector} .manage-list-item input[type="checkbox"]:checked`)).map(cb => cb.value);
const formatSize = (bytes) => {
if (bytes === 0) return '0 B';
const k = 1024, sizes = ['B', 'KB', 'MB', 'GB'], i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const rebuildCustomSelect = (selectId) => {
const select = document.getElementById(selectId);
if (!select) return;
let sibling = select.nextElementSibling;
while (sibling && sibling.classList.contains('custom-select-wrapper')) {
const toRemove = sibling;
sibling = sibling.nextElementSibling;
toRemove.remove();
}
buildCustomSelectElement(select);
};
const changeScanSelectMode = (mode) => {
const select = document.getElementById('scan_method_select');
if (!select) return;
select.innerHTML = `<option value="none" selected disabled data-i18n="btn_select">${getMsg('btn_select')}</option>`;
if (mode === 'scan') {
select.innerHTML += `
<option value="smart" data-i18n="scan_smart">${getMsg('scan_smart')}</option>
<option value="file" data-i18n="scan_file">${getMsg('scan_file')}</option>
<option value="path" data-i18n="scan_path">${getMsg('scan_path')}</option>
<option value="full" data-i18n="scan_full">${getMsg('scan_full')}</option>`;
} else if (mode === 'action') {
select.innerHTML += `
<option value="delete" data-i18n="act_del">${getMsg('act_del')}</option>
<option value="ignore" data-i18n="act_ignore">${getMsg('act_ignore')}</option>
<option value="quarantine" data-i18n="act_quarantine">${getMsg('act_quarantine')}</option>
<option value="whitelist" data-i18n="act_white">${getMsg('act_white')}</option>`;
}
rebuildCustomSelect('scan_method_select');
};
const checkVirusListEmpty = () => {
updateCustomSelectUI('scan_method_select', 'none');
if (appState.virusResults.length === 0) {
appState.virusMap.clear();
changeScanSelectMode('scan');
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) {
title.setAttribute('data-i18n', 'scan_virus');
title.textContent = getMsg('scan_virus');
}
if (text) {
text.setAttribute('data-i18n', 'scan_virus_desc');
text.removeAttribute('data-dynamic-msg');
text.textContent = getMsg('scan_virus_desc');
}
}
};
const triggerScan = (method) => {
if (!window.pywebview) return;
appState.scanning = true;
appState.virusMap.clear();
appState.virusResults = [];
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
changeScanSelectMode('scan');
document.getElementById('scan_method_select').classList.add('hidden');
const stopBtn = document.getElementById('stop_btn');
stopBtn.classList.remove('hidden');
stopBtn.disabled = false;
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.setAttribute('data-i18n', 'msg_init'); text.removeAttribute('data-dynamic-msg'); text.textContent = getMsg('msg_init'); }
window.pywebview.api.trigger_scan(method);
};
const handleVirusActions = (action) => {
if (!window.pywebview) return;
const paths = Array.from(appState.virusMap.entries()).filter(([_, checked]) => checked).map(([p]) => p);
if (paths.length === 0) return updateCustomSelectUI('scan_method_select', 'none');
const wrapper = document.getElementById('scan_method_select').nextElementSibling;
if (wrapper) wrapper.style.pointerEvents = 'none';
const finalize = () => {
if (wrapper) wrapper.style.pointerEvents = '';
checkVirusListEmpty();
};
const removeItems = (removedPaths) => {
const removedSet = new Set(removedPaths);
removedPaths.forEach(p => appState.virusMap.delete(p));
appState.virusResults = appState.virusResults.filter(r => !removedSet.has(r.path));
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', false);
};
if (action === 'delete') {
window.pywebview.api.solve_scan(paths).then((deleted) => { removeItems(deleted); finalize(); });
} else if (action === 'ignore') {
window.pywebview.api.remove_virus_result(paths).then(() => { removeItems(paths); finalize(); });
} else if (action === 'quarantine' || action === 'whitelist') {
const listKey = action === 'quarantine' ? 'quarantine' : 'white_list';
window.pywebview.api.manage_named_list(listKey, paths, 'add').then(() => {
window.pywebview.api.remove_virus_result(paths).then(() => { removeItems(paths); finalize(); });
});
}
};
const fetchProcs = () => {
if (!appState.taskmgrActive || !window.pywebview) return;
const widget = document.querySelector('#taskmgr_window .manage-widget');
const blockFetch = widget && (widget.gridState?.checkedSet.size > 0 || widget.matches(':hover')) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
if (!blockFetch) {
window.pywebview.api.get_process_list().then(procs => {
const mapped = procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" }));
renderDataGrid('#taskmgr_window', mapped, cols.process, 'pid', false);
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
}).catch(() => { if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000); });
} else {
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
}
};
const refreshConfigLists = () => {
if (!window.pywebview) return;
window.pywebview.api.get_config().then(cfg => {
renderDataGrid('#whitelist_window', (cfg.white_list || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
renderDataGrid('#quarantine_window', (cfg.quarantine || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
renderDataGrid('#custom_protect_window', (cfg.custom_rule || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
const popupRules = (cfg.block_list || []).map(item => ({ exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.exe || item.title }));
renderDataGrid('#popup_window', popupRules, cols.popup, 'value');
});
};
const switchPage = (targetId) => {
const oldActive = document.querySelector('.page.active');
if (oldActive && oldActive.id !== targetId) {
if (oldActive.id === 'junk_window') {
appState.junkList = [];
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
const input = document.querySelector('#junk_window .search-box input');
if (input) input.value = '';
} else if (oldActive.id === 'repair_window') {
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
}
}
document.querySelectorAll('.page, .nav-btn').forEach(el => el.classList.remove('active'));
document.getElementById(targetId)?.classList.add('active');
document.querySelector(`aside .nav-btn[data-target="${targetId}"]`)?.classList.add('active');
appState.taskmgrActive = targetId === 'taskmgr_window';
if (appState.taskmgrActive) fetchProcs();
else if (appState.taskmgrTimer) { clearTimeout(appState.taskmgrTimer); appState.taskmgrTimer = null; }
if (['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].includes(targetId)) refreshConfigLists();
if (targetId === 'log_export_window' && window.pywebview) {
window.pywebview.api.get_logs().then(logs => {
const logList = logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id }));
renderDataGrid('#log_export_window', logList, cols.log, 'id', true);
});
}
};
window.addVirusResult = (label, path) => {
if (!appState.virusMap.has(path)) {
appState.virusResults.push({ label, path });
appState.virusMap.set(path, true);
const widget = document.querySelector('#scan_window .virus-widget');
if (widget && widget.renderData) {
if (window.virusRenderTimeout) clearTimeout(window.virusRenderTimeout);
window.virusRenderTimeout = setTimeout(() => widget.renderData(widget.gridState.filterText, false), 50);
}
}
};
window.updateScanProgress = (path) => {
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.removeAttribute('data-i18n'); text.removeAttribute('data-dynamic-msg'); text.textContent = path; }
};
window.updateDeleteProgress = (path) => {
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_deleting'); title.textContent = getMsg('status_deleting'); }
if (text) { text.removeAttribute('data-i18n'); text.removeAttribute('data-dynamic-msg'); text.textContent = path; }
};
window.finishScan = (msgData, count) => {
appState.scanning = false;
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'scan_virus'); title.textContent = getMsg('scan_virus'); }
if (text) {
if (typeof msgData === 'object' && msgData !== null) {
text.removeAttribute('data-i18n');
text.dataset.dynamicMsg = JSON.stringify(msgData);
text.textContent = msgData[appState.lang] || msgData["english_switch"];
} else {
text.removeAttribute('data-dynamic-msg');
text.setAttribute('data-i18n', msgData);
text.textContent = getMsg(msgData);
}
}
document.getElementById('stop_btn')?.classList.add('hidden');
document.getElementById('scan_method_select')?.classList.remove('hidden');
changeScanSelectMode(count > 0 ? 'action' : 'scan');
updateCustomSelectUI('scan_method_select', 'none');
};
window.updateLogs = (entry) => {
const logWidget = document.querySelector('.log-text');
if (logWidget) {
let parts = [`[${entry.time_str}]`, entry.level, getMsg(entry.action || '')];
if (entry.source) parts.push(`Src: ${entry.source}`);
if (entry.target) parts.push(`Tgt: ${entry.target}`);
if (entry.code) parts.push(`Code: ${entry.code}`);
if (entry.pid) parts.push(`PID: ${entry.pid}`);
if (entry.hash) parts.push(`Hash: ${entry.hash}`);
if (entry.detail) parts.push(`Detail: ${entry.detail}`);
if (entry.operate !== null) parts.push(`Op: ${entry.operate}`);
parts.push(`Success: ${entry.success}`);
let val = logWidget.value + parts.join(' | ') + '\n';
if (val.length > 20000) val = val.substring(val.indexOf('\n', val.length - 15000) + 1);
logWidget.value = val;
logWidget.scrollTop = logWidget.scrollHeight;
}
if (document.getElementById('log_export_window')?.classList.contains('active') && window.pywebview) {
window.pywebview.api.get_logs().then(logs => {
const logList = logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id }));
renderDataGrid('#log_export_window', logList, cols.log, 'id', true);
});
}
if ((entry.action === 'System' && entry.detail === 'Engine Initialization Complete') || (entry.level === 'WARN' && entry.action === 'init_engine_thread')) {
const overlay = document.getElementById('loading_overlay');
if (overlay && !overlay.classList.contains('fade-out')) {
overlay.classList.add('fade-out');
setTimeout(() => {
document.querySelector('.app-container')?.classList.add('fade-in');
if (window.onEngineReady) window.onEngineReady();
}, 400);
}
}
};
window.triggerContextScan = (path) => {
if (appState.scanning) return;
switchPage('scan_window');
if (window.pywebview) {
appState.scanning = true;
appState.virusMap.clear();
appState.virusResults = [];
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
document.getElementById('scan_method_select')?.classList.add('hidden');
const stopBtn = document.getElementById('stop_btn');
if (stopBtn) { stopBtn.classList.remove('hidden'); stopBtn.disabled = false; }
changeScanSelectMode('scan');
const title = document.querySelector('#scan_window .section-title');
const text = document.getElementById('progress_text');
if (title) { title.setAttribute('data-i18n', 'status_scanning'); title.textContent = getMsg('status_scanning'); }
if (text) { text.setAttribute('data-i18n', 'msg_init'); text.removeAttribute('data-dynamic-msg'); text.textContent = getMsg('msg_init'); }
window.pywebview.api.start_scan([path]);
}
};
window.revertSwitch = () => {};
document.querySelectorAll('[data-target]').forEach(btn => btn.addEventListener('click', (e) => switchPage(e.currentTarget.getAttribute('data-target'))));
document.getElementById('quick_scan_btn')?.addEventListener('click', () => { if (!appState.scanning) { switchPage('scan_window'); triggerScan('smart'); }});
document.getElementById('stop_btn')?.addEventListener('click', (e) => { if (window.pywebview) window.pywebview.api.stop_scan(); e.target.disabled = true; });
document.getElementById('theme_select')?.addEventListener('change', (e) => { applyTheme(e.target.value); window.pywebview?.api.update_config('theme', e.target.value); });
document.getElementById('lang_select')?.addEventListener('change', (e) => { translateText(e.target.value); window.pywebview?.api.update_config('language', e.target.value); });
document.getElementById('scan_method_select')?.addEventListener('change', (e) => {
const val = e.target.value;
if (val === 'none' || appState.scanning) return;
if (['smart', 'file', 'path', 'full'].includes(val)) triggerScan(val);
else if (['delete', 'ignore', 'quarantine', 'whitelist'].includes(val)) handleVirusActions(val);
});
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
toggle.addEventListener('change', async (e) => {
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
const key = switchMap[index];
if (window.pywebview && key) {
toggle.disabled = true;
try {
const result = await window.pywebview.api.update_config(key, e.target.checked);
if (result !== undefined && result !== null) toggle.checked = result;
} catch { toggle.checked = !e.target.checked; }
finally { toggle.disabled = false; }
}
});
});
document.querySelector('#junk_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_init'); e.target.textContent = getMsg('msg_init');
const widget = document.querySelector('#junk_window .manage-widget');
if (widget?.gridState) widget.gridState.checkedSet.clear();
window.pywebview.api.scan_system_junk().then(list => {
appState.junkList = list.map(item => ({ path: item.path, sizeStr: formatSize(item.size), size: item.size }));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
e.target.setAttribute('data-i18n', 'btn_scan'); e.target.textContent = getMsg('btn_scan');
});
});
document.querySelector('#junk_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#junk_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.clean_system_junk(checked).then(size => {
window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_cleaned")}${formatSize(size)}`, "info");
appState.junkList = appState.junkList.filter(item => !checked.includes(item.path));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
});
});
document.querySelector('#repair_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
window.pywebview.api.scan_system_repair().then(list => renderDataGrid('#repair_window', list, cols.repair, 'value', true));
});
document.querySelector('#repair_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#repair_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.execute_system_repair(checked).then(() => {
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_repaired"), "info");
renderDataGrid('#repair_window', [], cols.repair, 'value');
});
});
['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].forEach(id => {
const listKey = id === 'popup_window' ? 'block_list' : (id === 'custom_protect_window' ? 'custom_rule' : id.replace('_window', ''));
document.querySelector(`#${id} .modern-btn:not(.primary-btn)`)?.addEventListener('click', () => {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0 && window.pywebview) window.pywebview.api.remove_list_items(listKey, checked).then(refreshConfigLists);
});
if (id !== 'popup_window') {
document.querySelector(`#${id} .primary-btn`)?.addEventListener('click', () => {
window.pywebview?.api.select_files().then(files => {
if (files?.length > 0) window.pywebview.api.manage_named_list(listKey, files, 'add').then(refreshConfigLists);
});
});
}
});
document.querySelector('#popup_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_click_target'); e.target.textContent = getMsg('msg_click_target'); e.target.disabled = true;
window.pywebview.api.capture_popup_window().then(rule => {
e.target.setAttribute('data-i18n', 'btn_add'); e.target.textContent = getMsg('btn_add'); e.target.disabled = false;
if (rule) window.pywebview.api.add_popup_rule(rule).then(ok => ok && refreshConfigLists());
});
});
document.querySelector('#taskmgr_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked').forEach(cb => {
if (cb.dataset.path && cb.dataset.path !== "path_unknown") window.pywebview.api.open_file_location(cb.dataset.path);
});
});
document.querySelector('#taskmgr_window .danger-btn')?.addEventListener('click', () => {
const cbs = document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked');
if (cbs.length > 0 && window.pywebview) {
window.pywebview.api.show_confirm(getMsg("msg_end_proc_title"), getMsg("msg_end_proc_confirm")).then(res => {
if (res) Promise.all(Array.from(cbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => {
window.pywebview.api.get_process_list().then(procs => renderDataGrid('#taskmgr_window', procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" })), cols.process, 'pid', false));
});
});
}
});
document.querySelector('#log_export_window .primary-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0) return window.pywebview?.api.show_alert(getMsg("title_prompt"), getMsg("msg_sel_log_export"), "warning");
window.pywebview?.api.export_logs(checked).then(res => res && window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_export_success"), "info"));
});
document.querySelector('#log_export_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.show_confirm(getMsg("title_prompt"), getMsg("msg_del_log_confirm")).then(res => {
if (res) window.pywebview.api.clear_logs(checked).then(() => {
window.pywebview.api.get_logs().then(logs => renderDataGrid('#log_export_window', logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id })), cols.log, 'id', true));
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_log_deleted"), "info");
});
});
});
document.getElementById('minimize_button')?.addEventListener('click', () => window.pywebview?.api.minimize());
document.getElementById('close_button')?.addEventListener('click', () => window.pywebview?.api.hide_window());
const contextMenu = document.createElement('div');
contextMenu.className = 'custom-context-menu';
contextMenu.innerHTML = `<div class="custom-context-menu-item" id="ctx_open_location"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg><span data-i18n="ctx_open_loc"></span></div>`;
document.body.appendChild(contextMenu);
let ctxTarget = null;
document.addEventListener('contextmenu', (e) => {
const li = e.target.closest('.manage-list-item');
if (li) {
const cb = li.querySelector('input[type="checkbox"]');
if (cb && cb.dataset.path && cb.dataset.path !== "path_unknown") {
e.preventDefault();
ctxTarget = cb.dataset.path;
const span = contextMenu.querySelector('span');
span.textContent = getMsg('ctx_open_loc');
contextMenu.style.left = `${e.clientX}px`;
contextMenu.style.top = `${e.clientY}px`;
contextMenu.classList.add('show');
const rect = contextMenu.getBoundingClientRect();
if (rect.right > window.innerWidth) contextMenu.style.left = `${window.innerWidth - rect.width - 5}px`;
if (rect.bottom > window.innerHeight) contextMenu.style.top = `${window.innerHeight - rect.height - 5}px`;
return;
}
}
contextMenu.classList.remove('show');
});
document.addEventListener('click', (e) => { if (!e.target.closest('.custom-context-menu')) contextMenu.classList.remove('show'); });
document.addEventListener('scroll', () => contextMenu.classList.remove('show'), true);
document.getElementById('ctx_open_location').addEventListener('click', () => {
if (window.pywebview && ctxTarget) window.pywebview.api.open_file_location(ctxTarget);
contextMenu.classList.remove('show');
});
initI18nKeys();
const bindSettingAction = (key, action) => {
const item = Array.from(document.querySelectorAll('#about_window .list-item, #setting_window .list-item')).find(el => {
const h2 = el.querySelector('h2');
return h2 && h2.getAttribute('data-i18n') === key;
});
if (item) {
const btn = item.querySelector('button');
if (btn) {
btn.addEventListener('click', action);
} else {
item.style.cursor = 'pointer';
item.dataset.action = key;
item.addEventListener('click', action);
}
}
};
bindSettingAction('web_official', () => window.pywebview?.api.open_website());
bindSettingAction('update_check', () => {
window.pywebview?.api.check_update().then(res => {
if (res.error) window.pywebview.api.show_alert(getMsg("title_error"), getMsg("msg_update_fail"), "error");
else if (res.has_update) window.pywebview.api.show_confirm(getMsg("title_prompt"), `${getMsg("msg_new_version")} ${res.latest}\n(${res.current} -> ${res.latest})`).then(ok => ok && window.pywebview.api.open_url(res.url));
else window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_latest_version")} ${res.current}`, "info");
});
});
bindSettingAction('opt_reset', () => {
window.pywebview?.api.show_confirm(getMsg("title_prompt"), getMsg("opt_reset_desc")).then(res => {
if (res) window.pywebview.api.reset_config().then(() => window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_reset_success"), "info"));
});
});
document.querySelectorAll('select.modern-select').forEach(buildCustomSelectElement);
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
renderDataGrid('#taskmgr_window', [], cols.process, 'pid', false);
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
renderDataGrid('#whitelist_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#quarantine_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#custom_protect_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#popup_window', [], cols.popup, 'value', false);
renderDataGrid('#log_export_window', [], cols.log, 'id', true);
window.addEventListener('pywebviewready', () => {
window.pywebview.api.get_config().then(cfg => {
const theme = cfg.theme || "white_switch";
const lang = cfg.language || "english_switch";
appState.firstLaunch = cfg.first_launch;
applyTheme(theme);
translateText(lang);
updateCustomSelectUI('theme_select', theme);
updateCustomSelectUI('lang_select', lang);
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
if (appState.firstLaunch) {
toggle.checked = false;
toggle.disabled = true;
} else if (switchMap[index]) {
toggle.checked = !!cfg[switchMap[index]];
}
});
window.pywebview.api.init_ui_ready();
}).catch(err => {
console.error("Config load failed:", err);
window.pywebview.api.init_ui_ready();
});
});
window.onEngineReady = async () => {
if (!appState.firstLaunch) return;
await window.pywebview.api.update_config("first_launch", false);
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
const seq = ["cloud_switch", "process_switch", "document_switch", "system_switch", "network_switch", "driver_switch"];
for (const key of seq) {
const index = switchMap.indexOf(key);
if (index !== -1) {
const toggle = document.querySelectorAll('.toggle-switch input')[index];
if (toggle) {
toggle.checked = true;
try {
const res = await window.pywebview.api.update_config(key, true);
if (res !== undefined && res !== null) toggle.checked = res;
} catch { toggle.checked = false; }
finally { toggle.disabled = false; }
await new Promise(r => setTimeout(r, 200));
}
}
}
document.querySelectorAll('.toggle-switch input').forEach(t => t.disabled = false);
appState.firstLaunch = false;
};
});
+427
View File
@@ -0,0 +1,427 @@
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PYAS Security</title>
<link rel="stylesheet" href="/static/css/style.css">
<script defer src="/static/js/i18n.js"></script>
<script defer src="/static/js/main.js"></script>
</head>
<body>
<div id="loading_overlay">
<svg class="spinner" viewBox="0 0 50 50">
<circle class="path" cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
</svg>
</div>
<div class="app-container" style="opacity: 0; transition: opacity 0.5s ease;">
<header class="navigate-window pywebview-drag-region">
<div class="navigate-title" data-i18n>PYAS Security</div>
<div class="window-controls">
<button class="ctrl-btn" id="about_button" data-target="about_window">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
</button>
<button class="ctrl-btn" id="minimize_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</button>
<button class="ctrl-btn" id="close_button">
<svg viewBox="0 0 24 24" class="theme-svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</header>
<div class="main-layout">
<aside class="options-window pywebview-drag-region">
<button class="nav-btn active" data-target="home_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
</button>
<button class="nav-btn" data-target="scan_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
</button>
<button class="nav-btn" data-target="tool_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
</button>
<button class="nav-btn" data-target="protect_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
</button>
<div class="spacer"></div>
<button class="nav-btn" data-target="setting_window">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</button>
</aside>
<main class="content-window">
<section id="home_window" class="page active">
<div class="home-top-widget">
<div class="state-widget">
<svg viewBox="0 0 24 24" class="state-icon theme-svg" style="width: 120px; height: 120px;" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><polyline points="9 12 11 14 15 10"></polyline></svg>
<h2 class="state-title" data-i18n>此裝置已受到防護</h2>
</div>
<div class="quick-actions">
<div class="list-item" style="cursor: pointer;" id="quick_scan_btn" data-action="scan">
<div class="item-info">
<h2 data-i18n>智能掃描</h2><p data-i18n>此選項可以快速掃描裝置安全性</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="log_export_window">
<div class="item-info">
<h2 data-i18n>日誌導出</h2><p data-i18n>此選項可以篩選日誌並匯出存檔</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</div>
<div class="log-widget">
<textarea class="log-text" readonly></textarea>
</div>
</section>
<section id="scan_window" class="page">
<div class="progress-widget">
<div class="header-group">
<h2 class="section-title" data-i18n>病毒掃描</h2>
<p class="section-desc" id="progress_text" data-i18n>此選項可以選擇進階掃描方式</p>
</div>
<div class="action-group modern-action-group">
<select id="scan_method_select" class="modern-select">
<option value="none" selected disabled data-i18n>選擇</option>
<option value="smart" data-i18n>智能掃描</option>
<option value="file" data-i18n>檔案掃描</option>
<option value="path" data-i18n>路徑掃描</option>
<option value="full" data-i18n>全盤掃描</option>
</select>
<button class="action-btn modern-btn hidden" id="stop_btn" data-i18n>停止</button>
</div>
</div>
<div class="scan-content">
<div class="virus-widget">
<ul class="virus-list" id="virus_list"></ul>
</div>
</div>
</section>
<section id="protect_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>進程防護</h2><p data-i18n>此選項可以終止惡意進程運行</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檔案防護</h2><p data-i18n>此選項可以隔離惡意檔案建立</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>系統防護</h2><p data-i18n>此選項可以實時修復系統異常</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>驅動防護</h2><p data-i18n>此選項可以實時攔截惡意行為</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>網路防護</h2><p data-i18n>此選項可以攔截惡意網路連接</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item" style="cursor: pointer;" data-target="custom_protect_window">
<div class="item-info">
<h2 data-i18n>自訂防護</h2><p data-i18n>此選項可以導入自訂防護規則</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="tool_window" class="page">
<div class="list-widget">
<div class="list-item" style="cursor: pointer;" data-target="taskmgr_window">
<div class="item-info">
<h2 data-i18n>進程管理</h2><p data-i18n>此選項可以實時查看系統進程</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="junk_window">
<div class="item-info">
<h2 data-i18n>垃圾清理</h2><p data-i18n>此選項可以清理暫存垃圾檔案</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="repair_window">
<div class="item-info">
<h2 data-i18n>系統修復</h2><p data-i18n>此選項可以修復系統登錄檔項目</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="popup_window">
<div class="item-info">
<h2 data-i18n>彈窗攔截</h2><p data-i18n>此選項可以選擇指定窗口攔截</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="whitelist_window">
<div class="item-info">
<h2 data-i18n>白名單</h2><p data-i18n>此選項可以查看並管理白名單</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="quarantine_window">
<div class="item-info">
<h2 data-i18n>隔離區</h2><p data-i18n>此選項可以查看並管理隔離區</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
</div>
</section>
<section id="setting_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>增強模式</h2><p data-i18n>此選項可以增強靜態掃描引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>擴展引擎</h2><p data-i18n>此選項可以使用擴展規則引擎</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>雲端服務</h2><p data-i18n>此選項可以自動上報可疑檔案</p>
</div>
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>選單掃描</h2><p data-i18n>此選項可以啟用右鍵掃描功能</p>
</div>
<label class="toggle-switch"><input type="checkbox"><span class="slider"></span></label>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯色主題</h2><p data-i18n>此選項可以選擇主題顏色</p>
</div>
<select id="theme_select" class="modern-select">
<option value="white_switch" data-i18n>白色主題</option>
<option value="red_switch" data-i18n>紅色主題</option>
<option value="yellow_switch" data-i18n>黃色主題</option>
<option value="green_switch" data-i18n>綠色主題</option>
<option value="blue_switch" data-i18n>藍色主題</option>
<option value="black_switch" data-i18n>黑色主題</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>顯示語言</h2><p data-i18n>此選項可以選擇顯示語言</p>
</div>
<select id="lang_select" class="modern-select">
<option value="english_switch">English</option>
<option value="traditional_switch">繁體中文</option>
<option value="simplified_switch">简体中文</option>
<option value="japanese_switch">日本語</option>
<option value="korean_switch">한국어</option>
<option value="french_switch">Français</option>
<option value="spanish_switch">Español</option>
<option value="hindi_switch">हिन्दी</option>
<option value="arabic_switch">العربية</option>
<option value="russian_switch">Русский</option>
<option value="slovenian_switch">Slovenščina</option>
</select>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>重置選項</h2><p data-i18n>此選項可以重置所有設定</p>
</div>
<button class="action-btn modern-btn danger-btn" data-i18n>執行</button>
</div>
</div>
</section>
<section id="about_window" class="page">
<div class="list-widget">
<div class="list-item">
<div class="item-info">
<h2 data-i18n>產品金鑰</h2><p data-i18n>此選項可以啟用產品金鑰</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>官方網站</h2><p data-i18n>此選項可以前往官方網站</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg></button>
</div>
<div class="list-item">
<div class="item-info">
<h2 data-i18n>檢查更新</h2><p data-i18n>此選項可以檢查可用更新</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg></button>
</div>
<div class="license-widget">
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
Last updated: 2025-12-19
1. License and Ownership
- This software and its source code are source available for public view.
- Access or execution of certain features may require an activation key.
- 87owo reserves all legal rights, no intellectual property is transferred.
- All project archives, source code, and binaries remain fully proprietary.
2. Use and Prohibitions
- You may view, download, and modify the code for local and personal use.
- Redistribution of any original or modified code requires prior consent.
- Commercial hosting or SaaS deployment is prohibited without permission.
- Reverse engineering, license removal, or illegal use is strictly barred.
3. Data and Privacy
- This project may collect operational data for optimization and analysis.
- All collected data is strictly anonymized to ensure the user's privacy.
- Transmission of information is encrypted to prevent unauthorized access.
4. Updates and Terms
- New releases may include critical patches, enhancements, or UI changes.
- Continued use of these services constitutes acceptance of updated terms.
- The author reserves the right to modify these license terms at any time.
5. Warranty and Liability
- All services and assets are provided "as is" without any form of warranty.
- 87owo is not liable for any direct or indirect damages from software use.
- Users assume all risks regarding performance, data, and system integrity.
© 2020-2026 87owo (PYAS Security). All rights reserved.</textarea>
</div>
</div>
</section>
<section id="taskmgr_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>進程管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>查看</button>
<button class="action-btn modern-btn danger-btn" data-i18n>結束</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="whitelist_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>白名單管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="quarantine_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>隔離區管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="junk_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>垃圾清理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="repair_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>系統修復</h2>
<div class="spacer"></div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>修復</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="popup_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>彈窗攔截</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋窗口..." placeholder="搜尋窗口...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="custom_protect_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="protect_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>自訂防護</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋規則..." placeholder="搜尋規則...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="log_export_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="home_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>日誌導出</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋日誌..." placeholder="搜尋日誌...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>導出</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
</main>
</div>
</div>
</body>
</html>
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020-2025 87owo (PYAS Security)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+31
View File
@@ -0,0 +1,31 @@
87owo Publicly Available Source Code License Agreement
Last updated: 2025-12-19
1. License and Ownership
- This software and its source code are source available for public view.
- Access or execution of certain features may require an activation key.
- 87owo reserves all legal rights, no intellectual property is transferred.
- All project archives, source code, and binaries remain fully proprietary.
2. Use and Prohibitions
- You may view, download, and modify the code for local and personal use.
- Redistribution of any original or modified code requires prior consent.
- Commercial hosting or SaaS deployment is prohibited without permission.
- Reverse engineering, license removal, or illegal use is strictly barred.
3. Data and Privacy
- This project may collect operational data for optimization and analysis.
- All collected data is strictly anonymized to ensure the user's privacy.
- Transmission of information is encrypted to prevent unauthorized access.
4. Updates and Terms
- New releases may include critical patches, enhancements, or UI changes.
- Continued use of these services constitutes acceptance of updated terms.
- The author reserves the right to modify these license terms at any time.
5. Warranty and Liability
- All services and assets are provided "as is" without any form of warranty.
- 87owo is not liable for any direct or indirect damages from software use.
- Users assume all risks regarding performance, data, and system integrity.
© 2020-2026 87owo (PYAS Security). All rights reserved.
+2529 -1375
View File
File diff suppressed because it is too large. Load diff
-128
View File
@@ -1,128 +0,0 @@
translate_dict = {
"traditional_switch": {"开启":"開啟","关闭":"關閉","On":"開啟","Off":"關閉"},
"simplified_switch": {"開啟":"开启","關閉":"关闭","On":"开启","Off":"关闭",
"錯誤":"错误","發現":"发现","關閉":"关闭","裝置":"设备","記憶體":"内存",
"軟體":"软件","檔案":"文件","確定":"确认","設定":"设置","關於":"关于",
"總數":"总数","註冊":"注册","圖標":"图标","路徑":"路径","網路":"网络",
"位元":"字节","靈敏":"敏感","複製":"复制","開啟":"开启","靜態":"静态",
"訊":"讯","項":"项","掃":"扫","試":"试","黃":"黄","嗎":"吗","防":"保",
"鑰":"钥","當":"当","後":"后","護":"护","導":"导","區":"区","復":"复",
"攔":"拦","請":"请","統":"统","刪":"删","進":"进","個":"个","選":"选",
"啟":"启","監":"监","並":"并","徑":"径","盤":"盘","隱":"隐","動":"动",
"狀":"状","誌":"志","這":"这","會":"会","題":"题","擇":"择","語":"语",
"無":"无","顯":"显","變":"变","戶":"户","碼":"码","雲":"云","介":"界",
"稱":"称","類":"类","輸":"输","執":"执","數":"数","尋":"寻","網":"网",
"詢":"询","製":"制","員":"员","許":"许","條":"条","藍":"蓝","紅":"红",
"綠":"绿","態":"态","務":"务","連":"连","檢":"检","測":"测","關":"关",
"強":"强","驅":"驱","為":"为","腳":"脚","單":"单","開":"开","擴":"扩",
"惡":"恶","據":"据","庫":"库","經":"经","敗":"败","謝":"谢","別":"别",
"載":"载","誤":"误","報":"报","鏈":"链","竄":"窜","蹤":"踪","與":"与",
"異":"异","實":"实","時":"时","彈":"弹","窗":"窗","暫":"暂","訂":"订",
"資":"资","產":"产","金":"密","離":"离","規":"规","則":"则","來":"来",
"殼":"壳","碼":"码","線":"线",},
"english_switch": {"開啟":"On","關閉":"Off","开启":"On","关闭":"Off",",":", ",
"立即刪除":"Delete Now","安全模式":"Safe Mode","官方網站":"Official Website ",
"腳本":"Script ","鏈式":"Chain ","記憶體":"Memory ","加載":"Load ",
"行為":"Behavior ","可疑":"Suspicious ","勒索":"Ransom ","主要":"Main ",
"許可條款":"License Terms ","惡意":"Malicious", "開發":"Developers ",
"啟用":"Enable ","禁用":"Disable ","正在初始化中":"Initializing ",
"正在掃描":"Scanning ","正在刪除":"Deleting ","正在追蹤":"Tracking ",
"最小化到":"Minimize To ","靈敏度":"Sensitivity ","正在檢查":"Checking ",
"確定要":"Sure Want To ","引擎靈敏度":"Sensitivity ","更新":"Update ",
"白色主題":"White","黃色主題":"Yellow","紅色主題":"Red","語言":"Language ",
"藍色主題":"Blue","綠色主題":"Green","自訂主題":"Custom","黑色主題":"Black",
"特別感謝":"Contributors ","版本資訊":"Information","通訊":"Connect ",
"註冊":"Registry ","退出":"Quit ","檔案":"File ","當前":"Currently ",
"狀態":"State ","掃描":"Scan ","工具":"Tools ","防護":"Protect ",
"失敗":"Failed ","請":"Please ","路徑":"Path ","方式":"Method ",
"需要":"Need ","病毒":"Virus ","選擇":"Select ","返回":"Back",
"受到":"Been ","錯誤":"Error","完成":"Complete ","引擎":"Engine ",
"托盤":"Tray ","圖標":"Icon ","不安全":"Unsafe","增強":"Enhance ",
"系統":"System ","雲端":"Cloud ","重啟":"Reboot ","修復":"Repair ",
"裝置":"Device ","發現":"Found ","成功":"Success ","清理":"Cleanup ",
"確定":"OK","執行":"Execute ","警告":"Warning ","使用":"Use ",
"網路":"Network ","變更":"Change ","總數":"Total","結束":"End ",
"選項":"Options ","監控":"Monitor ","連接":"Connect ","可以":"Can ",
"進程":"Process ","攔截":"Block ","引導":"Boot ","刪除":"Delete ",
"分區":"Partition ","設定":"Setting ","關於":"About","模式":"Mode ",
"提高":"Improve ","安全":"Security ","顯色":"Color ","顏色":"Color ",
"主題":"Theme ", "回收":"Collect ", "無用":"Useless ","導入":"Import ",
"風險":"Risk ","介面":"UI ","核心":"Core ","服務":"Services ",
"管理":"Manage","所有":"All ","您":"You ","取消":"Cancel ","未":"Not ",
"垃圾":"Junk ","位元":"byte ","本機":"Computer ","停止":"Stop ",
"到":"To ","白名單":"White List ","軟體":"Software ","正在":"",
"增加":"Add ","移除":"Delete ","項目":"Items ","生成":"Create ","稱":"",
"複製":"Copy ","此":"This ","並":"And ","全盤":"Full ","最新":"Latest ",
"已存在":"Already ","數據庫":"Database ","誤報":"False Positive ",
"高":"High ","新":"New ","和":"And ","可能會":"Possible ","會":"Can ",
"日誌":"Log","已":"Has ","彈窗":"Window ","要":"To ","驅動":"Driver ",
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Rule ",
"密鑰":"Key ","檢測":"Detect ","製作":"Maker ","運行":"Running",
"擴展":"Extension ","套件":"Kit ","自動":"Auto ","上報":"Report ",
"分析":"Analysis ","自身":"Self ","其他":"Other ","配置":"Config ",
"丟失":"Lost ","網站":"Website ","不兼容":"Not Compatible ","列表":"List ",
"竄改":"Tamper ","第三方":"Other ","實時":"Real Time ","查看":"View ",
"指定":"Specify ","清空":"Empty ","資源回收桶":"Recycle Bin ", "無法":"Cannot ",
"與":"With ","當":"When ","功能":"Function ","異常":"Abnormal ", "來":"To ",
"重置":"Reset ","窗口":"Window ","暫存":"Temp ","自訂":"Custom ",
"不支持":"Not Support ","共":"Total ","秒":"Seconds ","耗時":"Takes ",
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
"建立":"Create ","環境":"Environment ","沙箱":"Sandbox ","化":"", "表":"",
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
"托管":"CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ", " ":" "},
"white_switch": {
"_white": "_black",},
"red_switch": {
"235, 235, 235": "255, 235, 235",
"245, 245, 245": "255, 225, 225",
"255, 255, 255": "255, 245, 245",
"240, 240, 240": "255, 220, 220",
"250, 250, 250": "255, 210, 210",
"220, 255, 220": "255, 220, 220",
"210, 255, 210": "255, 210, 210",
"_white": "_black"
},
"yellow_switch": {
"235, 235, 235": "255, 255, 235",
"245, 245, 245": "255, 255, 225",
"255, 255, 255": "255, 255, 245",
"240, 240, 240": "255, 255, 220",
"250, 250, 250": "255, 255, 210",
"220, 255, 220": "255, 255, 220",
"210, 255, 210": "255, 255, 210",
"_white": "_black"
},
"green_switch": {
"235, 235, 235": "235, 255, 235",
"245, 245, 245": "225, 255, 225",
"255, 255, 255": "245, 255, 245",
"240, 240, 240": "220, 255, 220",
"250, 250, 250": "210, 255, 210",
"_white": "_black"
},
"blue_switch": {
"235, 235, 235": "235, 235, 255",
"245, 245, 245": "225, 225, 255",
"255, 255, 255": "245, 245, 255",
"240, 240, 240": "220, 220, 255",
"250, 250, 250": "210, 210, 255",
"220, 255, 220": "220, 220, 255",
"210, 255, 210": "210, 210, 255",
"_white": "_black"
},
"black_switch": {
"255, 255, 255": "55, 55, 55",
"245, 245, 245": "75, 75, 75",
"235, 235, 235": "65, 65, 65",
"240, 240, 240": "80, 80, 80",
"250, 250, 250": "90, 90, 90",
"220, 255, 220": "80, 80, 80",
"210, 255, 210": "90, 90, 90",
"0, 0, 0": "255, 255, 255",
"_black": "_white"
}
}
+582 -111
View File
@@ -1,150 +1,621 @@
import os, yara, numpy, locale, pefile, onnxruntime
from PIL import Image
import os, re, yara, time, math, json, numpy, base64, datetime, requests
import ctypes, ctypes.wintypes, hashlib, pefile, threading, onnxruntime
####################################################################################################
class GUID(ctypes.Structure):
_fields_ = [
("Data1", ctypes.wintypes.DWORD),
("Data2", ctypes.wintypes.WORD),
("Data3", ctypes.wintypes.WORD),
("Data4", ctypes.c_ubyte * 8)
]
class WINTRUST_FILE_INFO(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
("hFile", ctypes.wintypes.HANDLE),
("pgKnownSubject", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA_UNION(ctypes.Union):
_fields_ = [
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
("pCatalog", ctypes.wintypes.LPVOID),
("pBlob", ctypes.wintypes.LPVOID),
("pSgnr", ctypes.wintypes.LPVOID),
("pCert", ctypes.wintypes.LPVOID)
]
class WINTRUST_DATA(ctypes.Structure):
_fields_ = [
("cbStruct", ctypes.wintypes.DWORD),
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
("pSIPClientData", ctypes.wintypes.LPVOID),
("dwUIChoice", ctypes.wintypes.DWORD),
("fdwRevocationChecks", ctypes.wintypes.DWORD),
("dwUnionChoice", ctypes.wintypes.DWORD),
("u", WINTRUST_DATA_UNION),
("dwStateAction", ctypes.wintypes.DWORD),
("hWVTStateData", ctypes.wintypes.HANDLE),
("pwszURLReference", ctypes.wintypes.LPCWSTR),
("dwProvFlags", ctypes.wintypes.DWORD),
("dwUIContext", ctypes.wintypes.DWORD),
("pSignatureSettings", ctypes.wintypes.LPVOID)
]
####################################################################################################
class sign_scanner:
def __init__(self):
self.verify = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
def init_windll(self, path):
for name in path:
try:
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
except Exception:
pass
try:
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
self.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.c_void_p]
except Exception:
pass
####################################################################################################
def sign_verify(self, file_path):
if os.name != 'nt':
return False
try:
fi = WINTRUST_FILE_INFO()
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
fi.pcwszFilePath = os.path.abspath(file_path)
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
td.pSIPClientData = None
td.dwUIChoice = 2
td.fdwRevocationChecks = 0
td.dwUnionChoice = 1
td.u = wt_union
td.dwStateAction = 0
td.hWVTStateData = None
td.pwszURLReference = None
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(td)) == 0
except Exception:
return False
####################################################################################################
class rule_scanner:
def __init__(self):
self.rules = {}
self.rules = None
self.network = []
def load_path(self, path):
def load_path(self, path, callback=None):
yara_files = {}
for root, _, files in os.walk(path):
for file in files:
self.load_file(os.path.join(root, file))
full_path = os.path.join(root, file)
if callback:
callback(full_path)
ext = os.path.splitext(file)[1].lower()
if ext in ('.yara', '.yar'):
namespace = os.path.relpath(full_path, path).replace(os.sep, '_')
yara_files[namespace] = full_path
elif ext in ('.yc', '.yrc'):
self.load_compiled_rule(full_path)
elif ext in ('.ip', '.txt'):
self.load_network_list(full_path)
def load_file(self, file):
ext = os.path.splitext(file)[1].lower()
if yara_files:
self.compile_all_rules(yara_files)
####################################################################################################
def load_compiled_rule(self, file):
try:
if ext in ('.yara', '.yar'):
self.rules[file] = yara.compile(file)
elif ext in ('.yc', '.yrc'):
self.rules[file] = yara.load(file)
elif ext in ('.ip', '.txt'):
with open(file, "r", encoding="utf-8", errors="ignore") as f:
self.network.extend(line.strip() for line in f if line.strip())
self.rules = yara.load(file)
except Exception:
return False
pass
def load_network_list(self, file):
try:
with open(file, "r", encoding="utf-8", errors="ignore") as f:
self.network.extend(line.strip() for line in f if line.strip())
except Exception:
pass
def compile_all_rules(self, file_map):
try:
self.rules = yara.compile(filepaths=file_map)
except Exception:
pass
####################################################################################################
def yara_scan(self, file_path):
try:
data = open(file_path, "rb").read() if isinstance(file_path, str) else file_path
for rules in self.rules.values():
matches = rules.match(data=data)
if matches:
rule_name = str(matches[0])
label = rule_name.split("_")[0]
level = rule_name.split("_")[-1]
return f"Rules/{label}", level
if not self.rules:
return False, False
matches = self.rules.match(filepath=file_path)
if matches:
rule_name = str(matches[0])
label = rule_name.split("_")[0]
level = rule_name.split("_")[-1]
return f"{label}:WinPE/Unknown.A!rb", level
return False, False
except Exception:
return False, False
class model_scanner:
SHELLS = {
"0","1","2","3","4","5","6","7","8","9","cry","tvm","dec","enc","vmp","upx","aes","lzma","press","pack",
"enigma","protect","secur","asmstub","base","bss","clr_uef","cursors","transit","trs_age","engine","fio",
"fothk","h~;","icapsec","malloc_h","miniex","mssmixer","ndr64","nsys_wr","obr","wow","wow64svc","wpp_sf",
"pad","pgae","poolmi","proxy","qihoo","retpol","uedbg","tracesup","rwexec","rygs","s:@","sanontcp","segm",
"test","res","wisevec","viahwaes","orpc","nep","ace","extjmp","no_bbt","data","page","hexpthk"}
####################################################################################################
class pe_scanner:
def __init__(self):
self.models = {}
self.suffix = {".com", ".exe", ".dll", ".sys", ".scr", ".bat", ".cmd", ".ps1", ".vbs", ".wsf"}
self.labels = ["Pefile/White", "Script/White", "Pefile/General", "Script/General"]
self.detect = {"Pefile/General", "Script/General"}
self.resize = (224, 224)
self.model = None
self.input_name = None
self.feature_order = []
self.signer = sign_scanner()
self.signer.init_windll(["wintrust"])
def load_path(self, path):
def load_path(self, path, callback=None):
for root, _, files in os.walk(path):
for file in files:
self.load_file(os.path.join(root, file))
full_path = os.path.join(root, file)
if callback:
callback(full_path)
if full_path.endswith('.onnx'):
self.load_model(full_path)
def load_file(self, file):
if file.lower().endswith('.onnx'):
try:
self.models[file] = onnxruntime.InferenceSession(file)
except Exception:
return False
def is_valid_suffix(self, file_path):
return os.path.splitext(file_path)[-1].lower() in self.suffix
def model_scan(self, file_path):
def load_model(self, model_path):
if not os.path.exists(model_path):
return
try:
if not self.models or not self.is_valid_suffix(file_path):
return False, False
self.model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.input_name = self.model.get_inputs()[0].name
feat_path = os.path.join(os.path.dirname(model_path), "features.json")
if os.path.exists(feat_path):
with open(feat_path, 'r', encoding='utf-8') as f:
self.feature_order = json.load(f)
except Exception:
pass
model = next(iter(self.models.values()))
shape = model.get_inputs()[0].shape
channels = shape[-1] if shape[-1] in (1, 3) else (shape[1] if shape[1] in (1, 3) else 1)
def _safe_float(self, val):
try:
f = float(val)
if math.isinf(f) or math.isnan(f):
return 0.0
return f
except Exception:
return 0.0
file_sections = self.get_type(file_path)
if not file_sections:
return False, False
def _calc_entropy(self, data):
if not data:
return 0.0
arr = numpy.frombuffer(data, dtype=numpy.uint8)
counts = numpy.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-numpy.sum(probs * numpy.log2(probs)))
batch_images = [self.preprocess_image(data, self.resize, channels) for data in file_sections.values()]
arr = numpy.stack([numpy.asarray(img).astype('float32') / 255.0 for img in batch_images])
if arr.ndim == 3:
arr = arr[..., None]
if shape[-1] in (1, 3):
model_input = arr
elif shape[1] in (1, 3):
model_input = arr.transpose(0, 3, 1, 2)
def extract_features(self, file_path):
pe = None
try:
fsize = os.path.getsize(file_path)
if fsize == 0 or fsize > 536870912:
return None
with open(file_path, "rb") as f:
file_bytes = f.read()
base = {}
dlls = set()
apis = set()
pe = pefile.PE(data=file_bytes, fast_load=True)
base['TrustSigned'] = 1.0 if self.signer.sign_verify(file_path) else 0.0
base['FileEntropy'] = self._calc_entropy(file_bytes)
base['FileSize'] = float(fsize)
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
base['NumberOfSections'] = getattr(fh, 'NumberOfSections', 0)
base['TimeDateStamp'] = getattr(fh, 'TimeDateStamp', 0)
base['PointerToSymbolTable'] = getattr(fh, 'PointerToSymbolTable', 0)
base['NumberOfSymbols'] = getattr(fh, 'NumberOfSymbols', 0)
base['SizeOfOptionalHeader'] = getattr(fh, 'SizeOfOptionalHeader', 0)
base['Characteristics'] = getattr(fh, 'Characteristics', 0)
curr_ts = datetime.datetime.utcnow().timestamp()
base['HasInvalidTimestamp'] = 1.0 if (base['TimeDateStamp'] < 631152000 or base['TimeDateStamp'] > curr_ts + 2592000) else 0.0
base['FileTimeException'] = 1.0 if base['TimeDateStamp'] == 0 else 0.0
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
op = pe.OPTIONAL_HEADER
fields = [
'Magic', 'MajorLinkerVersion', 'MinorLinkerVersion', 'SizeOfCode',
'SizeOfInitializedData', 'SizeOfUninitializedData', 'AddressOfEntryPoint',
'BaseOfCode', 'ImageBase', 'SectionAlignment', 'FileAlignment',
'MajorOperatingSystemVersion', 'MinorOperatingSystemVersion',
'MajorImageVersion', 'MinorImageVersion', 'MajorSubsystemVersion',
'MinorSubsystemVersion', 'SizeOfImage', 'SizeOfHeaders', 'CheckSum',
'Subsystem', 'DllCharacteristics', 'SizeOfStackReserve', 'SizeOfStackCommit',
'SizeOfHeapReserve', 'SizeOfHeapCommit', 'LoaderFlags', 'NumberOfRvaAndSizes'
]
for f_field in fields:
base[f_field] = getattr(op, f_field, 0)
base['IsDriver'] = 1.0 if base.get('Subsystem') == 1 else 0.0
if hasattr(op, 'DATA_DIRECTORY'):
for i, directory in enumerate(op.DATA_DIRECTORY):
base[f'DataDirectory_{i}_Size'] = directory.Size
base[f'DataDirectory_{i}_VA'] = directory.VirtualAddress
exc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_EXCEPTION', 3)
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies = []
raw_sizes = []
v_sizes = []
exec_sec = 0
write_sec = 0
read_sec = 0
sec_exc = 0
for section in pe.sections:
try:
s_data = section.get_data()
s_entropy = self._calc_entropy(s_data)
except Exception:
s_entropy = 0.0
entropies.append(s_entropy)
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
for flag in char_flags:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
base['SectionMeanEntropy'] = sum(entropies) / len(entropies) if entropies else 0.0
base['SectionMaxRawSize'] = max(raw_sizes) if raw_sizes else 0.0
base['SectionMinRawSize'] = min(raw_sizes) if raw_sizes else 0.0
base['SectionMeanRawSize'] = sum(raw_sizes) / len(raw_sizes) if raw_sizes else 0.0
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = float(exec_sec)
base['WritableSections'] = float(write_sec)
base['ReadableSections'] = float(read_sec)
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
base[f'{key}Length'] = 0.0
if hasattr(pe, 'FileInfo'):
for fileinfo_list in pe.FileInfo:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
try:
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS']
])
except Exception:
pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0:
base['HasTlsCallbacks'] = 1.0
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
base['IsDebug'] = 1.0 if flags & 0x1 else 0.0
base['IsPreRelease'] = 1.0 if flags & 0x2 else 0.0
base['IsPatched'] = 1.0 if flags & 0x4 else 0.0
base['IsPrivateBuild'] = 1.0 if flags & 0x8 else 0.0
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = float(len(pe.DIRECTORY_ENTRY_IMPORT))
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try:
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception:
pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ImportFunctionCount'] = float(func_count)
else:
base['ImportCount'] = 0.0
base['ImportFunctionCount'] = 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols'):
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols))
else:
base['ExportCount'] = 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = float(icon_count)
else:
base['IconCount'] = 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
base['DebugCount'] = float(len(pe.DIRECTORY_ENTRY_DEBUG))
else:
base['DebugCount'] = 0.0
for k in base:
base[k] = self._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception:
return None
finally:
if pe:
pe.close()
def pe_scan(self, file_path, enhanced_mode=False):
if not self.model or not self.feature_order:
return False, False
try:
raw_data = self.extract_features(file_path)
if not raw_data:
return False, False
input_name = model.get_inputs()[0].name
predictions = model.run(None, {input_name: model_input})[0]
vec = numpy.zeros((1, len(self.feature_order)), dtype=numpy.float32)
base = raw_data.get('Base', {})
dlls = set(raw_data.get('DLLs', []))
apis = set(raw_data.get('APIs', []))
for i, feat in enumerate(self.feature_order):
if feat.startswith('Dll_'):
if feat[4:] in dlls:
vec[0, i] = 1.0
elif feat.startswith('Api_'):
if feat[4:] in apis:
vec[0, i] = 1.0
else:
vec[0, i] = base.get(feat, 0.0)
outputs = self.model.run(None, {self.input_name: vec})
result = outputs[1]
prob = 0.0
if isinstance(result, list) and len(result) > 0:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
prob = float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, numpy.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
prob = float(result[0][1])
score = int(prob * 100)
if score >= 80:
return f"General:WinPE/Malware.{score}!ml", score
elif enhanced_mode and score >= 50:
return f"General:WinPE/Suspicious.{score}!ml", score
label_scores = {label: [] for label in self.labels}
for pre_answers in predictions:
for k, score in enumerate(pre_answers):
label_scores[self.labels[k]].append(score)
label_percentage = {label: (sum(scores)/len(scores))*100 for label, scores in label_scores.items() if scores}
if not label_percentage:
return False, False
best_label, best_conf = max(label_percentage.items(), key=lambda x: x[1])
if best_label in self.detect:
return best_label, int(best_conf)
return False, False
except Exception:
return False, False
def preprocess_image(self, file_data, size, channels=1):
width, height = size
wah = int(numpy.ceil(numpy.sqrt(len(file_data) / channels)))
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
img[:len(file_data)] = arr
if channels == 1:
image = Image.fromarray(img.reshape((wah, wah)), 'L')
else:
image = Image.fromarray(img.reshape((wah, wah, channels)), 'RGB')
return image.resize((width, height), Image.Resampling.NEAREST)
####################################################################################################
def is_text_file(self, content, sample_size=1024):
raw = content[:sample_size]
if not raw:
return False
text_char = set(range(32, 127)) | {9, 10, 13}
nontext = sum(b not in text_char for b in raw)
return nontext / len(raw) < 0.15
class cloud_scanner:
def __init__(self):
self.session = None
self.api_host = None
self.api_key = None
self.timeout = 30
self.lock = threading.RLock()
def get_type(self, file_path):
match_data = {}
if not self.is_valid_suffix(file_path):
return match_data
def _init_session(self, api_host, api_key):
with self.lock:
if self.session is None or self.api_host != api_host or self.api_key != api_key:
self.api_host = api_host.rstrip('/')
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.1"})
def _request(self, method, endpoint, **kwargs):
with self.lock:
current_session = self.session
current_host = self.api_host
if not current_session:
return None
try:
with pefile.PE(file_path, fast_load=True) as pe:
sec = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']]
if bool(sec.VirtualAddress and sec.Size):
return {}
for section in pe.sections:
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
if (section.Characteristics & 0x00000020) and name not in self.SHELLS:
match_data[name] = section.get_data()
except pefile.PEFormatError:
r = current_session.request(method, f"{current_host}{endpoint}", timeout=self.timeout, **kwargs)
if r.status_code == 200:
return r
except Exception:
pass
return None
def calc_hash(self, file_path, block_size=65536):
try:
h = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(block_size), b""):
h.update(chunk)
return h.hexdigest()
except Exception:
return ""
####################################################################################################
def rescan(self, sha256, api_host, api_key):
self._init_session(api_host, api_key)
r = self._request("POST", f"/api/rescan/{sha256}")
return r is not None and r.json().get('status') == 'success'
def upload_file(self, file_path, api_host, api_key, chunk_size=5242880, need_rescan=False):
try:
self._init_session(api_host, api_key)
sha256 = self.calc_hash(file_path)
if not sha256:
return False, None
status_req = self._request("GET", f"/api/processing_status/{sha256}")
if status_req:
status_data = status_req.json()
current_status = status_data.get('status')
if current_status == 'done':
if need_rescan:
self.rescan(sha256, api_host, api_key)
return True, sha256
elif current_status in ('queued', 'processing'):
return True, sha256
file_size = os.path.getsize(file_path)
if file_size > 104857600:
return False, sha256
if file_size > chunk_size:
total_chunks = (file_size + chunk_size - 1) // chunk_size
upload_id = os.urandom(16).hex()
with open(file_path, 'rb') as f:
for i in range(total_chunks):
chunk_data = f.read(chunk_size)
headers = {
"X-Chunk-Index": str(i),
"X-Total-Chunks": str(total_chunks),
"X-Upload-ID": upload_id
}
chunk_success = False
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
if r:
chunk_success = True
break
time.sleep(1)
if not chunk_success:
return False, sha256
return True, sha256
with open(file_path, 'rb') as f:
file_content = f.read()
if self.is_text_file(file_content):
match_data[os.path.splitext(file_path)[-1].lower()] = file_content
return match_data
file_data = f.read()
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), file_data)})
if r:
return True, sha256
time.sleep(1)
return False, sha256
except Exception:
return False, None
####################################################################################################
def get_result(self, sha256, api_host, api_key, max_retries=6, interval=10):
try:
if not sha256:
return False
self._init_session(api_host, api_key)
is_done = False
for _ in range(max_retries):
r = self._request("GET", f"/api/processing_status/{sha256}")
if r:
st = r.json().get('status', 'error')
if st == 'done':
is_done = True
break
if st in ['error', 'failed']:
return False
time.sleep(interval)
if not is_done:
return False
r = self._request("GET", f"/api/report/{sha256}")
if r:
data = r.json().get('data', {})
metadata = data.get('metadata', {})
label = metadata.get('label', 'Unsupport')
score = metadata.get('score', 0)
sims = data.get('similar', [])
is_malicious = 'General' in label
sim_malicious_count = 0
valid_sim_count = 0
for s in sims:
if s.get('similarity', 0) > 80:
valid_sim_count += 1
if "General" in s.get('label', ''):
sim_malicious_count += 1
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return f"General:WinPE/Unknown.{score}!cl"
except Exception:
pass
return False
-1572
View File
File diff suppressed because it is too large. Load diff
-14520
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -2,9 +2,9 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.3.2',
version='3.5.4.0',
company_name="PYAS Security",
file_description="Python Antivirus Software",
file_description="PYAS Security Antivirus",
internal_name="PYAS",
legal_copyright="PYAS Security",
original_filename="PYAS.exe",
+171
View File
@@ -0,0 +1,171 @@
#pragma once
#include <fltKernel.h>
#include <ntddk.h>
#include <ntdddisk.h>
#include <ntddscsi.h>
#include <ntstrsafe.h>
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
constexpr auto MAX_PATH_LEN = 1024;
constexpr auto PYAS_POOL_TAG = 'SAYP';
#ifndef PROCESS_TERMINATE
#define PROCESS_TERMINATE (0x0001)
#endif
#ifndef PROCESS_CREATE_THREAD
#define PROCESS_CREATE_THREAD (0x0002)
#endif
#ifndef PROCESS_VM_OPERATION
#define PROCESS_VM_OPERATION (0x0008)
#endif
#ifndef PROCESS_VM_READ
#define PROCESS_VM_READ (0x0010)
#endif
#ifndef PROCESS_VM_WRITE
#define PROCESS_VM_WRITE (0x0020)
#endif
#ifndef PROCESS_DUP_HANDLE
#define PROCESS_DUP_HANDLE (0x0040)
#endif
#ifndef PROCESS_CREATE_PROCESS
#define PROCESS_CREATE_PROCESS (0x0080)
#endif
#ifndef PROCESS_SET_INFORMATION
#define PROCESS_SET_INFORMATION (0x0200)
#endif
#ifndef PROCESS_QUERY_INFORMATION
#define PROCESS_QUERY_INFORMATION (0x0400)
#endif
#ifndef PROCESS_SUSPEND_RESUME
#define PROCESS_SUSPEND_RESUME (0x0800)
#endif
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#endif
#ifndef PROCESS_SET_QUOTA
#define PROCESS_SET_QUOTA (0x0100)
#endif
#ifndef THREAD_TERMINATE
#define THREAD_TERMINATE (0x0001)
#endif
#ifndef THREAD_SUSPEND_RESUME
#define THREAD_SUSPEND_RESUME (0x0002)
#endif
#ifndef THREAD_SET_CONTEXT
#define THREAD_SET_CONTEXT (0x0010)
#endif
#ifndef THREAD_SET_INFORMATION
#define THREAD_SET_INFORMATION (0x0020)
#endif
#ifndef THREAD_SET_THREAD_TOKEN
#define THREAD_SET_THREAD_TOKEN (0x0080)
#endif
#ifndef POOL_FLAG_NON_PAGED
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
#endif
#ifndef FSCTL_MANAGE_BYPASS_IO
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
typedef enum _FS_BPIO_OPERATIONS {
FS_BPIO_OP_ENABLE = 1,
FS_BPIO_OP_DISABLE = 2,
FS_BPIO_OP_QUERY = 3,
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
FS_BPIO_OP_STREAM_PAUSE = 6,
FS_BPIO_OP_STREAM_RESUME = 7,
FS_BPIO_OP_GET_INFO = 8
} FS_BPIO_OPERATIONS;
typedef struct _FS_BPIO_INPUT {
FS_BPIO_OPERATIONS Operation;
ULONG InFlags;
ULONGLONG Reserved1;
ULONGLONG Reserved2;
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
typedef struct _FS_BPIO_OUTPUT {
FS_BPIO_OPERATIONS Operation;
ULONG OutFlags;
ULONGLONG Reserved1;
ULONGLONG Reserved2;
NTSTATUS Status;
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
#endif
typedef struct _PYAS_MESSAGE {
ULONG MessageCode;
ULONG ProcessId;
WCHAR Path[MAX_PATH_LEN];
} PYAS_MESSAGE, * PPYAS_MESSAGE;
typedef struct _PYAS_USER_MESSAGE {
ULONG Command;
WCHAR Path[MAX_PATH_LEN];
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
typedef struct _DRIVER_DATA {
PDRIVER_OBJECT DriverObject;
PFLT_FILTER FilterHandle;
PFLT_PORT ServerPort;
PFLT_PORT ClientPort;
PEPROCESS PyasProcess;
ULONG PyasPid;
LARGE_INTEGER Cookie;
KSPIN_LOCK PortMutex;
KSPIN_LOCK TrackerMutex;
EX_RUNDOWN_REF PortRundown;
} DRIVER_DATA, * PDRIVER_DATA;
typedef struct _RULE_NODE {
struct _RULE_NODE* Next;
UNICODE_STRING Pattern;
} RULE_NODE, * PRULE_NODE;
extern DRIVER_DATA GlobalData;
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
NTSTATUS InitializeProcessProtection();
VOID UninitializeProcessProtection();
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
VOID UninitializeRegistryProtection();
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
VOID UnloadRules();
VOID InitializeRulesEngine();
VOID UninitializeRulesEngine();
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
BOOLEAN IsTargetProtected(HANDLE ProcessId);
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
}
FORCEINLINE VOID PyasFree(PVOID Ptr) {
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
}
+215
View File
@@ -0,0 +1,215 @@
#include "DriverCommon.h"
DRIVER_DATA GlobalData;
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
UNREFERENCED_PARAMETER(VolumeDeviceType);
UNREFERENCED_PARAMETER(VolumeFilesystemType);
return STATUS_SUCCESS;
}
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
return STATUS_SUCCESS;
}
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(Flags);
}
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
if (!(Flags & FLTFL_FILTER_UNLOAD_MANDATORY)) {
return STATUS_FLT_DO_NOT_DETACH;
}
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
GlobalData.ServerPort = NULL;
}
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
UninitializeRegistryProtection();
UninitializeProcessProtection();
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
UnloadRules();
UninitializeRulesEngine();
return STATUS_SUCCESS;
}
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
UNREFERENCED_PARAMETER(PortCookie);
UNREFERENCED_PARAMETER(OutputBuffer);
UNREFERENCED_PARAMETER(OutputBufferLength);
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
if (msg->Command == 1 || msg->Command == 2) {
msg->Path[MAX_PATH_LEN - 1] = L'\0';
UNICODE_STRING us;
RtlInitUnicodeString(&us, msg->Path);
if (us.Length > 0) {
if (msg->Command == 1) {
AddDynamicWhitelist(&us);
}
else {
RemoveDynamicWhitelist(&us);
}
}
}
}
if (ReturnOutputBufferLength) {
*ReturnOutputBufferLength = 0;
}
return STATUS_SUCCESS;
}
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
UNREFERENCED_PARAMETER(ServerPortCookie);
UNREFERENCED_PARAMETER(ConnectionContext);
UNREFERENCED_PARAMETER(SizeOfContext);
UNREFERENCED_PARAMETER(ConnectionPortCookie);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = ClientPort;
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
GlobalData.PyasProcess = PsGetCurrentProcess();
ExReInitializeRundownProtection(&GlobalData.PortRundown);
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
return STATUS_SUCCESS;
}
static VOID PortDisconnect(PVOID ConnectionCookie) {
UNREFERENCED_PARAMETER(ConnectionCookie);
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
GlobalData.ClientPort = NULL;
GlobalData.PyasPid = 0;
GlobalData.PyasProcess = NULL;
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
}
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
{ IRP_MJ_OPERATION_END }
};
CONST FLT_REGISTRATION FilterRegistration = {
sizeof(FLT_REGISTRATION),
FLT_REGISTRATION_VERSION,
0,
NULL,
Callbacks,
DriverUnload,
InstanceSetup,
InstanceQueryTeardown,
InstanceTeardownStart,
InstanceTeardownComplete,
NULL,
NULL,
NULL
};
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
PSECURITY_DESCRIPTOR sd = NULL;
OBJECT_ATTRIBUTES oa = { 0 };
UNICODE_STRING name = { 0 };
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
GlobalData.DriverObject = DriverObject;
KeInitializeSpinLock(&GlobalData.PortMutex);
KeInitializeSpinLock(&GlobalData.TrackerMutex);
ExInitializeRundownProtection(&GlobalData.PortRundown);
InitializeRulesEngine();
status = LoadRulesFromDisk(RegistryPath);
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
UnloadRules();
UninitializeRulesEngine();
return status;
}
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
if (NT_SUCCESS(status)) {
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
FltFreeSecurityDescriptor(sd);
}
if (!NT_SUCCESS(status)) {
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
UnloadRules();
UninitializeRulesEngine();
return status;
}
InitializeProcessProtection();
InitializeRegistryProtection(DriverObject);
PsSetLoadImageNotifyRoutine(ImageLoadNotify);
status = FltStartFiltering(GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
UninitializeRegistryProtection();
UninitializeProcessProtection();
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
GlobalData.ServerPort = NULL;
}
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
UnloadRules();
UninitializeRulesEngine();
}
return status;
}
+52
View File
@@ -0,0 +1,52 @@
[Version]
Signature = "$WINDOWS NT$"
Class = ActivityMonitor
ClassGuid = {b86dff51-a31e-4bac-b3cf-e8cfe75c9fc2}
Provider = %ManufacturerName%
DriverVer = 04/26/2026,3.5.1.0
CatalogFile = PYAS_Driver.cat
PnpLockdown = 1
[DestinationDirs]
DefaultDestDir = 12
PYAS_Driver.DriverFiles = 12
[SourceDisksNames]
1 = %DiskName%,,,""
[SourceDisksFiles]
PYAS_Driver.sys = 1,,
[DefaultInstall.NTamd64]
OptionDesc = %ServiceDescription%
CopyFiles = PYAS_Driver.DriverFiles
[DefaultInstall.NTamd64.Services]
AddService = %ServiceName%,,PYAS_Driver.Service
[PYAS_Driver.DriverFiles]
PYAS_Driver.sys
[PYAS_Driver.Service]
DisplayName = %ServiceName%
Description = %ServiceDescription%
ServiceBinary = %12%\PYAS_Driver.sys
Dependencies = FltMgr
ServiceType = 2
StartType = 3
ErrorControl = 1
LoadOrderGroup = "FSFilter Activity Monitor"
AddReg = PYAS_Driver.AddReg
[PYAS_Driver.AddReg]
HKR,"Parameters","DebugFlags",0x00010001,0x0
HKR,"Parameters","SupportedFeatures",0x00010001,0x3
HKR,"Parameters\Instances","DefaultInstance",0x00000000,"PYAS Instance"
HKR,"Parameters\Instances\PYAS Instance","Altitude",0x00000000,"320000"
HKR,"Parameters\Instances\PYAS Instance","Flags",0x00010001,0x0
[Strings]
ManufacturerName = "PYAS Security"
DiskName = "PYAS_Driver Source Disk"
ServiceName = "PYAS_Driver"
ServiceDescription = "PYAS Security Driver"
+35
View File
@@ -0,0 +1,35 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36518.9 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PYAS_Driver", "PYAS_Driver.vcxproj", "{699C79A8-A904-1976-F72A-CDD7E811CFD2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.ActiveCfg = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Build.0 = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|ARM64.Deploy.0 = Debug|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.ActiveCfg = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Build.0 = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Debug|x64.Deploy.0 = Debug|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.ActiveCfg = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Build.0 = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|ARM64.Deploy.0 = Release|ARM64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.ActiveCfg = Release|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Build.0 = Release|x64
{699C79A8-A904-1976-F72A-CDD7E811CFD2}.Release|x64.Deploy.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D166699A-50D5-470E-9559-39B8E842528D}
EndGlobalSection
EndGlobal
Binary file not shown.
+128
View File
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{699C79A8-A904-1976-F72A-CDD7E811CFD2}</ProjectGuid>
<TemplateGuid>{dd38f7fc-d7bd-488b-9242-7d8754cde80d}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
<Configuration>Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
<RootNamespace>PYAS_Driver</RootNamespace>
<WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
<Driver_SpectreMitigation>false</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>WDM</DriverType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
<EnableInf2cat>false</EnableInf2cat>
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DriverSign>
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
</DriverSign>
<ClCompile>
<LanguageStandard>stdcpp20</LanguageStandard>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DriverSign>
<FileDigestAlgorithm>sha256</FileDigestAlgorithm>
</DriverSign>
<ClCompile>
<TreatWarningAsError>false</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Inf Include="PYAS_Driver.inf" />
</ItemGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="DriverCommon.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DriverEntry.cpp" />
<ClCompile Include="ProtectBoot.cpp" />
<ClCompile Include="ProtectFile.cpp" />
<ClCompile Include="ProtectProcess.cpp" />
<ClCompile Include="ProtectRegistry.cpp" />
<ClCompile Include="ProtectRules.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Driver Files">
<UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
<Extensions>inf;inv;inx;mof;mc;</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Inf Include="PYAS_Driver.inf">
<Filter>Driver Files</Filter>
</Inf>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DriverCommon.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ProtectRules.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectBoot.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectRegistry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ProtectProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DriverEntry.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+33
View File
@@ -0,0 +1,33 @@
#include "DriverCommon.h"
#ifndef IOCTL_DISK_FORMAT_TRACKS
#define IOCTL_DISK_FORMAT_TRACKS CTL_CODE(IOCTL_DISK_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#endif
#ifndef IOCTL_DISK_FORMAT_TRACKS_EX
#define IOCTL_DISK_FORMAT_TRACKS_EX CTL_CODE(IOCTL_DISK_BASE, 0x000b, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#endif
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
ULONG IoControlCode = Data->Iopb->Parameters.DeviceIoControl.Common.IoControlCode;
if (IoControlCode == IOCTL_DISK_SET_DRIVE_LAYOUT_EX ||
IoControlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT ||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS ||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
return FLT_PREOP_COMPLETE;
}
}
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
+292
View File
@@ -0,0 +1,292 @@
#include "DriverCommon.h"
static BOOLEAN IsHoneyToken(PUNICODE_STRING Path) {
if (!Path || !Path->Buffer) return FALSE;
if (WildcardMatch(L"*PYAS_Honey*", Path->Buffer, Path->Length) ||
WildcardMatch(L"*Backup_Secret*", Path->Buffer, Path->Length)) {
return TRUE;
}
return FALSE;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
UNICODE_STRING FileName = { 0 };
NTSTATUS status = STATUS_SUCCESS;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status)) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
FileName = nameInfo->Name;
if (!FileName.Buffer || FileName.Length == 0) {
FltReleaseFileNameInformation(nameInfo);
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
HANDLE Pid = PsGetCurrentProcessId();
BOOLEAN Trusted = IsProcessTrusted(Pid);
ULONG CreateDisposition = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF;
BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE ||
CreateDisposition == FILE_SUPERSEDE ||
CreateDisposition == FILE_OVERWRITE ||
CreateDisposition == FILE_OVERWRITE_IF ||
CreateDisposition == FILE_OPEN_IF);
if (IsHoneyToken(&FileName)) {
if (!Trusted) {
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE)) || IsCreateAction) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (WildcardMatch(L"\\Device\\PhysicalDrive*", FileName.Buffer, FileName.Length) ||
(WildcardMatch(L"\\Device\\Harddisk*", FileName.Buffer, FileName.Length) && !WildcardMatch(L"*Volume*", FileName.Buffer, FileName.Length))) {
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
(FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) {
if (!Trusted) {
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (CheckProtectedPathRule(&FileName)) {
if (!Trusted) {
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | GENERIC_WRITE)) || IsCreateAction) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
if (CheckFileExtensionRule(&FileName)) {
if (!Trusted) {
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (DELETE | FILE_DELETE_CHILD)) {
if (CheckRansomActivity(Pid, &FileName, NULL, 0, FALSE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
if (infoClass != FileDispositionInformation &&
infoClass != FileRenameInformation &&
infoClass != FileRenameInformationEx) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
if (CheckProtectedPathRule(&nameInfo->Name)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
if (CheckFileExtensionRule(&nameInfo->Name)) {
if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation;
if (SecurityInfo & (OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) {
if (CheckProtectedPathRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
goto cleanup;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(CompletionContext);
UNREFERENCED_PARAMETER(FltObjects);
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
ULONG WriteLength = Data->Iopb->Parameters.Write.Length;
if (WriteLength == 0) return FLT_PREOP_SUCCESS_NO_CALLBACK;
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
status = FltParseFileNameInformation(nameInfo);
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
if (CheckFileExtensionRule(&nameInfo->Name)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
PVOID SystemBuffer = NULL;
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
if (!Mdl) {
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
}
}
}
if (Mdl) {
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
}
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
cleanup:
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
UNREFERENCED_PARAMETER(FltObjects);
UNREFERENCED_PARAMETER(CompletionContext);
if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode != FSCTL_MANAGE_BYPASS_IO) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK;
FS_BPIO_OPERATIONS Operation;
__try {
Operation = InputBuffer->Operation;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
if (Operation == FS_BPIO_OP_ENABLE) {
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
callbackStatus = FLT_PREOP_COMPLETE;
}
}
}
}
if (nameInfo) {
FltReleaseFileNameInformation(nameInfo);
}
return callbackStatus;
}
+219
View File
@@ -0,0 +1,219 @@
#include "DriverCommon.h"
static PVOID ObRegistrationHandle = NULL;
static OB_CALLBACK_REGISTRATION ObRegistration;
static OB_OPERATION_REGISTRATION ObCallbacks[2];
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
if (!FullImageName || !FullImageName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\System32\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\SysWOW64\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\WinSxS\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Windows\\Microsoft.NET\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Common Files\\Microsoft Shared\\*", FullImageName->Buffer, FullImageName->Length) ||
WildcardMatch(L"*\\Program Files*\\*", FullImageName->Buffer, FullImageName->Length)) {
return TRUE;
}
return FALSE;
}
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) {
UNREFERENCED_PARAMETER(ImageInfo);
if (KeGetCurrentIrql() > APC_LEVEL) return;
if (!FullImageName || !FullImageName->Buffer) return;
if (ProcessId == (HANDLE)0) return;
if (IsSystemImage(FullImageName)) return;
if (IsTargetProtected(ProcessId)) {
if (CheckFileExtensionRule(FullImageName)) {
SendMessageToUser(6001, (ULONG)(ULONG_PTR)ProcessId, FullImageName->Buffer, FullImageName->Length);
}
}
}
static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
if (ProcessId == (HANDLE)4) return TRUE;
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
NTSTATUS status;
BOOLEAN isCritical = FALSE;
PEPROCESS Process = NULL;
PUNICODE_STRING imageFileName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return FALSE;
status = SeLocateProcessImageName(Process, &imageFileName);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
if (WildcardMatch(L"*\\Windows\\System32\\csrss.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\lsass.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\services.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\wininit.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) {
isCritical = TRUE;
}
ExFreePool(imageFileName);
}
ObDereferenceObject(Process);
return isCritical;
}
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
NTSTATUS status;
BOOLEAN isBlacklisted = FALSE;
PEPROCESS Process = NULL;
PUNICODE_STRING imageFileName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return FALSE;
status = SeLocateProcessImageName(Process, &imageFileName);
if (NT_SUCCESS(status) && imageFileName) {
if (imageFileName->Buffer) {
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
isBlacklisted = TRUE;
}
}
ExFreePool(imageFileName);
}
ObDereferenceObject(Process);
return isBlacklisted;
}
static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
UNREFERENCED_PARAMETER(RegistrationContext);
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
if (!TargetProcess) return OB_PREOP_SUCCESS;
HANDLE TargetPid = PsGetProcessId(TargetProcess);
HANDLE SourcePid = PsGetCurrentProcessId();
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
if (IsBlacklistedAdminTool(SourcePid)) {
bIsTrusted = FALSE;
}
}
if (bIsTrusted) return OB_PREOP_SUCCESS;
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE |
PROCESS_CREATE_THREAD |
PROCESS_VM_READ |
PROCESS_DUP_HANDLE |
PROCESS_SUSPEND_RESUME |
PROCESS_SET_INFORMATION |
PROCESS_SET_QUOTA;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
return OB_PREOP_SUCCESS;
}
static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
UNREFERENCED_PARAMETER(RegistrationContext);
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
PETHREAD TargetThread = (PETHREAD)OperationInformation->Object;
if (!TargetThread) return OB_PREOP_SUCCESS;
PEPROCESS TargetProcess = IoThreadToProcess(TargetThread);
if (!TargetProcess) return OB_PREOP_SUCCESS;
HANDLE TargetPid = PsGetProcessId(TargetProcess);
HANDLE SourcePid = PsGetCurrentProcessId();
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
if (IsTargetProtected(TargetPid)) {
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
if (bIsTrusted) {
if (IsBlacklistedAdminTool(SourcePid)) {
bIsTrusted = FALSE;
}
}
if (bIsTrusted) return OB_PREOP_SUCCESS;
ACCESS_MASK DenyMask = THREAD_TERMINATE |
THREAD_SUSPEND_RESUME |
THREAD_SET_CONTEXT |
THREAD_SET_INFORMATION |
THREAD_SET_THREAD_TOKEN;
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
}
}
return OB_PREOP_SUCCESS;
}
NTSTATUS InitializeProcessProtection() {
static UNICODE_STRING Altitude = { 0 };
RtlInitUnicodeString(&Altitude, L"320000");
ObCallbacks[0].ObjectType = PsProcessType;
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ObCallbacks[0].PreOperation = PreOpenProcess;
ObCallbacks[0].PostOperation = NULL;
ObCallbacks[1].ObjectType = PsThreadType;
ObCallbacks[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ObCallbacks[1].PreOperation = PreOpenThread;
ObCallbacks[1].PostOperation = NULL;
ObRegistration.Version = OB_FLT_REGISTRATION_VERSION;
ObRegistration.OperationRegistrationCount = 2;
ObRegistration.Altitude = Altitude;
ObRegistration.RegistrationContext = NULL;
ObRegistration.OperationRegistration = ObCallbacks;
return ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
}
VOID UninitializeProcessProtection() {
if (ObRegistrationHandle) {
ObUnRegisterCallbacks(ObRegistrationHandle);
ObRegistrationHandle = NULL;
}
}
+248
View File
@@ -0,0 +1,248 @@
#include "DriverCommon.h"
static LARGE_INTEGER Cookie;
static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) {
if (!KeyName || !KeyName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Services\\bam\\*", KeyName->Buffer, KeyName->Length)) {
return TRUE;
}
return FALSE;
}
static BOOLEAN IsWriteAccess(ACCESS_MASK Access) {
if (Access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK |
DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) {
return TRUE;
}
return FALSE;
}
static PWCHAR GetFullPath(PVOID RootObject, PUNICODE_STRING CompleteName, PULONG OutLength) {
PWCHAR Buffer = NULL;
ULONG TotalSize = 0;
PCUNICODE_STRING RootName = NULL;
BOOLEAN NeedFreeRootName = FALSE;
if (RootObject) {
NTSTATUS status = CmCallbackGetKeyObjectIDEx(&Cookie, RootObject, NULL, &RootName, 0);
if (NT_SUCCESS(status) && RootName) {
NeedFreeRootName = TRUE;
}
}
ULONG RootLen = (RootName && RootName->Buffer) ? RootName->Length : 0;
ULONG RelLen = (CompleteName && CompleteName->Buffer) ? CompleteName->Length : 0;
TotalSize = RootLen + sizeof(WCHAR) + RelLen + sizeof(WCHAR);
Buffer = (PWCHAR)PyasAllocate(TotalSize);
if (!Buffer) {
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
return NULL;
}
RtlZeroMemory(Buffer, TotalSize);
PWCHAR Current = Buffer;
if (RootLen > 0) {
RtlCopyMemory(Current, RootName->Buffer, RootLen);
Current += (RootLen / sizeof(WCHAR));
if (Current > Buffer && *(Current - 1) != L'\\') {
*Current = L'\\';
Current++;
}
}
if (RelLen > 0) {
if (RootLen > 0 && CompleteName->Buffer[0] == L'\\') {
RtlCopyMemory(Current, CompleteName->Buffer + 1, RelLen - sizeof(WCHAR));
}
else {
RtlCopyMemory(Current, CompleteName->Buffer, RelLen);
}
}
if (NeedFreeRootName) CmCallbackReleaseKeyObjectIDEx(RootName);
if (OutLength) *OutLength = (ULONG)wcslen(Buffer) * sizeof(WCHAR);
return Buffer;
}
static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2) {
UNREFERENCED_PARAMETER(CallbackContext);
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_SUCCESS;
REG_NOTIFY_CLASS Class = (REG_NOTIFY_CLASS)(ULONG_PTR)Argument1;
NTSTATUS status = STATUS_SUCCESS;
if (Class == RegNtPreCreateKey || Class == RegNtPreOpenKey || Class == RegNtPreOpenKeyEx) {
PVOID RootObject = NULL;
PUNICODE_STRING CompleteName = NULL;
ACCESS_MASK DesiredAccess = 0;
if (Class == RegNtPreCreateKey) {
REG_CREATE_KEY_INFORMATION* Info = (REG_CREATE_KEY_INFORMATION*)Argument2;
RootObject = Info->RootObject;
CompleteName = Info->CompleteName;
DesiredAccess = Info->DesiredAccess;
}
else {
REG_OPEN_KEY_INFORMATION* Info = (REG_OPEN_KEY_INFORMATION*)Argument2;
RootObject = Info->RootObject;
CompleteName = Info->CompleteName;
DesiredAccess = Info->DesiredAccess;
}
if (!IsWriteAccess(DesiredAccess)) {
return STATUS_SUCCESS;
}
ULONG PathLen = 0;
PWCHAR FullPathBuffer = GetFullPath(RootObject, CompleteName, &PathLen);
if (FullPathBuffer) {
UNICODE_STRING FullPath;
RtlInitUnicodeString(&FullPath, FullPathBuffer);
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(FullPathBuffer);
}
return status;
}
if (Class == RegNtPreDeleteKey) {
REG_DELETE_KEY_INFORMATION* Info = (REG_DELETE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (CheckRegistryRule(KeyName)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
status = STATUS_ACCESS_DENIED;
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
if (Class == RegNtPreSetValueKey) {
REG_SET_VALUE_KEY_INFORMATION* Info = (REG_SET_VALUE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
if (Info->ValueName && Info->ValueName->Length > 0) {
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
RtlAppendUnicodeToString(&FullPath, L"\\");
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
}
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
if (Class == RegNtPreDeleteValueKey) {
REG_DELETE_VALUE_KEY_INFORMATION* Info = (REG_DELETE_VALUE_KEY_INFORMATION*)Argument2;
PCUNICODE_STRING KeyName = NULL;
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
if (KeyName) {
if (KeyName->Buffer) {
if (!IsBamRegistryPath(KeyName)) {
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
(Info->ValueName ? Info->ValueName->Length : 0) +
sizeof(WCHAR);
if (FullSize <= 0xFFFF) {
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
if (Buffer) {
UNICODE_STRING FullPath;
FullPath.Buffer = Buffer;
FullPath.Length = 0;
FullPath.MaximumLength = (USHORT)FullSize;
RtlCopyUnicodeString(&FullPath, KeyName);
if (Info->ValueName && Info->ValueName->Length > 0) {
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
RtlAppendUnicodeToString(&FullPath, L"\\");
}
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
}
if (CheckRegistryRule(&FullPath)) {
HANDLE Pid = PsGetCurrentProcessId();
if (!IsProcessTrusted(Pid)) {
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
status = STATUS_ACCESS_DENIED;
}
}
PyasFree(Buffer);
}
}
}
}
CmCallbackReleaseKeyObjectIDEx(KeyName);
}
}
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
UNICODE_STRING Altitude;
RtlInitUnicodeString(&Altitude, L"320000");
return CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
}
VOID UninitializeRegistryProtection() {
if (Cookie.QuadPart != 0) {
CmUnRegisterCallback(Cookie);
Cookie.QuadPart = 0;
}
}
+933
View File
@@ -0,0 +1,933 @@
#include "DriverCommon.h"
constexpr auto MAX_TRACKERS = 64;
constexpr auto RANSOM_TIME_WINDOW_MS = 3000;
constexpr auto RANSOM_COUNT_THRESHOLD = 5;
constexpr auto HIGH_ENTROPY_THRESHOLD = 10;
constexpr auto TRUST_CACHE_SIZE = 128;
constexpr auto TRUST_CACHE_TTL_SEC = 300;
typedef struct _RANSOM_TRACKER {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
ULONG ActivityCount;
LARGE_INTEGER LastActivityTime;
} RANSOM_TRACKER, * PRANSOM_TRACKER;
typedef struct _TRUST_CACHE_ENTRY {
PEPROCESS Process;
LARGE_INTEGER ProcessCreateTime;
BOOLEAN IsTrusted;
LARGE_INTEGER CacheTime;
} TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY;
static RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
static TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
static KSPIN_LOCK TrustCacheLock;
static BOOLEAN g_CacheInitialized = FALSE;
static ERESOURCE RuleLock;
static PRULE_NODE g_RegistryBlockList = NULL;
static PRULE_NODE g_RegistryTrustedList = NULL;
static PRULE_NODE g_ProcessTrustedPaths = NULL;
static PRULE_NODE g_ProcessExploitable = NULL;
static PRULE_NODE g_FileProtectedPaths = NULL;
static PRULE_NODE g_FileExceptionPaths = NULL;
static PRULE_NODE g_FileRansomExts = NULL;
const PCWSTR Helper_NaturallyCompressedExtensions[] = {
L".zip", L".7z", L".rar", L".tar", L".gz",
L".jpg", L".jpeg", L".png", L".webp", L".gif",
L".mp3", L".wav", L".aac", L".ogg", L".flac",
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
L".docx", L".xlsx", L".pptx", L".pdf", L".wps",
L".apk", L".jar", L".class", L".db", L".sqlite"
};
static VOID FreeList(PRULE_NODE* Head) {
PRULE_NODE Current = *Head;
while (Current) {
PRULE_NODE Next = Current->Next;
if (Current->Pattern.Buffer) PyasFree(Current->Pattern.Buffer);
PyasFree(Current);
Current = Next;
}
*Head = NULL;
}
static VOID AddRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Check = *Head;
while (Check) {
if (RtlEqualUnicodeString(&Check->Pattern, RuleStr, TRUE)) {
return;
}
Check = Check->Next;
}
PRULE_NODE Node = (PRULE_NODE)PyasAllocate(sizeof(RULE_NODE));
if (!Node) return;
SIZE_T Size = RuleStr->Length + sizeof(WCHAR);
Node->Pattern.Buffer = (PWCHAR)PyasAllocate(Size);
if (!Node->Pattern.Buffer) {
PyasFree(Node);
return;
}
RtlCopyMemory(Node->Pattern.Buffer, RuleStr->Buffer, RuleStr->Length);
Node->Pattern.Buffer[RuleStr->Length / sizeof(WCHAR)] = L'\0';
Node->Pattern.Length = RuleStr->Length;
Node->Pattern.MaximumLength = (USHORT)Size;
Node->Next = *Head;
*Head = Node;
}
static BOOLEAN HasSuffix(PCUNICODE_STRING String, PCWSTR Suffix) {
if (!String || !String->Buffer || !Suffix) return FALSE;
SIZE_T StringLenChars = String->Length / sizeof(WCHAR);
SIZE_T SuffixLenChars = 0;
while (Suffix[SuffixLenChars] != L'\0') {
SuffixLenChars++;
}
if (StringLenChars < SuffixLenChars) return FALSE;
PCWSTR Ptr = String->Buffer + (StringLenChars - SuffixLenChars);
for (SIZE_T i = 0; i < SuffixLenChars; i++) {
if (RtlDowncaseUnicodeChar(Ptr[i]) != RtlDowncaseUnicodeChar(Suffix[i])) {
return FALSE;
}
}
return TRUE;
}
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes) {
if (Pattern == NULL || String == NULL) return FALSE;
USHORT StringLenChars = StringLengthBytes / sizeof(WCHAR);
PCWSTR mp = NULL;
PCWSTR cp = NULL;
PCWSTR StringEnd = String + StringLenChars;
while (String < StringEnd) {
if (*Pattern == L'*') {
mp = ++Pattern;
cp = String + 1;
}
else if (*Pattern == L'?' || (RtlDowncaseUnicodeChar(*Pattern) == RtlDowncaseUnicodeChar(*String))) {
Pattern++;
String++;
}
else if (mp != NULL) {
Pattern = mp;
String = cp++;
}
else {
return FALSE;
}
}
while (*Pattern == L'*') {
Pattern++;
}
return (*Pattern == L'\0') ? TRUE : FALSE;
}
static ULONG ProcessJsonUnescape(PWCHAR Buffer, ULONG LengthChars) {
if (!Buffer || LengthChars == 0) return 0;
ULONG WriteIdx = 0;
ULONG ReadIdx = 0;
while (ReadIdx < LengthChars) {
if (Buffer[ReadIdx] == L'\\' && (ReadIdx + 1 < LengthChars)) {
WCHAR NextChar = Buffer[ReadIdx + 1];
if (NextChar == L'\\' || NextChar == L'"' || NextChar == L'/') {
Buffer[WriteIdx++] = NextChar;
ReadIdx += 2;
}
else if (NextChar == L'n') { Buffer[WriteIdx++] = L'\n'; ReadIdx += 2; }
else if (NextChar == L'r') { Buffer[WriteIdx++] = L'\r'; ReadIdx += 2; }
else if (NextChar == L't') { Buffer[WriteIdx++] = L'\t'; ReadIdx += 2; }
else {
Buffer[WriteIdx++] = Buffer[ReadIdx++];
}
}
else {
Buffer[WriteIdx++] = Buffer[ReadIdx++];
}
}
Buffer[WriteIdx] = L'\0';
return WriteIdx * sizeof(WCHAR);
}
static VOID SkipWhitespace(PCHAR* Ptr, PCHAR End) {
while (*Ptr < End) {
char c = **Ptr;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
(*Ptr)++;
}
else {
break;
}
}
}
static VOID ParseAndLoadRules(PCHAR JsonContent, ULONG ContentLength, PCSTR KeyName, PRULE_NODE* ListHead) {
if (!JsonContent || !KeyName || !ListHead) return;
PCHAR Ptr = JsonContent;
PCHAR End = JsonContent + ContentLength;
SIZE_T KeyLen = 0;
while (KeyName[KeyLen] != '\0') KeyLen++;
while (Ptr < End) {
if (*Ptr == '"') {
if ((SIZE_T)(End - Ptr) > KeyLen && RtlCompareMemory(Ptr + 1, KeyName, KeyLen) == KeyLen) {
if (*(Ptr + 1 + KeyLen) == '"') {
Ptr += 1 + KeyLen + 1;
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr != ':') continue;
Ptr++;
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr != '[') continue;
Ptr++;
while (Ptr < End) {
SkipWhitespace(&Ptr, End);
if (Ptr >= End || *Ptr == ']') {
if (Ptr < End) Ptr++;
return;
}
if (*Ptr == '"') {
PCHAR StartQuote = ++Ptr;
BOOLEAN Escaped = FALSE;
while (Ptr < End) {
if (*Ptr == '"' && Escaped == FALSE) break;
if (*Ptr == '\\') {
Escaped = (Escaped == FALSE) ? TRUE : FALSE;
}
else {
Escaped = FALSE;
}
Ptr++;
}
if (Ptr < End && *Ptr == '"') {
ULONG UTF8Len = (ULONG)(Ptr - StartQuote);
if (UTF8Len > 0) {
ULONG WideSize = 0;
RtlUTF8ToUnicodeN(NULL, 0, &WideSize, StartQuote, UTF8Len);
if (WideSize > 0) {
PWCHAR WideBuffer = (PWCHAR)PyasAllocate(WideSize + sizeof(WCHAR));
if (WideBuffer) {
ULONG ResultSize = 0;
RtlUTF8ToUnicodeN(WideBuffer, WideSize, &ResultSize, StartQuote, UTF8Len);
ULONG FinalSize = ProcessJsonUnescape(WideBuffer, ResultSize / sizeof(WCHAR));
UNICODE_STRING Us;
Us.Buffer = WideBuffer;
Us.Length = (USHORT)FinalSize;
Us.MaximumLength = (USHORT)(WideSize + sizeof(WCHAR));
AddRule(ListHead, &Us);
PyasFree(WideBuffer);
}
}
}
Ptr++;
}
}
else {
Ptr++;
}
}
return;
}
}
}
Ptr++;
}
}
VOID InitializeRulesEngine() {
ExInitializeResourceLite(&RuleLock);
KeInitializeSpinLock(&TrustCacheLock);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
g_CacheInitialized = TRUE;
}
VOID UninitializeRulesEngine() {
ExDeleteResourceLite(&RuleLock);
g_CacheInitialized = FALSE;
}
static VOID RemoveRule(PRULE_NODE* Head, PUNICODE_STRING RuleStr) {
if (!RuleStr || !RuleStr->Buffer) return;
PRULE_NODE Current = *Head;
PRULE_NODE Previous = NULL;
while (Current) {
if (RtlEqualUnicodeString(&Current->Pattern, RuleStr, TRUE)) {
PRULE_NODE ToDelete = Current;
if (Previous) {
Previous->Next = Current->Next;
}
else {
*Head = Current->Next;
}
Current = Current->Next;
if (ToDelete->Pattern.Buffer) PyasFree(ToDelete->Pattern.Buffer);
PyasFree(ToDelete);
}
else {
Previous = Current;
Current = Current->Next;
}
}
}
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
AddRule(&g_ProcessTrustedPaths, RuleStr);
AddRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr) {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
RemoveRule(&g_ProcessTrustedPaths, RuleStr);
RemoveRule(&g_FileExceptionPaths, RuleStr);
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
RtlZeroMemory(TrustCache, sizeof(TrustCache));
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath) {
NTSTATUS status = STATUS_SUCCESS;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatus = { 0 };
OBJECT_ATTRIBUTES oa = { 0 };
UNICODE_STRING ImagePathName;
OBJECT_ATTRIBUTES RegOa = { 0 };
HANDLE RegHandle = NULL;
PKEY_VALUE_PARTIAL_INFORMATION Info = NULL;
ULONG ResultLength = 0;
PWCHAR PathBuffer = NULL;
SIZE_T PathBufferSize = 0;
UNICODE_STRING FinalPath = { 0 };
RtlInitUnicodeString(&ImagePathName, L"ImagePath");
InitializeObjectAttributes(&RegOa, RegistryPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwOpenKey(&RegHandle, KEY_READ, &RegOa);
if (!NT_SUCCESS(status)) return status;
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, NULL, 0, &ResultLength);
if (status != STATUS_BUFFER_TOO_SMALL) {
ZwClose(RegHandle);
return status;
}
Info = (PKEY_VALUE_PARTIAL_INFORMATION)PyasAllocate(ResultLength);
if (!Info) {
ZwClose(RegHandle);
return STATUS_INSUFFICIENT_RESOURCES;
}
status = ZwQueryValueKey(RegHandle, &ImagePathName, KeyValuePartialInformation, Info, ResultLength, &ResultLength);
ZwClose(RegHandle);
if (!NT_SUCCESS(status)) {
PyasFree(Info);
return status;
}
if (Info->Type == REG_EXPAND_SZ || Info->Type == REG_SZ) {
PathBufferSize = Info->DataLength + 1024;
PathBuffer = (PWCHAR)PyasAllocate(PathBufferSize);
if (PathBuffer) {
RtlZeroMemory(PathBuffer, PathBufferSize);
if (Info->DataLength > 0) {
RtlCopyMemory(PathBuffer, Info->Data, Info->DataLength);
}
PWCHAR LastSlash = NULL;
PWCHAR Current = PathBuffer;
while (*Current) {
if (*Current == L'\\') LastSlash = Current;
Current++;
}
if (LastSlash) {
*LastSlash = L'\0';
SIZE_T CurrentPathLen = wcslen(PathBuffer);
const WCHAR FilterSuffix[] = L"\\Filter";
SIZE_T FilterLen = (sizeof(FilterSuffix) / sizeof(WCHAR)) - 1;
if (CurrentPathLen >= FilterLen) {
PWCHAR SuffixStart = PathBuffer + CurrentPathLen - FilterLen;
BOOLEAN Match = TRUE;
for (SIZE_T i = 0; i < FilterLen; i++) {
if (RtlDowncaseUnicodeChar(SuffixStart[i]) != RtlDowncaseUnicodeChar(FilterSuffix[i])) {
Match = FALSE;
break;
}
}
if (Match) {
*SuffixStart = L'\0';
}
}
RtlStringCbCatW(PathBuffer, PathBufferSize, L"\\Rules\\Rules_Driver_P1.json");
if (wcsncmp(PathBuffer, L"\\??\\", 4) != 0 &&
wcsncmp(PathBuffer, L"\\SystemRoot", 11) != 0 &&
wcsncmp(PathBuffer, L"\\DosDevices\\", 12) != 0) {
PWCHAR TmpBuffer = (PWCHAR)PyasAllocate(PathBufferSize + 16);
if (TmpBuffer) {
RtlStringCbCopyW(TmpBuffer, PathBufferSize + 16, L"\\??\\");
RtlStringCbCatW(TmpBuffer, PathBufferSize + 16, PathBuffer);
PyasFree(PathBuffer);
PathBuffer = TmpBuffer;
}
}
RtlInitUnicodeString(&FinalPath, PathBuffer);
}
}
}
PyasFree(Info);
if (!FinalPath.Buffer) {
if (PathBuffer) PyasFree(PathBuffer);
return STATUS_UNSUCCESSFUL;
}
InitializeObjectAttributes(&oa, &FinalPath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &oa, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (NT_SUCCESS(status)) {
FILE_STANDARD_INFORMATION FileInfo = { 0 };
status = ZwQueryInformationFile(FileHandle, &IoStatus, &FileInfo, sizeof(FileInfo), FileStandardInformation);
if (NT_SUCCESS(status) && FileInfo.EndOfFile.LowPart > 0) {
PVOID FileBuffer = PyasAllocate(FileInfo.EndOfFile.LowPart + 1);
if (FileBuffer) {
status = ZwReadFile(FileHandle, NULL, NULL, NULL, &IoStatus, FileBuffer, FileInfo.EndOfFile.LowPart, NULL, NULL);
if (NT_SUCCESS(status)) {
((PCHAR)FileBuffer)[FileInfo.EndOfFile.LowPart] = '\0';
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_BlockList", &g_RegistryBlockList);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Registry_TrustedList", &g_RegistryTrustedList);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_TrustedPaths", &g_ProcessTrustedPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_Process_ExploitableBlacklist", &g_ProcessExploitable);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ProtectedPaths", &g_FileProtectedPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_ExceptionPaths", &g_FileExceptionPaths);
ParseAndLoadRules((PCHAR)FileBuffer, FileInfo.EndOfFile.LowPart, "Rule_File_RansomwareExtensions", &g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
PyasFree(FileBuffer);
}
}
ZwClose(FileHandle);
}
if (PathBuffer) PyasFree(PathBuffer);
return status;
}
VOID UnloadRules() {
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&RuleLock, TRUE);
FreeList(&g_RegistryBlockList);
FreeList(&g_RegistryTrustedList);
FreeList(&g_ProcessTrustedPaths);
FreeList(&g_ProcessExploitable);
FreeList(&g_FileProtectedPaths);
FreeList(&g_FileExceptionPaths);
FreeList(&g_FileRansomExts);
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
}
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName) {
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
return STATUS_UNSUCCESSFUL;
}
NTSTATUS status;
PEPROCESS Process = NULL;
*ImageName = NULL;
status = PsLookupProcessByProcessId(ProcessId, &Process);
if (!NT_SUCCESS(status)) return status;
status = SeLocateProcessImageName(Process, ImageName);
ObDereferenceObject(Process);
return status;
}
static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) {
if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE;
if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE;
if (WildcardMatch(L"*\\Windows\\explorer.exe", Buffer, Length)) return TRUE;
return FALSE;
}
BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
if (ProcessId == (HANDLE)4) return TRUE;
PEPROCESS Process = NULL;
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
return FALSE;
}
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
if (TrustCache[Hash].Process == Process && TrustCache[Hash].ProcessCreateTime.QuadPart == createTime.QuadPart) {
LARGE_INTEGER Now;
KeQuerySystemTime(&Now);
if ((Now.QuadPart - TrustCache[Hash].CacheTime.QuadPart) < (TRUST_CACHE_TTL_SEC * 10000000LL)) {
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
ObDereferenceObject(Process);
return cachedResult;
}
}
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
ObDereferenceObject(Process);
return FALSE;
}
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isTrusted = FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
PRULE_NODE Node = g_ProcessExploitable;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
goto update_cache;
}
Node = Node->Next;
}
if (IsWindowsSystemApp(imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
goto update_cache;
}
Node = g_ProcessTrustedPaths;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, imageFileName->Buffer, imageFileName->Length)) {
isTrusted = TRUE;
goto update_cache;
}
Node = Node->Next;
}
}
update_cache:
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
TrustCache[Hash].Process = Process;
TrustCache[Hash].ProcessCreateTime = createTime;
TrustCache[Hash].IsTrusted = isTrusted;
KeQuerySystemTime(&TrustCache[Hash].CacheTime);
KeReleaseSpinLock(&TrustCacheLock, OldIrql);
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
if (imageFileName) ExFreePool(imageFileName);
ObDereferenceObject(Process);
return isTrusted;
}
BOOLEAN IsTargetProtected(HANDLE ProcessId) {
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
return FALSE;
}
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName) {
if (!KeyName || !KeyName->Buffer) return FALSE;
if (KeyName->Length < 4 * sizeof(WCHAR)) return FALSE;
if (WildcardMatch(L"*{645FF040-5081-101B-9F08-00AA002F954E}\\DefaultIcon", KeyName->Buffer, KeyName->Length)) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE AllowNode = g_RegistryTrustedList;
while (AllowNode) {
if (WildcardMatch(AllowNode->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
AllowNode = AllowNode->Next;
}
PRULE_NODE Node = g_RegistryBlockList;
BOOLEAN Match = FALSE;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, KeyName->Buffer, KeyName->Length)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
}
PRULE_NODE Node = g_FileRansomExts;
BOOLEAN Match = FALSE;
while (Node) {
if (HasSuffix(FileName, Node->Pattern.Buffer)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\System32\\config\\systemprofile*", FileName->Buffer, FileName->Length)) {
return FALSE;
}
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&RuleLock, TRUE);
PRULE_NODE ExNode = g_FileExceptionPaths;
while (ExNode) {
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return FALSE;
}
ExNode = ExNode->Next;
}
PRULE_NODE Node = g_FileProtectedPaths;
BOOLEAN Match = FALSE;
while (Node) {
if (WildcardMatch(Node->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
Match = TRUE;
break;
}
Node = Node->Next;
}
ExReleaseResourceLite(&RuleLock);
KeLeaveCriticalRegion();
return Match;
}
static BOOLEAN IsHighEntropy(PVOID Buffer, ULONG Length) {
if (!Buffer || Length < 256) return FALSE;
PUSHORT Histogram = (PUSHORT)PyasAllocate(256 * sizeof(USHORT));
if (!Histogram) return FALSE;
PUCHAR Ptr = (PUCHAR)Buffer;
ULONG ScanLen = (Length > 1024) ? 1024 : Length;
__try {
for (ULONG i = 0; i < ScanLen; i++) {
Histogram[Ptr[i]]++;
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(Histogram);
return FALSE;
}
USHORT MaxFreq = 0;
for (int i = 0; i < 256; i++) {
if (Histogram[i] > MaxFreq) MaxFreq = Histogram[i];
}
ULONG ExpectedAvg = ScanLen / 256;
BOOLEAN Result = (MaxFreq < (ExpectedAvg + HIGH_ENTROPY_THRESHOLD));
PyasFree(Histogram);
return Result;
}
static BOOLEAN IsNaturallyCompressed(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
for (int i = 0; i < sizeof(Helper_NaturallyCompressedExtensions) / sizeof(Helper_NaturallyCompressedExtensions[0]); i++) {
if (HasSuffix(FileName, Helper_NaturallyCompressedExtensions[i])) return TRUE;
}
return FALSE;
}
static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
if (WildcardMatch(L"*\\Windows\\Temp\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\SystemTemp\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\Prefetch\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Windows\\Logs\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Profiles\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\Program*\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\AppData\\Local\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*\\User Data\\*", FileName->Buffer, FileName->Length) ||
WildcardMatch(L"*$Recycle.Bin*", FileName->Buffer, FileName->Length)) {
return TRUE;
}
return FALSE;
}
static BOOLEAN IsUserDirectoryPath(PCUNICODE_STRING FileName) {
if (!FileName || !FileName->Buffer) return FALSE;
return WildcardMatch(L"*\\Users\\*", FileName->Buffer, FileName->Length);
}
static BOOLEAN IsExplorerProcess(HANDLE ProcessId) {
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
PUNICODE_STRING imageFileName = NULL;
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
BOOLEAN isExplorer = FALSE;
if (NT_SUCCESS(status) && imageFileName) {
if (imageFileName->Buffer && HasSuffix(imageFileName, L"\\explorer.exe")) {
isExplorer = TRUE;
}
ExFreePool(imageFileName);
}
return isExplorer;
}
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite) {
if (!IsUserDirectoryPath(FileName)) {
return FALSE;
}
if (IsNoisyRansomPath(FileName)) {
return FALSE;
}
if (!IsWrite) {
if (IsExplorerProcess(ProcessId)) return FALSE;
}
BOOLEAN SuspiciousWrite = FALSE;
if (IsWrite && WriteBuffer && WriteLength > 0) {
if (!IsNaturallyCompressed(FileName) && IsHighEntropy(WriteBuffer, WriteLength)) {
SuspiciousWrite = TRUE;
}
}
if (IsWrite && !SuspiciousWrite) {
return FALSE;
}
PEPROCESS Process = NULL;
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
return FALSE;
}
LARGE_INTEGER Now = { 0 };
KeQuerySystemTime(&Now);
LARGE_INTEGER createTime;
createTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
BOOLEAN Result = FALSE;
KIRQL OldIrql;
KeAcquireSpinLock(&GlobalData.TrackerMutex, &OldIrql);
PRANSOM_TRACKER Tracker = NULL;
PRANSOM_TRACKER CandidateSlot = NULL;
PRANSOM_TRACKER LruSlot = &RansomTrackers[0];
for (int i = 0; i < MAX_TRACKERS; i++) {
PRANSOM_TRACKER Current = &RansomTrackers[i];
if (Current->LastActivityTime.QuadPart < LruSlot->LastActivityTime.QuadPart) {
LruSlot = Current;
}
if (Current->Process == Process && Current->ProcessCreateTime.QuadPart == createTime.QuadPart) {
Tracker = Current;
break;
}
BOOLEAN IsExpired = FALSE;
if (Current->Process != NULL) {
LARGE_INTEGER Diff;
Diff.QuadPart = Now.QuadPart - Current->LastActivityTime.QuadPart;
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
IsExpired = TRUE;
}
}
if ((Current->Process == NULL || IsExpired) && !CandidateSlot) {
CandidateSlot = Current;
}
}
if (!Tracker) {
if (CandidateSlot) {
Tracker = CandidateSlot;
}
else {
Tracker = LruSlot;
}
Tracker->Process = Process;
Tracker->ProcessCreateTime = createTime;
Tracker->ActivityCount = 0;
Tracker->LastActivityTime = Now;
}
else {
LARGE_INTEGER Diff;
Diff.QuadPart = Now.QuadPart - Tracker->LastActivityTime.QuadPart;
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
Tracker->ActivityCount = 0;
}
Tracker->LastActivityTime = Now;
}
ULONG Weight = 2;
Tracker->ActivityCount += Weight;
if (Tracker->ActivityCount >= RANSOM_COUNT_THRESHOLD) {
Result = TRUE;
}
KeReleaseSpinLock(&GlobalData.TrackerMutex, OldIrql);
ObDereferenceObject(Process);
return Result;
}
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize) {
if (KeGetCurrentIrql() > APC_LEVEL) return STATUS_UNSUCCESSFUL;
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
return STATUS_PORT_DISCONNECTED;
}
NTSTATUS status = STATUS_PORT_DISCONNECTED;
if (GlobalData.ClientPort) {
PPYAS_MESSAGE msg = (PPYAS_MESSAGE)PyasAllocate(sizeof(PYAS_MESSAGE));
if (msg) {
msg->MessageCode = Code;
msg->ProcessId = Pid;
if (Path && PathSize > 0) {
size_t MaxSize = sizeof(msg->Path) - sizeof(WCHAR);
size_t BytesToCopy = PathSize > MaxSize ? MaxSize : PathSize;
__try {
RtlCopyMemory(msg->Path, Path, BytesToCopy);
msg->Path[BytesToCopy / sizeof(WCHAR)] = L'\0';
}
__except (EXCEPTION_EXECUTE_HANDLER) {
PyasFree(msg);
ExReleaseRundownProtection(&GlobalData.PortRundown);
return STATUS_ACCESS_VIOLATION;
}
}
LARGE_INTEGER timeout;
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
timeout.QuadPart = -(5 * 10000);
}
else {
timeout.QuadPart = 0;
}
status = FltSendMessage(GlobalData.FilterHandle, &GlobalData.ClientPort, msg, sizeof(PYAS_MESSAGE), NULL, NULL, &timeout);
PyasFree(msg);
}
else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
}
ExReleaseRundownProtection(&GlobalData.PortRundown);
return status;
}
+20
View File
@@ -0,0 +1,20 @@
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "PYAS Security Driver Protect"
#define VER_INTERNALNAME_STR "PYAS"
#define VER_LEGALCOPYRIGHT_STR "PYAS Security"
#define VER_ORIGINALFILENAME_STR "PYAS_Driver.sys"
#define VER_PRODUCTNAME_STR "PYAS"
#define VER_FILEVERSION 3,5,1,0
#define VER_FILEVERSION_STR "3,5,1,0"
#define VER_PRODUCTVERSION 3,5,1,0
#define VER_PRODUCTVERSION_STR "3,5,1,0"
#define VER_COMPANYNAME_STR "PYAS Security"
#include "common.ver"
+225
View File
@@ -0,0 +1,225 @@
{
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\DisableAntiSpyware",
"*\\DisableWindowsUpdateAccess",
"*\\EnableLUA",
"*\\ConsentPromptBehaviorAdmin",
"*\\NoControlPanel",
"*\\NoDrives",
"*\\NoFileMenu",
"*\\NoFind",
"*\\NoStartMenuPinnedList",
"*\\NoSetFolders",
"*\\NoSetFolderOptions",
"*\\NoViewOnDrive",
"*\\NoClose",
"*\\NoDesktop",
"*\\NoLogoff",
"*\\NoFolderOptions",
"*\\RestrictRun",
"*\\NoViewContextMenu",
"*\\HideClock",
"*\\NoStartMenuMyGames",
"*\\NoStartMenuMyMusic",
"*\\DisableCMD",
"*\\NoAddingComponents",
"*\\NoWinKeys",
"*\\NoStartMenuLogOff",
"*\\NoSimpleNetIDList",
"*\\NoLowDiskSpaceChecks",
"*\\DisableLockWorkstation",
"*\\Restrict_Run",
"*\\DisableTaskMgr",
"*\\DisableRegistryTools",
"*\\DisableChangePassword",
"*\\NoComponents",
"*\\NoStartMenuMorePrograms",
"*\\NoActiveDesktop",
"*\\NoSetActiveDesktop",
"*\\NoRecentDocsMenu",
"*\\NoWindowsUpdate",
"*\\NoChangeStartMenu",
"*\\NoFavoritesMenu",
"*\\NoRecentDocsHistory",
"*\\NoSetTaskbar",
"*\\NoSMHelp",
"*\\NoTrayContextMenu",
"*\\NoManageMyComputerVerb",
"*\\NoRealMode",
"*\\NoRun",
"*\\ClearRecentDocsOnExit",
"*\\NoActiveDesktopChanges",
"*\\NoStartMenuNetworkPlaces",
"*\\NoLockScreen",
"*\\HideFastUserSwitching",
"*\\ConsentPromptBehaviorUser",
"*\\EnableSecureUIAPaths",
"*\\NoSRM",
"*\\NoInteractiveServices",
"*\\ShutdownWithoutLogon",
"*\\AutoAdminLogon",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\CrashDumpEnabled",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Sources\\SetupHost.exe",
"*\\Work\\*\\DismHost.exe",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Thorium\\Application\\thorium.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\hrkill-*.exe",
"*\\bin\\HipsDaemon.exe",
"*\\360Safe.exe",
"*\\bilibili\\bilibili.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.exe",
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
"*\\Neverness To Everness\\NTELauncher.exe",
"*\\Application\\AndrowsLauncher.exe",
],
"Rule_Process_ExploitableBlacklist": [
"*\\cmd.exe",
"*\\powershell.exe",
"*\\pwsh.exe",
"*\\wscript.exe",
"*\\cscript.exe",
"*\\mshta.exe",
"*\\regsvr32.exe",
"*\\certutil.exe",
"*\\bitsadmin.exe",
"*\\wmic.exe",
"*\\hh.exe",
"*\\cmstp.exe",
"*\\installutil.exe",
"*\\regasm.exe",
"*\\regsvcs.exe",
"*\\msbuild.exe",
"*\\csc.exe",
"*\\vbc.exe",
"*\\jsc.exe",
"*\\bash.exe",
"*\\scrcons.exe",
"*\\schtasks.exe",
"*\\at.exe",
"*\\reg.exe",
"*\\sc.exe",
"*\\net.exe",
"*\\net1.exe",
"*\\whoami.exe",
"*\\ipconfig.exe",
"*\\taskkill.exe",
"*\\ProcessHacker.exe",
"*\\procmon.exe",
"*\\procexp.exe",
],
"Rule_File_ProtectedPaths": [
"*\\PYAS.exe",
"*\\Plugins\\Filter\\PYAS_Driver.sys",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\ProgramData\\PYAS\\*.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
"*\\cache\\*",
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".ppt", ".pptx", ".wps", ".txt", ".rtf",
".pdf", ".xls", ".xlsx", ".doc", ".docx",
".jpg", ".jpeg", ".png", ".webp", ".gif",
".mp3", ".wav", ".aac", ".ogg", ".flac",
".mp4", ".avi", ".mov", ".wmv", ".mkv",
]
}
+134 -38
View File
@@ -1,28 +1,32 @@
# PYAS
# PYAS Security
Python antivirus software uses deep learning and behavioral monitoring to block threats!
Antivirus software written in Python and C++ that blocks threats through Machine Learning and behavioral monitoring!
![PYAS_UI](https://github.com/user-attachments/assets/68765836-7272-482f-b8cd-d8ba728d88ab)
## Readme Language
繁體中文 : https://github.com/87owo/PYAS/blob/main/README_zh_TW.md
简体中文 : https://github.com/87owo/PYAS/blob/main/README_zh_CN.md
<img width="2440" height="1600" alt="PYAS_All" src="https://github.com/user-attachments/assets/1991aad3-64cc-4266-ac67-dab70c891ce7" />
## Requirements
Python 3.10 is recommended. Other Python versions may require different pip commands.
```
pip install numpy==1.26.4
pip install onnxruntime==1.18.1
pip install pefile==2023.2.7
pip install Pillow==11.0.0
pip install pyperclip==1.9.0
pip install PySide6==6.9.1
pip install requests==2.32.4
pip install yara-python==4.5.4
pip install pystray
pip install pefile
pip install requests
pip install pywebview
pip install Pillow
pip install yara-python
pip install numpy
pip install onnxruntime
```
Non-essential requirements installation, only used for model training or other functions.
```
pip install pandas
pip install scikit-learn
pip install lightgbm
pip install onnxmltools
pip install orjson
```
## File Information
@@ -31,38 +35,130 @@ The following lists the storage locations of all relevant code and other related
```
PYAS/
├── Engine/
│ ├── Models/ # Directory for ONNX AI models
│ └── Rules/ # Directory for YARA and network rules
├── Engine/ # Complete code for Yara signatures and AI model training
│ ├── Heuristic/
│ │ └── ... # Yara Rules
│ └── Properties/
│ └── ... # LightGBM Model
│
├── Plugins/
│ └── Filter/ # Windows kernel driver for security protection
├── Interface/ # Interface Interaction and Icons with WebView2
│ ├── static/
│ │ └── ... # style.css, main.js, icon.ico
│ └── templates/
│ └── ... # index.html
│
├── PYAS.py # Main application and GUI interface
├── PYAS_Config.py # Configuration handling and global parameters
├── PYAS_Engine.py # YARA and ONNX Scanning engine
├── PYAS_Interface.py # User interface definitions and widget logic
├── PYAS_Resource.py # Static resource management
├── PYAS_Version.py # Packaging and version info
└── ... # Other supplementary folders and files
├── Plugins/ # Main filter driver protect and rules
│ ├── Filter/
│ │ └── ... # DriverCommon.h, DriverEntry.cpp, ProtectRegistry.cpp, ...
│ └── Rules/
│ └── ... # Rules_Driver_P1.json
|
├── PYAS.py # Main antivirus application entry point
├── PYAS_Engine.py # Core scanning engine algorithm
├── PYAS_Version.py # Version metadata for packaging and updates
└── ...
```
## Packaged Releases
## Architecture diagram
Packaged Releases Download: https://github.com/87owo/PYAS/releases
PYAS Security antivirus software general architecture diagram.
```mermaid
graph TD
%% Global Styles
classDef userMode fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#0d47a1
classDef kernelMode fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#e65100
classDef storage fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#4a148c
classDef interaction fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,stroke-dasharray: 5 5,color:#1b5e20
subgraph UserMode ["User Mode (Python / Web)"]
UI["Web UI (pywebview)"]:::userMode
PYASCore["PYAS Core (PYAS.py)"]:::userMode
subgraph Engines ["Scanning Engines (PYAS_Engine.py)"]
PEScanner["PE/ML Scanner (pe_scanner)"]:::userMode
RuleScanner["YARA Scanner (rule_scanner)"]:::userMode
CloudScanner["Cloud API (cloud_scanner)"]:::userMode
SignScanner["Signature Verify (sign_scanner)"]:::userMode
end
CommClient["Filter Communication Client"]:::interaction
end
subgraph KernelMode ["Kernel Mode (C++ Minifilter Driver)"]
CommServer["ALPC Port (\\PYAS_Output_Pipe)"]:::interaction
DriverCore["Driver Entry (DriverEntry.cpp)"]:::kernelMode
RuleEngine["Rules & Trust Cache (ProtectRules.cpp)"]:::kernelMode
subgraph Protections ["Protection Subsystems"]
FileProtect["File Minifilter (ProtectFile.cpp)"]:::kernelMode
ProcessProtect["ObRegisterCallbacks (ProtectProcess.cpp)"]:::kernelMode
RegProtect["CmRegisterCallbackEx (ProtectRegistry.cpp)"]:::kernelMode
BootProtect["IRP_MJ_DEVICE_CONTROL (ProtectBoot.cpp)"]:::kernelMode
end
end
subgraph OS ["Windows OS & Storage"]
FS["File System (FltMgr)"]:::storage
Reg["Registry (CmMgr)"]:::storage
ProcMgr["Process/Thread Manager (ObMgr)"]:::storage
Disk["Physical Disk / MBR"]:::storage
end
CloudAPI["PYAS Cloud Server"]:::storage
%% User Mode Internal
UI <-->|JS Evaluate / JSON| PYASCore
PYASCore -->|Extract Features & Scan| Engines
CloudScanner -.->|HTTPS REST API| CloudAPI
PYASCore <-->|FilterSendMessage / FilterGetMessage| CommClient
%% User-Kernel Boundary
CommClient <-->|PYAS_MESSAGE / PYAS_USER_MESSAGE| CommServer
%% Kernel Mode Internal
CommServer <-->|Message Dispatch| DriverCore
DriverCore --> RuleEngine
RuleEngine --> Protections
%% Kernel-OS Boundary
FileProtect -->|IRP_MJ_CREATE, IRP_MJ_WRITE...| FS
ProcessProtect -->|PreOpenProcess, PreOpenThread| ProcMgr
RegProtect -->|RegNtPreCreateKey, RegNtPreSetValueKey...| Reg
BootProtect -->|IOCTL_DISK_FORMAT_TRACKS...| Disk
```
## Machine Learning
The machine learning-based virus scanning engine training achieved extremely high accuracy.
<img width="1191" height="1323" alt="train" src="https://github.com/user-attachments/assets/2eea8426-1f82-44e3-9a38-f96f9257cf50" />
## Support System
| Config | Permissions | System version | Available memory | Available storage |
|-----------|---------------|----------------|------------------|-------------------|
| Minimum | Administrator | Windows 8.1 | 200MB | 100MB |
| Recommend | Administrator | Windows 10 | 300MB | 200MB |
| Optimal | Administrator | Windows 11 | 500MB | 200MB |
Please ensure your computer supports Microsoft Visual C++ 2015-2022 Redistributable and Edge Webview2.
| Config | Permissions | System version | Processor | Memory | Storage |
|-----------|---------------|----------------------|-----------|--------|---------|
| Minimum | Administrator | >= Windows 10 (20H1) | 1 GHz | 300MB | 100MB |
| Recommend | Administrator | >= Windows 10 (21H2) | 3 GHz | 500MB | 200MB |
## Official Website
https://github.com/87owo/PYAS
If you are interested in this project, you can visit the website to see other related content.
Source Available : https://github.com/87owo/PYAS
Official Website : https://pyas-security.com/antivirus
Online Analyze : https://pyas-security.com/analyze
Packaged Download: https://github.com/87owo/PYAS/releases
## Project License
https://github.com/87owo/PYAS/blob/main/LICENSE.md
For any questions, needs, or bug feedback, please contact us through the following website.
Source Issues : https://github.com/87owo/PYAS/issues
Official Email : service@pyas-security.com
-61
View File
@@ -1,61 +0,0 @@
# PYAS (Python 杀毒软件)
![PYAS_UI](https://github.com/user-attachments/assets/68765836-7272-482f-b8cd-d8ba728d88ab)
## 安装要求
```
pip install numpy==1.26.4
pip install onnxruntime==1.18.1
pip install pefile==2023.2.7
pip install Pillow==11.0.0
pip install pyperclip==1.9.0
pip install PySide6==6.9.1
pip install requests==2.32.4
pip install yara-python==4.5.4
```
## 文件信息
```
PYAS/
├── PYAS.py # 主程序,提供 PySide6 视觉化操作介面
├── PYAS_Config.py # 设定档处理与全域参数管理
├── PYAS_Engine.py # 扫描引擎,整合 YARA 规则与 ONNX AI 模型
├── PYAS_Interface.py # UI 介面定义与元件交互
├── PYAS_Resource.py # 资源档与静态资源管理
├── PYAS_Version.py # 程式版本与打包资讯
├── Engine/
│ ├── Models/ # AI 模型(ONNX 格式)存放目录
│ └── Rules/ # YARA 规则与网路规则存放目录
├── Plugins/
│ └── Filter/ # 驱动程式目录
│ └── PYAS_Driver.sys # Windows 驱动程式(系统保护用)
└── ... # 其它辅助资料夹与档案
```
## 官方网页
https://pyantivirus.wixsite.com/pyas
https://github.com/87owo/PYAS
## 微软运行库
https://github.com/87owo/Microsoft_Runtime/releases
## 开源协议
https://github.com/87owo/PYAS/blob/main/LICENSE.md
## 支援系统
Windows 10, 11 (64-bit), 内存 500MB, 存储 200MB 或更高
其他系统或版本可能会遇到软体功能故障或程序崩溃
## 特别感谢
Wix, mtkiao129, 0sha0, AV-T Team of LisectGroup
Copyright© 2020~2025 PYAS Security By 87owo
-61
View File
@@ -1,61 +0,0 @@
# PYAS (Python 防毒軟體)
![PYAS_UI](https://github.com/user-attachments/assets/68765836-7272-482f-b8cd-d8ba728d88ab)
## 安裝要求
```
pip install numpy==1.26.4
pip install onnxruntime==1.18.1
pip install pefile==2023.2.7
pip install Pillow==11.0.0
pip install pyperclip==1.9.0
pip install PySide6==6.9.1
pip install requests==2.32.4
pip install yara-python==4.5.4
```
## 檔案資訊
```
PYAS/
├── PYAS.py # 主程式,提供 PySide6 視覺化操作介面
├── PYAS_Config.py # 設定檔處理與全域參數管理
├── PYAS_Engine.py # 掃描引擎,整合 YARA 規則與 ONNX AI 模型
├── PYAS_Interface.py # UI 介面定義與元件交互
├── PYAS_Resource.py # 資源檔與靜態資源管理
├── PYAS_Version.py # 程式版本與打包資訊
├── Engine/
│ ├── Models/ # AI 模型(ONNX 格式)存放目錄
│ └── Rules/ # YARA 規則與網路規則存放目錄
├── Plugins/
│ └── Filter/ # 驅動程式目錄
│ └── PYAS_Driver.sys # Windows 驅動程式(系統保護用)
└── ... # 其它輔助資料夾與檔案
```
## 官方網頁
https://pyantivirus.wixsite.com/pyas
https://github.com/87owo/PYAS
## 微軟運行庫
https://github.com/87owo/Microsoft_Runtime/releases
## 開源協議
https://github.com/87owo/PYAS/blob/main/LICENSE.md
## 支援系統
Windows 10, 11 (64-bit),記憶體 500MB, 磁碟容量 200MB 或更高
其他系統或版本可能會遇到軟體功能故障或程序崩潰
## 特別感謝
Wix, mtkiao129, 0sha0, AV-T Team of LisectGroup
Copyright© 2020~2025 PYAS Security By 87owo