【问题标题】:Is it possible to effectively parallelise a brute-force attack on 4 different password patterns?是否可以有效地并行对 4 种不同的密码模式进行暴力攻击?
【发布时间】:2017-12-31 12:22:44
【问题描述】:

在我的家庭作业任务中,我需要智能暴力破解一组密码。集合中的每个密码都有以下三种可能的掩码之一:

  1. %%@@

  2. @@%%

  3. @%%@

  4. %@@%

@ - 数字字符,% - 小写字母字符)。

此时我正在做这样的事情以在多线程中运行 only 一个 模式(第一个):

// Compile: $ gcc test.c -o test -fopenmp -O3 -std=c99

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <omp.h>

int main() {
    const char alp[26] = "abcdefghijklmnopqrstuvwxyz";
    const char num[10] = "0123456789";

    register int i;
    char pass[4];

    #pragma omp parallel for private(pass)
    for (i = 0; i < 67600; i++) {
        pass[3] = num[i        % 10];
        pass[2] = num[i /   10 % 10];
        pass[1] = alp[i /  100 % 26];
        pass[0] = alp[i / 2600 % 26];

        /* Slow password processing here */
    }

    return 0;
}

但是,不幸的是,该技术与搜索具有不同模式的密码无关。

所以我的问题是:
有没有办法构建一组有效的parallel for 指令,以便同时对每个密码模式进行攻击?

非常感谢您的帮助。

【问题讨论】:

    标签: c multithreading parallel-processing openmp brute-force


    【解决方案1】:

    这里的诀窍是要注意所有四个密码选项只是彼此的旋转/移位。

    也就是说,对于示例密码qr34 和您提到的模式,您正在查看:

    qr34 %%@@ #Original potential password
    4qr3 @%%@ #Rotate 1 place right
    34qr @@%% #Rotate 2 places right
    r34q %@@% #Rotate 3 places right
    

    鉴于此,您可以使用与第一个问题相同的生成技术。

    对于生成的每个潜在密码,检查潜在密码以及该密码的下三个班次。

    请注意,以下代码依赖于 C/C++ 的一个有趣属性:如果可以提前推断出语句的真值,则不会进一步执行。也就是说,给定语句if(A || B || C),如果A 为假,则必须评估B;但是,如果 B 为真,则永远不会评估 C

    这意味着我们可以拥有A=CheckPass(pass)B=CheckPass(RotatePass(pass))C=CheckPass(RotatePass(pass)),并保证密码只会根据需要轮换多次。

    请注意,此方案要求每个线程都有自己的潜在密码的私有副本。

    //Compile with, e.g.: gcc -O3 temp.c -std=c99 -fopenmp
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    
    int PassCheck(char *pass){
      return strncmp(pass, "4qr3", 4)==0;
    }
    
    //Rotate string one character to the right
    char* RotateString(char *str, int len){
      char lastchr = str[len-1];
      for(int i=len-1;i>0;i--)
        str[i]=str[i-1];
      str[0] = lastchr;
    
      return str;
    }
    
    int main(){
      const char alph[27] = "abcdefghijklmnopqrstuvwxyz";
      const char num[11]  = "0123456789";
      char goodpass[4]    = "----"; //Provide a default password to indicate an error state
    
      #pragma omp parallel for collapse(4)
      for(int i = 0; i < 26; i++)
      for(int j = 0; j < 26; j++)
      for(int m = 0; m < 10; m++)
      for(int n = 0; n < 10; n++){
        char pass[4] = {alph[i],alph[j],num[m],num[n]};
        if(
          PassCheck(pass)                 ||
          PassCheck(RotateString(pass,4)) ||
          PassCheck(RotateString(pass,4)) ||
          PassCheck(RotateString(pass,4))
        ){
          //It is good practice to use `critical` here in case two
          //passwords are somehow both valid. This won't arise in
          //your code, but is worth thinking about.
          #pragma omp critical
          {
            memcpy(goodpass, pass, 4);
            //#pragma omp cancel for //Escape for loops!
          }
        }
      }
    
      printf("Password was '%.4s'.\n",goodpass);
    
      return 0;
    }
    

    我注意到您正在使用

    生成密码
    pass[3] = num[i        % 10];
    pass[2] = num[i /   10 % 10];
    pass[1] = alp[i /  100 % 26];
    pass[0] = alp[i / 2600 % 26];
    

    这种技术有时很有用,尤其是在科学编程中,但通常只是为了解决方便性和内存局部性问题。

    例如,一个以a[y][x] 访问元素的数组数组可以写成一个以a[y*width+x] 访问元素的平面数组。这会提高速度,但这只是因为内存是连续的。

    在您的情况下,此索引不会产生任何速度提升,但确实使您更难以推理您的程序是如何工作的。由于这个原因,我会避免它。

    有人说“过早的优化是万恶之源”。对于微优化(例如您在此处尝试的优化)尤其如此。最大的速度提升来自高级算法决策,而不是来自繁琐的东西。 -O3 编译标志可以完成大部分您需要完成的工作,以使您的代码在此级别上快速运行。

    微优化假设在您的高级代码中做一些复杂的事情会使您以某种方式超越编译器。这不是一个好的假设,因为编译器通常非常聪明,而且明天会更聪明。您的时间非常宝贵:除非您有明确的理由,否则不要将其用于这些东西。 (进一步讨论“过早优化”是here。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-23
      相关资源
      最近更新 更多