Code Monkey home page Code Monkey logo

Comments (37)

hozuki avatar hozuki commented on July 2, 2024

Thank you. From the keys you use this should be from Dragalia Lost.

It seems CRI upgraded both ACB and HCA. For ACB they added a mask for byte alignment, which took me some time to figure out.
About the HCA file, it looks pretty normal, when comparing header parts one by one. One noticable difference with the files I experimented before is the header size is 397 (0x18d), much larger than old ones, 96 (0x60). Anyway, all the spaces excluding header data and the checksum are filled with 0.

Here is my guess. If HCAs created by older SDKs can be played in the lastest Atom, there are definitely some "signatures" in the file structure itself, not directly recognizable, that the new player understand and so it can decode the HCA in the old way.
Possible changes I can imagine are: 1) changing the decoding process; 2) adding some conditional branches to the existing decoding process; 3) changing value tables; 4) transforming encryption keys (after input, before initializing ciph part). Sure, combinations of these are also possible. Method 1 may bring stronger side effects to CRI's product development so I think it is less likely to happen than the others.
Some explanations to method 2. The main internal parameters of the decoding process are in comp part (i.e. comp1-comp8), whose values' meanings are not easily observable. Maybe, just maybe, the new decoding process used some values or value pairs that never appeared in older decoders, so new value handlers may have been introduced.

Well I think I can still get a latest version of the free version of the SDK but I don't know if it supports decoding encrypted audio. I'll try it later.

By the way I have sent you a friend request on Steam. You can contact me there but I seldom open Steam these days, mostly because I don't have time to play those games. :/

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

All right, ADX2LE does not provide encrypting/decrypting functionalities, even in CriAtomViewer. (sigh) I'll start RE instead.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I couldn't find any imports in CriAtomViewer that would suggest decoding functionality, I would think it is baked into the app directly

I tried to sort functions by size, there were a few large ones with seemingly many float datatypes but I have no experience in sampling code :/

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

Can the new player play HCAs of previous versions?

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

FYI, I'm using the "lite" version of the SDK (ADX2LE), not the full version (ADX2). It can only generate type-0 HCA (in plain text). Its player treats all HCAs as type-0; maybe it can recognize type-1. In all, lite version does not provide a way to encrypt (in Atom Craft) or decrypt (Atom Viewer). There is no place to enter encryption keys.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I'm trying to find any leftover ACBs in my PC to try and test on the new version

The CriAtomViewer that I tested with is of the full version and not the Lite Edition, there is an option in player preferences to set a key
screenshot_2018-10-10-20-14-12-076_com google chromeremotedesktop

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

You can use this file to run a test. The key is ‭59751358413602‬ (00003657 f27e3b22).

song_1001_mod.zip

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

It plays fine
hca

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

Okay I found an ACB of Theater Days' opening music shipped with the APK, plays properly with the correct key as well

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

@hozuki I noticed that some fields in the header are XORed with 0x80 as follows:
xor

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@hozuki I noticed that some fields in the header are XORed with 0x80 as follows:
xor

That's normal. When reading HCA header each header signature must be ANDed with 0x7f to "neutralize" the effect of XORing 0x80. Surprisingly, header signatures in official HCA builds (created by the full SDK, I guess) are always XORed. Signatures in type-0 (or type-1) HCAs (created by ADX2LE) are not XORed.

The file I posted above is a homebrew HCA, encoded using the ADX2LE encoder and transformed by hcacc to apply encryption. Those signatures stay untouched so they are not XORed.

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

After looking into libcri_ware_unity.so, comparing the one in Dragalia Lost (2018) and an old version (~2016), criWareUnity_SetDecryptionKey() seems unchanged. Code structure and parameters are the same; differences are caused by the compiler. Usages of global variables it affects (e.g. init state for decoders or something like that) also seem unchanged, but I'm not sure. According to @esterTion the key is directly intercepted before calling criWareUnity_SetDecryptionKey(), therefore the key is not tainted before the function call. I am currently stuck in here.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I have a feeling there is certain new metadata inserted into the ACB that tampers with the HCA in some way; I can open the AWB wave bank file but playing the individual Wave tracks through the viewer gives noise even with the correct key set, whereas if I loaded the ACB and played it through the cues it then works normally

from libcgss.

esterTion avatar esterTion commented on July 2, 2024

Weird, acb contains an inline awb (~11KB), but it should be empty since there's a awb by the side
BGM_OUT_0001_01_inline.awb.zip


Oops, I used another acb grabbed from ipa, attached one has ~7KB inline awb
raid_inline.awb.zip


D:\UserDocument\Downloads\FastHCADecoder-v2.2.1\audio>node a.js raid.awb
AFSArchive {
  r:
   Reader {
     buf: <Buffer 41 46 53 32 02 04 02 00 03 00 00 00 20 00 b2 80 00 00 01 00 02 00 26 00 00 00 e3 4e 16 00 a3 9d 2c 00 63 ec 42 00 00 00 00 00 00 00 00 00 00 00 00 00 ... >,
     pos: 0,
     length: 4385891 },
  length: 4385891,
  header:
   { offsetSize: 4,
     fileCount: 3,
     alignment: 2159149088,
     ids: [ 0, 1, 2 ],
     fileEndPoints: [ 38, 1461987, 2923939, 4385891 ] },
  files: { '0': <Buffer >, '1': <Buffer >, '2': <Buffer > } }

D:\UserDocument\Downloads\FastHCADecoder-v2.2.1\audio>node a.js raid_inline.awb
AFSArchive {
  r:
   Reader {
     buf: <Buffer 41 46 53 32 02 02 02 00 03 00 00 00 20 00 b2 80 00 00 01 00 02 00 1e 00 56 08 96 10 d6 18 00 00 c8 c3 c1 00 02 00 01 8d e6 ed f4 00 02 00 ac 44 00 00 ... >,
     pos: 0,
     length: 6358 },
  length: 6358,
  header:
   { offsetSize: 2,
     fileCount: 3,
     alignment: 2159149088,
     ids: [ 0, 1, 2 ],
     fileEndPoints: [ 30, 2134, 4246, 6358 ] },
  files: { '0': <Buffer >, '1': <Buffer >, '2': <Buffer > } }

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I think this inline AWB in some way affects playback of the underlying HCA

If you look at CpkMaker.dll of latest SDK, the AWB header inside CUtf class/CBinary initializes the Alignment field to be some large out of range number, older AWB format had it at 32 unless set

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

I think this inline AWB in some way affects playback of the underlying HCA

If you look at CpkMaker.dll of latest SDK, the AWB header inside CUtf class/CBinary initializes the Alignment field to be some large out of range number, older AWB format had it at 32 unless set

The alignments in new versions should be masked with 0xffff. But I don't know if higher bits matter in the later decoding process.

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

I updated ACB file parsing and extracting in two commits. I haven't pushed to the upstream yet.

Here are all the files included in the ACB. Some are unnamed (not appearing in the cue table).

raid.acb_files.zip

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

I think the higher bits do not affect decryption (not decoding, sorry). If you directly play the extracted HCA (so no interference from ACB stuff) using the key it should still be OK. If so the truth will lie in HCA decoder itself.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I just tried it, still noise, even with correct key

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

Well this is interesting... two-phase keys.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I just dropped a link to the new CriAtomViewer in your steam to try

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@FZFalzar Thanks for the tool.

I did some tests, including:

  • tampering ACBs from different games (they use different keys), their cues, visible or invisible file entries
  • using utf_tab to compare ACB structures
  • debugging the Atom Viewer (it internally calls criAtomExAcb_LoadAcbFile(), the function signature and logic are recognizable; reference: the Unity plugin lib)
  • some naive encryption, like XORing (because raid has 3 very similar HCAs)

Here are some results:

  • raid's structure is quite normal.
  • If the AWB is an internal one (packed inside ACB), the higher bits of the "byte alignment" field of AFS archive do affect entry locating and/or decryption and/or decoding. Setting it to a value different than the original one will output noise. However, the file entries can still be loaded; there is no access violation error.
  • If the AWB is an external one (independent file), the higher bits seems not doing anything. Maybe they are masked. Changing them makes no difference to the output.

An example of the internal AWB is OUT_COMMON.acb (in Dragalia Lost's APK assets). raid is an example of the external AWB.

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

To be clear, if the AWB is an external one, its header also appears in StreamAwbHeader table in the ACB. I tested (arbitary/original higher bits) x (modified header in ACB and/or AWB), all the combinations work without error.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

Thanks for your analysis so far, I think it has to do with how all the assets are marked as StreamingAssets using the API

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

That still didn't anwser the full question, where is the "switch" and how does it affect later prodecures.

Some interesting facts observed:

  1. The original blocks, directly read from file, passed data integrity check. (workflow: block data -[compute checksum]-> integrity check -[decrypt]-> plain data -[decode]-> waveform)
  2. No error occured on all-0 blocks; note that the decryption table always maps 0 -> 0.

I located the decoding functions in Atom Viewer. The code seems... different from known source code. There are also some new tables. Maybe it is because of many interferences. I'm not able to go through it yet.
I don't know what the code was like in previous (full) versions. In ADX2LE SDK v2.06 (currently latest) the decoding pattern is almost the same to known source.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I so happen to have a 2017 version of the SDK, what's peculiar is that the CriAtomViewer in this version gives noise when ACB with correct key is inserted, unlike the latest one, something must have changed. I can pass you this one too

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@FZFalzar Thank you. Some time ago I was going to ask if you can send a copy. But before I reply this message here (or on Steam) the decryption was done. So it is not needed; but you can still keep it for future research purposes, I guess. Again, thank you for your kindness.

Now good news: I just found out what they do to the decryption. The "garbage" higher bits are used to get the real decryption key. I reversed about 15 functions, read them thoroughly, understood nearly every line, and then this tiny mod was revealed. Well you also need some RE techniques, static and dynamic (mostly static) analyses... And yes I decrypted and decoded the audio file. Here it is, as a proof.

BGM_IN_RAID_0001_01.zip

About the ACB itself. It is composed by 3 tracks (or 2, because the last two seems to be identical), all of which are mixed in the final playback. So the file directly extracted contains only the soundtrack (w/o human voice).

Thanks to VGAudio, I made sure that the encoding/decoding skeleton, as well as the tables, are very unlikely to change, so I could focus on tracing the decryption keys.

I'm very tired now so I'll write an article explaining the "new" encryption soon. Hopefully tomorrow.
The formula is actually very simple: key' = key * ((uint64_t)(garbage << 16) | (uint16_t)(~garbage + 2)). (Obviously, when garbage == 0, like old previous ACBs, the key is unchanged.)(Sorry this sentence is wrong. They checked if garbage == 0.) But finding it is quite hard. However, it still cannot explain why the decryption & decoding still succeeded even if I changed these bits.
With this "new" encryption, it will be unsafe to directly decode HCA in the future. You have to consider the ACB/AWB file as well. As a side effect, the code in this repo also has to be updated... (sigh)

from libcgss.

blueskythlikesclouds avatar blueskythlikesclouds commented on July 2, 2024

Is this "garbage" unique for each AFS2?

from libcgss.

Thealexbarney avatar Thealexbarney commented on July 2, 2024

Not that you're wrong, but if you're posting proof it might be better to post the decrypted HCA =P
raid.zip

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@Thealexbarney Fair enough. :D

hca_proof.zip

The original tracks (3 individual, not 2, I didn't decode all at first), and their type-0 equivalents (simply using hcacc and voila). Decoded WAV files are too large so I'll not post them here. The true key after transformation is 0x5580E165A92C2C63 for this ACB/AWB.

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@blueskythlikesclouds

Is this "garbage" unique for each AFS2?

Yes. CRI stores them in the two unused bytes, right beside the byte alignment.
The former layout: 02 00 00 00 where all four bytes are considered as a uint32_t (align=32). But in practice the two higher bytes are always unused, so that's a nice place to hide something: 02 00 cd ab where 0xabcd is the secondary key ("garbage", lol).

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

@blueskythlikesclouds An example of different key: OUT_COMMON.acb (inside Dragalia Lost's APK assets folder) has the secondary key 0x5c83; this dword is 20 00 83 5c at offset 0x382c.

from libcgss.

Thealexbarney avatar Thealexbarney commented on July 2, 2024

Or you could read offset 0xE in the awb file.

BTW, don't know if you know already, but VGAudio has an HCA encoder. Much faster than hcaenc.dll too.

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

Or you could read offset 0xE in the awb file.

BTW, don't know if you know already, but VGAudio has an HCA encoder. Much faster than hcaenc.dll too.

Yes I saw that. I accidentally found it in Google when I didn't know where to go. At that time I was beginning to reverse all the decoding code.
And it is your work (both the encoder and the decoder, plus the theory unveiled) that tells me, that there is actually a way to decode HCA without using the tables in the official decoder. Thank you. Based on this fact, I was able to make a deduction. Now that they keep the tables unchanged, and the decoding code unchanged (can find part of the pattern, have to make a guess), something else must have been changed. Combining with the test results, it is probably related to the ACB/AWB itself, like the discussion above.

When I opened the GH repo tree and found out it can encode HCA, I was shocked. Then I tried a bit with various files. Just playing. XD
I haven't compared the speed; but a managed and open-source implementation is already a great idea.
I'm always afraid of signal processing though.

from libcgss.

FZFalzar avatar FZFalzar commented on July 2, 2024

I get the feeling @hozuki

DSP is a monster for the untrained, but really good work to all involved! I do hope this applies to new ACB/AWB formats and not just a one-off event for Dragalia. I doubt this is the case though since this is an implementation only CRIWARE can control

I'm looking forward to reading the write-up, and I'd send beer money if I can :D

from libcgss.

hozuki avatar hozuki commented on July 2, 2024

Hi all. The English translation is finally finished. Code for a new tool, acb2wavs, were pushed to DereTore and this repo. So generally everything is set and I think this issue can be closed now. Thanks everyone for participating. :)

from libcgss.

ActualMandM avatar ActualMandM commented on July 2, 2024

wanted to clarify, since sonic colors ultimate shipped its xml files: it's actually the awb hash
se_voice_system_E.zip

AwbHash="30898"
2021-09-13_21-50-44_HxD

from libcgss.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.