Aplicativo GUI menor que 1 kb

No lazer, eu me perguntava a possibilidade de criar um aplicativo com os seguintes requisitos:

  • pelo menos alguma função de aplicativo útil (ou seja, não é fictícia)
  • a presença de uma interface de janela
  • tamanho menor que 1 Kb

Em geral, o padrão de aplicação com tamanho de até 1 Kb é de 1k de introdução, que é uma espécie de demosceno. Na maioria das vezes, isso é escrito na inicialização do OpenGL por assembler e, em seguida, alimenta um shader, que faz o trabalho principal (desenha alguns fractais coloridos). Além disso, tudo isso é colhido pelo empacotador (por exemplo, crinkler).
Esses aplicativos são literalmente lambidos em um byte, seu desenvolvimento leva semanas e até meses.

Essa abordagem é muito dura, decidi não ir além da programação de aplicativos usual no WinAPI.

Nesse momento, uma venda de verão começou no Steam, onde os adultos foram convidados a abanar os alienígenas sabiamente com um mouse no navegador (sim, a Valve novamente faz jogos, como Gabe prometeu).
Isso me ofendeu profundamente e decidi tentar implementar a filtragem automática mais simples com configurações mínimas.

Necessário para caber em 1 KB:

  • criação e inicialização de interface
  • função de janela com manipuladores de eventos
  • lógica principal do aplicativo (criada nas funções GetAsyncKeyState e SendInput)

O aplicativo será criado no MSVC em C puro, sem assembler e SMS, e depois compactado com o empacotador de trituradores. Deve-se notar que o crinkler compacta os dados (especialmente rarefeitos) com muito mais eficiência do que o código (cerca de duas vezes). Portanto, tentaremos transferir o máximo de funcionalidade para os dados.

Começando com o clássico CreateWindow para cada elemento da janela, percebi que não podia caber no tamanho necessário.

Eu tive que procurar uma alternativa. E ela se tornou a função CreateDialogIndirect, criando um diálogo a partir da estrutura DLGTEMPLATE preenchida (consistindo em uma pilha DLGITEMTEMPLATE)

Para criação e preenchimento convenientes da estrutura, iniciei algumas macros como estas:

#define NUMCHARS(p) (sizeof(p)/sizeof((p)[0])) #define DLGCTL(a) struct{DWORD style; DWORD exStyle; short x; short y; short cx; short cy; WORD id; WORD sysClass; WORD idClass; WCHAR wszTitle[NUMCHARS(a)]; WORD cbCreationData;} #define RADIO(x,y,cx,cy,id,title) {WS_VISIBLE|WS_CHILD|BS_RADIOBUTTON, 0, (x)/2, (y)/2,\n (cx)/2,(cy)/2, id, 0xFFFF, 0x0080, title, 0} 

Agora você pode declarar e preencher a estrutura com os elementos da janela futura:

 static struct { DWORD style; DWORD dwExtendedStyle; WORD ccontrols; short x; short y; short cx; short cy; WORD menu; WORD windowClass; DLGCTL(LBL_BTN_LEFT) button_left; DLGCTL(LBL_BTN_MIDDLE) button_middle; DLGCTL(LBL_BTN_RIGHT) button_right; } Dlg = { WS_VISIBLE|WS_POPUP|WS_BORDER, 0, TOTAL_CONTROLS, 50/2, 50/2, WND_CX/2, WND_CY/2, 0, 0, RADIO(10, 0, 80, 30, IDC_BTN_LEFT, LBL_BTN_LEFT), RADIO(100, 0, 80, 30, IDC_BTN_MIDDLE, LBL_BTN_MIDDLE), RADIO(190, 0, 68, 30, IDC_BTN_RIGHT, LBL_BTN_RIGHT), }; 

Alimentamos a estrutura da função CreateDialogIndirect, e aqui está a janela resultante:



Como cabemos em 1 kb, não temos um manifesto, como todo o resto, e, portanto, também não há estilos visuais. Tudo é cinza e quadrado, como na juventude.

Mas ainda assim, evitamos, retirando o manifesto do shell32.dll e aplicando o contexto com base nele ao nosso aplicativo:

 static ACTCTX actCtx = {sizeof(ACTCTX), ACTCTX_FLAG_RESOURCE_NAME_VALID|ACTCTX_FLAG_SET_PROCESS_DEFAULT|ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID, "shell32.dll", 0, 0, tmp, (LPCTSTR)124, 0, 0}; GetSystemDirectory(tmp,sizeof(tmp)); LoadLibrary("shell32.dll"); ActivateActCtx(CreateActCtx(&actCtx),(ULONG_PTR*)&tmp); 

Já é elegante, moderno:



Conseguimos reduzir a função da janela para uma compacta:

 switch(msg) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BTN_LEFT: case IDC_BTN_MIDDLE: case IDC_BTN_RIGHT: input[0].mi.dwFlags = wParam; input[1].mi.dwFlags = (wParam<<1); CheckRadioButton(hWnd,IDC_BTN_LEFT,IDC_BTN_MIDDLE,wParam); break; case IDC_BTN_HOLD: case IDC_BTN_TRIGGER: trigger_mode = (wParam==IDC_BTN_TRIGGER); CheckRadioButton(hWnd,IDC_BTN_HOLD,IDC_BTN_TRIGGER,wParam); break; case IDC_EDIT_PERIOD: period = GetDlgItemInt(hWnd,IDC_EDIT_PERIOD,(BOOL*)&tmp[0],0); break; case IDC_BTN_EXIT: exit(0); } break; } return DefWindowProc(hWnd,msg,wParam,lParam); 

E então pensei que seria bom adicionar processamento de argumentos de linha de comando para que o usuário tivesse a oportunidade de começar com as configurações necessárias.

Por exemplo:

input.exe /L /T /P:20 /K:9 - 20 , / Tab

Mova a parte da funcionalidade para dentro dos dados e obtenha algo como:

 static unsigned int arg_to_cmd[] = {IDC_BTN_HOLD, 0, 0, IDC_EDIT_KEY, IDC_BTN_LEFT, IDC_BTN_MIDDLE, 0, 0, IDC_EDIT_PERIOD, 0, IDC_BTN_RIGHT, 0, IDC_BTN_TRIGGER}; i = (char*)GetCommandLine(); while(*i) { if (*(i++)=='/')//looking for argument switch(*i) { case 'L': case 'M': case 'R': case 'H': case 'T': SendMessage(hWnd,WM_COMMAND,arg_to_cmd[*i-'H'],0);//send button command break; case 'P': case 'K': t = atoi(i+2); SetDlgItemInt(hWnd,arg_to_cmd[*i-'H'],t,0); if(*i=='P') period = t; else key = t; break; } } 

O manipulador saiu muito pequeno. Naturalmente, não há verificações e proteções contra entrada incorreta, apenas o mínimo necessário.

Agora a funcionalidade principal (eu coloquei em um fluxo separado):

 while(1) { key_state = (GetAsyncKeyState(key)>>1); if (trigger_mode) { if ((key_state)&&(key_state!=prev_key_state)) active^= 1; prev_key_state = key_state; } else active = key_state; if (active) SendInput(2,(LPINPUT)&input,sizeof(INPUT)); Sleep(period); } 


Pressionamos o crinkler'om do arquivo obj resultante - a saída é 973 bytes.

Desses, os dados ocupam 163 bytes (taxa de compressão 33,1%), código - 517 bytes (taxa de compressão 68,9%).

É possível encolher e mais forte (outros 50 bytes), mas o objetivo já foi alcançado. Até 51 bytes sobressalentes permaneceram.

Os requisitos iniciais ao longo do caminho foram adicionados:

  • processando argumentos de linha de comando
  • exibindo uma janela com estilos visuais

Em alguns lugares, o código parece muito torto. Isso é porque eu escrevi torto. E em alguns lugares isso permitiu economizar espaço.

Certamente você pode criar mais algumas maneiras de reduzir o tamanho do aplicativo, mas não drasticamente (acho que o byte é 50).

Resultado:

Agora você pode chamar alienígenas no modo automático com dano selvagem por segundo.

É possível criar aplicativos ultracompactos com funcionalidade útil realmente usada e uma interface de janela.

Novidade:
Zero Eu montei um monte de técnicas / desenvolvimentos.

Conveniência:
Inútil, divertido para.

Código fonte
Binário

Críticas, desejos, sugestões aceitas, admiração, indignação.

Source: https://habr.com/ru/post/pt416135/


All Articles