想象一下你有一堆密码碎片(比如单词表中的单词),PRINCE算法就像是一个智能的"密码拼图大师",它能用各种方式把这些碎片组合起来,生成大量可能的密码组合。今天咱们就扒开princeprocessor(简称 pp)的代码,看看这个 "拼词高手" 到底怎么干活,以及它背后的设计巧思。
先搞懂:PRINCE不是加密,是"拼词配方"
PRINCE是Permuted Iterative Name Chaining Engine的缩写,翻译过来就是"排列迭代名称链引擎"。说白了,它的核心不是搞加密,而是根据词表生成"短词拼接"的候选密码------毕竟普通人设密码,爱用"生日+名字""单词+数字"这种组合,纯暴力枚举所有字符(比如a-z瞎凑)实在太笨了。
举个例子:词表里有"123"(3位)、"abc"(3位)、"xy"(2位),PRINCE会按规则拼出"123xy""abcxy""xy123"这些组合------这些比随机字符串更可能命中真实密码,这就是PRINCE的价值。
PRINCE的核心工作流程:像"密码拼工厂"
pp的代码逻辑其实就是一条"拼密码流水线",咱们一步一步看:
第一步:词表"分拣入库"------把素材按长度归类
pp拿到词表(比如rockyou.txt)后,第一件事是"分类打包":把所有词按长度扔进不同"仓库"。比如3位的词放进db_entries[3],2位的放进db_entries[2]------代码里的db_entry_t结构就是干这个的,每个长度对应一个仓库,仓库里存着该长度的所有词(elems_buf)。
如果开了去重(默认开),pp还会用哈希表(uniq_t)给每个词算哈希值(input_hash函数),避免同一个词存多次。比如词表里有两个"123",只留一个就行------这是典型的"用空间换时间",哈希表查重复比逐个比对快多了。
第二步:生成"拼词配方"------合法的"链(Chain)"
不是随便拼都有用,得按用户要求来:比如要5位密码(--pw-max=5)、至少用2个词拼(--elem-cnt-min=2),pp就得生成符合条件的"配方"------也就是代码里的chain_t(链)。
比如5位密码的合法配方可以是[3,2](3位词+2位词)或[2,3](2位词+3位词)。代码里的chain_gen_with_idx函数就是生成这些配方的:它把总长度拆成不同的长度组合,再用chain_valid_with_cnt_min/max筛掉不合格的(比如[5]这种只用1个词的就被筛了)。
第三步:按配方"拼词出货"------生成候选密码
有了素材(分好类的词)和配方(链),接下来就是拼词了。比如配方是[3,2],就从3号仓库拿"123",从2号仓库拿"xy",拼成"123xy"------代码里的chain_set_pwbuf_init是拼第一个候选词,chain_set_pwbuf_increment是"下一个"(比如换成"123ab")。
而且pp不会拼一个就写一次文件,而是攒够一整块(BUFSIZ大小)再输出(out_push+out_flush)------写文件是慢操作,批量写能省不少时间,这是工程优化的小细节。
代码里的"聪明设计":细节决定效率
pp能跑快、跑稳,靠的是这些藏在代码里的巧思:
1. 内存池(malloc_tiny):避免频繁申请小内存
拼密码要存大量短词,如果每次存一个词都调用malloc,会产生很多内存碎片,还慢。pp的做法是:一次性申请一大块内存(比如64KB),然后切着用------malloc_tiny函数就是干这个的,小块内存从预分配的大块里拿,效率高多了。
2. 大数处理(mpz_t):应对"组合爆炸"
比如3号仓库有1000个词,2号仓库有100个词,能拼出10万种组合,普通整数(比如u64)可能存不下位置。pp用GMP库的mpz_t类型(多精度整数)来记位置,再大的数也hold住。
3. 断点续跑(catch_int):不怕中途断档
如果生成到一半按了Ctrl+C,pp会把当前拼到哪的位置存到pp.save文件里,下次能接着跑------catch_int函数就是处理中断信号的,工程上的"用户友好设计"。
核心逻辑简化版:5行代码看懂PRINCE
咱们把pp的核心逻辑剥出来,写个"迷你版",一看就懂:
c
#include <stdio.h>
#include <string.h>
// 素材仓库:按长度存词
char* word_3[] = {"123", "abc"}; // 3位词仓库
char* word_2[] = {"xy", "ab"}; // 2位词仓库
// 拼词配方:[3,2]表示3位+2位
void prince_make(int len1, int len2) {
for (int i=0; i<2; i++) { // 遍历3位词
for (int j=0; j<2; j++) { // 遍历2位词
char pw[10];
strcpy(pw, word_3[i]);
strcat(pw, word_2[j]);
printf("候选密码:%s\n", pw);
}
}
}
int main() {
prince_make(3, 2); // 按[3,2]配方拼词
return 0;
}
运行结果会输出:
候选密码:123xy
候选密码:123ab
候选密码:abcxy
候选密码:abcab
这就是PRINCE的核心------按长度组合遍历词表,拼出候选密码!
流程原理图:PRINCE的"流水线"
词表输入 → 按长度分拣(db_entry_t)→ 去重(哈希表)→ 生成合法链(chain_t)→ 按链拼词(chain_set_pwbuf)→ 批量输出(out_flush)
↑ ↓
└──────────────────────── 中断保存进度(catch_int) ────────────────────┘
cpp
...
int main (int argc, char *argv[])
{
...
#define IDX_VERSION 'V'
#define IDX_USAGE 'h'
#define IDX_PW_MIN 0x1000
#define IDX_PW_MAX 0x2000
#define IDX_ELEM_CNT_MIN 0x3000
#define IDX_ELEM_CNT_MAX 0x4000
#define IDX_KEYSPACE 0x5000
#define IDX_WL_DIST_LEN 0x6000
#define IDX_WL_MAX 0x7000
#define IDX_CASE_PERMUTE 0x8000
#define IDX_SAVE_POS_DISABLE 0x9000
#define IDX_DUPE_CHECK_DISABLE 'c'
#define IDX_SKIP 's'
#define IDX_LIMIT 'l'
#define IDX_OUTPUT_FILE 'o'
struct option long_options[] =
{
{"version", no_argument, 0, IDX_VERSION},
{"help", no_argument, 0, IDX_USAGE},
{"keyspace", no_argument, 0, IDX_KEYSPACE},
{"pw-min", required_argument, 0, IDX_PW_MIN},
{"pw-max", required_argument, 0, IDX_PW_MAX},
{"elem-cnt-min", required_argument, 0, IDX_ELEM_CNT_MIN},
{"elem-cnt-max", required_argument, 0, IDX_ELEM_CNT_MAX},
{"wl-dist-len", no_argument, 0, IDX_WL_DIST_LEN},
{"wl-max", required_argument, 0, IDX_WL_MAX},
{"case-permute", no_argument, 0, IDX_CASE_PERMUTE},
{"dupe-check-disable", no_argument, 0, IDX_DUPE_CHECK_DISABLE},
{"save-pos-disable", no_argument, 0, IDX_SAVE_POS_DISABLE},
{"skip", required_argument, 0, IDX_SKIP},
{"limit", required_argument, 0, IDX_LIMIT},
{"output-file", required_argument, 0, IDX_OUTPUT_FILE},
{0, 0, 0, 0}
};
int elem_cnt_max_chgd = 0;
int option_index = 0;
int c;
while ((c = getopt_long (argc, argv, "Vhs:l:o:c", long_options, &option_index)) != -1)
{
switch (c)
{
case IDX_VERSION: version = 1; break;
case IDX_USAGE: usage = 1; break;
case IDX_KEYSPACE: keyspace = 1; break;
case IDX_PW_MIN: pw_min = atoi (optarg); break;
case IDX_PW_MAX: pw_max = atoi (optarg); break;
case IDX_ELEM_CNT_MIN: elem_cnt_min = atoi (optarg); break;
case IDX_ELEM_CNT_MAX: elem_cnt_max = atoi (optarg);
elem_cnt_max_chgd = 1; break;
case IDX_WL_DIST_LEN: wl_dist_len = 1; break;
case IDX_WL_MAX: wl_max = atoi (optarg); break;
case IDX_CASE_PERMUTE: case_permute = 1; break;
case IDX_DUPE_CHECK_DISABLE: dupe_check = 0; break;
case IDX_SAVE_POS_DISABLE: save_pos = 0; break;
case IDX_SKIP: mpz_set_str (skip, optarg, 10); break;
case IDX_LIMIT: mpz_set_str (limit, optarg, 10); break;
case IDX_OUTPUT_FILE: output_file = optarg; break;
default: return (-1);
}
}
if (elem_cnt_max_chgd == 0)
{
elem_cnt_max = MIN (pw_max, ELEM_CNT_MAX);
}
if (usage)
{
usage_big_print (argv[0]);
return (-1);
}
if (version)
{
printf ("v%4.02f\n", (double) VERSION_BIN / 100);
return (-1);
}
if ((optind != argc) && (optind + 1 != argc))
{
usage_mini_print (argv[0]);
return (-1);
}
char *wordlist = NULL;
if (optind + 1 == argc)
{
wordlist = argv[optind];
}
if (pw_min <= 0)
{
fprintf (stderr, "Value of --pw-min (%d) must be greater than %d\n", pw_min, 0);
return (-1);
}
if (pw_max <= 0)
{
fprintf (stderr, "Value of --pw-max (%d) must be greater than %d\n", pw_max, 0);
return (-1);
}
if (elem_cnt_min <= 0)
{
fprintf (stderr, "Value of --elem-cnt-min (%d) must be greater than %d\n", elem_cnt_min, 0);
return (-1);
}
if (elem_cnt_max < (1 - pw_max))
{
fprintf (stderr, "Value of --elem-cnt-max (%d) must be greater than %d\n", elem_cnt_max, (0 - pw_max));
return (-1);
}
if (pw_min > pw_max)
{
fprintf (stderr, "Value of --pw-min (%d) must be smaller or equal than value of --pw-max (%d)\n", pw_min, pw_max);
return (-1);
}
if (elem_cnt_max > 0 && elem_cnt_min > elem_cnt_max)
{
fprintf (stderr, "Value of --elem-cnt-min (%d) must be smaller or equal than value of --elem-cnt-max (%d)\n", elem_cnt_min, elem_cnt_max);
return (-1);
}
if (pw_min < IN_LEN_MIN)
{
fprintf (stderr, "Value of --pw-min (%d) must be greater or equal than %d\n", pw_min, IN_LEN_MIN);
return (-1);
}
if (pw_max > OUT_LEN_MAX)
{
fprintf (stderr, "Value of --pw-max (%d) must be smaller or equal than %d\n", pw_max, OUT_LEN_MAX);
return (-1);
}
if (elem_cnt_max > pw_max)
{
fprintf (stderr, "Value of --elem-cnt-max (%d) must be smaller or equal than value of --pw-max (%d)\n", elem_cnt_max, pw_max);
return (-1);
}
/**
* 操作系统特定设置
*/
#ifdef WINDOWS
setmode (fileno (stdout), O_BINARY);
#endif
/**
* 分配一些空间
*/
db_entry_t *db_entries = (db_entry_t *) calloc (pw_max + 1, sizeof (db_entry_t));
pw_order_t *pw_orders = (pw_order_t *) calloc (pw_max + 1, sizeof (pw_order_t));
u64 *wordlen_dist = (u64 *) calloc (pw_max + 1, sizeof (u64));
out_t *out = (out_t *) mem_alloc (sizeof (out_t));
out->fp = stdout;
out->len = 0;
if (dupe_check)
{
int in_max = MIN(IN_LEN_MAX, pw_max);
for (int pw_len = IN_LEN_MIN; pw_len <= in_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
const u32 hash_size = 1 << DEF_HASH_LOG_SIZE[pw_len];
const u32 hash_alloc = ALLOC_NEW_DUPES;
uniq_t *uniq = mem_alloc (sizeof (uniq_t));
uniq->hash_mask = hash_size - 1;
uniq->data = mem_alloc (hash_alloc * sizeof (uniq_data_t));
uniq->hash = mem_alloc (hash_size * sizeof (u32));
uniq->index = 0;
uniq->alloc = hash_alloc;
memset (uniq->hash, 0xff, hash_size * sizeof (u32));
db_entry->uniq = uniq;
}
}
/**
* 文件
*/
if (output_file)
{
out->fp = fopen (output_file, "ab");
if (out->fp == NULL)
{
fprintf (stderr, "%s: %s\n", output_file, strerror (errno));
return (-1);
}
}
/*
* 捕获用户中断信号
*/
if (save_pos)
{
signal (SIGINT, catch_int);
}
/**
* 从标准输入加载元素
*/
FILE *read_fp = stdin;
if (wordlist)
{
read_fp = fopen (wordlist, "rb");
if (read_fp == NULL)
{
fprintf (stderr, "%s: %s\n", wordlist, strerror (errno));
return (-1);
}
}
int wl_cnt = 0;
while (!feof (read_fp))
{
char buf[BUFSIZ];
char *input_buf = fgets (buf, sizeof (buf), read_fp);
if (input_buf == NULL) continue;
const int input_len = in_superchop (input_buf);
if (input_len < IN_LEN_MIN) continue;
if (input_len > IN_LEN_MAX) continue;
if (input_len > pw_max) continue;
db_entry_t *db_entry = &db_entries[input_len];
if (!dupe_check)
{
add_elem (db_entry, input_buf, input_len);
}
else
{
add_uniq (db_entry, input_buf, input_len);
}
if (case_permute)
{
const char old_c = input_buf[0];
const char new_cu = toupper (old_c);
const char new_cl = tolower (old_c);
if (old_c != new_cu)
{
input_buf[0] = new_cu;
if (!dupe_check)
{
add_elem (db_entry, input_buf, input_len);
}
else
{
add_uniq (db_entry, input_buf, input_len);
}
}
if (old_c != new_cl)
{
input_buf[0] = new_cl;
if (!dupe_check)
{
add_elem (db_entry, input_buf, input_len);
}
else
{
add_uniq (db_entry, input_buf, input_len);
}
}
}
wl_cnt++;
if (wl_max > 0 && wl_cnt == wl_max) break;
}
if (wordlist)
{
fclose (read_fp);
}
if (dupe_check)
{
int in_max = MIN(IN_LEN_MAX, pw_max);
for (int pw_len = IN_LEN_MIN; pw_len <= in_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
uniq_t *uniq = db_entry->uniq;
free (uniq->hash);
free (uniq->data);
free (uniq);
}
}
/**
* 初始链
*/
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
const int pw_len1 = pw_len - 1;
const u32 chains_cnt = 1 << pw_len1;
u8 buf[OUT_LEN_MAX];
chain_t chain_buf_new;
chain_buf_new.buf = buf;
for (u32 chains_idx = 0; chains_idx < chains_cnt; chains_idx++)
{
chain_gen_with_idx (&chain_buf_new, pw_len1, chains_idx);
// 确保所有元素确实存在
int valid1 = chain_valid_with_db (&chain_buf_new, db_entries);
if (valid1 == 0) continue;
// 通过验证元素数量是否在特定范围内来提升性能
int valid2 = chain_valid_with_cnt_min (&chain_buf_new, elem_cnt_min);
if (valid2 == 0) continue;
int eff_elem_cnt_max;
if (elem_cnt_max > 0)
{
eff_elem_cnt_max = elem_cnt_max;
}
else
{
eff_elem_cnt_max = pw_len + elem_cnt_max;
if (eff_elem_cnt_max <= elem_cnt_min) continue;
}
int valid3 = chain_valid_with_cnt_max (&chain_buf_new, eff_elem_cnt_max);
if (valid3 == 0) continue;
// 将链添加到数据库
check_realloc_chains (db_entry);
chain_t *chain_buf = &db_entry->chains_buf[db_entry->chains_cnt];
memcpy (chain_buf, &chain_buf_new, sizeof (chain_t));
chain_buf->buf = malloc_tiny (pw_len);
memcpy (chain_buf->buf, chain_buf_new.buf, pw_len);
mpz_init_set_si (chain_buf->ks_cnt, 0);
mpz_init_set_si (chain_buf->ks_pos, 0);
db_entry->chains_cnt++;
}
memset (db_entry->cur_chain_ks_poses, 0, OUT_LEN_MAX * sizeof (u64));
}
/**
* 计算候选密码输出长度分布
*/
if (wl_dist_len)
{
for (int pw_len = IN_LEN_MIN; pw_len <= pw_max; pw_len++)
{
if (pw_len <= IN_LEN_MAX)
{
db_entry_t *db_entry = &db_entries[pw_len];
wordlen_dist[pw_len] = db_entry->elems_cnt;
}
else
{
wordlen_dist[pw_len] = 1;
}
}
}
else
{
for (int pw_len = IN_LEN_MIN; pw_len <= pw_max; pw_len++)
{
if (pw_len < DEF_WORDLEN_DIST_CNT)
{
wordlen_dist[pw_len] = DEF_WORDLEN_DIST[pw_len];
}
else
{
wordlen_dist[pw_len] = 1;
}
}
}
/**
* 计算密钥空间相关内容
*/
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
int chains_cnt = db_entry->chains_cnt;
chain_t *chains_buf = db_entry->chains_buf;
mpz_set_si (tmp, 0);
for (int chains_idx = 0; chains_idx < chains_cnt; chains_idx++)
{
chain_t *chain_buf = &chains_buf[chains_idx];
chain_ks (chain_buf, db_entries, &chain_buf->ks_cnt);
mpz_add (tmp, tmp, chain_buf->ks_cnt);
}
mpz_add (total_ks_cnt, total_ks_cnt, tmp);
if (mpz_cmp_si (skip, 0))
{
mpz_init_set (pw_ks_cnt[pw_len], tmp);
}
}
if (total_ks_cnt == UINT128_MAX)
{
fprintf (stderr, "Warning: %d-bit keyspace saturated\n", FAKE_GMP);
}
if (keyspace)
{
mpz_out_str (stdout, 10, total_ks_cnt);
printf ("\n");
return 0;
}
/**
* 按ks排序链
*/
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
chain_t *chains_buf = db_entry->chains_buf;
const int chains_cnt = db_entry->chains_cnt;
qsort (chains_buf, chains_cnt, sizeof (chain_t), sort_by_ks);
}
/**
* 根据密码长度计数对全局顺序进行排序
*/
for (int pw_len = pw_min, order_pos = 0; pw_len <= pw_max; pw_len++, order_pos++)
{
db_entry_t *db_entry = &db_entries[pw_len];
const u64 elems_cnt = db_entry->elems_cnt;
pw_order_t *pw_order = &pw_orders[order_pos];
pw_order->len = pw_len;
pw_order->cnt = elems_cnt;
}
const int order_cnt = pw_max + 1 - pw_min;
qsort (pw_orders, order_cnt, sizeof (pw_order_t), sort_by_cnt);
/**
* 寻求某个起点
*/
if (mpz_cmp_si (skip, 0))
{
if (mpz_cmp (skip, total_ks_cnt) >= 0)
{
fprintf (stderr, "Value of --skip must be smaller than total keyspace\n");
return (-1);
}
}
if (mpz_cmp_si (limit, 0))
{
if (mpz_cmp (limit, total_ks_cnt) > 0)
{
fprintf (stderr, "Value of --limit cannot be larger than total keyspace\n");
return (-1);
}
mpz_add (tmp, skip, limit);
if (mpz_cmp (tmp, total_ks_cnt) > 0)
{
fprintf (stderr, "Value of --skip + --limit cannot be larger than total keyspace\n");
return (-1);
}
mpz_set (total_ks_cnt, tmp);
}
mpz_init_set (save, skip);
/**
* 跳转到第一个主循环,该循环将输出一个密码
*/
if (mpz_cmp_si (skip, 0))
{
mpz_t skip_left; mpz_init_set (skip_left, skip);
mpz_t main_loops; mpz_init (main_loops);
u64 outs_per_main_loop = 0;
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
mpz_init_set_si (pw_ks_pos[pw_len], 0);
outs_per_main_loop += wordlen_dist[pw_len];
}
// find pw_ks_pos[]
while (1)
{
mpz_fdiv_q_ui (main_loops, skip_left, outs_per_main_loop);
if (mpz_cmp_si (main_loops, 0) == 0)
{
break;
}
// 将主循环"main_loops"的次数增加
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
if (mpz_cmp (pw_ks_pos[pw_len], pw_ks_cnt[pw_len]) < 0)
{
mpz_mul_ui (tmp, main_loops, wordlen_dist[pw_len]);
mpz_add (pw_ks_pos[pw_len], pw_ks_pos[pw_len], tmp);
mpz_sub (skip_left, skip_left, tmp);
if (mpz_cmp (pw_ks_pos[pw_len], pw_ks_cnt[pw_len]) > 0)
{
mpz_sub (tmp, pw_ks_pos[pw_len], pw_ks_cnt[pw_len]);
mpz_add (skip_left, skip_left, tmp);
}
}
}
outs_per_main_loop = 0;
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
if (mpz_cmp (pw_ks_pos[pw_len], pw_ks_cnt[pw_len]) < 0)
{
outs_per_main_loop += wordlen_dist[pw_len];
}
}
}
mpz_sub (total_ks_pos, skip, skip_left);
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
int chains_cnt = db_entry->chains_cnt;
chain_t *chains_buf = db_entry->chains_buf;
mpz_set (tmp, pw_ks_pos[pw_len]);
for (int chains_idx = 0; chains_idx < chains_cnt; chains_idx++)
{
chain_t *chain_buf = &chains_buf[chains_idx];
if (mpz_cmp (tmp, chain_buf->ks_cnt) < 0)
{
mpz_set (chain_buf->ks_pos, tmp);
set_chain_ks_poses (chain_buf, db_entries, &tmp, db_entry->cur_chain_ks_poses);
break;
}
mpz_sub (tmp, tmp, chain_buf->ks_cnt);
db_entry->chains_pos++;
}
}
// clean up
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
mpz_clear (pw_ks_cnt[pw_len]);
mpz_clear (pw_ks_pos[pw_len]);
}
mpz_clear (skip_left);
mpz_clear (main_loops);
}
/**
* loop
*/
while (mpz_cmp (total_ks_pos, total_ks_cnt) < 0)
{
for (int order_pos = 0; order_pos < order_cnt; order_pos++)
{
pw_order_t *pw_order = &pw_orders[order_pos];
const int pw_len = pw_order->len;
char pw_buf[BUFSIZ];
pw_buf[pw_len] = '\n';
db_entry_t *db_entry = &db_entries[pw_len];
const u64 outs_cnt = wordlen_dist[pw_len];
u64 outs_pos = 0;
while (outs_pos < outs_cnt)
{
const int chains_cnt = db_entry->chains_cnt;
const int chains_pos = db_entry->chains_pos;
if (chains_pos == chains_cnt) break;
chain_t *chains_buf = db_entry->chains_buf;
chain_t *chain_buf = &chains_buf[chains_pos];
mpz_sub (total_ks_left, total_ks_cnt, total_ks_pos);
mpz_sub (iter_max, chain_buf->ks_cnt, chain_buf->ks_pos);
if (mpz_cmp (total_ks_left, iter_max) < 0)
{
mpz_set (iter_max, total_ks_left);
}
const u64 outs_left = outs_cnt - outs_pos;
mpz_set_ui (tmp, outs_left);
if (mpz_cmp (tmp, iter_max) < 0)
{
mpz_set (iter_max, tmp);
}
const u64 iter_max_u64 = mpz_get_ui (iter_max);
mpz_add (tmp, total_ks_pos, iter_max);
if (mpz_cmp (tmp, skip) > 0)
{
u64 iter_pos_u64 = 0;
if (mpz_cmp (total_ks_pos, skip) < 0)
{
mpz_sub (tmp, skip, total_ks_pos);
iter_pos_u64 = mpz_get_ui (tmp);
mpz_add (tmp, chain_buf->ks_pos, tmp);
set_chain_ks_poses (chain_buf, db_entries, &tmp, db_entry->cur_chain_ks_poses);
}
chain_set_pwbuf_init (chain_buf, db_entries, db_entry->cur_chain_ks_poses, pw_buf);
const u64 iter_pos_save = iter_max_u64 - iter_pos_u64;
while (iter_pos_u64 < iter_max_u64)
{
out_push (out, pw_buf, pw_len + 1);
chain_set_pwbuf_increment (chain_buf, db_entries, db_entry->cur_chain_ks_poses, pw_buf);
iter_pos_u64++;
}
mpz_add_ui (save, save, iter_pos_save);
}
else
{
mpz_add (tmp, chain_buf->ks_pos, iter_max);
set_chain_ks_poses (chain_buf, db_entries, &tmp, db_entry->cur_chain_ks_poses);
}
outs_pos += iter_max_u64;
mpz_add (total_ks_pos, total_ks_pos, iter_max);
mpz_add (chain_buf->ks_pos, chain_buf->ks_pos, iter_max);
if (mpz_cmp (chain_buf->ks_pos, chain_buf->ks_cnt) == 0)
{
db_entry->chains_pos++;
memset (db_entry->cur_chain_ks_poses, 0, OUT_LEN_MAX * sizeof (u64));
}
if (mpz_cmp (total_ks_pos, total_ks_cnt) == 0) break;
}
if (mpz_cmp (total_ks_pos, total_ks_cnt) == 0) break;
}
}
out_flush (out);
if (save_pos)
{
catch_int (0);
}
/**
* cleanup
*/
mpz_clear (iter_max);
mpz_clear (total_ks_cnt);
mpz_clear (total_ks_pos);
mpz_clear (total_ks_left);
mpz_clear (skip);
mpz_clear (limit);
mpz_clear (tmp);
mpz_clear (save);
for (int pw_len = pw_min; pw_len <= pw_max; pw_len++)
{
db_entry_t *db_entry = &db_entries[pw_len];
if (db_entry->chains_buf)
{
int chains_cnt = db_entry->chains_cnt;
chain_t *chains_buf = db_entry->chains_buf;
for (int chains_idx = 0; chains_idx < chains_cnt; chains_idx++)
{
chain_t *chain_buf = &chains_buf[chains_idx];
mpz_clear (chain_buf->ks_cnt);
mpz_clear (chain_buf->ks_pos);
}
free (db_entry->chains_buf);
}
if (db_entry->elems_buf) free (db_entry->elems_buf);
}
free (out);
free (wordlen_dist);
free (pw_orders);
free (db_entries);
return 0;
}
If you need the complete source code, please add the WeChat number (c17865354792)
运行测试
pp的基本用法是:./pp [参数] < 词表文件 或 ./pp [参数] 词表文件。
1. 最简单的测试:直接跑
bash
./pp < test.txt
输出会是按PRINCE规则拼接的候选密码,比如:
123
abc
xy
hello
123xy
abcxy
xy123
...
2. 指定密码长度范围
比如只生成3~5位的候选密码:
bash
./pp --pw-min=3 --pw-max=5 < test.txt
3. 查看总组合数(--keyspace)
想知道能生成多少候选密码,不用实际输出:
bash
./pp --keyspace < test.txt
4. 输出到文件
把结果存到output.txt:
bash
./pp --pw-min=3 --pw-max=5 -o output.txt < test.txt
常用参数说明
--pw-min=NUM:只生成≥NUM位的密码--pw-max=NUM:只生成≤NUM位的密码--elem-cnt-min=NUM:每个候选密码至少用NUM个词拼接(比如--elem-cnt-min=2就不会输出单个词)--keyspace:计算总组合数(适合估算工作量)-o FILE:输出到指定文件-c:关闭去重(加快加载词表,但可能重复)
背后的知识点:PRINCE用到了哪些"基本功"
别看pp功能简单,它其实融合了很多工程和算法的基础知识点:
- 数据结构 :用哈希表(
uniq_t)去重,用数组+结构体(db_entry_t)分类存储,用链表(uniq_data_t)处理哈希冲突; - 算法优化 :批量IO减少磁盘操作,内存池减少内存碎片,贪心策略优先拼高频长度的词(
sort_by_cnt); - 工程设计:模块化拆分(词表处理/链生成/拼词输出分开),容错设计(断点续跑),跨平台适配(Windows/Linux的换行处理);
- 密码学常识:懂用户密码的"人性规律"------短词拼接、数字+字母组合比随机字符更常见。
总结:PRINCE的设计精髓
PRINCE不是靠复杂算法取胜,而是把"工程细节"和"用户习惯"摸透了:
- 它不搞花里胡哨的加密,而是"顺着用户的思路拼密码";
- 它把性能优化藏在细节里(内存池、批量IO),让拼词又快又稳;
- 它的模块化设计让代码能灵活调整(比如改拼词长度、改元素数量)。
说白了,好的工具往往不是靠"黑科技",而是把简单的事做到极致------这就是PRINCE给我们的启发。
Welcome to follow WeChat official account【程序猿编码】