| import streamlit as st |
| import subprocess |
| import tempfile |
| import os |
| import shutil |
|
|
| |
| APP_TITLE = "Bitcoin Wallet.dat Password Recovery (btcrecover)" |
| APP_INTRO = """ |
| Upload your `wallet.dat` file and a password list to attempt recovery. |
| This tool uses `btcrecover` in the backend. |
| """ |
| WARNING_TEXT = """ |
| **β οΈ Important Considerations for Hugging Face Spaces:** |
| - **Resource Limits:** Hugging Face Spaces (especially free tier) have CPU, memory, and execution time limits. |
| - **Execution Time:** Password recovery can be very time-consuming. Long-running jobs might be terminated. This tool is best for very small password lists or educational purposes on this platform. |
| - **Security:** Your `wallet.dat` file is uploaded to the server for processing. While not stored permanently by this app, please be aware of this. |
| - **Patience:** The process can be slow. Output will stream below. |
| """ |
|
|
| def find_btcrecover_executable(): |
| """Tries to find the btcrecover executable.""" |
| executable = shutil.which("btcrecover") |
| if executable: |
| return executable |
| |
| possible_paths = [ |
| "/usr/local/bin/btcrecover", |
| os.path.expanduser("~/.local/bin/btcrecover") |
| ] |
| for path in possible_paths: |
| if os.path.exists(path) and os.access(path, os.X_OK): |
| return path |
| return None |
|
|
| def run_btcrecover(wallet_path, password_list_path, btcrecover_exe, extra_args=None): |
| """Runs btcrecover and streams its output.""" |
| command = [ |
| btcrecover_exe, |
| "--walletfile", wallet_path, |
| "--passwordlist", password_list_path, |
| ] |
| if extra_args: |
| command.extend(extra_args) |
|
|
| st.info(f"Executing: {' '.join(command)}") |
| |
| |
| output_placeholder = st.empty() |
| log_output = "" |
|
|
| try: |
| process = subprocess.Popen( |
| command, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| text=True, |
| bufsize=1, |
| universal_newlines=True |
| ) |
|
|
| if process.stdout: |
| for line in iter(process.stdout.readline, ''): |
| log_output += line |
| output_placeholder.code(log_output, language="text") |
| if "Password found:" in line: |
| st.balloons() |
| st.success(f"π Password potentially found! Check logs above. Line: {line.strip()}") |
| elif "Password not found" in line and "exhaustive" in line: |
| st.warning("Password not found in the list after exhaustive search by btcrecover.") |
| process.stdout.close() |
| |
| return_code = process.wait() |
|
|
| if return_code != 0 and "Password found:" not in log_output: |
| st.error(f"btcrecover exited with error code {return_code}. Check the logs.") |
| elif "Password found:" not in log_output and ("Password not found" not in log_output or "exhaustive" not in log_output) : |
| |
| st.info("btcrecover finished. Please review the logs above to determine the outcome.") |
|
|
|
|
| except FileNotFoundError: |
| st.error(f"Error: btcrecover executable not found at '{btcrecover_exe}'. Ensure it's installed and in PATH.") |
| except Exception as e: |
| st.error(f"An unexpected error occurred: {str(e)}") |
| st.code(log_output, language="text") |
|
|
| |
| st.set_page_config(page_title=APP_TITLE, layout="wide") |
| st.title(APP_TITLE) |
| st.markdown(APP_INTRO) |
| st.warning(WARNING_TEXT) |
|
|
| btcrecover_exe = find_btcrecover_executable() |
| if not btcrecover_exe: |
| st.error("`btcrecover` executable not found in the environment. The application cannot proceed.") |
| st.stop() |
| else: |
| st.sidebar.success(f"btcrecover found: {btcrecover_exe}") |
|
|
|
|
| |
| st.sidebar.header("1. Upload Files") |
| uploaded_wallet_file = st.sidebar.file_uploader("Upload wallet.dat", type=['dat', 'wallet']) |
| uploaded_password_list = st.sidebar.file_uploader("Upload password list (text file, one password per line)", type=['txt', 'list']) |
|
|
| |
| st.sidebar.markdown("--- OR ---") |
| passwords_text_area = st.sidebar.text_area("Paste passwords (one per line)", height=150, |
| help="If you provide passwords here, the uploaded password list will be ignored.") |
|
|
| st.sidebar.header("2. Options") |
| no_strict_verify = st.sidebar.checkbox("Disable strict wallet verification (`--no-strict-wallet-verify`)", |
| help="Useful for very old or slightly non-standard wallet.dat files.") |
| |
| |
|
|
|
|
| if st.sidebar.button("π Start Recovery Attempt"): |
| if not uploaded_wallet_file: |
| st.error("Please upload a wallet.dat file.") |
| elif not uploaded_password_list and not passwords_text_area.strip(): |
| st.error("Please upload a password list file OR paste passwords into the text area.") |
| else: |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".dat") as tmp_wallet, \ |
| tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".txt") as tmp_passlist: |
|
|
| |
| tmp_wallet.write(uploaded_wallet_file.getvalue()) |
| tmp_wallet_path = tmp_wallet.name |
|
|
| |
| if passwords_text_area.strip(): |
| tmp_passlist.write(passwords_text_area.strip()) |
| else: |
| tmp_passlist.write(uploaded_password_list.getvalue().decode()) |
| tmp_passlist_path = tmp_passlist.name |
| |
| |
| tmp_wallet.close() |
| tmp_passlist.close() |
|
|
| st.info(f"Wallet file saved to: {tmp_wallet_path}") |
| st.info(f"Password list saved to: {tmp_passlist_path}") |
|
|
| extra_args = [] |
| if no_strict_verify: |
| extra_args.append("--no-strict-wallet-verify") |
| |
| |
| |
|
|
| with st.spinner("Attempting password recovery... Please wait. This can take a very long time."): |
| run_btcrecover(tmp_wallet_path, tmp_passlist_path, btcrecover_exe, extra_args) |
|
|
| |
| try: |
| os.unlink(tmp_wallet_path) |
| os.unlink(tmp_passlist_path) |
| st.caption(f"Cleaned up temporary files: {os.path.basename(tmp_wallet_path)}, {os.path.basename(tmp_passlist_path)}") |
| except Exception as e: |
| st.warning(f"Could not clean up temporary files: {e}") |
| else: |
| st.info("Upload your files and click 'Start Recovery Attempt' in the sidebar.") |
|
|
| st.markdown("---") |
| st.markdown("Powered by [btcrecover](https://github.com/gurnec/btcrecover) and [Streamlit](https://streamlit.io/).") |
| st.markdown("Remember the limitations when running on free cloud platforms.") |