Author Topic: Small .exes in C  (Read 635 times)

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Small .exes in C
« on: September 09, 2017, 01:44:37 AM »
Some people say it is impossible to make smaller applications in C than in Assembly Language.
This is not totally true. Using as model the code from this message I made a C .exe of the same size as the MASM one, i.e 3072 bytes. The output is the same, the difference is that the encrypted string is not in the middle of the code. That could have been done as well with inline asm, but I prefered to keep it all in C this time.
The compiler used was VS 2017 using the Windows SDK 7.1 toolset.
The code and .exe are attached.


hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4934
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Small .exes in C
« Reply #1 on: September 09, 2017, 02:24:55 AM »
I am taking unfair advantage of you here Jose, not only am I using MASM but I used Pelle's linker.

MASM the magnificent and modest at 1536 bytes.  :P

I forgot to free the allocated memory. Main should look like this.

main proc

    LOCAL ptxt:DWORD

    call mangled
    mov ptxt, eax
    invoke StdOut, ptxt
    free ptxt

    ret

main endp


Builds at the same size.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

nidud

  • Member
  • *****
  • Posts: 1411
    • https://github.com/nidud/asmc
Re: Small .exes in C
« Reply #2 on: September 09, 2017, 03:20:49 AM »
Asmc: 1536 byte
Code: [Select]
;
; Build: asmc -pe -D__PE__ main.asm
;
include alloc.inc
include conio.inc
include stdio.inc
include stdlib.inc

.code

obs dd 0x0BB11E534, 0x0BB3F9751, 0x0DE51FE36, 0x0B034DE53, 0x0C346BB25, 0x0A6349B4A, \
       0x0D214E92F, 0x0B6668847, 0x09612E167, 0x0F17C880C, 0x09011A820, 0x0F165C944, \
       0x0D101AC20, 0x0B564CE4D, 0x0D044AB39, 0x0B127D84C, 0x0D745B76C, 0x0B8319709, \
       0x0DC5EF429, 0x0B83F9C09, 0x0DD49F97D, 0x08E66911E, 0x0FA13D93E, 0x0D676B457, \
       0x0A256D139, 0x0CD76A578

main proc
   
    numberOfElements = sizeof(obs) / sizeof(dword)

    mov esi,calloc(numberOfElements + 1, 4)
    mov edi,sizeof(obs)

    .for (edx=0: edx<numberOfElements : edx++)

        mov eax,obs[edx*4]
        xor eax,edi
        mov edi,obs[edx*4]
        mov ecx,numberOfElements - 1
        sub ecx,edx
        mov [esi+ecx*4],eax
    .endf

    printf(esi)
    _getch()
    exit(0)

main endp

    end main

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4934
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Small .exes in C
« Reply #3 on: September 09, 2017, 03:45:25 AM »
ust looking at the code, it appears that neither of you guys are decrypting the data, it looks like you have just stored the text data in numeric form.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Re: Small .exes in C
« Reply #4 on: September 09, 2017, 04:14:14 AM »
Just looking at the code, it appears that neither of you guys are decrypting the data, it looks like you have just stored the text data in numeric form.

 :biggrin:

Quote
1536 byte

If I use polink i get 1536 bytes as well.  :t
\masm32\bin\polink /SUBSYSTEM:console /NODEFAULTLIB main.obj \masm32\lib\msvcrt.lib

Sometimes, my antivirus bark when look at polink linked files.

Siekmanski

  • Member
  • *****
  • Posts: 1145
Re: Small .exes in C
« Reply #5 on: September 09, 2017, 04:25:33 AM »
In the masm32 SDK there is this 1K example.
c:\masm32\examples\exampl07\masm1k\masm1k.exe

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: Small .exes in C
« Reply #6 on: September 09, 2017, 05:14:39 AM »
Sorry, I am a bit late, folks - the smally.vcxproj tried to open Visual Studio, and that took a while :greensml:

And the behemoth complains:
Code: [Select]
Rebuild All: 0 succeeded, 1 failed, 0 skipped
No additional info why it fails - I assume it's because it's genuine Microsoft software :bgrin:

@nidud: fatal error A1000: cannot open file : alloc.inc

Re obfuscation, the reverse engineer should not have such an easy job imho:

Smally.exe:
Code: [Select]
00D61011             ³.  B9 0030D600          mov ecx, offset 00D63000
00D61016             ³.  8D50 64              lea edx, [eax+64]
00D61019             ³>  8B39                 Úmov edi, [ecx]
00D6101B             ³.  33FE                 ³xor edi, esi
00D6101D             ³.  893A                 ³mov [edx], edi
00D6101F             ³.  8B31                 ³mov esi, [ecx]
00D61021             ³.  83C1 04              ³add ecx, 4
00D61024             ³.  83EA 04              ³sub edx, 4
00D61027             ³.  81F9 6830D600        ³cmp ecx, offset 00D63068
00D6102D             ³. 7C EA                Àjl short 00D61019
00D6102F             ³.  50                   push eax                          ; ³format
00D61030             ³.  FF15 0020D600        call near [<&msvcrt.printf>]      ; ÀMSVCRT.printf

ObfuscateString.exe:
Code: [Select]
004011AA              .  E8 68000000          call 00401217                     ; ÀObfuscateString.00401217
004011AF              .  34 E5                xor al, E5
004011B1              .  11BB 51973FBB        adc [ebx+BB3F9751], edi
004011B7                 36                   db 36                             ; char '6'
004011B8                 FE                   db FE                             ; char 'þ'
004011B9                 51                   db 51                             ; char 'Q'
004011BA                 DE                   db DE                             ; char 'Þ'
004011BB                 53                   db 53                             ; char 'S'
004011BC                 DE                   db DE                             ; char 'Þ'
004011BD                 34                   db 34                             ; char '4'
004011BE                 B0                   db B0
004011BF                 25                   db 25                             ; char '%'
004011C0                 BB                   db BB
004011C1             Ú.  46                   inc esi
004011C2             À.  C3                   retn
...
00401217             Ú$  5A                   pop edx    ; here you land when using F7 - with F8 you'll never see this part ;-)
00401218             ³.  6A 1A                push 1A
0040121A             ³.  59                   pop ecx
0040121B             ³>  8B02                 Úmov eax, [edx]
0040121D             ³.  3342 FC              ³xor eax, [edx-4]
00401220             ³.  83C2 04              ³add edx, 4
00401223             ³.  50                   ³push eax
00401224             ³.  49                   ³dec ecx
00401225             ³. 75 F4                Àjnz short 0040121B
00401227             ³.  54                   push esp
00401228             ³.  6A 01                push 1
0040122A             ³.  6A 02                push 2
0040122C             ³.  E8 06130000          call 00402537

I like Erol's code. The Pelles C IDE opens in 3 seconds, it builds in 2 seconds, and it doesn't complain about any errors :t
Besides, the clear text becomes visible only when the whole loop is finished, that is even better than my version :t

Vortex

  • Member
  • *****
  • Posts: 1733
Re: Small .exes in C
« Reply #7 on: September 09, 2017, 05:29:09 AM »
Hi aw27,

Nice example. Thanks. Source code built with Pelles C.

A small modification in your code to avoid problems with getchar :

The header file stdio.h supplied with Pelles C :

Code: [Select]
/* macro overrides */
#if !defined(__MT__) && !defined(_DLL)
#define getc(str)     ((str)->ptr < (str)->getend ? *(str)->ptr++ : (fgetc)(str))
#define getchar()     (__filetab[0]->ptr < __filetab[0]->getend ? *__filetab[0]->ptr++ : (fgetc)(__filetab[0]))

Code: [Select]
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#undef getchar

Reference :

http://www.masmforum.com/board/index.php?PHPSESSID=786dd40408172108b65a5a36b09c88c0&topic=14078.0

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Re: Small .exes in C
« Reply #8 on: September 09, 2017, 03:38:53 PM »
Nice tip Siekmanski. :t
Adding
#pragma comment(linker,"/merge:.rdata=.data")
#pragma comment(linker,"/merge:.data=.text")
Fixed base Address:YES and Randomized Base Address: NO under Linker Advanced

reduces the file linked with MS Link to 1024 bytes and the file linked with polink to 1024 bytes as well.

Thank you Vortex too.

hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4934
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Small .exes in C
« Reply #9 on: September 09, 2017, 03:54:46 PM »
What is being missed here is the MASM example stores 2 sequences of data that are XORRED together to produce the string. One in the code section, the other in the data section so to make a comparable example you need to add the code to do that otherwise you are not comparing the same thing.

The /MERGE option often drops the size by one 512 byte section but its not much use for any viable executable file.
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Re: Small .exes in C
« Reply #10 on: September 09, 2017, 04:26:31 PM »
What is being missed here is the MASM example stores 2 sequences of data that are XORRED together to produce the string. One in the code section, the other in the data section so to make a comparable example you need to add the code to do that otherwise you are not comparing the same thing.

The /MERGE option often drops the size by one 512 byte section but its not much use for any viable executable file.
The MASM example creates a DATA section which was not used in the code. But even merging sections I can not go under 1536 bytes both with link.exe and polink.exe.  :icon_confused:

The reason may be some overhead added by the SDK.
If I don't use the SDK I can indeed reach the 1024 bytes size.

Code: [Select]
.386
.model flat, stdcall
option casemap :None

includelib \masm32\lib\msvcrt.lib
getchar proto C
printf proto C :ptr, :vararg

STD_OUTPUT_HANDLE equ -11

.code
main proc C

call @F
@obs$ dd 0BB11E534h,0BB3F9751h,0DE51FE36h,0B034DE53h,0C346BB25h,0A6349B4Ah,0D214E92Fh,0B6668847h,09612E167h,0F17C880Ch,09011A820h,
0F165C944h,0D101AC20h,0B564CE4Dh,0D044AB39h,0B127D84Ch,0D745B76Ch,0B8319709h,0DC5EF429h,0B83F9C09h,0DD49F97Dh,
08E66911Eh,0FA13D93Eh,0D676B457h,0A256D139h,0CD76A578h
@@:
pop edx
mov ecx, sizeof @obs$/4
push 0
.Repeat
mov eax, [edx]
mov ebx, [edx-4]
xor eax, [edx-4]
add edx, 4
push eax
dec ecx
.Until Zero?
INVOKE printf, esp
INVOKE getchar

add esp, sizeof @obs$ +4
ret

main endp
end main
linked with POLINK as follows:
\masm32\bin\polink /MERGE:.rdata=.data /MERGE:.data=.text /SUBSYSTEM:console xor2.obj


hutch--

  • Administrator
  • Member
  • ******
  • Posts: 4934
  • Mnemonic Driven API Grinder
    • The MASM32 SDK
Re: Small .exes in C
« Reply #11 on: September 09, 2017, 04:47:26 PM »
Jose,

I think you have mis-understood what the code does. The code in the data section is loaded into the EDX register then XORRED against the address stored in ESI. This is how the string is decrypted and the result is returned in EAX.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

mangled proc

    LOCAL pMem  :DWORD

  ; --------------------------------------------------------
  ; Return value in EAX is the memory pointer to the result
  ; The data length is returned in ECX. Deallocate memory in
  ; EAX with the Windows API function GlobalFree() or the
  ; MASM32 macro "free".
  ; --------------------------------------------------------

    push ebx
    push esi
    push edi

    mov pMem, alloc(102)

    mov esi, pMem

    mov DWORD PTR [esi+52], 2701825359
    mov DWORD PTR [esi+32], 4160494258
    mov DWORD PTR [esi+68], 202942781
    mov DWORD PTR [esi+16], 4294820983
    mov DWORD PTR [esi+44], 4205567486
    mov DWORD PTR [esi+0],  4017705100
    mov DWORD PTR [esi+84], 3971489685
    mov DWORD PTR [esi+4],  3496616615
    mov DWORD PTR [esi+12], 2682706439
    mov DWORD PTR [esi+92], 3083553528
    mov DWORD PTR [esi+96], 555372704
    mov DWORD PTR [esi+76], 3809159684
    mov DWORD PTR [esi+24], 2440797704
    mov DWORD PTR [esi+60], 2520516412
    mov DWORD PTR [esi+36], 2923264119
    mov DWORD PTR [esi+8],  2102397535
    mov DWORD PTR [esi+72], 2337906632
    mov DWORD PTR [esi+56], 1913045396
    mov DWORD PTR [esi+88], 4275180973
    mov DWORD PTR [esi+80], 1300258737
    mov DWORD PTR [esi+64], 50501529
    mov BYTE PTR [esi+100], 216
    mov DWORD PTR [esi+28], 2224536879
    mov DWORD PTR [esi+20], 1926482737
    mov DWORD PTR [esi+48], 2526482862
    mov DWORD PTR [esi+40], 3701432909

    mov edi, 101
    or ebx, -1

  @@:
    add ebx, 1
    movzx edx, BYTE PTR [mangled_pad+ebx]
    xor [esi+ebx], dl
    sub edi, 1
    jnz @B

  ; -------------------------------------------------
  ; EAX is the memory pointer, ECX is the BYTE length
  ; -------------------------------------------------
    mov eax, pMem
    mov ecx, 101

    pop edi
    pop esi
    pop ebx

    ret

  .data
  mangled_pad \
    db 205,36,89,128,201,71,74,164,54,103,53,81,39,146,147,235
    db 20,172,210,172,69,174,165,23,40,246,26,245,15,222,248,224
    db 215,58,136,152,87,23,95,200,56,17,252,189,138,184,139,159
    db 195,115,242,242,43,240,110,129,240,160,114,19,16,39,81,247
    db 242,254,108,100,29,192,108,44,160,254,43,239,97,88,43,151
    db 222,127,242,40,227,122,202,159,200,53,183,144,159,35,165,210
    db 197,34,52,44,210
  .code

mangled endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
hutch at movsd dot com
http://www.masm32.com    :biggrin:  :biggrin:

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Re: Small .exes in C
« Reply #12 on: September 09, 2017, 04:53:24 PM »
@Hutch
I was always talking about the code in this message: http://masm32.com/board/index.php?topic=6524.msg69994#msg69994

I have not yet looked properly at your code. I will later. :t

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: Small .exes in C
« Reply #13 on: September 09, 2017, 05:20:52 PM »
@Hutch
I was always talking about the code in this message: http://masm32.com/board/index.php?topic=6524.msg69994#msg69994

You made some adjustments, what is their purpose?

        call @F
@obs$   dd 0BB11E534h,0BB3F9751h,0DE51FE36h,0B034DE53h,0C346BB25h,0A6349B4Ah,0D214E92Fh,0B6668847h,09612E167h,0F17C880Ch,09011A820h,
        0F165C944h,0D101AC20h,0B564CE4Dh,0D044AB39h,0B127D84Ch,0D745B76Ch,0B8319709h,0DC5EF429h,0B83F9C09h,0DD49F97Dh,
        08E66911Eh,0FA13D93Eh,0D676B457h,0A256D139h,0CD76A578h
@@:
        pop edx
        mov ecx, sizeof @obs$/4
        push 0
        .
Repeat
                mov eax, [edx]
                mov ebx, [edx-4]
                xor eax, [edx-4]
                add edx, 4
                push eax
                dec ecx
        .Until Zero?
        INVOKE printf, esp

        add esp, sizeof @obs$ +4

aw27

  • Member
  • ****
  • Posts: 857
  • Let's Make ASM Great Again!
Re: Small .exes in C
« Reply #14 on: September 09, 2017, 07:01:18 PM »
@Hutch
I was always talking about the code in this message: http://masm32.com/board/index.php?topic=6524.msg69994#msg69994

You made some adjustments, what is their purpose?

        call @F
@obs$   dd 0BB11E534h,0BB3F9751h,0DE51FE36h,0B034DE53h,0C346BB25h,0A6349B4Ah,0D214E92Fh,0B6668847h,09612E167h,0F17C880Ch,09011A820h,
        0F165C944h,0D101AC20h,0B564CE4Dh,0D044AB39h,0B127D84Ch,0D745B76Ch,0B8319709h,0DC5EF429h,0B83F9C09h,0DD49F97Dh,
        08E66911Eh,0FA13D93Eh,0D676B457h,0A256D139h,0CD76A578h
@@:
        pop edx
        mov ecx, sizeof @obs$/4
        push 0
        .
Repeat
                mov eax, [edx]
                mov ebx, [edx-4]
                xor eax, [edx-4]
                add edx, 4
                push eax
                dec ecx
        .Until Zero?
        INVOKE printf, esp

        add esp, sizeof @obs$ +4

The push 0 is to make sure the string will be null terminated. The other was just for debugging, I forgot to remove it.