Skip to content

Instantly share code, notes, and snippets.

@imneonizer
Last active July 31, 2025 08:45
Show Gist options
  • Save imneonizer/a403fa5f3fe897df756807819024aae1 to your computer and use it in GitHub Desktop.
Save imneonizer/a403fa5f3fe897df756807819024aae1 to your computer and use it in GitHub Desktop.
# usage: python crack_pdf_password.py --pdf EAadhaar.pdf --age 70
import itertools
import multiprocessing
from multiprocessing import Manager, Pool
from tqdm import tqdm
from PyPDF2 import PdfReader
import time
import threading
import argparse
from datetime import datetime
class PDFPasswordCracker:
def __init__(self, pdf_path, approx_age=65, num_workers=4):
self.pdf_path = pdf_path
self.num_workers = num_workers
self.letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
self.approx_age = approx_age
self.chunk_size = 100 # number of prefixes per task
current_year = datetime.now().year
start_year = current_year - (approx_age + 35)
end_year = current_year - (approx_age - 5)
self.years = [str(y) for y in range(start_year, end_year + 1)]
self.total_attempts = 26**4 * len(self.years)
def _attempt_passwords(self, args):
chunk, password_found_event, shared_dict, counter, lock = args
with open(self.pdf_path, "rb") as f:
reader = PdfReader(f)
for prefix_tuple in chunk:
if password_found_event.is_set():
break
prefix = "".join(prefix_tuple)
for year in self.years:
if password_found_event.is_set():
break
password = prefix + year
try:
if reader.decrypt(password):
with lock:
shared_dict["password"] = password
password_found_event.set()
return
except Exception:
pass
with lock:
counter["value"] += 1
def _chunk_prefixes(self, iterable, chunk_size):
it = iter(iterable)
while True:
chunk = list(itertools.islice(it, chunk_size))
if not chunk:
break
yield chunk
def crack(self):
manager = Manager()
password_found_event = manager.Event()
shared_dict = manager.dict()
counter = manager.dict()
counter["value"] = 0
lock = manager.Lock()
# Create chunks of work
prefix_gen = itertools.product(self.letters, repeat=4)
chunked_prefixes = list(self._chunk_prefixes(prefix_gen, self.chunk_size))
args = [
(chunk, password_found_event, shared_dict, counter, lock)
for chunk in chunked_prefixes
]
# Progress bar thread
def progress_updater(total, counter_dict, stop_event):
with tqdm(total=total, desc="Brute-forcing", unit="attempt") as pbar:
last = 0
while not stop_event.is_set():
with lock:
current = counter_dict["value"]
pbar.update(current - last)
last = current
time.sleep(0.1)
stop_event = threading.Event()
progress_thread = threading.Thread(
target=progress_updater, args=(self.total_attempts, counter, stop_event)
)
progress_thread.start()
with Pool(processes=self.num_workers) as pool:
pool.map(self._attempt_passwords, args)
stop_event.set()
progress_thread.join()
if "password" in shared_dict:
print(f"\n[✓] Password found: {shared_dict['password']}")
return shared_dict["password"]
else:
print("\n[-] Password not found.")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Brute-force Aadhaar PDF password")
parser.add_argument("--pdf", help="Path to Aadhaar PDF")
parser.add_argument(
"--age",
type=int,
default=100,
help="Approximate age of the person (default: 100)",
)
parser.add_argument(
"--workers",
type=int,
default=multiprocessing.cpu_count(),
help="Number of parallel workers (default: CPU cores)",
)
args = parser.parse_args()
cracker = PDFPasswordCracker(
args.pdf, approx_age=args.age, num_workers=args.workers
)
cracker.crack()
# Use this if first 4 letter of name is already known
import PyPDF2
pdf_path = "EAadhaar.pdf"
prefix = "GENI"
# Load the PDF
with open(pdf_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
for year in range(1925, 2026):
password = f"{prefix}{year}"
try:
if reader.decrypt(password):
print(f"Password found: {password}")
break
except Exception as e:
continue
else:
print("Password not found in given range.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment