I think you identified the purpose of the functions and its parameters correctly. So, the function sub_44d84
has four parameters: (<decryptionFunction=0x1>, <IV>, <bytesToDecrypt>, <decryptedBytes>)
and calls the sub_46d554
function for every blocks using CBC.
The sub_46d554
function received 2 parameters:
R0
: Encrypted input block
R1
: Decrypted output block
The bytes from the input blocks are read out from the first part of the function, performs the decryption steps and writes out to the output buffer in the last part of the function.
To understand the decryption function, first building blocks of the obfuscation should be understand. So, I picked up some example codes after the second bytes was read out from the input block (the start of the function is a little bit messy because of the compiler optimization).
In the above image the line marked with green reads out the second byte of the input block and the orange line initializes the R2
register. The yellow and blue blocks are perform similar functions. Both blocks starts with an addition, stores the result to a local variable and check whether the result was equal to 0xFF
. If the result was 0xFF
the value of the R2
was changed. Since the R2
was changed only if the addition was 0xFF
and the addition used always the LR
(which contained the second byte of the input), the value of the R2
register can be changed only once. Because the new R2
value depends only from the LR
these blocks implements a lookup table, which is a substitution box.
[EDIT] The lookup table is built up from the building blocks similar to the highlighted ones. So, in pseudo code, the lookup table is implemented in the following way:
if (input == 0xb1) out = 0x5b
if (input == 0x42) out = 0x56
...
To understand the reason why the result is stored into a local variable, let's check the next usage of the local variable var_1A8
.
It reads out the local variable, which was the result of the addition, and checks whether it is 0xFF
similar to the previous cases. Since the value of this local variable will be 0xFF
only if the result of the addition was 0xFF
in the place, where the value was originally stored, the comparison checks whether the input byte is equal to a specific value again.
If we search for the local variable var_1A8
in the whole function, we receive a lot of results with a specific patter (STR
, LDR
, LDR
, LDR
) repeatedly.
The STR
means the storage of the addition and the LDR
means the usage of this addition. So, every bytes are checked four times and than another byte will be used.
By comparing all of the input bytes usages with the local variable usages, the following patter can be found:
i0 = input[0x00]
d0_0 = table0[i0]
id = input[0x0d]
d0_d = table1[id]
ia = input[0x0a]
d0_a = table2[ia]
i7 = input[0x07]
d0_7 = table3[i7]
d1_0 = table4[i0]
d1_d = table5[id]
d1_a = table6[ia]
d1_7 = table7[i7]
d2_0 = table8[i0]
d2_d = table9[id]
d2_a = table10[ia]
d2_7 = table11[i7]
d3_0 = table12[i0]
d3_d = table13[id]
d3_a = table14[ia]
d3_7 = table15[i7]
So, reads out 4 bytes, performs the substitution and stores the result in a local variable. After it, the same was performed with the next 4 bytes and so on. After the whole block was processed the stored results were read out and xored value of some previous results was used as a new input, for example:
nb_0 = d0_4 ^ d2_d ^ ...
d0_0 = table16[nb_0]
nb_1 = d1_2 ^ d3_8 ^ ...
d0_1 = table17[nb_1]
...
d1_0 = table20[nb_0]
d1_d = table21[nb_1]
...
By checking the pattern of the local variable usage, it clearly seen that it uses 9 round of substitution.
Based on the above, I think, that the decryption function can be an AES-128 implementation. Because the add round key, sub bytes and shift rows steps operations can be implemented with lookup tables in one step, and the mix columns operation in another step. Because there is 9 round only, the initial round and the first round of the AES may be implemented with one lookup table.
So, the key (if it is really an AES implementation, the expanded key) is mixed into the implementation. Although I'm not a crytographer, I guess that you may retrieve the expanded AES key by xoring the values of the lookup tables with the Rijndael S-Box values. However, it seems to be a hard and time-consuming process, so you may consider to emulate the code if you want to just decrypt some data.
sub_44d84(<decryptionFunction=0x1>, <IV>, <bytesToDecrypt>, <decryptedBytes>)
andsub_46d554(<bytesToDecrypt>, <output>)
.