2011-09-18 57 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