File size: 11,787 Bytes
48ccb12 8c24af7 48ccb12 b737f2b 48ccb12 b737f2b 8c24af7 48ccb12 8c24af7 6ba34a3 48ccb12 8c24af7 6ba34a3 8c24af7 48ccb12 8c24af7 48ccb12 8c24af7 48ccb12 6ba34a3 8c24af7 48ccb12 8c24af7 48ccb12 8c24af7 48ccb12 8c24af7 b737f2b 8c24af7 b737f2b 8c24af7 b737f2b 8c24af7 6ba34a3 b737f2b 8c24af7 b737f2b 6ba34a3 8c24af7 6ba34a3 b737f2b 6ba34a3 b737f2b 6ba34a3 b737f2b 8c24af7 48ccb12 b737f2b 8c24af7 48ccb12 8c24af7 b737f2b 6ba34a3 b737f2b 6ba34a3 b737f2b 8c24af7 48ccb12 6ba34a3 8c24af7 48ccb12 8c24af7 6ba34a3 48ccb12 b737f2b 6ba34a3 856d71b 6ba34a3 8c24af7 b737f2b 6ba34a3 b737f2b 6ba34a3 b737f2b 6ba34a3 8c24af7 856d71b 6ba34a3 856d71b 8c24af7 b737f2b 8c24af7 b737f2b 8c24af7 856d71b 8c24af7 856d71b 8c24af7 b737f2b 6ba34a3 b737f2b 8c24af7 6ba34a3 b737f2b 8c24af7 48ccb12 b737f2b 48ccb12 8c24af7 b737f2b 6ba34a3 b737f2b 48ccb12 8c24af7 b737f2b 8c24af7 856d71b 8c24af7 856d71b b737f2b 8c24af7 b737f2b 8c24af7 b737f2b 6ba34a3 b737f2b 856d71b 6ba34a3 856d71b 6ba34a3 48ccb12 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
import streamlit as st
from pywallet import wallet # 确保这个库能正常工作,并且你知道它的局限性
import time
import itertools
import string
import threading
# --- 配置 ---
DEFAULT_CHARSET = string.ascii_lowercase + string.digits
MAX_BRUTEFORCE_LEN = 4 # 保持较小以进行测试,实际破解中此长度几乎无效
# --- 辅助函数 ---
WALLET_NOT_ENCRYPTED_SENTINEL = "WALLET_NOT_ENCRYPTED" # 定义一个哨兵值
def try_decrypt_wallet(wallet_data_bytes, password_attempt):
"""
尝试用给定的密码解密钱包。
返回 True 如果成功, False 如果密码错误, WALLET_NOT_ENCRYPTED_SENTINEL 如果未加密。
"""
try:
w = wallet.Wallet.from_binary(wallet_data_bytes)
if w.is_encrypted():
if w.decrypt(password_attempt):
return True # 解密成功
else:
return False # 密码错误
else:
# 钱包未加密,返回哨兵值
return WALLET_NOT_ENCRYPTED_SENTINEL
except Exception as e:
# print(f"Decryption error with password '{password_attempt}': {e}") # 调试时取消注释
return False
def generate_passwords_bruteforce(charset, max_len):
"""
(非常不推荐) 基本的暴力破解密码生成器
"""
for length in range(1, max_len + 1):
for item in itertools.product(charset, repeat=length):
yield "".join(item)
# --- 后台工作函数 ---
def background_cracker_task(wallet_bytes_data, passwords_iterable, task_id, method_name):
"""
在后台线程中运行破解过程。
更新 st.session_state 来反映进度和结果。
"""
# 初始化任务状态
st.session_state[f'task_{task_id}_status'] = 'running'
st.session_state[f'task_{task_id}_found_password'] = None
st.session_state[f'task_{task_id}_error_message'] = None
st.session_state[f'task_{task_id}_attempts'] = 0
st.session_state[f'task_{task_id}_time_taken'] = 0
start_time = time.time()
found = False
processed_passwords = 0
for pwd_count, pwd in enumerate(passwords_iterable):
if st.session_state.get('current_task_id') != task_id:
st.session_state[f'task_{task_id}_error_message'] = "任务被中止。"
st.session_state[f'task_{task_id}_status'] = 'failed'
return
processed_passwords = pwd_count + 1
st.session_state[f'task_{task_id}_attempts'] = processed_passwords
decryption_status = try_decrypt_wallet(wallet_bytes_data, pwd)
if decryption_status is True: # 密码正确
st.session_state[f'task_{task_id}_found_password'] = pwd
st.session_state[f'task_{task_id}_status'] = 'success'
found = True
break
elif decryption_status == WALLET_NOT_ENCRYPTED_SENTINEL: # 钱包未加密
st.session_state.info_message = "钱包文件未加密。" # 在这里设置
st.session_state[f'task_{task_id}_found_password'] = WALLET_NOT_ENCRYPTED_SENTINEL # 使用哨兵值标记
st.session_state[f'task_{task_id}_status'] = 'success' # 同样视为成功
found = True
break
# else: decryption_status is False, 密码错误,继续循环
end_time = time.time()
st.session_state[f'task_{task_id}_time_taken'] = end_time - start_time
if not found and st.session_state.get(f'task_{task_id}_status') == 'running':
st.session_state[f'task_{task_id}_error_message'] = "未找到密码。"
st.session_state[f'task_{task_id}_status'] = 'failed'
# --- Streamlit UI ---
st.set_page_config(page_title="Wallet.dat Cracker (Background)", layout="wide")
st.title("Bitcoin Wallet.dat 密码破解 (后台运行)")
st.warning(
"""
**重要声明:**
1. 此工具仅用于恢复 **您自己** 的钱包密码。
2. 破解过程 **极其缓慢**,对复杂密码几乎无效。
3. **请勿将您的真实钱包文件上传到不受信任的在线服务。此工具应在本地运行。**
4. 对于任何数据丢失或法律问题,作者概不负责。
"""
)
# 初始化 session_state 变量 (如果它们不存在)
if 'current_task_id' not in st.session_state:
st.session_state.current_task_id = None
if 'task_counter' not in st.session_state:
st.session_state.task_counter = 0
if 'info_message' not in st.session_state:
st.session_state.info_message = None
# --- 文件上传 ---
uploaded_wallet_file = st.file_uploader("1. 上传你的 wallet.dat 文件", type=["dat"])
if uploaded_wallet_file:
wallet_bytes_data_main = uploaded_wallet_file.getvalue()
st.success(f"已上传 {uploaded_wallet_file.name} ({len(wallet_bytes_data_main)} bytes)")
crack_method = st.radio(
"2. 选择破解方法:",
('使用密码列表', '尝试单个密码', '基本暴力破解 (极慢,后台运行)'),
key="crack_method_radio"
)
current_task_id = st.session_state.get('current_task_id')
task_status = None
if current_task_id:
task_status = st.session_state.get(f'task_{current_task_id}_status')
if task_status == 'running':
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
with st.spinner(f"正在后台破解... 已尝试 {attempts} 个密码。请勿关闭此页面。"):
st.empty()
time.sleep(0.5)
try:
st.experimental_rerun()
except AttributeError:
st.warning("您的 Streamlit 版本较旧,spinner 状态可能不会实时更新。")
elif task_status == 'success':
found_pwd_or_status = st.session_state.get(f'task_{current_task_id}_found_password')
time_taken = st.session_state.get(f'task_{current_task_id}_time_taken', 0)
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
if found_pwd_or_status == WALLET_NOT_ENCRYPTED_SENTINEL:
st.success(f"✅ 钱包文件未加密。")
# info_message 应该已经被后台任务设置,这里不需要再次设置
else:
st.success(f"🎉 密码找到: **{found_pwd_or_status}** 🎉")
st.balloons()
st.info(f"总耗时: {time_taken:.2f} 秒。尝试密码数: {attempts}。")
if st.button("清除结果并开始新任务", key="clear_success"):
st.session_state.current_task_id = None
st.session_state.info_message = None
try:
st.experimental_rerun()
except AttributeError:
pass
elif task_status == 'failed':
error_msg = st.session_state.get(f'task_{current_task_id}_error_message', "未知错误")
time_taken = st.session_state.get(f'task_{current_task_id}_time_taken', 0)
attempts = st.session_state.get(f'task_{current_task_id}_attempts', 0)
st.error(f"破解失败: {error_msg}")
st.info(f"总耗时: {time_taken:.2f} 秒。尝试密码数: {attempts}。")
if st.button("清除结果并开始新任务", key="clear_failed"):
st.session_state.current_task_id = None
st.session_state.info_message = None
try:
st.experimental_rerun()
except AttributeError:
pass
# 显示来自后台任务的一次性消息 (例如 "钱包未加密")
# 这个消息会在 'success' 状态时由后台任务设置
# 确保它只在相关任务完成后显示一次,并且在清除任务时也被清除
if st.session_state.get('info_message') and task_status == 'success' and st.session_state.get(
f'task_{current_task_id}_found_password') == WALLET_NOT_ENCRYPTED_SENTINEL:
# st.info(st.session_state.info_message) # 这行其实可以省略,因为 "钱包文件未加密" 已经在 success 块中显示
pass # The message is handled within the success block now
if not task_status or task_status in ['success', 'failed']:
passwords_to_try_iterable = None
can_start = False
start_button_label = "开始破解"
method_name_for_task = ""
if crack_method == '使用密码列表':
uploaded_password_list = st.file_uploader(
"上传密码列表文件 ( .txt, 每行一个密码)", type=["txt"], key="pwdlist_uploader"
)
if uploaded_password_list:
try:
passwords_list = [
line.strip() for line in uploaded_password_list.read().decode().splitlines() if line.strip()
]
if passwords_list:
st.info(f"密码列表加载了 {len(passwords_list)} 个密码。")
passwords_to_try_iterable = passwords_list
can_start = True
start_button_label = "开始使用列表破解"
method_name_for_task = "list"
else:
st.warning("上传的密码列表为空或无效。")
except Exception as e:
st.error(f"读取密码列表文件错误: {e}")
else:
st.info("请上传密码列表文件。")
elif crack_method == '尝试单个密码':
single_password = st.text_input("输入要尝试的密码:", type="password", key="single_pwd_input")
if single_password:
passwords_to_try_iterable = [single_password]
can_start = True
start_button_label = "尝试此密码"
method_name_for_task = "single"
else:
st.info("请输入要尝试的密码。")
elif crack_method == '基本暴力破解 (极慢,后台运行)':
charset_input = st.text_input("字符集:", DEFAULT_CHARSET, key="charset_input")
max_len_input = st.number_input(
"最大密码长度 (警告: 长度大于4会非常慢):",
min_value=1, max_value=8, value=MAX_BRUTEFORCE_LEN, key="maxlen_input"
)
if charset_input:
estimated_total = sum(len(charset_input) ** i for i in range(1, max_len_input + 1))
st.warning(f"使用字符集 '{charset_input}' 和最大长度 {max_len_input} 进行暴力破解,"
f"可能需要尝试 {estimated_total} 个密码。这会非常耗时。")
passwords_to_try_iterable = generate_passwords_bruteforce(charset_input, max_len_input)
can_start = True
start_button_label = "开始暴力破解 (后台)"
method_name_for_task = "bruteforce"
else:
st.error("字符集不能为空。")
if can_start and st.button(start_button_label, key=f"start_button_{method_name_for_task}"):
st.session_state.task_counter += 1
new_task_id = st.session_state.task_counter
st.session_state.current_task_id = new_task_id
st.session_state.info_message = None # 清除旧的 "钱包未加密" 消息,准备新任务
thread = threading.Thread(
target=background_cracker_task,
args=(wallet_bytes_data_main, passwords_to_try_iterable, new_task_id, method_name_for_task)
)
thread.daemon = True
thread.start()
try:
st.experimental_rerun()
except AttributeError:
pass
else:
st.info("请先上传 wallet.dat 文件。")
st.markdown("---")
st.markdown("免责声明:此工具仅供教育和个人恢复目的。请负责任地使用。") |