Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreading with inline assembly and access to a c variable

I'm using inline assembly to construct a set of passwords, which I will use to brute force against a given hash. I used this website as a reference for the construction of the passwords.

This is working flawlessly in a singlethreaded environment. It produces an infinite amount of incrementing passwords.

As I have only basic knowledge of asm, I understand the idea. The gcc uses ATT, so I compile with -masm=intel

During the attempt to multithread the program, I realize that this approach might not work.
The following code uses 2 global C variables, and I assume that this might be the problem.

__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");

It produces a non deterministic result in the plaintext variable.

How can i create a workaround, that every thread accesses his own plaintext variable? (If this is the problem...).

I tried modifying this code, to use extended assembly, but I failed every time. Probably due to the fact that all tutorials use ATT syntax.

I would really appreciate any help, as I'm stuck for several hours now :(

Edit: Running the program with 2 threads, and printing the content of plaintext right after the asm instruction, produces:
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;
} 
like image 907
nce Avatar asked Aug 12 '26 21:08

nce


2 Answers

Since you're already using pthreads, another option is making the variables that are modified by several threads into per-thread variables (threadspecific data). See pthread_getspecific OpenGroup manpage. The way this works is like:

In the main thread (before you create other threads), do:

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

and then within each thread, where you use the plaintext / charsetTable variables (or more such), do:

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;

Or create / use several keys, one per such variable; in that case, you don't get the str container / double indirection / additional malloc.

Intel assembly syntax with gcc inline asm is, hm, not great; in particular, specifying input/output operands is not easy. I think to get that to use the pthread_getspecific mechanism, you'd change your code to do:

__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"
    ...

That way, it becomes lock-free, at the expense of using more memory (one plaintext / charsetTable per thread), and the expense of an additional function call (to pthread_getspecific()). Also, if you do the above, make sure you free() each thread's specific data via pthread_atexit(), or else you'll leak.

If your function is fast to execute, then a lock is a much simpler solution because you don't need all the setup / cleanup overhead of threadspecific data; if the function is either slow or very frequently called, the lock would become a bottleneck though - in that case the memory / access overhead for TSD is justified. Your mileage may vary.

like image 93
FrankH. Avatar answered Aug 15 '26 10:08

FrankH.


Protect this function with mutex outside of inline Assembly block.

like image 43
Alex F Avatar answered Aug 15 '26 11:08

Alex F



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!