Back
Featured image of post DefCamp2022 can-you-crack-this Writeup

DefCamp2022 can-you-crack-this Writeup

English? Translate!

can-you-crack-this

The main function reads the input and constrains the length.

  v11 = std::operator<<<std::char_traits<char>>(&std::cout, "Enter your public key: ");
  std::ostream::operator<<(v11, std::endl<char,std::char_traits<char>>);
  std::operator>><char>(&std::cin, public_key);
  if ( (unsigned __int64)std::string::length(public_key) >= 20 ) {
    v9 = std::operator<<<std::char_traits<char>>(&std::cout, "Enter serial key:");
    std::ostream::operator<<(v9, std::endl<char,std::char_traits<char>>);
    std::operator>><char>(&std::cin, serial_key);
    v8 = 4 * std::string::length(public_key) - 1;
    if ( v8 == std::string::length(serial_key) ) {
      std::string::basic_string(str_public_key, public_key);
      std::string::basic_string(str_serial_key, serial_key);
      v6 = verify_serial((__int64)str_public_key, (__int64)str_serial_key);
      std::string::~string(str_serial_key);
      std::string::~string(str_public_key);
      if ( v6 ) {
        v5 = std::operator<<<std::char_traits<char>>(&std::cout, "Serial accepted.");
        std::ostream::operator<<(v5, std::endl<char,std::char_traits<char>>);
      }
      else {
        v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Try harder.");
        std::ostream::operator<<(v4, std::endl<char,std::char_traits<char>>);
      }
      v17 = 0;
      v14 = 1;
    }
    else {
      v7 = std::operator<<<std::char_traits<char>>(&std::cout, "Invalid serial length.");
      std::ostream::operator<<(v7, std::endl<char,std::char_traits<char>>);
      v17 = 0;
      v14 = 1;
    }
  }
  else {
    v10 = std::operator<<<std::char_traits<char>>(&std::cout, "Invalid length.");
    std::ostream::operator<<(v10, std::endl<char,std::char_traits<char>>);
    v17 = 0;
    v14 = 1;
  }

It requires that the two input strings satisfy the constraint len(serial) = len(publik_key) * 4 - 1 and passes the two strings into the verify_serial() function.

_BOOL8 __fastcall verify_serial(__int64 a1, __int64 a2) {
  __int64 v2; // rsi
  __int64 v3; // rax
  const char *v4; // rax
  __int64 v6; // [rsp+10h] [rbp-C0h]
  bool v7; // [rsp+1Bh] [rbp-B5h]
  char *char_from_pb_key; // [rsp+20h] [rbp-B0h]
  char v10[24]; // [rsp+38h] [rbp-98h] BYREF
  char sub_serial[28]; // [rsp+50h] [rbp-80h] BYREF
  int v12; // [rsp+6Ch] [rbp-64h]
  __int64 count; // [rsp+70h] [rbp-60h]
  char sub_serial_v12[24]; // [rsp+88h] [rbp-48h] BYREF
  __int64 pos_v13; // [rsp+A0h] [rbp-30h]
  __int64 v16; // [rsp+A8h] [rbp-28h]
  char v17[28]; // [rsp+B0h] [rbp-20h] BYREF
  _BOOL4 v18; // [rsp+CCh] [rbp-4h]

  std::string::basic_string<std::nullptr_t>(v17, "-");
  v16 = 0LL;
  pos_v13 = 0LL;
  std::string::basic_string((__int64)sub_serial_v12);
  count = 0LL;
  while ( 1 ) {
    pos_v13 = std::string::find(a2, v17, 0LL);
    if ( pos_v13 == -1 )
      break;
    ++count;
    if ( pos_v13 != 3 ) {
      v18 = 0;
      v12 = 1;
      goto L_EXIT;
    }
    std::string::substr(sub_serial, a2, 0LL, 3LL);
    std::string::operator=((__int64)sub_serial_v12, (__int64)sub_serial);
    std::string::~string(sub_serial);
    std::string::basic_string(v10, sub_serial_v12);
    v2 = v16++;
    char_from_pb_key = (char *)std::string::at(a1, v2);
    v7 = !verify_char(v10, *char_from_pb_key);
    std::string::~string(v10);
    if ( v7 ) {
      v18 = 0;
      v12 = 1;
      goto L_EXIT;
    }
    v6 = pos_v13;
    v3 = std::string::length(v17);
    std::string::erase(a2, 0LL, v3 + v6);
  }
  if ( count == 19 ) {
    v4 = (const char *)std::string::c_str(a2);
    v18 = strncmp(v4, "bss", 3uLL) == 0;
  }
  else {
    v18 = 0;
  }
  v12 = 1;
L_EXIT:
  std::string::~string(sub_serial_v12);
  std::string::~string(v17);
  return v18;
}

This function splits the Serial Key into 20 parts by - and requires each part to be 3 in length, ending with bss.

The three characters of each Serial Key part and one Public Key character in the corresponding position are passed to the verify_char() function.

_BOOL8 __fastcall verify_char(char *a1, char a2) {
  const char *v2; // rax
  int v3; // eax
  unsigned __int64 v5; // [rsp+0h] [rbp-20h]
  __int64 v6; // [rsp+8h] [rbp-18h]
  __int64 v7; // [rsp+10h] [rbp-10h]

  v2 = (const char *)std::string::c_str(a1);
  v7 = strtol(v2, 0LL, 16);
  v6 = fib(v7);                                 // fib / 100
  v5 = v6;
  do {
    do
      --v5;
    while ( !check_arm(v5) );
  } while ( (unsigned int)notused(v5) || v5 > 0x10E47F4C575565LL );
  if ( v7 >= 100 ) {
    if ( (unsigned int)isprint(a2) ) {
      v3 = UN++;
      used[v3] = v5;
      return v5 && v6 && v5 != v6 && (v6 - v5) % 100 == a2;
    }
  }
  return 0;
}

This function first converts the hex string (format of input) to int, then uses fib() to calculate the value of that index in the Fibonacci series and divides it by 100 to return v6.

The check_arm() function is as follows

_BOOL8 __fastcall check_arm(__int64 a1) {
  return (countSetBits(a1) & 1) == 1;
}

__int64 __fastcall countSetBits(__int64 a1) {
  unsigned int v2; // [rsp+0h] [rbp-Ch]

  v2 = 0;
  while ( a1 ) {
    a1 &= a1 - 1;
    ++v2;
  }
  return v2;
}

countSetBits() is used to calculate the number of 1 in the binary form of the argument, so the number of 1 is odd to exit the inner do while loop. For the outer loop, the number v5 must be less than 0x10E47F4C575565, and notused() will record the previous v5 and cannot be reused.

In addition, since v6 - v5 > 0x20 (isprint(a2) and (v6 - v5) % 100 == a2) is required, it is obvious that the upper limit of v5 needs to be used in the construction. So that the value of v5 in 20 calculations can be determined, and the Serial Key and Public Key can be constructed by finding (v6, a2) that match the condition.

#include <stdio.h>

unsigned long long fib(unsigned long long x) {
    long long v5[4096];
    v5[0] = 0;
    v5[1] = 1;
    for (int i = 2; i <= x; i++)
        v5[i] = v5[i - 1] + v5[i - 2];
    return v5[x] / 100uLL;
}

long long countSetBits(long long x) {
    unsigned int v2 = 0;
    while (x) {
        x &= x - 1;
        v2++;
    }
    return v2;
}

int check_arm(unsigned long long x) {
    return (countSetBits(x) & 1) == 1;
}

void next(long long *v5) {
    do {
        --*v5;
    } while (!check_arm(*v5));
    return;
}

int main() {
    unsigned long long v7;
    long long v5 = 0x10E47F4C575565LL;
    int serial[105];
    int public[105];
    for (int i = 0; i < 20; i++){
        next(&v5);
        for (int j = 100; j < 0x1000; j++){
            long long tmp = (fib(j) - v5) % 100;
            // add constraints of fib(j) > 0x10E47F4C575565LL is better
            if (tmp > 0x20){
                serial[i] = j;
                public[i] = tmp;
                break;
            }
        }
    }
    printf("Public Key: ");
    for (int i = 0; i < 20; i++)
        printf("%c", public[i]);
    printf("\n");
    printf("Serial Key: ");
    for (int i = 0; i < 19; i++)
        printf("%03x-", serial[i]);
    printf("bss\n");
    return 0;
}
// Public Key: ;=>ABDGH#%&(+-.12479
// Serial Key: 066-066-066-066-066-066-066-066-064-064-064-064-064-064-064-064-064-064-064-bss
Built with Hugo
Theme Stack designed by Jimmy