aianyu commited on
Commit
6ba34a3
·
verified ·
1 Parent(s): 8c24af7

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -56
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
- # 我们在主UI线程中通过session_state传递这个信息,而不是在后台线程直接调用st.info()
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' # 或 'aborted'
78
  return
79
 
80
  processed_passwords = pwd_count + 1
81
  st.session_state[f'task_{task_id}_attempts'] = processed_passwords
82
 
83
- # 更新进度(可选,如果spinner的文本不直接显示这个)
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 try_decrypt_wallet(wallet_bytes_data, pwd):
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
- # 对于非常快的循环,可以考虑不那么频繁地更新session_state以减少开销
94
- # 例如: if processed_passwords % 100 == 0: st.session_state...
95
- # 但目前每次更新是了让spinner中的计数尽可能准确
 
 
 
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: # 用于生成唯一的 task_id
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" # 添加key以帮助Streamlit跟踪小部件状态
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
- # st.progress(progress_val) # 如果有进度条
154
- st.empty() # 保持 spinner 可见
155
- time.sleep(0.5) # 调整此处的休眠时间,控制UI刷新频率
156
  try:
157
  st.experimental_rerun()
158
- except AttributeError: # 兼容非常旧的 Streamlit 版本
159
  st.warning("您的 Streamlit 版本较旧,spinner 状态可能不会实时更新。")
160
 
161
 
162
  elif task_status == 'success':
163
- found_pwd = st.session_state.get(f'task_{current_task_id}_found_password')
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
- st.success(f"🎉 密码找到: **{found_pwd}** 🎉")
167
- st.balloons()
 
 
 
 
 
 
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
- # 显示来自 try_decrypt_wallet 的一次性消息 (例如 "钱包未加密")
193
- if st.session_state.get('info_message') and task_status != 'running': # 仅当任务不在运行时显示
194
- st.info(st.session_state.info_message)
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() # 立即重新运行脚本以显示 spinner
271
  except AttributeError:
272
- pass # 旧版 Streamlit
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 文件。")