Skip to content

Commit

Permalink
Support for multibyte characters in passwords (fixes Aorimn#323, Aori…
Browse files Browse the repository at this point in the history
…mn#113)

Using iconv() to convert the password from the local character set to UTF-16LE used by BitLocker.

Ported Aorimn#118 from develop (merged) to the master branch.

Co-authored-by: José Luis González García <[email protected]>
  • Loading branch information
robert-scheck and José Luis González García committed Jan 5, 2025
1 parent 3e7aea1 commit 817116a
Show file tree
Hide file tree
Showing 6 changed files with 397 additions and 12 deletions.
2 changes: 2 additions & 0 deletions include/dislocker/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,6 @@ VALUE rb_hexdump(uint8_t* data, size_t data_len);

#endif /* _HAVE_RUBY */

size_t strlen_utf16(char* data, size_t max_length);

#endif // COMMON_H
6 changes: 6 additions & 0 deletions include/dislocker/ntfs/encoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,11 @@ int asciitoutf16(const uint8_t* ascii, uint16_t* utf16);

int utf16bigtolittleendian(uint16_t* utf16, size_t utf16_length);

int toutf16(const uint8_t* inbuffer, uint8_t* outbuffer);

char* getlocalcharset();

char** buildcharactersetslist(void);


#endif /* ENCODING_H */
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
# Don't use `-read_only_relocs' here as it seems to only work for 32 bits
# binaries
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-bind_at_load")
# Library for libiconv
set (LIB ${LIB} iconv)
else()
# Useless warnings when used within Darwin
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
Expand Down
46 changes: 34 additions & 12 deletions src/accesses/user_pass/user_pass.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,32 +256,54 @@ int user_key(const uint8_t *user_password,
}


uint16_t* utf16_password = NULL;
size_t utf16_length = 0;
uint8_t user_hash[32] = {0,};
void* utf16_password = NULL;
size_t utf8_length = 0;
size_t utf16_length = 0;
size_t utf16_real_length = 0;
uint8_t user_hash[32] = {0,};

/*
* We first get the SHA256(SHA256(to_UTF16(user_password)))
*/
utf16_length = (strlen((char*) user_password)+1) * sizeof(uint16_t);
utf16_password = dis_malloc(utf16_length);
utf8_length = strlen((char*) user_password);
dis_printf(L_DEBUG, "Length of string password: %d\n", utf8_length);
/* Expected length of UTF-16 string */
utf16_length = (utf8_length+1) * 2;
dis_printf(L_DEBUG, "Expected length of UTF-16 string password: %d\n", utf16_length);

if(!asciitoutf16(user_password, utf16_password))
utf16_password = (uint8_t*)dis_malloc(utf16_length);
memset(utf16_password, 0, utf16_length);

if(!toutf16(user_password, (uint8_t*)utf16_password))
{
dis_printf(
L_ERROR,
"Can't convert user password to UTF-16, aborting.\n"
"Can't convert user password to UTF-16, now trying with the original way...\n"
);
memclean(utf16_password, utf16_length);
return FALSE;

memset(utf16_password, 0, utf16_length);

if(!asciitoutf16(user_password, (uint16_t*)utf16_password))
{
dis_printf(
L_ERROR,
"Can't convert user password to UTF-16, aborting.\n"
);
memclean(utf16_password, utf16_length);
return FALSE;
}
}

/* Final real length of the UTF-16 string (without the '\0\0' ending) */
utf16_real_length = strlen_utf16(utf16_password, utf16_length);
dis_printf(L_DEBUG, "Real length of UTF-16 string password: %d\n", utf16_real_length);

dis_printf(L_DEBUG, "UTF-16 user password:\n");
hexdump(L_DEBUG, (uint8_t*) utf16_password, utf16_length);
hexdump(L_DEBUG, (uint8_t*) utf16_password, utf16_real_length);

/* We're not taking the '\0\0' end of the UTF-16 string */
SHA256((unsigned char *) utf16_password, utf16_length-2, user_hash);
SHA256((unsigned char *) user_hash, 32, user_hash);
SHA256((unsigned char *) utf16_password, utf16_real_length, user_hash);
SHA256((unsigned char *) user_hash, 32, user_hash);

/*
* We then pass it to the key stretching manipulation
Expand Down
26 changes: 26 additions & 0 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,29 @@ VALUE rb_hexdump(uint8_t* data, size_t data_len)
}

#endif /* _HAVE_RUBY */


/**
* Counts the numbers of bytes that represent a string in a UTF-16 null-terminated string
*
* @param data A pointer to the string
* @return The number of bytes in the string
*/
size_t strlen_utf16(char *data, size_t max_length)
{
size_t i = 0;
/* There should be at least 2 bytes (one character) */
if (!data || max_length < 2)
return 0;

while (i < (max_length - 1))
{
/* Taking 2 bytes at a time (one character)
* If both values in current (even) index i and next (odd) index i are 0
* then it is the end of the string
*/
if (data[i] == 0 && data[i+1] == 0) break;
i+=2;
}
return i;
}
Loading

0 comments on commit 817116a

Please sign in to comment.