[ENG] nolibc - reversing

mntly·2024년 8월 27일
0

CTF

목록 보기
4/9

This post explains the process of reversing about the pwnable problem, nolibc from the Project SEKAI CTF 2024.
Total reversed source code is located in Github below.

Reversed Source Code

Everything is stripped.

Ferthermore, it's hard to solve strip because executable file doesn't use libc.

Why call all the function calls use syscall

[Fig 1] Stored syscall numbers in .data segment


printf(unsigned __int8* a1)

__int64 __fastcall printf(unsigned __int8 *a1)
{
  __int64 result; // rax

  while ( 1 ) {
    result = *a1;
    if ( !(_BYTE)result )
      break;
    __asm { syscall; LINUX - }
    ++a1;
  }
  return result;
}

[Fig 2] Assembly of printf function - 1

[Fig 3] Assembly of printf function - 2


[Fig 3] has information of syscall which is called from printf function.

These informations are summarized in the table below.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
write0x011rdi (a1)1

printf(unsigned __int8* a1) function
: Print the string indicated by received a1 one byte at a time until Null terminator.

printfn(unsigned __int8 *a1)

__int64 __fastcall printfn(unsigned __int8 *a1)
{
  __int64 result; // rax

  printf(a1);
  result = syscall_write;
  __asm { syscall; LINUX - }
  return result;
}

[Fig 4] Assembly of printfn function

[Fig 5] The value stored at unk_3000


[Fig 4] has information of syscall which is called from printfn function.

These informations are summarized in the table below.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
write0x011address of '\n'1

printfn(unsigned __int8* a1) function
: Print the string indicated by received a1 one byte at a time until Null terminator.
Then add the '\n' at the end

read_Helper(__int64 a1, signed int length)

__int64 __fastcall read_Helper(__int64 a1, signed int length)
{
  char v3; // [rsp+1Bh] [rbp-31h] BYREF
  __int64 v4; // [rsp+1Ch] [rbp-30h]
  __int64 v5; // [rsp+24h] [rbp-28h]
  char *v6; // [rsp+2Ch] [rbp-20h]
  __int64 v7; // [rsp+34h] [rbp-18h]
  __int64 v8; // [rsp+3Ch] [rbp-10h]
  unsigned int i; // [rsp+48h] [rbp-4h]

  for ( i = 0; (int)i < length; ++i ) {
    v8 = syscall_read;                          // 0
    v7 = 0LL;
    v6 = &v3;
    v5 = 1LL;
    __asm { syscall; LINUX - }                  // read(0, v6, 1)
    v4 = syscall_read;
    if ( v3 == '\n' ) {                         // enter
      *(_BYTE *)((int)i + a1) = 0;
      return i;
    }
    *(_BYTE *)(a1 + (int)i) = v3;
  }
  return i;
}

[Fig 6] Assembly of read_Helper function - 1

[Fig 7] Assembly of read_Helper function - 2

[Fig 8] Assembly of read_Helper function - 3


[Fig 7] has information of syscall which is called from read_Helper function.

These informations are summarized in the table below.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
read0x000address of v3 (rbp-0x31)1

read_Helper(__int64 a1, signed int length) 함수의 기능
: Read input one byte at a time up to the length and write it to the memory pointed to by a1.

If '\n' is received within the length limit, convert the '\n' to a NULL terminator, and end the input.
If '\n' isn't received, end the input without adding a NULL terminator.

After the input ends, return the length of the received string.

custom_scanf()

__int64 custom_scanf()
{
  char v1[36]; // [rsp+0h] [rbp-30h] BYREF
  int v2; // [rsp+24h] [rbp-Ch]
  int i; // [rsp+28h] [rbp-8h]
  unsigned int v4; // [rsp+2Ch] [rbp-4h]

  v4 = 0;
  v2 = read_Helper((__int64)v1, 32);
  for ( i = 0; i < v2; ++i ) {
    if ( v1[i] <= 0x2F || v1[i] > 0x39 )        // only '1' ~ '9'
      return 0xFFFFFFFFLL;
    v4 = 10 * v4 + v1[i] - '0';
  }
  return v4;
}

custom_scanf() function
: Receive 32 byte length string that consists only of decimal numbers,
return the integer converted from the input string

strlen_includeNULL(__int64 a1)

__int64 __fastcall strlen_includeNULL(__int64 a1)
{
  unsigned int i; // [rsp+14h] [rbp-4h]

  for ( i = 0; *(_BYTE *)((int)i + a1); ++i )
    ;
  return i;
}

strlen_includeNULL(__int64 a1) functions
: Return the length of the string pointed by received a1.
This function calculates the length of the string including the NULL terminator.

strcmp(int64 a1, int64 a2)

_BOOL8 __fastcall strcmp(__int64 a1, __int64 a2)
{
  int len_a1; // [rsp+18h] [rbp-8h]
  int i; // [rsp+1Ch] [rbp-4h]

  len_a1 = strlen_includeNULL(a1);
  if ( len_a1 != (unsigned int)strlen_includeNULL(a2) )
    return 0LL;
  for ( i = 0; i < len_a1 && *(_BYTE *)(i + a1) == *(_BYTE *)(i + a2); ++i )
    ;
  return !*(_BYTE *)(i + a1) && !*(_BYTE *)(i + a2) && len_a1 == i;
}

strcmp(__int64 a1, __int64 a2) function
: Determine whether two strings pointed by received a1 and a2 are equals and return the result.

If two strings are the same then return 1, if not return 0.

exit()

void __noreturn exit()
{
  __asm { syscall; LINUX - }
}

[Fig 9] Assembly of exit function


[Fig 9] has information of syscall which is called from exit function.

These informations are summarized in the table below.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
exit0x3c0?????0

exit() function
: Terminate the program

printInt(int a1)

__int64 __fastcall printInt(int a1)
{
  int v1; // eax
  int v2; // eax
  int v3; // eax
  int v5; // [rsp+Ch] [rbp-34h]
  unsigned __int8 v6[35]; // [rsp+10h] [rbp-30h] BYREF
  unsigned __int8 v7; // [rsp+33h] [rbp-Dh]
  int v8; // [rsp+34h] [rbp-Ch]
  int i; // [rsp+38h] [rbp-8h]
  int v10; // [rsp+3Ch] [rbp-4h]

  v5 = a1;
  v10 = 0;
  if ( a1 ) {
    if ( a1 < 0 ) {
      v2 = v10++;
      v6[v2] = 45;
      v5 = -a1;
    }
    v8 = v10;
    while ( v5 ) {
      v3 = v10++;
      v6[v3] = (char)v5 % 10 + 48;
      v5 /= 10;
    }
    for ( i = v8; i < v10 / 2; ++i ) {
      v7 = v6[i];
      v6[i] = v6[v10 - i - 1];
      v6[v10 - i - 1] = v7;
    }
  } else {
    v1 = v10++;
    v6[v1] = 48;
  }
  v6[v10] = 0;
  return printf(v6);
}

printInt(int a1) 함수의 기능
: Convert received integer, a1, to string and print it.

strstr(int64 a1, int64 a2)

__int64 __fastcall strstr(__int64 a1, __int64 a2)
{
  int j; // [rsp+18h] [rbp-8h]
  int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; *(_BYTE *)(i + a1); ++i ) {
    for ( j = 0; *(_BYTE *)(j + a2) && *(_BYTE *)(i + j + a1) == *(_BYTE *)(j + a2); ++j )
      ;
    if ( !*(_BYTE *)(j + a2) )
      return 1LL;
  }
  return 0LL;
}

strstr(__int64 a1, __int64 a2) function
: Determine whether the strings pointed by received a2 has the string pointed by received a1 and return the result.

If the string pointed by a2 has the string pointed by a1 then return 1, if not return 0.

open_write_close()

// (filename, v2, v6)
__int64 open_write_close()
{
  __asm { syscall; LINUX - }
  if ( syscall_open < 0 )
    return 0xFFFFFFFFLL;
  __asm
  {
    syscall; LINUX -
    syscall; LINUX -
  }
  return (unsigned int)syscall_write;
}

[Fig 10] Assembly of open_write_close function - 1

[Fig 11] Assembly of open_write_close function - 2

[Fig 12] Assembly of open_write_close function - 3

[Fig 13] Assembly of open_write_close function - 4


[Fig 11], [Fig 12], [Fig 13] has information of 3 syscalls which are called from open_write_close function.

These informations are summarized in the table below in the order of calls.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)return
open0x02rdi (filename)0x41 (O_CREATO_WRONLY)0x1b6 (0u0666 : rw-rw-rw-)

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
write0x01fd (eax)rsi (v2)edx

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
close0x03fd00

open_write_close() function
: Open filename file,
Read v6 byte from the memory pointed by v2 and write it into filename file,
Close filename file.

open_read_close()

// (path, buf, 0x7FFF)
__int64 open_read_close()
{
  __asm { syscall; LINUX - }
  if ( syscall_open < 0 )                       // read only로 path 파일 열기
    return 0xFFFFFFFFLL;
  __asm
  {
    syscall; LINUX -                            // 읽은 내용을 buf에 넣기
    syscall; LINUX -
  }                                             // close
  return (unsigned int)syscall_read;
}

[Fig 14] Assembly of open_read_close function - 1

[Fig 15] Assembly of open_read_close function - 2

[Fig 16] Assembly of open_read_close function - 3

[Fig 17] Assembly of open_read_close function - 4


[Fig 15], [Fig 16], [Fig 17] has information of 3 syscalls which are called from open_read_close function.

These informations are summarized in the table below in the order of calls.

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)return
open0x02rdi (path)00fd (rax)

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
read0x00fd (rax)rsi (buf)edx (0x7FFF)

syscallraxarg0 (rdi)arg1 (rsi)arg2 (rdx)
close0x03fd00

open_read_close() function
: Open path file,
Read 0x7FFF (edx) byte from this file and read this to the memory pointed by buf,
Close path file.

malloc_guess(int size)

// 1 <= size <= 257 or 32 or 0x7FFF or large
int *__fastcall malloc_guess(int size)
{
  __int64 v2; // [rsp+4h] [rbp-20h]
  signed int aligned_size; // [rsp+10h] [rbp-14h]
  __int64 v4; // [rsp+14h] [rbp-10h]
  __int64 v5; // [rsp+1Ch] [rbp-8h]

  if ( !size )
    return 0LL;
  aligned_size = (size + 15) & 0xFFFFFFF0;
  v5 = first_freed_heap;
  v4 = 0LL;
  while ( 1 ) {
    if ( !v5 )
      return 0LL;
    if ( aligned_size <= *(_DWORD *)v5 )
      break;
    v4 = v5;
    v5 = *(_QWORD *)(v5 + 8);
  }
  if ( *(int *)v5 >= (unsigned __int64)(aligned_size + 16LL) ) {
    v2 = aligned_size + 16LL + v5;
    *(_DWORD *)v2 = *(_DWORD *)v5 - aligned_size - 16;
    *(_QWORD *)(v2 + 8) = *(_QWORD *)(v5 + 8);
    *(_QWORD *)(v5 + 8) = v2;
    *(_DWORD *)v5 = aligned_size;
  }
  if ( v4 )
    *(_QWORD *)(v4 + 8) = *(_QWORD *)(v5 + 8);
  else
    first_freed_heap = *(_QWORD *)(v5 + 8);
  return (int *)(v5 + 16);
}

We can find the code below while analyzing the decompiled binary.

sub_12C8("Enter a string: ");
v1 = sub_1031((unsigned int)(v2 + 1));
if ( !v1 )
{
	sub_1322("Failed to allocate memory");
	sub_1322(&unk_3124);
    sub_18E6(&unk_3124);
}

In the above code, we can guess if the return value of the function, sub_1031 is 0 then this binary prints Failed to allocate memory. (If you understand at least 2 functions related to print (printf, printfn) then you can confidence sub_1322 prints given string.)

At this point, we can guess that the function, sub_1031 is related to allocating the memory same as the function, malloc. If you analyze the process of this function then you can be confident that sub_1031 performs the malloc.

I wrote the process of this function at [English] Project SEKAI CTF 2024 - nolibc : Attack Surface in malloc_guess.

In addition, you can find the function performs the free, if you do cross-reference to parameter on sub_1031 or the function called after sub_1031 is called from the function which calls sub_1031.

Additionally, by cross-referencing the parameters passed on to sub_1031, or analyzing the functions called after sub_1031 is called, we can also find the functions that function as free.

free_guess(unsigned __int64 ptr)

int *__fastcall free_guess(unsigned __int64 ptr)
{
  int *result; // rax
  unsigned __int64 free_heap_header; // [rsp+18h] [rbp-18h]
  unsigned __int64 prev_heap_header; // [rsp+20h] [rbp-10h]
  unsigned __int64 v4; // [rsp+28h] [rbp-8h]

  if ( ptr ) {
    result = (int *)&unk_5000;
    if ( ptr >= (unsigned __int64)&unk_5000 ) {
      result = &syscall_read;
      if ( ptr < (unsigned __int64)&syscall_read ) {
        free_heap_header = ptr - 16;
        v4 = first_freed_heap;
        prev_heap_header = 0LL;
        while ( v4 && v4 < free_heap_header ) {
	        // go to the heap right before the ptr heap
          prev_heap_header = v4;
          v4 = *(_QWORD *)(v4 + 8);
        }
        if ( prev_heap_header ) {
          *(_QWORD *)(free_heap_header + 8) = *(_QWORD *)(prev_heap_header + 8);
          *(_QWORD *)(prev_heap_header + 8) = free_heap_header;
        } else {
	        // modify freed heap, connect previous freed heap
          *(_QWORD *)(free_heap_header + 8) = first_freed_heap;
          first_freed_heap = ptr - 16;
        }
        return (int *)merge_guess();
      }
    }
  }
  return result;
}

merge_guess()

__int64 merge_guess()
{
  __int64 result; // rax
  int v1; // [rsp+0h] [rbp-Ch]
  int *heap_header; // [rsp+4h] [rbp-8h]

  result = first_freed_heap;
  heap_header = (int *)first_freed_heap;
  while ( heap_header ) {
    result = *((_QWORD *)heap_header + 1);
    if ( !result )
      break;
    if ( (int *)((char *)heap_header + *heap_header + 16) == *((int **)heap_header + 1) ) {
      *heap_header += **((_DWORD **)heap_header + 1) + 16;
      result = (__int64)heap_header;
      *((_QWORD *)heap_header + 1) = *(_QWORD *)(*((_QWORD *)heap_header + 1) + 8LL);
    } else {
      result = *((_QWORD *)heap_header + 1);
      heap_header = (int *)result;
    }
  }
  if ( heap_header ) {
    v1 = (_DWORD)&unk_10000 - ((char *)heap_header - (char *)&unk_5000);
    result = (unsigned int)*heap_header;
    if ( v1 > (int)result ) {
      result = (__int64)heap_header;
      *heap_header = v1 - 16;
    }
  }
  return result;
}

The process pf above two functions

  • Ensure that the previously freed chunk (prev_heap_header), which is located at a lower memory address than the current chunk to be freed (free_heap_header), points to the current chunk.
  • Additionally, update the current chunk (free_heap_header) to point to the chunk that the previously freed chunk (prev_heap_header) was pointing to.
  • After that, call the merge_guess function to merge adjacent freed chunks.

[Fig 18] The process of free_guess & merge_guess example - 1

[Fig 19] The process of free_guess & merge_guess example - 2

Function that run at the start of the program (start)

void __noreturn start()
{
  int choice_login_reg_exit; // [rsp+8h] [rbp-8h]
  int choice_UserMenu; // [rsp+Ch] [rbp-4h]

  init();
  while ( 1 ) {
    printfn((__int64)"Welcome to String Storage!");
    printfn((__int64)"Please login or register an account to continue :)");
    printfn((__int64)&enter);                   // 개행?
    while ( UserIndex == -1 ) {
      printfn((__int64)"1. Login");
      printfn((__int64)"2. Register");
      printfn((__int64)"3. Exit");
      printf("Choose an option: ");
      choice_login_reg_exit = custom_scanf();
      printfn((__int64)&enter);                 // 개행?
      if ( choice_login_reg_exit == 1 ) {
        login();
      } else if ( choice_login_reg_exit == 2 ) {
        register();
      } else {
        if ( choice_login_reg_exit == 3 )
          exit();
        printfn((__int64)"Invalid option");
      }
      printfn((__int64)&enter);
    }
    printf("Welcome to String Storage, ");
    printf(*(unsigned __int8 **)UserInfo_struct_list[UserIndex]);
    printfn((__int64)"!");
    printfn((__int64)&enter);
    while ( 1 ) {
      printfn((__int64)"1. Add string");
      printfn((__int64)"2. Delete string");
      printfn((__int64)"3. View strings");
      printfn((__int64)"4. Save to File");
      printfn((__int64)"5. Load from File");
      printfn((__int64)"6. Logout");
      printf("Choose an option: ");
      choice_UserMenu = custom_scanf();
      printfn((__int64)&enter);
      switch ( choice_UserMenu ) {
        case 1:
          AddString();
          goto LABEL_26;
        case 2:
          DelString();
          goto LABEL_26;
        case 3:
          ViewString();
          goto LABEL_26;
        case 4:
          SaveFile();
          goto LABEL_26;
        case 5:
          LoadFile();
          goto LABEL_26;
      }
      if ( choice_UserMenu == 6 )
        break;
      printfn((__int64)"Invalid option");
LABEL_26:
      printfn((__int64)&enter);
    }
    UserIndex = -1;
  }
}

init()

void *init() {
  void *result; // rax

  first_freed_heap = (__int64)&unk_5000;
  unk_5000 = (_DWORD)&unk_10000;
  result = &unk_5000;
  *((_QWORD *)&unk_5000 + 1) = 0LL;
  return result;
}

login()

__int64 login()
{
  __int64 password; // [rsp+8h] [rbp-18h]
  __int64 name; // [rsp+10h] [rbp-10h]
  int i; // [rsp+1Ch] [rbp-4h]

  printf("Username: ");
  name = malloc_guess(64LL);
  if ( name ) {
    read_Helper(name, 64);
    if ( (unsigned int)strlen_includeNULL(name) ) {
      printf("Password: ");
      password = malloc_guess(64LL);
      if ( password ) {
        read_Helper(password, 64);
        if ( (unsigned int)strlen_includeNULL(password) ) {
          if ( NumUser ) {
            for ( i = 0; i < NumUser; ++i ) {
              if ( (unsigned int)strcmp(**((_QWORD **)&UserInfo_struct_list + i), name)// UserInfo_struct = &name; &password
                && (unsigned int)strcmp(*(_QWORD *)(*((_QWORD *)&UserInfo_struct_list + i) + 8LL), password) ) {
                UserIndex = i;                  // 찾은 User의 index
              }
            }
            if ( UserIndex == -1 )
              printfn((__int64)"Invalid username or password");
            else
              printfn((__int64)"Logged in successfully!");
            free_guess(name);
            return free_guess(password);
          } else {
            return printfn((__int64)"No users registered");
          }
        } else {
          printfn((__int64)"Invalid password");
          free_guess(password);
          return login();
        }
      } else {
        printfn((__int64)"Invalid password");
        free_guess(0LL);
        return login();
      }
    } else {
      printfn((__int64)"Invalid username");
      free_guess(name);
      return login();
    }
  } else {
    printfn((__int64)"Invalid username");
    free_guess(0LL);
    return login();
  }
}

register()

__int64 register()
{
  struct_UserInfo_struct *UserInfo_struct; // [rsp+8h] [rbp-18h]
  int *password; // [rsp+10h] [rbp-10h]
  int *name; // [rsp+18h] [rbp-8h]

  if ( NumUser > 0 )
    return printfn((__int64)"You can only register one account!");
  printf("Username: ");
  name = malloc_guess(32);
  if ( name ) {
    read_Helper((__int64)name, 32);
    if ( (unsigned int)strlen_includeNULL((__int64)name) ) {
      printf("Password: ");
      password = malloc_guess(32);
      if ( password ) {
        read_Helper((__int64)password, 32);
        if ( (unsigned int)strlen_includeNULL((__int64)password) ) {
          UserInfo_struct = (struct_UserInfo_struct *)malloc_guess(0x4010);
          UserInfo_struct->UserName = name;
          UserInfo_struct->PassWord = password;
          UserInfo_struct->strIndex = 0;
          UserInfo_struct_list[NumUser++] = UserInfo_struct;
          return printfn((__int64)"User registered successfully!");
        } else {
          printfn((__int64)"Invalid password");
          free_guess((unsigned __int64)password);
          return register();
        }
      } else {
        printfn((__int64)"Invalid password");
        free_guess(0LL);
        return register();
      }
    } else {
      printfn((__int64)"Invalid username");
      free_guess((unsigned __int64)name);
      return register();
    }
  } else {
    printfn((__int64)"Invalid username");
    free_guess(0LL);
    return register();
  }
}
  • Allocate 32 byte memory to store UserName.
  • Allocate 32 byte memory to store PassWord.
  • Allocate 0x4010 byte to store infromation about new User. ⇒ Generate UserInfo_struct which stores information about User.

[Fig 20] UserInfo_struct structure

+) strIndex stores the number of strings stored in the current UserInfo_struct - 1.

AddString()

__int64 AddString()
{
  int *strbuf; // [rsp+0h] [rbp-10h]
  int strlength; // [rsp+Ch] [rbp-4h]

  if ( *(int *)(UserInfo_struct_list[UserIndex] + 16LL) > 2046 )
    return printfn((__int64)"You have reached the maximum number of strings");
  printf("Enter string length: ");
  strlength = custom_scanf();
  if ( strlength > 0 && strlength <= 256 ) {
    printf("Enter a string: ");
    strbuf = malloc_guess(strlength + 1);
    if ( !strbuf ) {
      printfn((__int64)"Failed to allocate memory");
      printfn((__int64)&enter);
      exit();
    }
    read_Helper((__int64)strbuf, strlength + 1);
    *(_QWORD *)(UserInfo_struct_list[UserIndex]
              + 8 * ((int)(*(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL))++ + 2LL)
              + 8) = strbuf;
    return printfn((__int64)"String added successfully!");
  } else {
    printfn((__int64)"Invalid length");
    return printfn((__int64)&enter);
  }
}
  • Receive an integer between 1 and 256, allocate memory of that length (+1) and store the input string in the allocated memory.

  • Then, store the address of the saved string in the UserInfo_struct structure.

  • The string can have a maximum length of 2047 (0x7FF) characters.

DelString()

__int64 DelString()
{
  int del_Index; // [rsp+8h] [rbp-8h]
  int i; // [rsp+Ch] [rbp-4h]

  if ( *(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL) ) {
    printf("Enter the index of the string to delete: ");
    del_Index = custom_scanf();
    if ( del_Index >= 0 && del_Index < *(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL) ) {
      free_guess(*(_QWORD *)(UserInfo_struct_list[UserIndex] + 8 * (del_Index + 2LL) + 8));
      for ( i = del_Index; i < *(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL) - 1; ++i )
        *(_QWORD *)(UserInfo_struct_list[UserIndex] + 8 * (i + 2LL) + 8) = *(_QWORD *)(UserInfo_struct_list[UserIndex]
                                                                                     + 8 * (i + 1 + 2LL)
                                                                                     + 8);
      --*(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL);
      return printfn((__int64)"String deleted successfully!");
    } else {
      printfn((__int64)"Invalid index");
      return printfn((__int64)&enter);
    }
  } else {
    printfn((__int64)"No strings to delete");
    return printfn((__int64)&enter);
  }
}
  • Receive an index, delete the string stored at that index, and free that memory space.

  • Then, iterate through the UserInfo_struct structure and shift the string pointers forward by one position to fill the space left by the deleted string pointer.

ViewString()

__int64 ViewString()
{
  __int64 result; // rax
  int i; // [rsp+Ch] [rbp-4h]

  if ( *(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL) ) {
    for ( i = 0; ; ++i ) {
      result = *(unsigned int *)(UserInfo_struct_list[UserIndex] + 16LL);
      if ( i >= (int)result )
        break;
      printf("String ");
      printInt((unsigned int)i);
      printf(": ");
      printfn(*(_QWORD *)(UserInfo_struct_list[UserIndex] + 8 * (i + 2LL) + 8));
    }
  } else {
    printfn((__int64)"No strings to view");
    return printfn((__int64)&enter);
  }
  return result;
}

SaveFile()

int *SaveFile()
{
  int v1; // [rsp+8h] [rbp-28h]
  int *buf; // [rsp+10h] [rbp-20h]
  int *filename; // [rsp+18h] [rbp-18h]
  int j; // [rsp+24h] [rbp-Ch]
  int i; // [rsp+28h] [rbp-8h]
  unsigned int v6; // [rsp+2Ch] [rbp-4h]

  printf("Enter the filename: ");
  filename = malloc_guess(32);
  if ( filename
    && (read_Helper((__int64)filename, 32), (unsigned int)strlen_includeNULL((__int64)filename))
    && !(unsigned int)strstr((__int64)filename, (__int64)"flag") ) {
    buf = malloc_guess(0x7FFF);
    if ( !buf ) {
      printfn((__int64)"Failed to allocate memory");
      printfn((__int64)&enter);
      exit();
    }
    v6 = 0;
    for ( i = 0; i < *(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL); ++i ) {
      v1 = strlen_includeNULL(*(_QWORD *)(UserInfo_struct_list[UserIndex] + 8 * (i + 2LL) + 8));
      for ( j = 0; j < v1; ++j )
        *((_BYTE *)buf + (int)v6++) = *(_BYTE *)(*(_QWORD *)(UserInfo_struct_list[UserIndex] + 8 * (i + 2LL) + 8) + j);
      *((_BYTE *)buf + (int)v6++) = '\n';
    }
    if ( (int)open_write_close(filename, buf, v6) >= 0 )// write string in buf to file filename {
      printfn((__int64)"Strings saved to file successfully!");
      return free_guess((unsigned __int64)buf);
    } else {
      printfn((__int64)"Failed to write file");
      return (int *)printfn((__int64)&enter);
    }
  } else {
    printfn((__int64)"Invalid filename");
    return (int *)printfn((__int64)&enter);
  }
}
  • Receive a file name with a size of 32 bytes and write the strings stored so far to that file.

  • If the file name contains the string "flag", immediately exit the SaveFile function.

  • Before writing to the file, allocate memory of size 0x7FFF bytes (buf) to be used for file writing.

    • If the file name passes the "flag" filtering but fails to open or write, do not free the allocated memory space (buf).

    • If the file name passes the "flag" filtering and the file is successfully opened and written to, free the allocated memory space (buf).

LoadFile()

int *LoadFile()
{
  int *v1; // [rsp+0h] [rbp-30h]
  int read_length; // [rsp+Ch] [rbp-24h]
  int *buf; // [rsp+10h] [rbp-20h]
  int *path; // [rsp+18h] [rbp-18h]
  int i; // [rsp+20h] [rbp-10h]
  int v6; // [rsp+24h] [rbp-Ch]
  int v7; // [rsp+28h] [rbp-8h]
  int v8; // [rsp+2Ch] [rbp-4h]

  printf("Enter the filename: ");
  path = malloc_guess(32);
  if ( path
    && (read_Helper((__int64)path, 32), (unsigned int)strlen_includeNULL((__int64)path))
    && !(unsigned int)strstr((__int64)path, (__int64)"flag") ) {
    buf = malloc_guess(0x7FFF);
    if ( !buf ) {
      printfn("Failed to allocate memory");
      printfn((unsigned __int8 *)&enter);
      exit();
    }                                           // (path, buf, 0x7FFF)
    read_length = open_read_close();            // 정상적으로 파일 열림 => 읽은 byte 수 / 비정상적 => -1
    if ( read_length >= 0 ) {
      v8 = 0;
      v7 = 0;
      while ( v8 < read_length ) {
        v6 = 0;
        while ( *((_BYTE *)buf + v8) != '\n' ) {
          ++v6;
          ++v8;
        }
        v1 = malloc_guess(v6 + 1);
        if ( !v1 ) {
          printfn("Failed to allocate memory");
          printfn((unsigned __int8 *)&enter);
          exit();
        }
        for ( i = 0; i < v6; ++i )
          *((_BYTE *)v1 + i) = *((_BYTE *)buf + v7++);
        *((_BYTE *)v1 + v6) = 0;
        *(_QWORD *)(UserInfo_struct_list[UserIndex]
                  + 8 * ((int)(*(_DWORD *)(UserInfo_struct_list[UserIndex] + 16LL))++ + 2LL)
                  + 8) = v1;
        ++v8;
        ++v7;
      }
      printfn("Strings loaded from file successfully!");
      return free_guess((unsigned __int64)buf);
    } else {
      printfn("Failed to read file");
      return (int *)printfn((unsigned __int8 *)&enter);
    }
  } else {
    printfn("Invalid filename");
    return (int *)printfn((unsigned __int8 *)&enter);
  }
}
  • Receive a file name with a size of 32 bytes and store the contents of that file in the UserInfo_struct.

  • If the file name contains the string "flag", immediately exit the LoadFile function.

  • Before storing the file contents, allocate memory of size 0x7FFF bytes (buf) to be used for reading the file.

    • If the file name passes the "flag" filtering but fails to open or read, do not free the allocated memory space (buf).

    • If the file name passes the "flag" filtering and the file is successfully opened and read, free the allocated memory space (buf).

0개의 댓글

관련 채용 정보