Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Como distribuir um executável de forma segura?

Eu fiz um programa em C++ para automatizar uma tarefa de digitação usando o editor Dev-C++ 5.11, e compartilhei o executável para dois amigos junto com o código fonte, mas nos computadores de ambos teve consequências horríveis: em um, o computador simplesmente formatou, e no outro o sistema reiniciou forçadamente.

Eu não fiz o curso de C++, estava brincando com a linguagem localmente e tinha dado um resultado legal, por isso compartilhei com eles e sinto que fiz um erro de amador terrível.

Eu compilei o código na minha máquina e enviei pra eles, pensando que como era um executável nativo do windows ele funcionaria tranquilamente em outro computador com o mesmo sistema.

No final das contas, não houve grandes perdas para os dois, mas eu queria tirar essa dúvida:

Por quê isso aconteceu? Como eu poderia distribuir o executável evitando que um erro catastrófico acontecesse?

Desculpe se isso acabar sendo justamente abordado em algum dos cursos
3 respostas

Não uso Windows há mais de 15 anos. Nunca programei no Windows, apenas em plataformas UNIX, no entanto, nunca vi algo deste tipo ocorrer, nem em C, C++ ou assembly. Como Windows não é uma plataforma com a qual eu tenha familiaridade, posso estar enganado sobre alguns pontos, mas seguem algumas considerações:

  1. Dois computadores que tenham a mesma combinação de SO e ISA são capazes de executar um binário sem requerer recompilação. No entanto, a compatibilidade de versões é garantida apenas para trás (o Windows 10 consegue rodar aplicações de versões anteriores, o inverso não é verdadeiro). Lembre-se também que ao usar uma biblioteca compartilhada que não seja padrão, ou terá de instalá-la para o usuário, ou terá de notifica-lo sobre a necessidade de faze-lo. Do contrário o programa terá uma falha de carregamento em runtime, o que terminará prematuramente a aplicação.

  2. É verdade que o modelo de endereçamento do Windows está longe de ser bom, mas, em geral, cada programa é executado em um bloco de memória virtual segmentada, que isola sua aplicação das demais e segrega a área do kernel. Sua aplicação roda em modo de usuário, e fazer uma escalada de privilégios não é algo trivial, requer explorar vulnerabilidades de segurança do sistema. Corromper o sistema acidentalmente, mesmo o Windows, é muito difícil.

  3. Você pode acidentalmente apagar ou sobrescrever arquivos de usuário, mas não arquivos de administrador. Formatar um disco então nem se fala. Por meio de uma interface gráfica de usuário é simples, toda a complexidade da tarefa é abstraída, basta preencher alguns dados e clicar em um botão, entretanto, fazê-lo manualmente por meio de chamadas de sistema altamente específicas requer alto grau de conhecimento sobre a plataforma. Além disso, até onde sei, não existe meio de formatar a unidade em que o SO está instalado dentro do ambiente de execução.

Resumindo, ou você estava brincando com código malicioso, ou seus amigos estão lhe enganando. Agora, supondo que este problema de fato tenha ocorrido, o único meio de saber a causa é vendo o código.

Muito obrigado, sua explicação foi bem completa e meio que era o que eu estava esperando (talvez torcendo). Meu código não tinha nada de malicioso, ele só gera um arquivo de texto com separações de páginas cuja quantidade é definida pelo usuário, usando fopen.Com a sua resposta eu imagino que o problema pode ter sido nas

Sobre o ponto 1, verificando com eles vi que nós estávamos utilizando a mesma versão do windows, então não seria o caso, então provavelmente foi algo com as bibliotecas instaladas, e aí eu vacilei em não pedir para eles instalarem nada.Mas ainda assim, o programa deveria só fechar, como você disse...

Eu acho que a hipótese de estarem me enganando é bastante válida, mas se puder dar uma olhada no meu código eu agradeço! Vai que eu cometi um atentado à engenharia sem perceber.

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

int main ()
{

  setlocale(LC_ALL,"");

  FILE *arq;
  int numPaginas, result;
  char strNumPaginas[128];

  printf ("Gerador de páginas de capítulo v1.0\n\n");

  arq = fopen("capitulo.txt", "wt");  // Cria um arquivo de texto para gravação
  if (arq == NULL)
  {
      printf("Problemas na criação do arquivo\n");  // Mensagem de erro
      return 0;
  }

  result = fprintf(arq, "Tradutor: \nRevisor: \nEditor: \n\n\n\n"); // Escreve o cabeçalho no arquivo de texto
  if(result == EOF) // Retorna mensagem de erro caso algo dê errado
      printf("Erro na Gravação do cabecalho\n");

  printf ("Número de páginas do capítulo: "); // Digite um número inteiro e dê enter
  fgets (strNumPaginas, 128, stdin); // Recupera o número digitado pelo usuário
  numPaginas = atoi (strNumPaginas); // Converte o número recuperado, que é uma string, para int

  for(int x = 1; x <= numPaginas; x++) // Repete o loop pelo número de páginas digitadas
  {
    result = fprintf(arq,"* Página %d *\n\n\n\n", x); // Escreve o número da página no arquivo a cada repetição
    if(result == EOF)
      printf("Erro na Gravação\n"); 

  };

  printf ("Prontinho! Obrigado por usar o programa!\n\n");
  fclose(arq);

  return 0;
}
solução!

Fiz algumas alterações pontuais no código. Minhas habilidades em C estão um pouco enferrujadas, faz algum tempo que não programo com frequência na linguagem, por isso limitei-me a reproduzir a forma com que trataste os erros. Não vi qualquer problema grave em seu código, recomendo a leitura de https://www.tutorialspoint.com/cprogramming/c_error_handling.htm .

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

#define HEADER_MSG "Tradutor: \nRevisor: \nEditor: \n\n\n\n"
#define ERROR_MSG(x) "Erro na Gravação do " x "\n"
#define THE_PAGE "* Página %d *\n\n\n\n"
#define THE_CHAPTER "* Capítulo %d *\n\n\n\n"

#define BUFFER_SIZE 128

void printing_pages_on(FILE *const file, int chapterNumber) {
    static char buffer[BUFFER_SIZE] = "";

    if(fprintf(file, THE_CHAPTER, chapterNumber) == EOF)
        printf(ERROR_MSG("capítulo"));     

    printf("Número de páginas do capítulo %d: ", chapterNumber);
    fgets(buffer, BUFFER_SIZE, stdin);
    int numPaginas = atoi(buffer);

    for (int x = 1; x <= numPaginas; x++)
        if(fprintf(file, THE_PAGE, x) == EOF)
            printf(ERROR_MSG("número da página"));     
}

int main(int argc, char **argv) {
    FILE *arq;

    setlocale(LC_ALL,"");

    printf("Gerador de páginas de capítulo v1.0\n\n");

    if ((arq = fopen("capitulo.txt", "wt")) != NULL) {
        if(fprintf(arq, HEADER_MSG) == EOF)
            printf(ERROR_MSG("cabecalho"));

        const int CHAPTERS_NUMBER = (argc > 1) ? atoi(argv[1]) : 1;            
        for (int i = 1; i <= CHAPTERS_NUMBER; i++)
            printing_pages_on(arq, i);

        printf("Prontinho! Obrigado por usar o programa!\n\n");
        fclose(arq);
    } else {
        printf("Problemas na criação do arquivo\n");
    }

    return 0;
}

criei o script makefile abaixo, que funciona tanto no Linux quanto no Mac. Para Windows, você pode usar o WSL; ver https://docs.microsoft.com/pt-br/windows/wsl/install-win10:


CC=clang                        # poderia ser o gcc
CFLAGS=-Wall -O3 -std=c11       # habilita warnings, 
                                # seta otimizações para o nível mais alto,
                                # estabelece o uso do padrão ISO C11 

SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
EXE=prog

all: $(EXE)

$(EXE): $(OBJ)
        $(CC) $^ -o $@ $(CFLAGS)

%.o: %.c
        $(CC) -o $@ -c $< $(CFLAGS)

clean:
        @rm *.o

remove: clean
        @rm prog

para compilar o programa digitei make no terminal. Ao executar a aplicação, com ./prog 5, obtive algo como:

Gerador de páginas de capítulo v1.0

Número de páginas do capítulo 1: 10
Número de páginas do capítulo 2: 5
Número de páginas do capítulo 3: 25
Número de páginas do capítulo 4: 8
Número de páginas do capítulo 5: 20
Prontinho! Obrigado por usar o programa!

Mas ainda que seja interessante como exercício, este tipo de programa não é o mais indicado para ser feito em C, talvez uma linguagem de script, como Python ou mesmo Shell, seria mais indicada para esta tarefa. C é indicada para tarefas de alta performance, como motores gráficos, jogos, máquinas de busca, compiladores, sistemas operacionais, simuladores, etc. Em Python este mesmo programa ficaria assim:

def printing(chapters: int):
    try:
        with open("capitulo.txt", "w+") as file:
            def title(name: str, number: int):
                file.write("* %s %d *\n" % (name, number))

            file.write("Tradutor: \nRevisor: \nEditor: \n")
            for i in range(1, chapters + 1):
                pages = int(input("Número de páginas do capítulo %d: " % i))

                title("Capítulo", i)
                for j in range(1, pages + 1):
                    title("Página", j)

            print("Fim!")
    except IOError:
        print("Erro de operação em arquivo")


print("Gerador de páginas de capítulo v1.0");

printing(int(input("Quantos capítulos devo gerar? ")))