パスワヌドハッキングを䜿甚したOpenCLの探玢

゚ントリヌ



最近、GPGPUに関するさたざたな蚘事やプレれンテヌションを読んだ埌、ビデオカヌドのプログラミングも自分で詊しおみるこずにしたした。 実際、この分野での技術の遞択は玠晎らしいものではありたせん-CUDAnVidia独自の暙準ずOpenCL無料の暙準、ATIのGPU、nVidia、および䞭倮凊理装眮のみがただ生きおおり、開発䞭です。 私のラップトップにはATIグラフィックカヌドMobility Radeon 5650 HDがあるため、遞択肢は完党に1぀のオプション-OpenCLに限定されたした。 この蚘事では、OpenCLをれロから孊習するプロセスず、その起源に぀いお説明したす。



OpenCLおよびPyOpenClの抂芁



䞀芋するず、Cの制埡コヌドず、いわゆるカヌネルカヌネルのコヌドの䞡方が、非垞に混乱しおいるように芋えたした。 提䟛されおいるC APIでは、特に少なくずもいく぀かの゚ラヌの凊理では、単玔なプログラムの起動でさえ倚くの行を必芁ずするため、より䟿利で人道的なものを芋぀けたいず思いたした。 その遞択はPyOpenCLラむブラリにかかっおいたした。その名前から、制埡コヌドはPythonで曞かれおいるこずが明らかです。 OpenCLコヌドを初めお芋た人でも、すべおがより理解しやすいように芋えたすもちろん、これは単玔な䟋にのみ適甚されたす。 ただし、カヌネル自䜓のコヌドはただわずかに倉曎されたCで蚘述されおいるため、ただ孊習する必芁がありたす。 それに関する完党なドキュメントは、暙準の開発者のWebサむト Khronos で入手でき、特定の実装に関する情報は、それぞれATIおよびnVidia Webサむトで入手できたす。

最も簡単な䟋2぀の配列を远加を䜿甚しお、蚀語の第䞀印象を埗るこずができたす。



__kernel void sum(__global const float *a, __global const float *b, __global float *c) { int gid = get_global_id(0); c[gid] = a[gid] + b[gid]; }
      
      







そしお、この䟋を実行しお怜蚌するために必芁な完党なコヌドは次のずおりですPyOpenCLドキュメントから取埗。



非衚瀺のテキスト
 import pyopencl as cl import numpy import numpy.linalg as la a = numpy.random.rand(50000).astype(numpy.float32) b = numpy.random.rand(50000).astype(numpy.float32) ctx = cl.create_some_context() queue = cl.CommandQueue(ctx) mf = cl.mem_flags a_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=a) b_buf = cl.Buffer(ctx, mf.READ_ONLY | mf.COPY_HOST_PTR, hostbuf=b) dest_buf = cl.Buffer(ctx, mf.WRITE_ONLY, b.nbytes) prg = cl.Program(ctx, """ __kernel void sum(__global const float *a, __global const float *b, __global float *c) { int gid = get_global_id(0); c[gid] = a[gid] + b[gid]; } """).build() prg.sum(queue, a.shape, None, a_buf, b_buf, dest_buf) a_plus_b = numpy.empty_like(a) cl.enqueue_copy(queue, a_plus_b, dest_buf) print la.norm(a_plus_b - (a+b))
      
      









特定の行がすぐに衚瀺されたすコンテキストの䜜成、実行キュヌ、デバむスぞのバッファヌの䜜成ずコピヌ、カヌネル自䜓のコンパむルず起動。 OpenCLのコンテキストずキュヌの詳现に぀いおはドキュメントを参照しおください。比范的単玔なプログラムの堎合、必芁なキュヌずコンテキストは1぀だけです。これらは、䟋ず非垞によく䌌た行で䜜成されたす。 䞀般に、OpenCLプログラムの蚈算の構造は次のようになりたす。







SHA1ハッシュ



レベルを䞋げお、カヌネルコヌドがどのように機胜するかを理解するずきが来たした。 OpenCL関数を倖郚から実行するには、__ kernel属性で指定する必芁があり、倀の型がvoidであり、盎接倀int、float4、...たたは領域ぞのポむンタヌである特定の数の匕数が必芁ですメモリ__global、__ constant、__ local。 たた、䟿宜䞊、カヌネルから呌び出すこずができる他の関数をプログラムで宣蚀できたすが、これはパフォヌマンスに圱響を䞎えたせん。すべおの関数は自動的に眮き換えられたす぀たり、むンラむンディレクティブず同様。 これは、OpenCLの再垰がたったくサポヌトされおいないためです。



OpenCLが倉曎されたC蚀語であるずいう事実を䜿甚しお、SHA1などのハッシュ関数の既補の実装を䜿甚し、わずかな倉曎を䜿甚できたす。



非衚瀺のテキスト
 #define K1 0x5A827999 #define K2 0x6ED9EBA1 #define K3 0x8F1BBCDC #define K4 0xCA62C1D6 #define a0 0x67452301; #define b0 0xEFCDAB89; #define c0 0x98BADCFE; #define d0 0x10325476; #define e0 0xC3D2E1F0; #define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ #define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ #define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ #define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ #define ROTL(n,X) ( ( ( X ) << n ) | ( ( X ) >> ( 32 - n ) ) ) #define expand(W,i) ( W[ i & 15 ] = ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) #define subRound(a, b, c, d, e, f, k, data) \ ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) #define REVERSE(value) value = ((value & 0xFF000000) >> 24) | ((value & 0x00FF0000) >> 8) | ((value & 0x0000FF00) << 8) | ((value & 0x000000FF) << 24) long sha1(uint *eData, const int length) { unsigned int A = a0; unsigned int B = b0; unsigned int C = c0; unsigned int D = d0; unsigned int E = e0; ((__local char *)eData)[length] = 0x80; for (int i = 0; i <= length / 4; i++) { REVERSE(eData[i]); } eData[14] = 0; eData[15] = length * 8; subRound( A, B, C, D, E, f1, K1, eData[ 0 ] ); subRound( E, A, B, C, D, f1, K1, eData[ 1 ] ); subRound( D, E, A, B, C, f1, K1, eData[ 2 ] ); subRound( C, D, E, A, B, f1, K1, eData[ 3 ] ); subRound( B, C, D, E, A, f1, K1, eData[ 4 ] ); subRound( A, B, C, D, E, f1, K1, eData[ 5 ] ); subRound( E, A, B, C, D, f1, K1, eData[ 6 ] ); subRound( D, E, A, B, C, f1, K1, eData[ 7 ] ); subRound( C, D, E, A, B, f1, K1, eData[ 8 ] ); subRound( B, C, D, E, A, f1, K1, eData[ 9 ] ); subRound( A, B, C, D, E, f1, K1, eData[ 10 ] ); subRound( E, A, B, C, D, f1, K1, eData[ 11 ] ); subRound( D, E, A, B, C, f1, K1, eData[ 12 ] ); subRound( C, D, E, A, B, f1, K1, eData[ 13 ] ); subRound( B, C, D, E, A, f1, K1, eData[ 14 ] ); subRound( A, B, C, D, E, f1, K1, eData[ 15 ] ); subRound( E, A, B, C, D, f1, K1, expand( eData, 16 ) ); subRound( D, E, A, B, C, f1, K1, expand( eData, 17 ) ); subRound( C, D, E, A, B, f1, K1, expand( eData, 18 ) ); subRound( B, C, D, E, A, f1, K1, expand( eData, 19 ) ); subRound( A, B, C, D, E, f2, K2, expand( eData, 20 ) ); subRound( E, A, B, C, D, f2, K2, expand( eData, 21 ) ); subRound( D, E, A, B, C, f2, K2, expand( eData, 22 ) ); subRound( C, D, E, A, B, f2, K2, expand( eData, 23 ) ); subRound( B, C, D, E, A, f2, K2, expand( eData, 24 ) ); subRound( A, B, C, D, E, f2, K2, expand( eData, 25 ) ); subRound( E, A, B, C, D, f2, K2, expand( eData, 26 ) ); subRound( D, E, A, B, C, f2, K2, expand( eData, 27 ) ); subRound( C, D, E, A, B, f2, K2, expand( eData, 28 ) ); subRound( B, C, D, E, A, f2, K2, expand( eData, 29 ) ); subRound( A, B, C, D, E, f2, K2, expand( eData, 30 ) ); subRound( E, A, B, C, D, f2, K2, expand( eData, 31 ) ); subRound( D, E, A, B, C, f2, K2, expand( eData, 32 ) ); subRound( C, D, E, A, B, f2, K2, expand( eData, 33 ) ); subRound( B, C, D, E, A, f2, K2, expand( eData, 34 ) ); subRound( A, B, C, D, E, f2, K2, expand( eData, 35 ) ); subRound( E, A, B, C, D, f2, K2, expand( eData, 36 ) ); subRound( D, E, A, B, C, f2, K2, expand( eData, 37 ) ); subRound( C, D, E, A, B, f2, K2, expand( eData, 38 ) ); subRound( B, C, D, E, A, f2, K2, expand( eData, 39 ) ); subRound( A, B, C, D, E, f3, K3, expand( eData, 40 ) ); subRound( E, A, B, C, D, f3, K3, expand( eData, 41 ) ); subRound( D, E, A, B, C, f3, K3, expand( eData, 42 ) ); subRound( C, D, E, A, B, f3, K3, expand( eData, 43 ) ); subRound( B, C, D, E, A, f3, K3, expand( eData, 44 ) ); subRound( A, B, C, D, E, f3, K3, expand( eData, 45 ) ); subRound( E, A, B, C, D, f3, K3, expand( eData, 46 ) ); subRound( D, E, A, B, C, f3, K3, expand( eData, 47 ) ); subRound( C, D, E, A, B, f3, K3, expand( eData, 48 ) ); subRound( B, C, D, E, A, f3, K3, expand( eData, 49 ) ); subRound( A, B, C, D, E, f3, K3, expand( eData, 50 ) ); subRound( E, A, B, C, D, f3, K3, expand( eData, 51 ) ); subRound( D, E, A, B, C, f3, K3, expand( eData, 52 ) ); subRound( C, D, E, A, B, f3, K3, expand( eData, 53 ) ); subRound( B, C, D, E, A, f3, K3, expand( eData, 54 ) ); subRound( A, B, C, D, E, f3, K3, expand( eData, 55 ) ); subRound( E, A, B, C, D, f3, K3, expand( eData, 56 ) ); subRound( D, E, A, B, C, f3, K3, expand( eData, 57 ) ); subRound( C, D, E, A, B, f3, K3, expand( eData, 58 ) ); subRound( B, C, D, E, A, f3, K3, expand( eData, 59 ) ); subRound( A, B, C, D, E, f4, K4, expand( eData, 60 ) ); subRound( E, A, B, C, D, f4, K4, expand( eData, 61 ) ); subRound( D, E, A, B, C, f4, K4, expand( eData, 62 ) ); subRound( C, D, E, A, B, f4, K4, expand( eData, 63 ) ); subRound( B, C, D, E, A, f4, K4, expand( eData, 64 ) ); subRound( A, B, C, D, E, f4, K4, expand( eData, 65 ) ); subRound( E, A, B, C, D, f4, K4, expand( eData, 66 ) ); subRound( D, E, A, B, C, f4, K4, expand( eData, 67 ) ); subRound( C, D, E, A, B, f4, K4, expand( eData, 68 ) ); subRound( B, C, D, E, A, f4, K4, expand( eData, 69 ) ); subRound( A, B, C, D, E, f4, K4, expand( eData, 70 ) ); subRound( E, A, B, C, D, f4, K4, expand( eData, 71 ) ); subRound( D, E, A, B, C, f4, K4, expand( eData, 72 ) ); subRound( C, D, E, A, B, f4, K4, expand( eData, 73 ) ); subRound( B, C, D, E, A, f4, K4, expand( eData, 74 ) ); subRound( A, B, C, D, E, f4, K4, expand( eData, 75 ) ); subRound( E, A, B, C, D, f4, K4, expand( eData, 76 ) ); subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) ); subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) ); subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) ); A += a0; B += b0; C += c0; D += d0; E += e0; return as_ulong((uint2)(D, E)); }
      
      









ここでいく぀かの明確化が必芁です。 クラックのための「実隓的な」パスワヌドハッシュずしお、私はリヌクされたLinkedInハッシュを取りたした。これはほが600䞇䞀意です。 リスト内の存圚をかなり迅速に確認するためのいく぀かのオプションがありたす。ハッシュテヌブルを䜿甚したした詳现は埌ほど。 メモリ消費を削枛し、SHA1の20バむト党䜓ではなく、最埌の8バむト、぀たり 1぀のlong / ulong倀。 もちろん、これは誀った䞀臎の可胜性を高めたすが、それは非垞に小さいたたです。私が持っおいたすべおのパスワヌドの䞭で、私はそのようなケヌスを6぀しか持っおいたせんでした。 したがっお、切り捚おられた倀最埌の8バむトは、䞊蚘の関数からすぐに返されたす。 それ以倖の堎合、すべおが暙準であり、SHA1アルゎリズムは、56バむト未満の文字列の堎合の仕様に埓っお盎接実装されたす。



怜玢の構成



次に、怜玢自䜓を敎理する必芁がありたす。 最も単玔なオプションは、すべおの䜍眮に察しお同じ文字セットのブルヌトフォヌスであり、これは、たずえば同様の方法で盎接実装できたす。



非衚瀺のテキスト
 __kernel void do_brute( __global const long *table, const ulong start_num, __global ulong *result, __global uint *res_ind) { char s[64]; uint *s_l = (__local uint *)s; int i, j; ulong _n, n; ulong start = start_num + get_global_id(0) * HASHES_PER_WORKITEM; for (j = 0; j < HASHES_PER_WORKITEM; j++) { n = _n = j + start; for (i = 15; i >= 0; i--) { s_l[i] = 0; } for (i = COMB_LEN - 1; i >= 0; i--) { s[i] = charset[n % CHARS_CNT]; n /= CHARS_CNT; } if (check_in_table(table, sha1(s_l, COMB_LEN))) { result[atomic_inc(res_ind)] = _n; } } }
      
      







ここで、HASHES_PER_WORKITEMは、1回の実行で1぀の䜜業項目ストリヌムで凊理されるハッシュの数、COMB_LENは組み合わせの長さ、charsetは文字の配列、CHARS_CNTは配列内の文字の数です。 ご芧のずおり、起動時に、ハッシュテヌブルぞのポむンタヌ、怜玢を開始するパスワヌド番号、および結果を衚瀺する配列ぞのポむンタヌずその䞭のむンデックスがこのカヌネルに枡されたす。



OpenCLでは、䞀床に1぀のスレッドが起動されるわけではありたせんが、その䞀郚はグロヌバルワヌクサむズず呌ばれ、すべおのスレッドは同じ匕数を受け取りたす。 それらのそれぞれがキヌスペヌスの䞀郚を敎理するために、文字列ulong start = start_num + get_global_id(0) * HASHES_PER_WORKITEM;



特定のスレッドの番号を蚈算したすget_global_id0は、0から珟圚のグロヌバル䜜業サむズたでのスレッドむンデックスを返す暙準関数です。



次に、各ストリヌムHASHES_PER_WORKITEMはパスワヌドを列挙したす。各パスワヌドはsha1関数によっおハッシュ化され、埌述するcheck_in_table関数の存圚を確認したす。 この圢匏で、ハッシュテヌブルの最も単玔な実装では、1秒あたり玄2,000䞇のパスワヌドの結果が埗られたした。たずえば、ラップトップで3億以䞊のハッシュを生成するoclHascat怜蚌が倚数のハッシュのリストを通過する堎合でもこれを正圓化するものではありたせん。 今埌は、単玔なブルヌトフォヌスで1秒間に1億6,000䞇の速床を達成できたず蚀いたす。これはoclHascatの半分の速床ハッシュ1぀です。



ハッシュテヌブル



そのため、ハッシュの存圚を確認したす。 実装された最初のオプションは、 オヌプンアドレス指定の最も単玔なハッシュテヌブルでした。 問題を耇雑にしないように、プロセッサずビデオカヌドではなく、OpenCLでは、芁求のみがありたした。 このケヌスは次のようになりたした。



非衚瀺のテキスト
 bool check_in_table( __global const long *table, const long value) { uint index = calc_index(value); uint step = calc_step(value); for (uint a = 1; ; a++) { index %= TABLE_SIZE; if (table[index] == 0) { return false; } if (table[index] == value) { return true; } index += a * step + 1; } }
      
      







さたざたなテヌブルサむズずプロヌブ方法を詊したしたが、速床はそれほど向䞊したせんでした。 GPUハッシュテヌブルに関する資料を探しお、特定のCuckoo Hashtable ロシア語ぞの翻蚳が確立されおいるかどうかはわかりたせんに぀いお蚀及しおいるVasily Volkovの蚘事「GPUでの効率的なハッシュテヌブルの構築」に出䌚いたした。聞かなかった。 簡単に蚀えば、その本質は、1぀ではなくいく぀かのハッシュ関数を䜿甚するこずず、特別な塗り぀ぶし方法にありたす。その埌、k個以䞋のメモリアクセスに察しお芁玠が芋぀かりたす。kはハッシュ関数の数です。 占有メモリよりも速床が重芁なので、k = 2を䜿甚したした。 それを埋めるこずはCPUでも発生したす。



おわりに



たた、もちろん、最適化はコヌドの別の郚分、぀たりパスワヌドの生成に圱響したした。 䞊蚘のバヌゞョンでは、いく぀かの最適ではない堎所がすぐに衚瀺されたす。たずえば、次の各パスワヌドは最初から生成されたすが、前のものは倉曎できたす。 特にOpenCLに固有の最適化には他の堎所がありたすより高速なロヌカルの代わりに、文字の配列にグロヌバルメモリたたは定数メモリを䜿甚したす特定の実装の開発者から盎接メモリ領域の詳现を読む方が良いです ただし、カヌネルコヌドのさたざたな最適化に関する個別の蚘事を曞く䟡倀はありたすが、ここでは、GPU向けのプログラミングを行う堎合、さたざたなオプションを詊しおその速床を調べる䟡倀があるず蚀いたす。 目で蚀うず、動䜜が速くなるずは限りたせん。 特定の呜什を削陀しおも、実行速床が倧幅に䜎䞋する堎合がありたす。



将来的には、䜍眮ごずに異なるアルファベットのサポヌトを远加したした。さらに重芁なこずは、個々の文字だけでなく、単語のアルファベットもサポヌトするこずです。 利䟿性ず柔軟性のために、カヌネルコヌドはMakoテンプレヌト゚ンゞンによっお凊理されたす。 これらはすべおアヌカむブにありたす以䞋を参照。



結論



だから、私が最終的に取埗したもの





有圢の結果





印象



PS



さたざたなコメントや掚奚事項を歓迎したす。



All Articles