[Python] NDT files decryptor

08/10/2013 21:29 neuronet#76
All battle logic is server side
09/20/2013 01:23 lordos85#77
Quote:
Originally Posted by neuronet View Post
All battle logic is server side
Not really Neuro, last week on ESPAO ppl managed to get all +10 equips even mounts, decos and outfits with cheat engine, and not only the looks also the stats, but they made a maint on this monday with a 7 days rollback and now it doesnt work. Hell...they even changed weaps stats and got 15kk might ON FL. I think it might worth the time to investigate this xD ill upload a video showing that

09/20/2013 02:05 raabbat#78
anyone manage to figure out how to make this work on Int?
09/26/2013 14:19 test, please ignore#79
Here is the NDT decryptor/encryptor code in C#, enjoy.
How to use:
1) compile using Visual C# into a binary
2) make it a default program to open NDT files
3) click on NDT file to extract.

P.S. Sorry, I don't trust binary files, so I'm not going to post one here for the others.

Code:
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace UnpackNdt
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential, Pack=1)]
        public struct NdtFileHeader
        {
            public UInt32 MagicBytes;
            public UInt32 Version;
            public UInt32 FileSize;
            public byte Key;
            public byte Undefined1;
            public byte Undefined2;
            public byte Undefined3;
            public UInt32 Undefined4;
            public UInt32 Undefined5;
        }

        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                return;
            }

            string inputFilePath = args[0];

            if (Path.GetExtension(inputFilePath).ToLowerInvariant() == ".ndt")
            {
                string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFilePath), Path.GetFileNameWithoutExtension(inputFilePath) + ".txt");
                byte[] input = File.ReadAllBytes(inputFilePath);
                byte[] output = Decrypt(input);
                File.WriteAllBytes(outputFilePath, output);
            }
            else
            {
                string outputFilePath = Path.Combine(Path.GetDirectoryName(inputFilePath), Path.GetFileNameWithoutExtension(inputFilePath) + ".ndt2");
                byte[] input = File.ReadAllBytes(inputFilePath);
                byte[] output = Encrypt(input, 77);
                File.WriteAllBytes(outputFilePath, output);
            }
        }

        static byte[] Decrypt(byte[] input)
        {
            int inputLength = input.Length;
            int outputLength = inputLength - 24;
            byte[] output = new byte[outputLength];
            
            // extract header
            byte[] headerArray = new byte[24];
            Array.Copy(input, headerArray, 24);
            NdtFileHeader header;
            FromArray(headerArray, out header);

            if (header.Version == 0x00010001)
            {
                // decode the data using the Key from the header
                for (int i = 0; i < outputLength; i++)
                {
                    byte c = input[i + 24];
                    c -= header.Key;
                    if (i + 1 != outputLength)
                    {
                        byte next = input[i + 1 + 24];
                        c ^= next;
                    }
                    output[i] = c;
                }
            }
            else if (header.Version == 0x00030001)
            {
                int[] shufflingMap = new int[] { 1, 3, 2, 3, 1, 5, 4, 2, 1, 4, 2, 8, 4, 2, 6, 8, 2, 6, 4 };
                int shufflingMapLength = shufflingMap.Length;
                int numberOfDwords = outputLength / 4;
                Array.Copy(input, 24, output, 0, outputLength);

                for (int i = numberOfDwords - 1; i >= 0; i--)
                {
                    // calculate which Dword to swap with
                    int shuffleBy = shufflingMap[i % shufflingMapLength];
                    int j = (i + shuffleBy) % numberOfDwords;

                    UInt32 iDword = GetUInt32(output, i * 4, 4);
                    UInt32 jDword = GetUInt32(output, j * 4, 4);
                    SetUInt32(jDword, output, i * 4, 4);
                    SetUInt32(iDword, output, j * 4, 4);
                }

                UInt32 adjustedKey = header.Key - (UInt32)0x57D3CEFF;
                UInt32 sum = 0;
                UInt32 carryOver = 0;
                for (int i = 0; i < numberOfDwords; i++)
                {
                    UInt32 iDword = GetUInt32(output, i * 4, 4);
                    UInt32 decodedValue = adjustedKey + iDword + sum + carryOver;
                    SetUInt32(decodedValue, output, i * 4, 4);
                    sum += header.Key;
                    carryOver = iDword;
                }
            }

            return output;
        }

        static UInt32 GetUInt32(byte[] array, int index, int length)
        {
            UInt32 result = 0;
            for (int i = length; i > 0; i--)
            {
                result = result << 8;
                result += array[index + i - 1];
            }
            return result;
        }

        static void SetUInt32(UInt32 value, byte[] array, int index, int length)
        {
            for (int i = 0; i < length; i++)
            {
                array[index + i] = (byte)(value % 256);
                value = value / 256;
            }
        }

        static byte[] Encrypt(byte[] input, byte key)
        {
            int inputLength = input.Length;
            int outputLength = inputLength + 24;
            byte[] output = new byte[outputLength];

            // output header header
            NdtFileHeader header = new NdtFileHeader();
            header.MagicBytes = 0x0052434e; // NCR
            header.Version = 0x00010001;    // v. 1.0.1
            header.FileSize = (uint) outputLength;
            header.Key = key;
            byte[] headerArray = new byte[24];
            ToArray(header, headerArray);
            Array.Copy(headerArray, output, 24);

            // encode the data using the Key
            for (int i = inputLength - 1; i >= 0; i--)
            {
                byte c = input[i];
                if (i != inputLength - 1)
                {
                    byte next = output[i + 1 + 24];
                    c ^= next;
                }
                c += header.Key;
                output[i + 24] = c;
            }

            return output;
        }

        private static void FromArray<T>(byte[] input, out T output)
        {
            // Pin the managed memory while, copy it out the data, then unpin it
            GCHandle handle = GCHandle.Alloc(input, GCHandleType.Pinned);
            output = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
            handle.Free();
        }

        private static void ToArray<T>(T input, byte[] output)
        {
            // Pin the managed memory while, copy it out the data, then unpin it
            GCHandle handle = GCHandle.Alloc(output, GCHandleType.Pinned);
            Marshal.StructureToPtr(input, handle.AddrOfPinnedObject(), false);
            handle.Free();
        }
    }
}
01/26/2023 18:28 []Dark#80
here is code from previous post but updated to be able to encrypt back to v3:
Do not remember why I needed this, but post it here just in case if I will be back playing AO :o
01/27/2023 05:15 ANR2K#81
Just a reminder, that you need to save the .txt file as UTF-16 LE encoding in order for the raw text file to be recognized by the game when renaming it to .ndt
02/04/2023 20:41 SHUNZU#82
wht the function for this code?