The Price of Scripting: DietLibC vs ASM

performance
programming

(pico) #1

Continuing the discussion from The Price of Scripting:

There you go

I´m sorry, but I’m not going to implement the code challenger we used in The Price of Scripting in assembler… it is just no point on doing that. So I will use a simple hello world program that will wait for the user to press a key, so we can check the memory usage of the process.

In order to be able to, somehow, compare this data with the previous table, I’m including the figures for C++ and C just for comparison purposes. However, for such a small program some values are nor much relevant. You will also notice the page size granularity (4Kb for linux)

All the programs are compiled as static binaries so we account for any dynamic library they use, as for instance the C and C++ standard libraries.

C++

So, the C++ code used is this:

#include <iostream>

int main (void)
{
  
  std::cout << "Hello World" << std::endl;
  std::cin.get();
  return 0;
}

C

The C code is:

#include <stdio.h>
int main (void)
{
  printf ("Hello World!\n");
  getchar();
  return 0;
}

Assembler

And the assembler code for Linux 64 bits is:

	msg 	db      "hello, world!",0x0a
	key 	db      0
section .text	
	global _start
_start:
	mov     rax, 1
	mov     rdi, 1
	mov     rsi, msg
	mov     rdx, 14
	syscall	
	mov rax, 0
	mov rdi, 0
	mov rsi, key
	mov rdx, 1
	syscall
	mov    rax, 60
	mov    rdi, 0
	syscall

Results

For these three programs I had got the following results in my system:

Language | Size       | Strip   | Mem  | Virt Mem | Res Mem | Wr Mem | Sh Mem
---------+------------+---------+------+----------+---------+--------+---------
C++      |  1.6   M   | 1.3   M | 68 K | 1.6 M    | 256 K   | 68 K   | 196 KB
C        |  857   K   | 778   K | 32 K | 1.1 M    |   4 K   | 52 K   | N/A
Diet     |   8.5  K   |  4.9  K | 16 K | 156 K    |   4 K   | 16 K   | N/A
Asm      | 1002       | 544     |  8 K | 152 K    |   4 K   |  8 K   | N/A

The 2 first columns are the binary size, just after compiling and after stripping it.
Some conclusions:

  • The file size of the assembler binary version is around 10 times smaller than the dietlibc version. And it goes as low as 544 bytes for a static binary (no library dependency at all)
  • At run-time, the difference between dietlibc and the assembler version, resources-wise is not much for this very small program.

So, unless you really have a size constraint (what it usually happens with shellcodes and remote payloads), you will not gain much benefit coding your application in assembler… But, as usual, it always depends on what you are doing. If you are good at assembler, your code will likely run a lot faster.


#2

Hey thanks! Not too read up on C++ (I might be wrong in this- please correct me if I am) But wouldn’t using namespace std be a bit easier? Making something like this:

#include <iostream>
using namespace std;

int main (void);
{
    cout << "Hello World";
    cin.get();
    return 0;
}

I know that using both “std;;” and “using namespace std” would be correct, but wouldn’t it be a bit easier to do “using namespace std”?


#3

Ignorant of me to say “easier”. Sorry if it’s not as easier to use the other option. Anyways good job!


(The C# Dude) #4

I’m not a good C++ programmer, but I heard using namespace std is bad practice. See here :slight_smile:.


#5

Thanks mate! This clears a lot up :slight_smile:


(Command-Line Ninja) #6

Woah! Nice! I didn’t expect you to be so prompt! @0x00pf really is a total badass! :slight_smile: Awesome!


(pico) #7

Just for fun.

I adapted the technique from this great article, to build a custom ELF 64

BITS 64
	        org 0x400000
  ehdr:                                                 ; Elf32_Ehdr
                db      0x7F, "ELF", 2, 1, 1, 0         ;   e_ident
        times 8 db      0
                dw      2                               ;   e_type
                dw      0x3e                            ;   e_machine
                dd      1                               ;   e_version
                dq      _start                          ;   e_entry
                dq      phdr - $$                       ;   e_phoff
                dq      0                               ;   e_shoff
                dd      0                               ;   e_flags
                dw      ehdrsize                        ;   e_ehsize
                dw      phdrsize                        ;   e_phentsize
                dw      1                               ;   e_phnum
                dw      0                               ;   e_shentsize
                dw      0                               ;   e_shnum
                dw      0                               ;   e_shstrndx
  
  ehdrsize      equ     $ - ehdr
  
  phdr:                                                 ; Elf32_Phdr
                dd      1                               ;   p_type
                dd      5                               ;   p_offset
	        dq      0
                dq      $$                              ;   p_vaddr
                dq      $$                              ;   p_paddr
                dq      filesize                        ;   p_filesz
                dq      filesize                        ;   p_memsz
                dq      0x1000                          ;   p_align
  
  phdrsize      equ     $ - phdr
	

_start:
	mov     rax, 1
	mov     rdi, 1
	mov     rsi, msg
	mov     rdx, 14
	syscall	
	mov     rax, 0
	mov     rdi, 0
	mov     rsi, key
	mov     rdx, 1
	syscall
	mov    rax, 60
	mov    rdi, 0
	syscall


	msg 	db      "hello, world!",0x0a
	key 	db      0

	
	filesize equ $ - $$

Compile with

 nasm -f bin -o hellos hello64.asm; chmod +x hellos

And get

Language | Size       | Strip   | Mem  | Virt Mem | Res Mem | Wr Mem | Sh Mem
---------+------------+---------+------+----------+---------+--------+---------
ASM ELF  |  201       |     264 |  8 K | 148 K    |   4 K   |  8 K   | N/A

Yes… it grows when we try to strip it!


(system) #8

This topic was automatically closed after 30 days. New replies are no longer allowed.