具有内联汇编和访问 c 变量的多线程
Posted
技术标签:
【中文标题】具有内联汇编和访问 c 变量的多线程【英文标题】:Multithreading with inline assembly and access to a c variable 【发布时间】:2011-09-18 14:11:23 【问题描述】:我正在使用内联汇编来构造一组密码,我将使用这些密码来暴力破解给定的哈希值。我用这个website 作为密码构造的参考。
这在单线程环境中完美运行。它会产生无限数量的递增密码。
因为我只有基本的 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 个线程运行程序,并在 asm 指令之后打印明文内容,生成:b
b
@987654327 @d
f
f
...
编辑2:
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;
【问题讨论】:
如果两个或多个线程可以访问它们,您需要锁定对plaintext
和 charsetTable
变量的访问。从您发布的内容很难判断这是否真的是问题所在。你能告诉我们线程代码吗?
你真的应该将你的asm
声明为volatile
,否则 GCC 可能会移动它。此外,您应该在clobberlist 中有memory
。见here。
尽量避免使用全局变量。在这种情况下,简单的方法是使用堆栈变量。另一种更复杂的可能性是使用线程局部变量。
顺便说一句,asm 代码很慢。特别是应该避免使用 xlat
指令(这可能是 20 年前最快的方式)。
【参考方案1】:
由于您已经在使用 pthread,另一种选择是将由多个线程修改的变量变成每个线程的变量(线程特定的数据)。见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()
)。另外,如果您执行上述操作,请确保您通过pthread_atexit()
free()
每个线程的特定数据,否则您会泄漏。
如果您的函数执行速度很快,那么锁是一个更简单的解决方案,因为您不需要线程特定数据的所有设置/清理开销;如果该函数很慢或被非常频繁地调用,那么锁将成为一个瓶颈——在这种情况下,TSD 的内存/访问开销是合理的。您的里程可能会有所不同。
【讨论】:
这看起来很有希望。我一定会试试的。感谢您的精彩而详细的帖子。欣赏它! 我将此标记为我的解决方案,因为这是对我的问题的直接回答,所以我摆脱了全局变量。速度测量显示,它甚至快了一点。再次感谢 是的,它可能会更快,因为它是“无数据共享”的情况(每个线程复制一次变量);更高的内存占用与更好的并行性。【参考方案2】:使用内联汇编块之外的互斥锁保护此函数。
【讨论】:
我想过。现在我试了一下。它似乎正在工作。但我想这意味着速度的重大损失。我必须锁定plaintext
然后复制内容,然后释放锁定,并实际散列明文。但似乎别无选择。谢谢:)以上是关于具有内联汇编和访问 c 变量的多线程的主要内容,如果未能解决你的问题,请参考以下文章