Изучение Opencl На Примере Взлома Паролей



Введение Недавно, прочитав различные статьи и презентации о GPGPU, я тоже решил попробовать программировать для видеокарт. На самом деле выбор технологий в этой области невелик — сейчас живы и развиваются только CUDA (собственный стандарт nVidia) и OpenCL (свободный стандарт, работающий на графических процессорах от ATI, nVidia, а также на центральных процессорах).

В связи с тем, что в моем ноутбуке установлена видеокарта ATI (Mobility Radeon 5650 HD), выбор полностью свелся к одному варианту — OpenCL. В этой статье пойдет речь о процессе изучения OpenCL с нуля, а также о том, что из этого получилось.



Обзор OpenCL и PyOpenCl

На первый взгляд мне все показалось очень запутанным, как управляющий код на C, так и код так называемых ядер.

В предоставленном C API даже запуск самой простой программы занимает большое количество строк, особенно с обработкой хоть каких-то ошибок, поэтому хотелось найти что-то более удобное и гуманное.

Выбор пал на библиотеку.

PyOpenCL , из названия которого понятно, что управляющий код написан на Python. В нем все выглядит более понятно даже для того, кто впервые видит код OpenCL (конечно, это касается только простых примеров).

Однако код самих ядер по-прежнему написан на слегка модифицированном языке C, поэтому его вам все равно придется изучить.

Полную документацию по нему можно получить на сайте разработчика стандарта ( Хронос ), а информацию о конкретных реализациях можно найти на сайтах ATI и nVidia соответственно.

Первое впечатление о языке можно получить на простом примере (добавление двух массивов):

  
  
   

__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 вы можете прочитать в документации, а для относительно простых программ вам понадобится только одна очередь и один контекст, который будет создан строками, очень похожими на те, что в примере.

В целом структура вычислений в программах OpenCL часто выглядит примерно так:

  • создание контекста, очереди, компиляция программы
  • копирование данных (буферов) на устройство, не изменяющихся в процессе выполнения
  • цикл
    • копирование данных, специфичных для итерации, на устройство
    • выполнение ядра
    • копирование вычисленных данных обратно в основную память, возможна некоторая обработка


SHA1-хеширование

Пришло время спуститься на уровень ниже и понять, как работает сам код ядра.

Для кого, чтобы функция OpenCL запускалась извне, она должна быть обозначена атрибутом __kernel, иметь тип значения void и ряд аргументов, которые могут быть как прямыми значениями (int, float4,.

) или указатели на области памяти __global, __constant, __local. Также для удобства в программе можно объявлять и другие функции, вызываемые из ядра, и это не влияет на производительность: все функции подставляются автоматически (т. е.

как и в случае с директивой inline).

Это связано с тем, что рекурсия в 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) {

Теги: #opencl #взлом #пароли #python #python #GPGPU

Вместе с данным постом часто просматривают: