Confirming buda's findings, with a few amendments; was there myself at some point, just didn't give it too much thought
Spot for processing
item name by index:
Code: Select all
ACOrigins.exe+15958B9 - 8B 53 10 - mov edx,[rbx+10] <-- contains index to name
ACOrigins.exe+15958BC - 4C 8D 45 28 - lea r8,[rbp+28]
ACOrigins.exe+15958C0 - 41 B1 01 - mov r9l,01
ACOrigins.exe+15958C3 - 48 8B C8 - mov rcx,rax
ACOrigins.exe+15958C6 - E8 7583EDFF - call ACOrigins.exe+146DC40
This index is later on passed on to a function that processes it in the
LocalizationManager's context:
Code: Select all
ACOrigins.exe+146DD3A - 4D 8B C5 - mov r8,r13 <-- r8 becomes the pointer to the decrypted string
ACOrigins.exe+146DD3D - 41 8B D7 - mov edx,r15d <-- hello index :)
ACOrigins.exe+146DD40 - E8 CB000000 - call ACOrigins.exe+146DE10
ACOrigins.exe+146DD45 - 84 C0 - test al,al <-- check r8 after this call
Decryption of
string_size + 1 big encrypted index (e.g.: "Royal Chariot" is 12 chars big; size = 0xC + 1)
Code: Select all
ACOrigins.exe+146DEB0 - 8B 0A - mov ecx,[rdx]
ACOrigins.exe+146DEB2 - 41 8B C2 - mov eax,r10d
ACOrigins.exe+146DEB5 - 0FC9 - bswap ecx
ACOrigins.exe+146DEB7 - 2B C1 - sub eax,ecx
ACOrigins.exe+146DEB9 - 78 26 - js ACOrigins.exe+146DEE1
ACOrigins.exe+146DEBB - 44 8B 5A 04 - mov r11d,[rdx+04]
ACOrigins.exe+146DEBF - 41 FF C1 - inc r9d
ACOrigins.exe+146DEC2 - 8B 42 08 - mov eax,[rdx+08]
ACOrigins.exe+146DEC5 - 8B F9 - mov edi,ecx
ACOrigins.exe+146DEC7 - 41 0FCB - bswap r11d
ACOrigins.exe+146DECA - 0FC8 - bswap eax
ACOrigins.exe+146DECC - 45 8B DB - mov r11d,r11d
ACOrigins.exe+146DECF - 48 83 C2 0C - add rdx,0C
ACOrigins.exe+146DED3 - 44 8B C0 - mov r8d,eax
ACOrigins.exe+146DED6 - 4C 03 DB - add r11,rbx
ACOrigins.exe+146DED9 - 4C 03 C3 - add r8,rbx
ACOrigins.exe+146DEDC - 44 3B CE - cmp r9d,esi
ACOrigins.exe+146DEDF - 72 CF - jb ACOrigins.exe+146DEB0
ACOrigins.exe+146DEE1 - 4D 85 C0 - test r8,r8
First-up, engine decodes the key to first 2 to-be-decrypted WORDs here:
Code: Select all
ACOrigins.exe+146E1AC - 66 44 89 32 - mov [rdx],r14w
ACOrigins.exe+146E1B0 - 8B 7D 17 - mov edi,[rbp+17]
ACOrigins.exe+146E1B3 - 0FB7 DE - movzx ebx,si
ACOrigins.exe+146E1B6 - E9 D5FEFFFF - jmp ACOrigins.exe+146E090
Then using a decrypted offset will fetch the next WORD ("R" as widechar -> 00 52):
Code: Select all
ACOrigins.exe+146E093 - 45 0FB7 34 84 - movzx r14d,word ptr [r12+rax*4]
ACOrigins.exe+146E098 - 41 0FB7 74 84 02 - movzx esi,word ptr [r12+rax*4+02]
ACOrigins.exe+146E09E - 66 41 C1 CE 08 - ror r14w,08
ACOrigins.exe+146E0A3 - 66 C1 CE 08 - ror si,08
ACOrigins.exe+146E0A7 - 66 85 F6 - test si,si
And writes every 4 WORDs here:
Code: Select all
ACOrigins.exe+146E2BC - 66 44 89 32 - mov [rdx],r14w <--
ACOrigins.exe+146E2C0 - 8B 7D 17 - mov edi,[rbp+17]
ACOrigins.exe+146E2C3 - 85 FF - test edi,edi
ACOrigins.exe+146E2C5 - 0F84 CB000000 - je ACOrigins.exe+146E396
ACOrigins.exe+146E2CB - 48 8B 45 0F - mov rax,[rbp+0F]
ACOrigins.exe+146E2CF - FF CF - dec edi
ACOrigins.exe+146E2D1 - 8D 0C 3F - lea ecx,[rdi+rdi]
ACOrigins.exe+146E2D4 - 0FB7 1C 01 - movzx ebx,word ptr [rcx+rax]
ACOrigins.exe+146E2D8 - 8B 45 1B - mov eax,[rbp+1B]
ACOrigins.exe+146E2DB - 48 8B 0D 6E323603 - mov rcx,[ACOrigins.exe+47D1550]
ACOrigins.exe+146E2E2 - 25 FFFFFF1F - and eax,1FFFFFFF
ACOrigins.exe+146E2E7 - 3B C7 - cmp eax,edi <-- check if >= 4
By the time the iterator finishes this loop, this is my buffer:
24232A650 -> R o -> 0x52 0x00 0x6F 0x00
Then it will decode the key to next 2 WORDs and redo the loop:
Code: Select all
ACOrigins.exe+146E1AC - 66 44 89 32 - mov [rdx],r14w <-- store key
ACOrigins.exe+146E1B0 - 8B 7D 17 - mov edi,[rbp+17]
ACOrigins.exe+146E1B3 - 0FB7 DE - movzx ebx,si
ACOrigins.exe+146E1B6 - E9 D5FEFFFF - jmp ACOrigins.exe+146E090
..
..
ACOrigins.exe+146E2BC - 66 44 89 32 - mov [rdx],r14w <-- store WORD
ACOrigins.exe+146E2C0 - 8B 7D 17 - mov edi,[rbp+17]
ACOrigins.exe+146E2C3 - 85 FF - test edi,edi
..
..
ACOrigins.exe+146E2E7 - 3B C7 - cmp eax,edi <-- check if >= 4
This is where the buffer is shifted every 4 processed WORDs:
Code: Select all
ACOrigins.exe+146E250 - 48 85 C9 - test rcx,rcx
ACOrigins.exe+146E253 - 74 07 - je ACOrigins.exe+146E25C
ACOrigins.exe+146E255 - 41 0FB7 00 - movzx eax,word ptr [r8]
ACOrigins.exe+146E259 - 66 89 01 - mov [rcx],ax
ACOrigins.exe+146E25C - 48 83 C1 02 - add rcx,02
ACOrigins.exe+146E260 - 49 83 C0 02 - add r8,02
ACOrigins.exe+146E264 - 49 FF C1 - inc r9
ACOrigins.exe+146E267 - 4D 3B CA - cmp r9,r10
ACOrigins.exe+146E26A - 75 E4 - jne ACOrigins.exe+146E250
First-up, the allocator is here:
Code: Select all
ACOrigins.exe+146E215 - 8B D3 - mov edx,ebx
ACOrigins.exe+146E217 - E8 140539FF - call ACOrigins.exe+7FE730
ACOrigins.exe+146E21C - 4C 8B 45 F7 - mov r8,[rbp-09]
Result of the call, in my case, is 0x26367A0. Then comes this bit:
Code: Select all
ACOrigins.exe+146E250 - 48 85 C9 - test rcx,rcx
ACOrigins.exe+146E253 - 74 07 - je ACOrigins.exe+146E25C
ACOrigins.exe+146E255 - 41 0FB7 00 - movzx eax,word ptr [r8] <--
ACOrigins.exe+146E259 - 66 89 01 - mov [rcx],ax <--
ACOrigins.exe+146E25C - 48 83 C1 02 - add rcx,02
ACOrigins.exe+146E260 - 49 83 C0 02 - add r8,02
ACOrigins.exe+146E264 - 49 FF C1 - inc r9
ACOrigins.exe+146E267 - 4D 3B CA - cmp r9,r10
ACOrigins.exe+146E26A - 75 E4 - jne ACOrigins.exe+146E250
R8 = 24232A650
RCX = 26367A0
So decrypted "R o y a" from 24232A650 is copied to 26367A0.
And so on..
Decryption ends here:
Code: Select all
ACOrigins.exe+146E4A6 - 48 85 D2 - test rdx,rdx
ACOrigins.exe+146E4A9 - 74 03 - je ACOrigins.exe+146E4AE
ACOrigins.exe+146E4AB - 66 89 32 - mov [rdx],si <-- writes the final 0x00 0x00; the NULL-terminator
ACOrigins.exe+146E4AE - 48 8B 55 F7 - mov rdx,[rbp-09]
ACOrigins.exe+146E4B2 - 49 8B CE - mov rcx,r14
ACOrigins.exe+146E4B5 - E8 F60839FF - call ACOrigins.exe+7FEDB0
Then buffer's loaded here:
Code: Select all
ACOrigins.exe+146E4AE - 48 8B 55 F7 - mov rdx,[rbp-09] <-- get buffer
ACOrigins.exe+146E4B2 - 49 8B CE - mov rcx,r14 <-- get stack for result
ACOrigins.exe+146E4B5 - E8 F60839FF - call ACOrigins.exe+7FEDB0
And the CALL will allocate, copy string to allocated address and store it in [R14] (same R8 I mentioned in the beginning, 2nd snippet).
Function then exits successfully (MOV AL,1):
Code: Select all
ACOrigins.exe+146E52C - B0 01 - mov al,01
Now, if you properly feed the right parameters to this function - ACOrigins.exe+146DE10 - as in RCX,RDX,R8, you will get the decrypted string out of the index buda mentioned
BR,
Sun