Skip to content

Instantly share code, notes, and snippets.

@lazydogP
Last active July 30, 2022 00:05
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lazydogP/57c431cc7fddb5b969559c4c8cd8c881 to your computer and use it in GitHub Desktop.
Save lazydogP/57c431cc7fddb5b969559c4c8cd8c881 to your computer and use it in GitHub Desktop.
Keygen for eCDP
#!/usr/bin/env python3
# Keygen for McDonald's eCDP(eCrew Development Program),
# a Nintendo DS software to train employees.
# This keygen is for the only dumped Japanese version of eCDP.
# ROM: https://archive.org/details/mcdonalds-japan-ecdp-rom-training-nintendo-ds-cartridge-dump
# Usage: Select the third option in main menu, enter two 6-digit numbers as you like,
# and use this script to calculate the third code.
def rol(n, rotation, width=32):
rotation %= width
if rotation == 0:
return n
n &= (2**width-1)
return (n << rotation) | (n >> (width - rotation))
def gen_index(sum_offset, length):
if length == 0:
return 0
if length <= sum_offset:
count = 0x1c
seed = sum_offset >> 4
if length <= seed >> 0xc:
count -= 0x10
seed = seed >> 0x10
if length <= seed >> 4:
count -= 8
seed = seed >> 8
if length <= seed:
count -= 4
seed = seed >> 4
# Doing bitwise operation in Python is painful...
sum_offset = rol(sum_offset, count)
sum_offset *= 2
carry = (sum_offset & (2**32)) != 0
for i in range(count, 32):
seed = seed * 2 + (~length & 2**32-1) + 1
seed += 1 if carry else 0
carry = (seed & (2 ** 32)) != 0
seed = seed & (2**32-1)
if not carry:
seed += length
seed = seed & (2 ** 32 - 1)
sum_offset *= 2
carry = (sum_offset & (2 ** 32)) != 0
sum_offset &= (2 ** 32-1)
pass
return seed
else:
return sum_offset
def gen_index_2(sum_offset, length):
# Well, didn't expect that.
return sum_offset % length
def calc_shorten_index(input_merge):
hex_char = "0123456789ABCDEF" # located at 0x0225189c
sum_offset = 0
for c in input_merge:
sum_offset += hex_char.index(c)
return gen_index(sum_offset, 7)
pass
def shuffle(input_merge, shuffle_index):
# located at 0x02251948
shuffle_map = [[0x01,0x0A,0x16,0x04,0x07,0x18,0x0C,0x10,0x05,0x17,0x09,0x03,0x12,0x08,0x15,0x13,0x0B,0x02,0x0F,0x0D,0x11,0x0E,0x06,0x14],
[0x07,0x0C,0x0E,0x11,0x09,0x16,0x10,0x06,0x14,0x0D,0x01,0x02,0x12,0x08,0x13,0x0B,0x0F,0x0A,0x18,0x15,0x04,0x05,0x03,0x17],
[0x0F,0x04,0x09,0x03,0x06,0x07,0x11,0x12,0x15,0x16,0x02,0x08,0x05,0x17,0x0C,0x0D,0x01,0x18,0x0B,0x14,0x0E,0x10,0x13,0x0A],
[0x02,0x0A,0x0E,0x12,0x0B,0x03,0x0C,0x06,0x13,0x07,0x11,0x09,0x15,0x18,0x10,0x17,0x14,0x0F,0x04,0x01,0x05,0x08,0x16,0x0D],
[0x0B,0x02,0x09,0x16,0x14,0x01,0x12,0x11,0x15,0x06,0x0F,0x17,0x07,0x10,0x0C,0x0E,0x08,0x18,0x13,0x03,0x0A,0x0D,0x04,0x05],
[0x09,0x0F,0x05,0x0D,0x16,0x15,0x12,0x11,0x03,0x0A,0x04,0x10,0x0E,0x14,0x02,0x01,0x13,0x0C,0x06,0x0B,0x17,0x18,0x07,0x08],
[0x12,0x02,0x0C,0x09,0x0D,0x0E,0x04,0x07,0x16,0x14,0x17,0x01,0x11,0x03,0x10,0x15,0x08,0x0A,0x05,0x13,0x0B,0x18,0x0F,0x06]]
shuffle_method = shuffle_map[shuffle_index]
shuffled_str = ""
for i in range(0x18):
shuffled_str += input_merge[shuffle_method[i]-1]
return shuffled_str
pass
def hex2ints(hex_str):
result = []
for i in range(6):
result.append(int(hex_str[4*i:4*i+4], 16))
return result
def ints_encrypt(numbers):
key = 0x3e0f83e1 # located at 0x02251364
result = []
for i in range(6):
r0 = numbers[i] >> 0x1f
mul = key * numbers[i]
r2, r6 = mul & (2**32-1), mul >> 32
r6 = r0 + r6 >> 3
mul = 0x21 * r6
r0, r2 = mul & (2**32-1), mul >> 32
r6 = numbers[i] - r0
result.append(r6 + 1)
return result
def ints2str(numbers):
chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ" # located at 0x022518b0
s = ""
for i in numbers:
s += chars[i-1]
return s
def calc_serial_code(mac_code, code1, code2):
full_code = mac_code.upper() + code1 + code2
shorten_index = calc_shorten_index(full_code)
shuffle_str = shuffle(full_code, shorten_index)
shuffle_numbers = hex2ints(shuffle_str)
encrypted_numbers = ints_encrypt(shuffle_numbers)
serial_code = ints2str(encrypted_numbers)
return serial_code
print("eCDP Keygen by lazydog")
# My NO$GBA emulator shows "0009BF000031"
mac = input("Input MAC address:")
rest_no = input("Input Restaurant No.:")
manage_no = input("Input Management No. of DS card:")
print("Here's your serial:", calc_serial_code(mac, rest_no, manage_no))
print("Have fun!")
@dogtopus
Copy link

dogtopus commented Jan 2, 2021

Dont understand the ceil part, but that is no problem.

ceil() is probably the wrong way to put this out. That magic number is the modular inverse of 33 mod 0x100000000 (https://www.wolframalpha.com/input/?i=33%5E-1+mod+0x100000000).

Still I don't know why it takes the upper half of 64 bit results and does some operation with it. Normally you only look at the results within the mod space (in this case mod 0x100000000 aka 32-bit integer, etc.) when you "divide" or multiply by the inverse value. Maybe some better bit and number theory magicians can answer my question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment