2011-09-18 60 views
3

我正在使用内联汇编来构建一组密码,我将使用这些密码对给定散列进行强力操作。我用这个website作为构建密码的参考。使用内联汇编和访问c变量的多线程

这是在单线程环境中工作完美无缺。它会产生无限量的递增密码。

因为我只有asm的基本知识,所以我理解这个想法。 gcc的使用ATT,所以我-masm=intel

编译在试图多线程程序时,我意识到,这种做法可能无法正常工作。
下面的代码使用2个全局C变量,而我假设,这可能是问题。

__asm__("pushad\n\t" 
    "mov edi, offset plaintext\n\t" <---- global variable 
    "mov ebx, offset charsetTable\n\t" <---- again 
    "L1: movzx eax, byte ptr [edi]\n\t" 
    " movzx eax, byte ptr [charsetTable+eax]\n\t" 
    " cmp al, 0\n\t" 
    " je L2\n\t" 
    " mov [edi],al\n\t" 
    " jmp L3\n\t" 
    "L2: xlat\n\t" 
    " mov [edi],al\n\t" 
    " inc edi\n\t" 
    " jmp L1\n\t" 
    "L3: popad\n\t"); 

它在纯文本变量中产生非确定性结果。

如何创建一个解决方法,每个线程访问自己的明文变量? (如果这是问题...)。

我试图修改这个代码,使用扩展组件,但我每次都失败。可能是因为所有教程都使用ATT语法。

我真的很感激任何帮助,因为我被困了几个小时,现在:(

编辑:2个线程运行该程序,并在打印的明文权的汇编指令后的内容,产生:
b
b
d
d
f
f
...

EDIT2

pthread_create(&thread[i], NULL, crack, (void *) &args[i])) 
[...] 
void *crack(void *arg) { 
struct threadArgs *param = arg; 
struct crypt_data crypt; // storage for reentrant version of crypt(3) 

char *tmpHash = NULL; 

size_t len = strlen(param->methodAndSalt); 
size_t cipherlen = strlen(param->cipher); 

crypt.initialized = 0; 

for(int i = 0; i <= LIMIT; i++) { 
    // intel syntax  
    __asm__ ("pushad\n\t" 
    //mov edi, offset %0\n\t" 
    "mov edi, offset plaintext\n\t" 
    "mov ebx, offset charsetTable\n\t" 
    "L1: movzx eax, byte ptr [edi]\n\t" 
    " movzx eax, byte ptr [charsetTable+eax]\n\t" 
    " cmp al, 0\n\t" 
    " je L2\n\t" 
    " mov [edi],al\n\t" 
    " jmp L3\n\t" 
    "L2: xlat\n\t" 
    " mov [edi],al\n\t" 
    " inc edi\n\t" 
    " jmp L1\n\t" 
    "L3: popad\n\t"); 

    tmpHash = crypt_r(plaintext, param->methodAndSalt, &crypt); 
    if(0 == memcmp(tmpHash+len, param->cipher, cipherlen)) { 
     printf("success: %s\n", plaintext); 
     break; 
    } 
} 
return 0; 
} 
+0

如果两个或多个线程可以访问它们,您需要锁定对“明文”和“charsetTable”变量的访问。从你发布的内容中很难判断,这是否真的是问题所在。你能告诉我们线程代码吗? –

+1

您应该真正将'asm'声明为'volatile',否则GCC可能会将其移动。另外,你应该在clobberlist中有“内存”。见[here](http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.3)。 – user786653

+0

尽量避免全局变量。在这种情况下,简单的方法是使用堆栈变量。另一种更复杂的可能性是使用线程局部变量。 – hirschhornsalz

回答

2

既然你已经使用并行线程,另一种选择是使由多个线程修改成每线程变量(threadspecific数据)的变量。请参阅pthread_getspecific OpenGroup manpage。其工作原理是这样的:

在主线程(在创建其他线程之前),这样做:

static pthread_key_y tsd_key; 
(void)pthread_key_create(&tsd_key); /* unlikely to fail; handle if you want */ 

,然后每个线程,在您使用的plaintext/charsetTable变量(或多个这样的内),请执行以下操作:

struct { char *plainText, char *charsetTable } *str = 
    pthread_getspecific(tsd_key); 

if (str == NULL) { 
    str = malloc(2 * sizeof(char *)); 
    str.plainText = malloc(size_of_plaintext); 
    str.charsetTable = malloc(size_of_charsetTable); 
    initialize(str.plainText);   /* put the data for this thread in */ 
    initialize(str.charsetTable);  /* ditto */ 
    pthread_setspecific(tsd_key, str); 
} 
char *plaintext = str.plainText; 
char *charsetTable = str.charsetTable; 

或者创建/使用几个键,每个这样的变量一个;在这种情况下,您不会收到str容器/双重间接/其他malloc

英特尔汇编语法与gcc内联asm是,嗯,不是很好;特别是指定输入/输出操作数并不容易。我认为,以获取使用pthread_getspecific机制,你会改变你的代码,这样做:

__asm__("pushad\n\t" 
    "push tsd_key\n\t"    <---- threadspecific data key (arg to call) 
    "call pthread_getspecific\n\t" <---- gets "str" as per above 
    "add esp, 4\n\t"     <---- get rid of the func argument 
    "mov edi, [eax]\n\t"    <---- first ptr == "plainText" 
    "mov ebx, [eax + 4]\n\t"   <---- 2nd ptr == "charsetTable" 
    ... 

这样一来,就变成无锁的,在使用更多的内存每个线程(一个明文/ charsetTable的代价)以及额外功能调用的花费(至pthread_getspecific())。此外,如果你这样做,确保你free()每个线程的具体数据通过pthread_atexit(),否则你会泄漏。

如果你的函数执行速度很快,那么锁定是一个更简单的解决方案,因为你不需要线程特定数据的所有设置/清理开销;如果函数调用缓慢或频繁调用,锁定将成为瓶颈 - 在这种情况下,TSD的内存/访问开销是合理的。你的旅费可能会改变。

+0

这看起来很有前途。我一定会尝试。感谢您的精彩和详细的帖子。欣赏它! – nce

+0

我标记为我的解决方案,因为这是我的问题的直接答案,我摆脱了全局变量。速度测量表明,速度甚至更快。再次感谢 – nce

+0

是的,它可能会更快,因为它是一个“无数据共享”的情况下(每个线程复制一个或多个变量);更高的内存占用量与更好的并行性。 –

1

保护与互斥该功能内联汇编块之外。

+0

我想过了。现在我试了一下。它似乎在工作。但我想这意味着速度的重大损失。我必须锁定“明文”,然后复制内容,然后释放锁,并实际散列明文。但似乎这不是别的选择。谢谢:) – nce