statifier logo
 
ELF STATIFIER
 
About
 Main Page

Get It
 Via the Web
 Via Anonymous SVN
 Precompiled Packages

Documentation
 README
 News
 ChangeLog
 Installation
 THANKS
 TODO
 FAQ

 STATIFIER.1

Development
documentation
 Background
 More Details
 More Problems
 Implementation
 Statified Layout
 Data Flow
 Porting

Resources
 WEB SVN
 SourceForge Page
 Mailing List
 Forums
 Related Projects

My Other SF projects
 kbde
 ksm
 nfstimesync
 noexec
 pkgrebuild
 rpmrebuild (with Eric Gerbier)
 rrp_statify

SourceForge.net Logo

 
STATIFIER'S IMPLEMENTATION
Statifier implementation.

On the top level "statifing" process do following:
- create starter.
- save all segments created by loader.
- create "non-loadable" part of the file 
  (ehdr, phdrs, shdrs, non-allocated sections)
- combine non-loadable part and load segments to "statified" executable.
- "inject" starter.

Note. Before version 1.6.0 starter got loadable segment for itself.
But someone need to decide what virtual address got this additional segment.
x86 and alpha (for example) have different memory layouts.
Worse, there are such thing like exec-shield and randomization, which
make memory layout different and hardly predictable.
So, I gave up with this task - find unoccupied virtual address and
decide use starter injection.

Create starter.

As was said before, starter do following:
- set loader's variables to the correct values.
- if loader/kernel use TLS invoke 'set_thread_area' with correct arguments.
- restore "almost all" registers to the values and jump to _dl_start_user.

Starter have following structure:

module1: /* (dl-var) set loader's variables */
	module1 code
        ...
	jump    next
module1_data:
        ...
next:
module2: /* (set_thread_area) set_thread_area */
	module2 code
	...
	jump	next
module2_data:
	...
next:
module3: /* (regs) restore registers  - should be last */
	module3 code
	...
	jump _dl_start_user
module3_data:
	...

So, "create starter" process is as simple as following:
- have prebuilt files with module1, module2 and module3 code
- somehow create files with module1, module2 and module3 data
- cat mod1_code mod1_data mod2_code mod2_data mod3_code mod3_data > starter
  
I choose this structure for starter for two reason:
- modules may be easily added/removed as needed at "statifying" time
- there is no need in compiler/assembler/linker to build starter.

What kind of data we need to create starter ?

Data for dl-var module

dl-var module should set correct values for the following
variables:
   argc, argv, envp, auxv, platform, platformlen
So, their addresses is a data for dl-var modules.

Where can these addresses be found ?
It's easy: just use command like
   objdump -t /lib/ld-linux.so.2

Data for set_thread_area module

set_thread_area module needs all data set_thread_are system call needs:
- entry_number
- base
- limit
- flags
and one more - set_thread_area's system call number.

Here we have got two questions:
- is thread local storage in use ?
- if so, where get set_thread_area's data from ? 

Data for regs module.

It's quite clear that for restoring registers one should have
registers' values.

What's not so clear, where one can get these values from.


Save all segments created by loader.

It's  very simple - just somehow do it.

Create non-loadable part of the file.

Just set up ehdr, phdrs and shrds as needed.

Combine "statified" executable.

It's as simple as 'cat'

"Inject" starter.
I don't want to look for the unused virtual address, so I need find
appropriative place in the statified exe and inject starter there.

That's all.
Yes, that's all, but yet some questions are left unanswered:
- is thread local storage in use ?
- if so, where get set_thread_area's data from ? 
- where get registers' value from ?
- how save all segments, created by loader.
- how find the place where starter can be injected ?

- is thread local storage in use ?
- where get set_thread_area's data from ? 
- where get registers' value from ?
- how save all segments, created by loader.
In statifier before version 1.7.0 I used gdb to do all of this.
But I encountered too many buggy gdb in the wild
(especially on x86_64 systems).
I finally was fed up with it and wrote some "mini debugger" - my_gdb 
for statifier. 

So, "data collection" with my_gdb looks like following:

- set breakpoint on '_dl_start_user' function.
- in the loop:
-    run executable to be statified with PTRACE_SYSCALL
-    process will run till next syscall OR breakpoint

-    if process was stoppd due set_thread_area syscall, 
     then dump syscall's arguments

-    if process was stoped due _dl_start_user breakpoint
     then dump registers' values and loaded segments
     After that kill application and exit.

Please note, that statifying (unlike ldd) never run libraries' init functions.


How find the place where starter can be injected ?

As was told before, phdr has field p_filesz, which show how
much memory really used by this segment. But segments loaded
into memory as whole pages, so (usually) there is a gap between
end of segment's data and page boundary.
So, any segment which has execute permission and gap more than
starter size is good place for starter injection.