Skip to content
View in the app

A better way to browse. Learn more.

ResHax

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.
Help us keep the site running.
Zero Tolerance for Disrespect

[PC] Crazy Shooting Bubble Fighter .pkn / .bfz

Featured Replies

Hi, I'm looking for tools/scripts capable of unpacking (preferably, both) the .pkn and .bfz archive types used by the PC game Bubble Fighter / 버블파이터. Below are some notes I've compiled on the two formats so people can get a quick and rough idea of what they are .

.pkn

  • used around 2016 and onwards for all clients; replacing .bfz file format

  • proprietary, so no existing game extractors for these archives work

  • someone who's developing an offline version of the game claimed he was able to unpack the .pkn files; however, he didn't elaborate on how he managed to do so

.bfz

  • used for earlier versions of the game until 2016

  • versions of the game released before it released in Korea may have used different file however none of those versions have resurfaced yet so its hard to tell

  • unlike .pkn archives which are proprietary, bfz files are just zip files under a dif. suffix (.bfz = "Bubble Fighter Zip"?)

  • because .bfz files are just zip files in disguise they should be easier to open than pkn; however, not only are most of the files (minus a good amount of .png files) in these archives stored in deflate format, but seemingly each archive for every different version (this includes updates and not just regional releases) of the game also uses different passwords/keys

The sample files are in the 3 links below. The first one is a .config bfz archive from a 2013 Indonesian client, and the last 2 are config .pkn archives from the latest version of the game before it shut down. If you need me to upload the .exe for the game itself and/or attach the sample files directly to my post than I gladly will.
https://files.catbox.moe/pl6ayl.bfz https://files.catbox.moe/df7fiw.pkn https://files.catbox.moe/w9ydwu.pkn

  • Localization

Inspecting the Pet_Snake.bfz file shows that it indeed is a protected ZIP file with legacy ZipCrypto. The game executable is protected by Themida (😒) so I had to dump the running process to perform some analysis.

The ZIP password is derived from the archive's resource path. The loader normalizes the separators, gets rid of the filename, takes the final directory component, and finally converts it all to uppercase. The string is then ran through an embedded lookup table and finally the results are all concatenated resulting in the valid password for this particular BFP file.

Here is a script I wrote that will automatically generate the proper ZIP password for the given BFP file:

TABLES = [
    "as89f7d6af98e7f6a9s87f6as98f76asdf8yasuefyae8f7as6ef9ase87fa6se9",
    "alkjdfLKDFJeofias4894ewfadfklDFJfsafjlasff89a4fhJLKDSfHdf98daf9s",
    "afoiefajsfiasdf87ysda987dsfvhsdkvhs8dr7vhsdkfghsie4g8hsdfukzDFDf",
    "faklsjdfiod9f8asdfjKLDFjds9f8jasdklfjsdf9asd8fuseijkasdkHDFKJDf8",
    "dflkjasdf98asfuyaiufhaos8efhLHFDJSFhasod8fsdfkLDfd9f8aslkejfDLkf",
    "dkfjapsdoifjas9d8fajsdfiashdJKFSHdfdfaskdljf9sd8fasd89f7asieufa9",
]

def bfz_password(package_path):
    path = package_path.encode("ascii")

    return "".join(
        TABLES[i % 6][path[i % len(path)] % 64]
        for i in range(64)
    )

def reconstruct_bfz_path(resource_path: str) -> str:
    normalized_path = resource_path.replace("\\", "/")
    separator_index = normalized_path.rfind("/")

    resource_directory = normalized_path[:separator_index]
    directory_separator = resource_directory.rfind("/")
    archive_name = resource_directory[directory_separator + 1:]

    return f"{resource_directory}/{archive_name}.bfz"

def derive_bfz_kdf_input(resource_path: str) -> str:
    archive_path = reconstruct_bfz_path(resource_path)
    return archive_path.upper()

if __name__ == "__main__":
    resource = "<FILENAME>"

    logical_archive_path = reconstruct_bfz_path(resource)
    password_generator_input = derive_bfz_kdf_input(resource)
    encrypted_password = bfz_password(password_generator_input)

    print(f"Resource path: {resource}")
    print(f"Logical archive path: {logical_archive_path}")
    print(f"KDF input: {password_generator_input}")
    print(f"Encrypted password: {encrypted_password}")

image.png

I am taking the Pet_Snake.bfz file as example and I have opened in my tool. You can also open it in any other tool like 7zip, NotePad++, etc... as long as you can get the names of the files inside of the ZIP archive.

You simply take any of the names, in this case we take Models/Characters/Pet_Snake/common/pis-basic-jump.face, and paste it in the <FILENAME> placeholder inside of the python script and run it.

Resource path: Models/Characters/Pet_Snake/common/pis-basic-jump.face
Logical archive path: Models/Characters/Pet_Snake/common/common.bfz
KDF input: MODELS/CHARACTERS/PET_SNAKE/COMMON/COMMON.BFZ
Encrypted password: 7aejsjajsauk94fjf8afasf9sefskd7f8aDj6fdsud8Lf8ya7odskos8flhps9sd

The encrypted password can then be used as password to extract the data from the ZIP file.


I did not yet take a look at the PKN files you've supplied. I am planning on doing this soon.

Edited by Falkrian

  • Localization

OK. So I took a look at the Korean Bubble Fighter executable (after stepping into the ring with Themida (😒) again) you've provided and I saw that it uses SNOW 2.0 Stream Cipher but, with a few Nexon introduced twists.

image.pngthis

I used the following C++ implementation of the SNOW Stream Cipher from this GitHub Repository as reference and wrote my own version in Python and added the Nexon induced twists myself.

Use the attached Python script as follows:

python .\BubbleFighterPKN.py "path\to\pkn\file"

image.png

I have tested the Python script on the samples you've given me so I hope it will work on the rest of the files as well. I have no idea what your future plans are with this but, I wish you all the best. 👍

BubbleFighterPKN.py

Edited by Falkrian

  • 2 weeks later...
  • Localization

@Falkrian Just tried your script on the Chinese (2011-04-11) & Korean (2026-05-08 & 2025-12-17) version but it doesn't seem to get the key right, the version I'm working with can be found on archive.org (don't wanna link it cause anti piracy rules and stuff)

Config0000.zip

Edited by NeoGT404

  • Localization
34 minutes ago, NeoGT404 said:

@Falkrian Just tried your script on the Chinese (2011-04-11) & Korean (2026-05-08 & 2025-12-17) version but it doesn't seem to get the key right, the version I'm working with can be found on archive.org (don't wanna link it cause anti piracy rules and stuff)

Config0000.zip

Thank you for letting me know! I will look into it. 👍

  • Localization

The same modified SNOW 2.0 implementation is used, but the checksum was removed from each file-table record and the stored filenames are relative. This is why the script failed. I have attached a modified version of the script that accounts for this. The usage remains the same.

Please let me know if you encounter any other issues. 👍

BubbleFighterPKN_v2.py

Create an account or sign in to comment

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.