GUI application less than 1 KB

At leisure, I wondered about the possibility of creating an application, with the following requirements:

  • at least some useful application function (that is, not a dummy)
  • window interface
  • size less than 1 KB

In general, the benchmark application with a size of up to 1 Kb is 1k intro, which is a kind of demostsen. Most often, this is an assembler initialization of OpenGL followed by feeding the shader to it, which performs the main work (renders some colored fractals). Plus, all this is reaped by the packer (for example crinkler).
These applications are literally licked to a byte, their development takes weeks and even months.

This approach is too harsh, I decided not to go beyond the usual application programming on WinAPI.

At this time, a summer sale started on Steam, where adults were supposed to awfully shut up aliens in the browser (yes, Valve makes games again, as promised by Gabe).
This offended me to the depths of my soul, and I decided to try to implement the simplest autoclicker with minimal settings.

Required to fit in 1 KB:

  • interface creation and initialization
  • window function with event handlers
  • main application logic (built on the GetAsyncKeyState and SendInput functions)

The application will be created in MSVC on pure C without assembler and SMS, and then compressed with the crinkler packer. It should be noted that the data (especially sparse) crinkler compresses much more efficiently than the code (about two times). Therefore, we will try to transfer the maximum of the functional inside the data.

Starting with the classic CreateWindow for each window element, I realized that I didn’t fit into the required size.

I had to look for an alternative. And it was the CreateDialogIndirect function, which creates a dialog from the filled DLGTEMPLATE structure (consisting of the DLGITEMTEMPLATE heap).

For easy creation and filling in the structure, I started a few macros like these:

#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}

Now you can declare and fill the structure with elements of the future window:

staticstruct
{
	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),
};

We feed the structure of the CreateDialogIndirect function, and here’s the resulting window:



Since we fit into 1 kb, we don’t have a manifesto, and so on, and therefore also no visual styles. All gray and square, as in youth.

But we will still twist, pulling the manifest from shell32.dll and applying a context based on it to our application:

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);

Already stylish, fashionable:



We managed to shrink the window function to a rather compact one:

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);

And then I thought it would be good to add processing of command line arguments so that the user could run with the necessary settings.

For example:

input.exe /L /T /P:20 /K:9 - кликать левой кнопкой мыши каждые 20 мсек, режим включается/выключается клавишей Tab

We transfer some of the functionality inside the data and get something like:

staticunsignedint 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 argumentswitch(*i)
    {
      case'L':
      case'M':
      case'R':
      case'H':
      case'T':
        SendMessage(hWnd,WM_COMMAND,arg_to_cmd[*i-'H'],0);//send button commandbreak;
      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;
    }
  }

The handler came out quite small. Naturally, no checks and protection against incorrect input, only the required minimum.

Now the main functionality (I brought it to a separate thread):

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);
}


We press the resulting obj-file crinkler'om - output 973 bytes.

Of these, 163 are bytes of data (compression ratio 33.1%), code - 517 bytes (compression ratio 68.9%).

You can shrink and more (even byte 50), but the goal and so achieved. There are even 51 spare bytes left.

To the initial requirements in the course were added:

  • processing command line arguments
  • displaying a window with visual styles

In some places the code looks quite crooked. This is because I wrote it crookedly. And in some places it allowed us to save space.

Surely you can come up with a couple of ways to reduce the size of the application, but not drastically (I think the byte is 50).

Result:

Now you can call aliens automatically with wild damage per second.

It is quite possible to create ultra-compact applications with actually used useful functionality and window interface.

Novelty:
Zero. Gathered into a bunch of several techniques \ developments.

Expediency:
Useless, fun for.

Source
Binary

Accept criticism, suggestions, suggestions, admiration, indignation.

Also popular now: