PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)

想象一下你有一堆密码碎片(比如单词表中的单词),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功能简单,它其实融合了很多工程和算法的基础知识点:

  1. 数据结构 :用哈希表(uniq_t)去重,用数组+结构体(db_entry_t)分类存储,用链表(uniq_data_t)处理哈希冲突;
  2. 算法优化 :批量IO减少磁盘操作,内存池减少内存碎片,贪心策略优先拼高频长度的词(sort_by_cnt);
  3. 工程设计:模块化拆分(词表处理/链生成/拼词输出分开),容错设计(断点续跑),跨平台适配(Windows/Linux的换行处理);
  4. 密码学常识:懂用户密码的"人性规律"------短词拼接、数字+字母组合比随机字符更常见。

总结:PRINCE的设计精髓

PRINCE不是靠复杂算法取胜,而是把"工程细节"和"用户习惯"摸透了:

  • 它不搞花里胡哨的加密,而是"顺着用户的思路拼密码";
  • 它把性能优化藏在细节里(内存池、批量IO),让拼词又快又稳;
  • 它的模块化设计让代码能灵活调整(比如改拼词长度、改元素数量)。

说白了,好的工具往往不是靠"黑科技",而是把简单的事做到极致------这就是PRINCE给我们的启发。

Welcome to follow WeChat official account【程序猿编码

相关推荐
高洁011 小时前
具身智能-视觉语言导航(VLN)
深度学习·算法·aigc·transformer·知识图谱
Croa-vo1 小时前
TikTok 数据工程师三轮 VO 超详细面经:技术深挖 + 建模推导 + 压力测试全记录
javascript·数据结构·经验分享·算法·面试
蘑菇小白1 小时前
时间复杂度
数据结构·算法
亿坊电商1 小时前
24H-无人共享KTV:如何实现安全的自助服务?
大数据·物联网·安全
飞行增长手记1 小时前
什么是高匿代理IP?安全吗?怎么选?
网络协议·tcp/ip·安全
czlczl200209251 小时前
算法:组合问题
算法·leetcode·职场和发展
bkspiderx1 小时前
安全扫描问题:目标主机支持RSA密钥交换(风险分析与解决方案)
安全·rsa·交换·密钥交换·目标主机支持rsa密钥交换·rsa密钥交换·rsa密钥
CoderYanger1 小时前
优选算法-字符串:63.二进制求和
java·开发语言·算法·leetcode·职场和发展·1024程序员节
J***Q2921 小时前
DevOps金融服务安全要求
运维·安全·devops