/*
 * TL;DR: Эксплуатация Format String Vulnerability (уязвимости форматной строки)
на Windows имеет три фундаментальных отличия от Linux. Во-первых, стандартный
Microsoft CRT не поддерживает позиционные аргументы (вида %44$n), поэтому
добираться до нужного указателя на стеке придется цепочкой из %p %p %p....
Во-вторых, спецификатор %n отключен в Windows по умолчанию из соображений
безопасности. В-третьих, из-за соглашения о вызовах x64 (RCX, RDX, R8, R9)
первые несколько %p выведут значения из регистров, а не со стека.

Ниже представлены адаптированные исходники для Windows и решение на базе твоего
модуля go_pwner. Уязвимый файл (fs3_win.c)

Чтобы таск можно было решить, нам необходимо искусственно разрешить
использование %n с помощью функции _set_printf_count_output(1). Без неё попытка
передать %n приведет к немедленному завершению процесса (Secure CRT ругнется на
инвалидный параметр). Я также переписал чтение на ReadFile(STD_INPUT_HANDLE),
чтобы таск легко биндился на порт через socat. */

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void setup() {
  // Отключение буферизации для работы по сети
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);

  // КРИТИЧНО ДЛЯ WINDOWS CTF: Включаем поддержку %n в Microsoft CRT.
  // Начиная с VS2015 этот спецификатор отключен по умолчанию для защиты от
  // сплойтов.
  _set_printf_count_output(1);
}

int main() {
  setup();

  char buf[32];
  char fmt_str[256];
  char *my_secret_value = "my secret value";
  DWORD bytesRead;
  HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);

  // Указатель выделяется на куче. Сам указатель лежит на стеке.
  int *print_flag = malloc(sizeof(int));
  *print_flag = 0;

  printf("Enter your format string: ");

  // Читаем полезную нагрузку
  ReadFile(hStdin, fmt_str, 255, &bytesRead, NULL);
  if (bytesRead > 0) {
    // Убираем перенос строки, если он есть, для чистоты вывода
    if (fmt_str[bytesRead - 1] == '\n')
      bytesRead--;
    if (fmt_str[bytesRead - 1] == '\r')
      bytesRead--;
    fmt_str[bytesRead] = '\0';
  }

  // УЯЗВИМОСТЬ
  // fmt_str летит в RCX, 0xdeadbeef летит в RDX
  printf(fmt_str, 0xdeadbeef);
  printf("\n");

  if (*print_flag) {
    HANDLE hFile = CreateFileA("flag.txt", GENERIC_READ, FILE_SHARE_READ, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile != INVALID_HANDLE_VALUE) {
      ReadFile(hFile, buf, 18, &bytesRead, NULL);
      buf[bytesRead] = '\0';

      // Пишем в stdout через WinAPI
      HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      WriteFile(hStdout, buf, bytesRead, &bytesRead, NULL);

      CloseHandle(hFile);
    } else {
      printf("flag.txt not found!\n");
    }
  }

  return 0;
}
