密码破解!字典攻击(C/C++代码实现)

字典攻击是一种通过系统地将字典中的每个单词作为密码输入,从而侵入受密码保护的计算机、网络或其他IT资源的方法。字典攻击也可以用于查找解密加密消息或文档所需的密钥。

字典攻击之所以有效,是因为许多计算机用户和企业坚持使用普通单词作为密码。这些攻击通常针对使用多字密码的系统不成功,也经常针对由大小写字母和随机组合的数字组成的密码不成功。

在密码要求很高的系统中,暴力攻击方法有时是有效的,在这种方法中,字符和空格的每一个可能组合都要测试到一定的最大长度。然而,暴力攻击可能需要很长时间才能产生结果。

强随机密码无法轻易预测,而且它们极不可能包含在预定的密码库中。由于字典攻击的猜测尝试仅限于预先选择的列表,因此基本上不可能破解不可预测的密码。

字典攻击是如何工作的?

字典攻击使用预先选择的单词和短语库来猜测可能的密码。它的操作假设用户倾向于从基本的密码列表中提取,如“password”、“123abc”和“123456”

这些列表不像其他暴力攻击那样广泛,但它们可能会变得相当大。手动处理和测试所有这些密码不是一种实用的方法。因此,通常需要额外的技术来加快该过程。攻击者使用支持程序,如密码字典或其他暴力攻击工具。

字典攻击的实施方式取决于攻击者登录的帐户、网络或设备是联机还是脱机。在在线攻击中,攻击者必须注意他们可以用来猜测正确密码的尝试次数。经过一定次数的尝试后,站点管理员、帐户经理、用户或入侵检测系统可能会检测到攻击,或者密码尝试限制可能会发挥作用。如果发生上述任何一种情况,系统都可以将攻击者锁定。

使用较短优先级的可能密码列表的字典攻击可能更成功。老练的黑客还可以禁用检测功能或密码尝试限制。

对于离线攻击,黑客可以尝试的密码数量几乎没有限制。但是,执行脱机攻击需要从系统访问密码存储文件。只有这样,才能在离线环境中发起字典攻击。

暴力攻击与字典攻击

暴力攻击和字典攻击的主要区别在于尝试的密码排列次数。

如何保护自己免受字典攻击

通过限制给定时间段内允许的尝试次数并明智地选择密码或密钥,可以将密码或解密密钥攻击的漏洞降至接近零。一种使系统免受字典攻击并实际上免受暴力攻击的方法需要以下三个条件:

1.只允许三次密码尝试;

2.在允许接下来的三次尝试之前需要经过15分钟的时间;

3.密码或密钥是一长串毫无意义的字母、数字和特殊符号。

垃圾邮件发送者经常使用字典攻击的形式。邮件会发送到由单词或名称组成的电子邮件地址,后跟@符号和特定域的名称。一长串给定的名字,如zhangsan、xiaowu、lilei或lisi再加上域名,通常都是成功的。

密码破解!字典攻击(C/C++代码实现)

应用程序将带有散列密码的文件作为第一个参数和可选的第二个参数字典文件。

...
/* 结构,包含线程的所有必要数据,在main中创建并作为参数传递给所有线程 */
struct thread_shared_data{
    char** dict_ptr;
    char** pass_ptr;
    char** br_pass_ptr;
    bool* is_cracked_ptr;
    unsigned long dict_size;
    unsigned long pass_size;
    unsigned long br_pass_size;
    unsigned int basic_thread_count;
    unsigned int two_word_thread_count;
};

/* 退出应用程序的函数,也是SIGINT的处理程序(CTRL+C)  */
void quit_program()
{
    printf("\nQuiting\n");
    running = false;
    show_stats = true;
    pthread_cond_broadcast(&pass_cracked_cv);
}

/* 使使用者线程打印统计信息的函数,也是SIGHUP的处理程序 */
void print_statistics()
{
    show_stats = true;
    pthread_mutex_lock(&cond_mutex);
    pthread_cond_broadcast(&pass_cracked_cv);
    pthread_mutex_unlock(&cond_mutex);
}

/* 函数,该函数使用MD5算法对给定字符串进行散列 */
void md5_hash(char * in_str, char ** out_str)
{
    if(*out_str != NULL) free(*out_str);
    *out_str = (char *)malloc(33*sizeof(char));

    unsigned char digest[16];
    MD5_CTX ctx;
    MD5_Init(&ctx);

    MD5_Update(&ctx, in_str, strlen(in_str));
    MD5_Final(digest, &ctx);

    for(int n = 0; n < 16; ++n)
        sprintf(&(*out_str)[n*2],"%02x", (unsigned int)digest[n]);
}
void *consumer_thread(void *arg);
void check_passwords(char * created_pass, struct thread_shared_data * my_tsd, unsigned int id);
void change_string_by_id(unsigned int id, char ** str);
void reset_thread(unsigned int id, unsigned long * num, bool * first_loop, unsigned long * i);
void *cracking_thread_basic(void *arg);
void *cracking_thread_two_words(void *arg);
void *cracking_thread_numbers(void *arg);
int read_file(char * file_name, char *** container_ptr, unsigned long * size);
...
int main(int argc, char * argv[])
{
...
    /* 检查是否给出了参数 */
    if(argc <= 1)
    {
        printf("main: passwords_file argument not given\n");
        printf(USAGE);
        exit(1);
    }

    /* 把字典读入存储器 */
    /* 检查参数中是否提供了字典 */
    if(argc >= 3)
    {
        if(read_file(argv[2], &tsd.dict_ptr, &tsd.dict_size) == -1)
        {
            printf("main: dictionary file: %s not found\n", argv[2]);
            exit(1);
        }
    }
    else /* 读取默认词典 */
    if(read_file(DEFAULT_DICTIONARY, &tsd.dict_ptr, &tsd.dict_size) == -1)
    {
        printf("main: default dictionary file: %s not found\n", DEFAULT_DICTIONARY);
        printf(USAGE);
        exit(1);
    }

    /* 将散列密码读入内存 */
    if(read_file(argv[1], &tsd.pass_ptr, &tsd.pass_size) == -1)
    {
        printf("main: passwords file: %s not found\n", argv[1]);
        exit(1);
    }
    else /* allocate是密码破解的数组,并将所有位初始化为零==false */
        tsd.is_cracked_ptr = (bool *)calloc(tsd.pass_size, sizeof(bool));

    /* 信号处理  */
    signal(SIGINT, quit_program);
    signal(SIGHUP, print_statistics);

    /* 创建线程并初始化互斥、cond值和读写锁 */
...
    for(int i = 2; i < NUM_THREADS; i+=2)
    {
        pthread_create(&threads[i], NULL, cracking_thread_basic, (void *) &tsd);
        pthread_create(&threads[i + 1], NULL, cracking_thread_two_words, (void *) &tsd);
    }
...
    while(running)
    {
        printf("main: waiting for input:\n");
        scanf("%s", input);
        if(!strcmp(input, "exit")) quit_program();
        else if(!strcmp(input, "stats")) print_statistics();
        else
        {
            if(read_file(input, &tmp_ptr, &tmp_size) == -1)
                printf("main: passwords file %s not found\n", input);
            else
            {
                /* reset */
                printf("main: new passwords file loaded\n");
                reset = true;
                pthread_rwlock_wrlock(&tsd_rwlock);
                printf("main: reset\n");
                print_statistics();
                for(unsigned long i = 0; i < tsd.pass_size; i++) free(tsd.pass_ptr[i]);
                free(tsd.pass_ptr);
                for(int i = 0; i < tsd.br_pass_size; i++) free(tsd.br_pass_ptr[i]);
                free(tsd.br_pass_ptr);
                tsd.br_pass_ptr = NULL;
                tsd.pass_ptr = tmp_ptr;
                tsd.pass_size = tmp_size;
                tmp_ptr = NULL;
                free(tsd.is_cracked_ptr);
                tsd.is_cracked_ptr = (bool *)calloc(tsd.pass_size, sizeof(bool));
                tsd.br_pass_size = 0;
                printf("main: start\n");
                reset = false;
                pthread_rwlock_unlock(&tsd_rwlock);
            }
        }
    }
    free(input);

    /* 清理程序 */
    pthread_mutex_destroy(&count_mutex);
    pthread_mutex_destroy(&cond_mutex);
    pthread_cond_destroy(&pass_cracked_cv);
    pthread_rwlock_destroy(&tsd_rwlock);

    printf("main: freeing memory\n");
    /* 免费字典数组 */
    for(unsigned long i = 0; i < tsd.dict_size; i++)
        free(tsd.dict_ptr[i]);

    free(tsd.dict_ptr);
    /* 免费密码数组 */
    if(tsd.pass_ptr != NULL)
    {
        for(unsigned long i = 0; i < tsd.pass_size; i++)
            free(tsd.pass_ptr[i]);

        free(tsd.pass_ptr);
    }
    /* 免费破解密码数组 */
    if(tsd.br_pass_ptr != NULL)
        for(unsigned long i = 0; i < tsd.br_pass_size; i++)
            free(tsd.br_pass_ptr[i]);

...
}

编译运行:
在这里插入图片描述If you need the complete source code, please add the WeChat number (c17865354792)

它总共创建了8个线程,第一个线程(使用者)接收来自破解线程的破解密码,然后打印它,这个线程通过条件值与其他线程通信。其他线程使用给定或默认字典生成密码,每个线程使用不同的方法生成密码。

三个基本的破解线程根据后一种不同的情况创建密码,并在生成的单词之前或之后添加数字。另外三个线程生成由“”、“2”、“4”分隔的两个单词密码,或者什么都不生成,单词的修改与基本线程中的相同。最后一个线程生成数字密码。主循环正在读取用户输入。

总结

在网络攻击中,黑客会反复尝试以其他用户身份登录。如果黑客有一个可能的密码列表,这种方法效果最好。然而,由于这可能是一个耗时的过程,黑客有可能在代码被破解之前被管理员或用户自己检测到黑客企图。

另一方面,离线攻击的特点是,对密码的尝试次数没有网络限制。黑客的方法是从他们试图强制访问的系统中获取一个带有密码的文件。这样,这是一种比在线方法更复杂的字典攻击类型。但一旦他们有了正确的密码,他们就可以在没有人注意到的情况下登录。

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

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是用 C++ 代码实现字典树(Trie)的模糊搜索: ```c++ #include <iostream> #include <vector> #include <string> #include <cstring> using namespace std; const int MAX_N = 1000005; const int MAX_M = 10005; const int MAX_L = 25; struct TrieNode { int son[26]; bool is_end; } trie[MAX_N]; int tot = 1; void insert(string s) { int p = 1; for (int i = 0; i < s.length(); i++) { int c = s[i] - 'a'; if (!trie[p].son[c]) { trie[p].son[c] = ++tot; } p = trie[p].son[c]; } trie[p].is_end = true; } vector<string> ans; void dfs(int p, string s, int cnt, int k) { if (trie[p].is_end && cnt <= k) { ans.push_back(s); } if (cnt > k) { return; } for (int i = 0; i < 26; i++) { if (trie[p].son[i]) { char c = 'a' + i; dfs(trie[p].son[i], s + c, cnt + (c != s[s.length() - 1]), k); } } } int main() { int n, m, k; cin >> n >> m >> k; for (int i = 1; i <= n; i++) { string s; cin >> s; insert(s); } for (int i = 1; i <= m; i++) { string s; cin >> s; ans.clear(); dfs(1, "", 0, k); int cnt = 0; for (int j = 0; j < ans.size(); j++) { if (ans[j].find(s) != string::npos) { cnt++; } } cout << cnt << endl; } return 0; } ``` 在这个代码中,`trie` 数组表示字典树,`tot` 表示字典树的节点数量。`insert()` 函数用于将单词插入字典树。`dfs()` 函数用于进行深度优先搜索,其中 `p` 表示当前节点的编号,`s` 表示当前字符串,`cnt` 表示当前编辑距离,`k` 表示最大编辑距离。在 `dfs()` 函数中,如果当前节点是一个单词的结尾,并且编辑距离不超过 `k`,则将当前字符串加入答案数组 `ans` 中。然后遍历当前节点的所有儿子节点,如果某个儿子节点存在,则继续递归搜索。在递归搜索时需要注意,如果当前字符和上一个字符相同,则不需要增加编辑距离,否则需要增加编辑距离。 在主函数中,首先读入字典树中单词的数量 `n`,查询的数量 `m`,以及最大编辑距离 `k`。然后逐个读入查询字符串,对于每个查询字符串,先清空答案数组 `ans`,然后调用 `dfs()` 函数进行搜索。搜索完成后,遍历答案数组 `ans`,如果某个字符串中包含查询字符串,则将计数器 `cnt` 加一。最后输出计数器 `cnt` 即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值