Upload app.py
Browse files
app.py
CHANGED
|
@@ -9,15 +9,16 @@ import threading
|
|
| 9 |
DEFAULT_CHARSET = string.ascii_lowercase + string.digits
|
| 10 |
MAX_BRUTEFORCE_LEN = 4 # 保持较小以进行测试,实际破解中此长度几乎无效
|
| 11 |
|
| 12 |
-
|
| 13 |
# --- 辅助函数 ---
|
|
|
|
|
|
|
|
|
|
| 14 |
def try_decrypt_wallet(wallet_data_bytes, password_attempt):
|
| 15 |
"""
|
| 16 |
尝试用给定的密码解密钱包。
|
| 17 |
-
返回 True 如果成功, False 如果
|
| 18 |
"""
|
| 19 |
try:
|
| 20 |
-
# pywallet.Wallet.from_binary() 似乎是存在的,用于从字节加载
|
| 21 |
w = wallet.Wallet.from_binary(wallet_data_bytes)
|
| 22 |
|
| 23 |
if w.is_encrypted():
|
|
@@ -26,13 +27,9 @@ def try_decrypt_wallet(wallet_data_bytes, password_attempt):
|
|
| 26 |
else:
|
| 27 |
return False # 密码错误
|
| 28 |
else:
|
| 29 |
-
#
|
| 30 |
-
|
| 31 |
-
if 'info_message' not in st.session_state or st.session_state.info_message is None:
|
| 32 |
-
st.session_state.info_message = "钱包文件未加密。"
|
| 33 |
-
return True
|
| 34 |
except Exception as e:
|
| 35 |
-
# 实际应用中,这里可能需要更详细的错误记录
|
| 36 |
# print(f"Decryption error with password '{password_attempt}': {e}") # 调试时取消注释
|
| 37 |
return False
|
| 38 |
|
|
@@ -58,51 +55,42 @@ def background_cracker_task(wallet_bytes_data, passwords_iterable, task_id, meth
|
|
| 58 |
st.session_state[f'task_{task_id}_error_message'] = None
|
| 59 |
st.session_state[f'task_{task_id}_attempts'] = 0
|
| 60 |
st.session_state[f'task_{task_id}_time_taken'] = 0
|
| 61 |
-
# st.session_state[f'task_{task_id}_progress'] = 0.0 # 如果可以预估总量
|
| 62 |
|
| 63 |
start_time = time.time()
|
| 64 |
found = False
|
| 65 |
processed_passwords = 0
|
| 66 |
|
| 67 |
-
# 尝试获取密码总数(如果可能)
|
| 68 |
-
total_passwords_for_progress = 0
|
| 69 |
-
if isinstance(passwords_iterable, list): # 对于列表,总数已知
|
| 70 |
-
total_passwords_for_progress = len(passwords_iterable)
|
| 71 |
-
# 对于生成器(如暴力破解),总数通常未知或难以计算,除非预先计算
|
| 72 |
-
|
| 73 |
for pwd_count, pwd in enumerate(passwords_iterable):
|
| 74 |
-
# 检查任务是否被中止 (例如,用户启动了新任务或关闭了应用)
|
| 75 |
if st.session_state.get('current_task_id') != task_id:
|
| 76 |
st.session_state[f'task_{task_id}_error_message'] = "任务被中止。"
|
| 77 |
-
st.session_state[f'task_{task_id}_status'] = 'failed'
|
| 78 |
return
|
| 79 |
|
| 80 |
processed_passwords = pwd_count + 1
|
| 81 |
st.session_state[f'task_{task_id}_attempts'] = processed_passwords
|
| 82 |
|
| 83 |
-
|
| 84 |
-
# if total_passwords_for_progress > 0:
|
| 85 |
-
# st.session_state[f'task_{task_id}_progress'] = processed_passwords / total_passwords_for_progress
|
| 86 |
|
| 87 |
-
if
|
| 88 |
st.session_state[f'task_{task_id}_found_password'] = pwd
|
| 89 |
st.session_state[f'task_{task_id}_status'] = 'success'
|
| 90 |
found = True
|
| 91 |
break
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
end_time = time.time()
|
| 98 |
st.session_state[f'task_{task_id}_time_taken'] = end_time - start_time
|
| 99 |
|
| 100 |
-
if not found and st.session_state.get(f'task_{task_id}_status') == 'running':
|
| 101 |
st.session_state[f'task_{task_id}_error_message'] = "未找到密码。"
|
| 102 |
st.session_state[f'task_{task_id}_status'] = 'failed'
|
| 103 |
|
| 104 |
-
# 线程结束后,Streamlit 会因为 session_state 的变化在下一次 rerun 时更新 UI
|
| 105 |
-
|
| 106 |
|
| 107 |
# --- Streamlit UI ---
|
| 108 |
st.set_page_config(page_title="Wallet.dat Cracker (Background)", layout="wide")
|
|
@@ -121,23 +109,22 @@ st.warning(
|
|
| 121 |
# 初始化 session_state 变量 (如果它们不存在)
|
| 122 |
if 'current_task_id' not in st.session_state:
|
| 123 |
st.session_state.current_task_id = None
|
| 124 |
-
if 'task_counter' not in st.session_state:
|
| 125 |
st.session_state.task_counter = 0
|
| 126 |
-
if 'info_message' not in st.session_state:
|
| 127 |
st.session_state.info_message = None
|
| 128 |
|
| 129 |
# --- 文件上传 ---
|
| 130 |
uploaded_wallet_file = st.file_uploader("1. 上传你的 wallet.dat 文件", type=["dat"])
|
| 131 |
|
| 132 |
if uploaded_wallet_file:
|
| 133 |
-
wallet_bytes_data_main = uploaded_wallet_file.getvalue()
|
| 134 |
st.success(f"已上传 {uploaded_wallet_file.name} ({len(wallet_bytes_data_main)} bytes)")
|
| 135 |
|
| 136 |
-
# --- 破解方法选择 ---
|
| 137 |
crack_method = st.radio(
|
| 138 |
"2. 选择破解方法:",
|
| 139 |
('使用密码列表', '尝试单个密码', '基本暴力破解 (极慢,后台运行)'),
|
| 140 |
-
key="crack_method_radio"
|
| 141 |
)
|
| 142 |
|
| 143 |
current_task_id = st.session_state.get('current_task_id')
|
|
@@ -145,30 +132,34 @@ if uploaded_wallet_file:
|
|
| 145 |
if current_task_id:
|
| 146 |
task_status = st.session_state.get(f'task_{current_task_id}_status')
|
| 147 |
|
| 148 |
-
# --- 显示任务状态和结果 ---
|
| 149 |
if task_status == 'running':
|
| 150 |
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
|
| 151 |
-
# progress_val = st.session_state.get(f'task_{current_task_id}_progress', 0.0)
|
| 152 |
with st.spinner(f"正在后台破解... 已尝试 {attempts} 个密码。请勿关闭此页面。"):
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
time.sleep(0.5) # 调整此处的休眠时间,控制UI刷新频率
|
| 156 |
try:
|
| 157 |
st.experimental_rerun()
|
| 158 |
-
except AttributeError:
|
| 159 |
st.warning("您的 Streamlit 版本较旧,spinner 状态可能不会实时更新。")
|
| 160 |
|
| 161 |
|
| 162 |
elif task_status == 'success':
|
| 163 |
-
|
| 164 |
time_taken = st.session_state.get(f'task_{current_task_id}_time_taken', 0)
|
| 165 |
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
st.info(f"总耗时: {time_taken:.2f} 秒。尝试密码数: {attempts}。")
|
|
|
|
| 169 |
if st.button("清除结果并开始新任务", key="clear_success"):
|
| 170 |
st.session_state.current_task_id = None
|
| 171 |
-
st.session_state.info_message = None
|
| 172 |
try:
|
| 173 |
st.experimental_rerun()
|
| 174 |
except AttributeError:
|
|
@@ -189,14 +180,16 @@ if uploaded_wallet_file:
|
|
| 189 |
except AttributeError:
|
| 190 |
pass
|
| 191 |
|
| 192 |
-
# 显示来自
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
| 196 |
|
| 197 |
-
# --- 启动按钮 (仅当没有任务运行时显示或任务已完成/失败) ---
|
| 198 |
if not task_status or task_status in ['success', 'failed']:
|
| 199 |
-
passwords_to_try_iterable = None
|
| 200 |
can_start = False
|
| 201 |
start_button_label = "开始破解"
|
| 202 |
method_name_for_task = ""
|
|
@@ -207,7 +200,6 @@ if uploaded_wallet_file:
|
|
| 207 |
)
|
| 208 |
if uploaded_password_list:
|
| 209 |
try:
|
| 210 |
-
# 直接将文件对象传递或读取为列表
|
| 211 |
passwords_list = [
|
| 212 |
line.strip() for line in uploaded_password_list.read().decode().splitlines() if line.strip()
|
| 213 |
]
|
|
@@ -228,7 +220,7 @@ if uploaded_wallet_file:
|
|
| 228 |
elif crack_method == '尝试单个密码':
|
| 229 |
single_password = st.text_input("输入要尝试的密码:", type="password", key="single_pwd_input")
|
| 230 |
if single_password:
|
| 231 |
-
passwords_to_try_iterable = [single_password]
|
| 232 |
can_start = True
|
| 233 |
start_button_label = "尝试此密码"
|
| 234 |
method_name_for_task = "single"
|
|
@@ -259,17 +251,16 @@ if uploaded_wallet_file:
|
|
| 259 |
st.session_state.current_task_id = new_task_id
|
| 260 |
st.session_state.info_message = None # 清除旧的 "钱包未加密" 消息,准备新任务
|
| 261 |
|
| 262 |
-
# 启动后台线程
|
| 263 |
thread = threading.Thread(
|
| 264 |
target=background_cracker_task,
|
| 265 |
args=(wallet_bytes_data_main, passwords_to_try_iterable, new_task_id, method_name_for_task)
|
| 266 |
)
|
| 267 |
-
thread.daemon = True
|
| 268 |
thread.start()
|
| 269 |
try:
|
| 270 |
-
st.experimental_rerun()
|
| 271 |
except AttributeError:
|
| 272 |
-
pass
|
| 273 |
|
| 274 |
else:
|
| 275 |
st.info("请先上传 wallet.dat 文件。")
|
|
|
|
| 9 |
DEFAULT_CHARSET = string.ascii_lowercase + string.digits
|
| 10 |
MAX_BRUTEFORCE_LEN = 4 # 保持较小以进行测试,实际破解中此长度几乎无效
|
| 11 |
|
|
|
|
| 12 |
# --- 辅助函数 ---
|
| 13 |
+
WALLET_NOT_ENCRYPTED_SENTINEL = "WALLET_NOT_ENCRYPTED" # 定义一个哨兵值
|
| 14 |
+
|
| 15 |
+
|
| 16 |
def try_decrypt_wallet(wallet_data_bytes, password_attempt):
|
| 17 |
"""
|
| 18 |
尝试用给定的密码解密钱包。
|
| 19 |
+
返回 True 如果成功, False 如果密码错误, WALLET_NOT_ENCRYPTED_SENTINEL 如果未加密。
|
| 20 |
"""
|
| 21 |
try:
|
|
|
|
| 22 |
w = wallet.Wallet.from_binary(wallet_data_bytes)
|
| 23 |
|
| 24 |
if w.is_encrypted():
|
|
|
|
| 27 |
else:
|
| 28 |
return False # 密码错误
|
| 29 |
else:
|
| 30 |
+
# 钱包未加密,返回哨兵值
|
| 31 |
+
return WALLET_NOT_ENCRYPTED_SENTINEL
|
|
|
|
|
|
|
|
|
|
| 32 |
except Exception as e:
|
|
|
|
| 33 |
# print(f"Decryption error with password '{password_attempt}': {e}") # 调试时取消注释
|
| 34 |
return False
|
| 35 |
|
|
|
|
| 55 |
st.session_state[f'task_{task_id}_error_message'] = None
|
| 56 |
st.session_state[f'task_{task_id}_attempts'] = 0
|
| 57 |
st.session_state[f'task_{task_id}_time_taken'] = 0
|
|
|
|
| 58 |
|
| 59 |
start_time = time.time()
|
| 60 |
found = False
|
| 61 |
processed_passwords = 0
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
for pwd_count, pwd in enumerate(passwords_iterable):
|
|
|
|
| 64 |
if st.session_state.get('current_task_id') != task_id:
|
| 65 |
st.session_state[f'task_{task_id}_error_message'] = "任务被中止。"
|
| 66 |
+
st.session_state[f'task_{task_id}_status'] = 'failed'
|
| 67 |
return
|
| 68 |
|
| 69 |
processed_passwords = pwd_count + 1
|
| 70 |
st.session_state[f'task_{task_id}_attempts'] = processed_passwords
|
| 71 |
|
| 72 |
+
decryption_status = try_decrypt_wallet(wallet_bytes_data, pwd)
|
|
|
|
|
|
|
| 73 |
|
| 74 |
+
if decryption_status is True: # 密码正确
|
| 75 |
st.session_state[f'task_{task_id}_found_password'] = pwd
|
| 76 |
st.session_state[f'task_{task_id}_status'] = 'success'
|
| 77 |
found = True
|
| 78 |
break
|
| 79 |
+
elif decryption_status == WALLET_NOT_ENCRYPTED_SENTINEL: # 钱包未加密
|
| 80 |
+
st.session_state.info_message = "钱包文件未加密。" # 在这里设置
|
| 81 |
+
st.session_state[f'task_{task_id}_found_password'] = WALLET_NOT_ENCRYPTED_SENTINEL # 使用哨兵值标记
|
| 82 |
+
st.session_state[f'task_{task_id}_status'] = 'success' # 同样视为成功
|
| 83 |
+
found = True
|
| 84 |
+
break
|
| 85 |
+
# else: decryption_status is False, 密码错误,继续循环
|
| 86 |
|
| 87 |
end_time = time.time()
|
| 88 |
st.session_state[f'task_{task_id}_time_taken'] = end_time - start_time
|
| 89 |
|
| 90 |
+
if not found and st.session_state.get(f'task_{task_id}_status') == 'running':
|
| 91 |
st.session_state[f'task_{task_id}_error_message'] = "未找到密码。"
|
| 92 |
st.session_state[f'task_{task_id}_status'] = 'failed'
|
| 93 |
|
|
|
|
|
|
|
| 94 |
|
| 95 |
# --- Streamlit UI ---
|
| 96 |
st.set_page_config(page_title="Wallet.dat Cracker (Background)", layout="wide")
|
|
|
|
| 109 |
# 初始化 session_state 变量 (如果它们不存在)
|
| 110 |
if 'current_task_id' not in st.session_state:
|
| 111 |
st.session_state.current_task_id = None
|
| 112 |
+
if 'task_counter' not in st.session_state:
|
| 113 |
st.session_state.task_counter = 0
|
| 114 |
+
if 'info_message' not in st.session_state:
|
| 115 |
st.session_state.info_message = None
|
| 116 |
|
| 117 |
# --- 文件上传 ---
|
| 118 |
uploaded_wallet_file = st.file_uploader("1. 上传你的 wallet.dat 文件", type=["dat"])
|
| 119 |
|
| 120 |
if uploaded_wallet_file:
|
| 121 |
+
wallet_bytes_data_main = uploaded_wallet_file.getvalue()
|
| 122 |
st.success(f"已上传 {uploaded_wallet_file.name} ({len(wallet_bytes_data_main)} bytes)")
|
| 123 |
|
|
|
|
| 124 |
crack_method = st.radio(
|
| 125 |
"2. 选择破解方法:",
|
| 126 |
('使用密码列表', '尝试单个密码', '基本暴力破解 (极慢,后台运行)'),
|
| 127 |
+
key="crack_method_radio"
|
| 128 |
)
|
| 129 |
|
| 130 |
current_task_id = st.session_state.get('current_task_id')
|
|
|
|
| 132 |
if current_task_id:
|
| 133 |
task_status = st.session_state.get(f'task_{current_task_id}_status')
|
| 134 |
|
|
|
|
| 135 |
if task_status == 'running':
|
| 136 |
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
|
|
|
|
| 137 |
with st.spinner(f"正在后台破解... 已尝试 {attempts} 个密码。请勿关闭此页面。"):
|
| 138 |
+
st.empty()
|
| 139 |
+
time.sleep(0.5)
|
|
|
|
| 140 |
try:
|
| 141 |
st.experimental_rerun()
|
| 142 |
+
except AttributeError:
|
| 143 |
st.warning("您的 Streamlit 版本较旧,spinner 状态可能不会实时更新。")
|
| 144 |
|
| 145 |
|
| 146 |
elif task_status == 'success':
|
| 147 |
+
found_pwd_or_status = st.session_state.get(f'task_{current_task_id}_found_password')
|
| 148 |
time_taken = st.session_state.get(f'task_{current_task_id}_time_taken', 0)
|
| 149 |
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
|
| 150 |
+
|
| 151 |
+
if found_pwd_or_status == WALLET_NOT_ENCRYPTED_SENTINEL:
|
| 152 |
+
st.success(f"✅ 钱包文件未加密。")
|
| 153 |
+
# info_message 应该已经被后台任务设置,这里不需要再次设置
|
| 154 |
+
else:
|
| 155 |
+
st.success(f"🎉 密码找到: **{found_pwd_or_status}** 🎉")
|
| 156 |
+
st.balloons()
|
| 157 |
+
|
| 158 |
st.info(f"总耗时: {time_taken:.2f} 秒。尝试密码数: {attempts}。")
|
| 159 |
+
|
| 160 |
if st.button("清除结果并开始新任务", key="clear_success"):
|
| 161 |
st.session_state.current_task_id = None
|
| 162 |
+
st.session_state.info_message = None
|
| 163 |
try:
|
| 164 |
st.experimental_rerun()
|
| 165 |
except AttributeError:
|
|
|
|
| 180 |
except AttributeError:
|
| 181 |
pass
|
| 182 |
|
| 183 |
+
# 显示来自后台任务的一次性消息 (例如 "钱包未加密")
|
| 184 |
+
# 这个消息会在 'success' 状态时由后台任务设置
|
| 185 |
+
# 确保它只在相关任务完成后显示一次,并且在清除任务时也被清除
|
| 186 |
+
if st.session_state.get('info_message') and task_status == 'success' and st.session_state.get(
|
| 187 |
+
f'task_{current_task_id}_found_password') == WALLET_NOT_ENCRYPTED_SENTINEL:
|
| 188 |
+
# st.info(st.session_state.info_message) # 这行其实可以省略,因为 "钱包文件未加密" 已经在 success 块中显示
|
| 189 |
+
pass # The message is handled within the success block now
|
| 190 |
|
|
|
|
| 191 |
if not task_status or task_status in ['success', 'failed']:
|
| 192 |
+
passwords_to_try_iterable = None
|
| 193 |
can_start = False
|
| 194 |
start_button_label = "开始破解"
|
| 195 |
method_name_for_task = ""
|
|
|
|
| 200 |
)
|
| 201 |
if uploaded_password_list:
|
| 202 |
try:
|
|
|
|
| 203 |
passwords_list = [
|
| 204 |
line.strip() for line in uploaded_password_list.read().decode().splitlines() if line.strip()
|
| 205 |
]
|
|
|
|
| 220 |
elif crack_method == '尝试单个密码':
|
| 221 |
single_password = st.text_input("输入要尝试的密码:", type="password", key="single_pwd_input")
|
| 222 |
if single_password:
|
| 223 |
+
passwords_to_try_iterable = [single_password]
|
| 224 |
can_start = True
|
| 225 |
start_button_label = "尝试此密码"
|
| 226 |
method_name_for_task = "single"
|
|
|
|
| 251 |
st.session_state.current_task_id = new_task_id
|
| 252 |
st.session_state.info_message = None # 清除旧的 "钱包未加密" 消息,准备新任务
|
| 253 |
|
|
|
|
| 254 |
thread = threading.Thread(
|
| 255 |
target=background_cracker_task,
|
| 256 |
args=(wallet_bytes_data_main, passwords_to_try_iterable, new_task_id, method_name_for_task)
|
| 257 |
)
|
| 258 |
+
thread.daemon = True
|
| 259 |
thread.start()
|
| 260 |
try:
|
| 261 |
+
st.experimental_rerun()
|
| 262 |
except AttributeError:
|
| 263 |
+
pass
|
| 264 |
|
| 265 |
else:
|
| 266 |
st.info("请先上传 wallet.dat 文件。")
|