GNU/Linux >> Znalost Linux >  >> Linux

Nástroje zpětného inženýrství v Linuxu – řetězce, nm, ltrace, strace, LD_PRELOAD

Tento článek vysvětluje nástroje a příkazy, které lze použít ke zpětnému inženýrství spustitelného souboru v prostředí Linuxu.

Reverzní inženýrství je akt zjišťování, co dělá software, ke kterému není k dispozici žádný zdrojový kód. Zpětné inženýrství vám nemusí poskytnout přesné podrobnosti o softwaru. Ale můžete docela dobře pochopit, jak byl software implementován.

Reverzní inženýrství zahrnuje následující tři základní kroky:

  1. Shromažďování informací
  2. Určení chování programu
  3. Zachycování hovorů knihovny

I. Shromažďování informací

Prvním krokem je shromáždit informace o cílovém programu a o tom, co dělá. Pro náš příklad si vezmeme příkaz „kdo“. Příkaz ‘kdo’ vytiskne seznam aktuálně přihlášených uživatelů.

1. Příkaz Strings

Strings je příkaz, který tiskne řetězce tisknutelných znaků v souborech. Nyní to tedy použijeme proti našemu cílovému (who) příkazu.

# strings /usr/bin/who

Některé z důležitých řetězců jsou,

users=%lu
EXIT
COMMENT
IDLE
TIME
LINE
NAME
/dev/
/var/log/wtmp
/var/run/utmp
/usr/share/locale
Michael Stone
David MacKenzie
Joseph Arceneaux

Z výstupu about můžeme vědět, že „kdo“ používá nějaké 3 soubory (/var/log/wtmp, /var/log/utmp, /usr/share/locale).

Přečtěte si více:Příklady příkazů Linux Strings (Vyhledat text v binárních souborech UNIX)

2. nm Příkaz

nm, se používá k výpisu symbolů z cílového programu. Pomocí nm můžeme poznat lokální a knihovní funkce a také použité globální proměnné. nm nemůže pracovat na programu, který je prokládaný pomocí příkazu „strip“.

Poznámka:Ve výchozím nastavení je příkaz „who“ odstraněn. Pro tento příklad jsem znovu zkompiloval příkaz „who“.

# nm /usr/bin/who

Zobrazí se následující seznam:

08049110 t print_line
08049320 t time_string
08049390 t print_user
08049820 t make_id_equals_comment
080498b0 t who
0804a170 T usage
0804a4e0 T main
0804a900 T set_program_name
08051ddc b need_runlevel
08051ddd b need_users
08051dde b my_line_only
08051de0 b time_format
08051de4 b time_format_width
08051de8 B program_name
08051d24 D Version
08051d28 D exit_failure

Ve výše uvedeném výstupu:

  • t|T – Symbol je přítomen v sekci .text code
  • b|B – Symbol je v sekci .data inicializované OSN
  • D|d – Symbol je v sekci Initialized .data.

Velké nebo malé písmeno určuje, zda je symbol místní nebo globální.

Z výstupu about můžeme vědět následující,

  • Má globální funkci (main,set_program_name,usage,atd..)
  • Má některé místní funkce (print_user, time_string atd..)
  • Má globální inicializované proměnné (Version,exit_failure)
  • Má proměnné inicializované OSN (time_format, time_format_width atd..)

Někdy můžeme pomocí názvů funkcí odhadnout, co funkce udělají.

Přečtěte si více:10 praktických příkladů příkazů Linux nm

Další příkazy, které lze použít k získání informací, jsou

  • příkaz ldd
  • příkaz fixační jednotky
  • příkaz lsof
  • /proc souborový systém

II. Určení chování programu

3. Příkaz ltrace

Sleduje volání funkce knihovny. V tomto procesu spustí program.

# ltrace /usr/bin/who

Výstup je uveden níže.

utmpxname(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0
setutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 1
getutxent(0x8050c6c, 0xb77068f8, 0, 0xbfc5cdc0, 0xbfc5cd78)          = 0x9ed5860
realloc(NULL, 384)                                                   = 0x09ed59e8
getutxent(0, 384, 0, 0xbfc5cdc0, 0xbfc5cd78)                         = 0x9ed5860
realloc(0x09ed59e8, 768)                                             = 0x09ed59e8
getutxent(0x9ed59e8, 768, 0, 0xbfc5cdc0, 0xbfc5cd78)                 = 0x9ed5860
realloc(0x09ed59e8, 1152)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1152, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 1920)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 1920, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
realloc(0x09ed59e8, 3072)                                            = 0x09ed59e8
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)                = 0x9ed5860
getutxent(0x9ed59e8, 3072, 0, 0xbfc5cdc0, 0xbfc5cd78)

Můžete pozorovat, že existuje sada volání getutxentu a jeho rodiny funkcí knihovny. Můžete si také všimnout, že ltrace poskytuje výsledky v pořadí, v jakém jsou funkce v programu volány.

Nyní víme, že příkaz „who“ funguje voláním getutxentu a jeho rodiny funkcí, aby získal přihlášené uživatele.

4. Příkaz strace

Příkaz strace se používá ke sledování systémových volání prováděných programem. Pokud program nepoužívá žádnou knihovní funkci a používá pouze systémová volání, pak pomocí plain ltrace nemůžeme sledovat provádění programu.

# strace /usr/bin/who
[b76e7424] brk(0x887d000)               = 0x887d000
[b76e7424] access("/var/run/utmpx", F_OK) = -1 ENOENT (No such file or directory)
[b76e7424] open("/var/run/utmp", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
.
.
.
[b76e7424] fcntl64(3, F_SETLKW, {type=F_RDLCK, whence=SEEK_SET, start=0, len=0}) = 0
[b76e7424] read(3, "\10\325"..., 384) = 384
[b76e7424] fcntl64(3, F_SETLKW, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0

Můžete pozorovat, že kdykoli je volána funkce malloc, volá systémové volání brk(). Funkce knihovny getutxent ve skutečnosti volá systémové volání ‚open‘ k otevření ‚/var/run/utmp‘ a vloží zámek pro čtení a přečte obsah a poté zámky uvolní.

Nyní jsme potvrdili, že příkaz who přečetl soubor utmp, aby zobrazil výstup.

„Strace“ i „ltrace“ má řadu dobrých možností, které lze použít.

  • -p pid – Připojí se k zadanému pid. Užitečné, pokud je program již spuštěn a chcete znát jeho chování.
  • -n 2 – Odsadit každé vnořené volání o 2 mezery.
  • -f – Sledovat rozvětvení

Přečtěte si více:7 vzorových příkladů pro ladění provádění programu v Linuxu

III. Zachycování hovorů knihovny

5. LD_PRELOAD &LD_LIBRARY_PATH

LD_PRELOAD nám umožňuje přidat knihovnu ke konkrétnímu provádění programu. Funkce v této knihovně přepíše skutečnou funkci knihovny.

Poznámka:Toto nemůžeme použít s programy nastavenými s bitem „suid“.

Vezměme si následující program.

#include <stdio.h>
int main() {
  char str1[]="TGS";
  char str2[]="tgs";
  if(strcmp(str1,str2)) {
    printf("String are not matched\n");
  }
  else {
    printf("Strings are matched\n");
  }
}

Zkompilujte a spusťte program.

# cc -o my_prg my_prg.c
# ./my_prg

Vypíše „Řetězce se neshodují“.

Nyní napíšeme vlastní knihovnu a uvidíme, jak dokážeme zachytit funkci knihovny.

#include <stdio.h>
int strcmp(const char *s1, const char *s2) {
  // Always return 0.
  return 0;
}

Zkompilujte a nastavte proměnnou LD_LIBRARY_PATH na aktuální adresář.

# cc -o mylibrary.so -shared library.c -ldl
# LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

Nyní bude vytvořen soubor s názvem ‚library.so‘.
Nastavte proměnnou LD_PRELOAD na tento soubor a spusťte program pro porovnání řetězců.

# LD_PRELOAD=mylibrary.so ./my_prg

Nyní vypíše „Strings are matched“, protože používá naši verzi funkce strcmp.

Poznámka:Pokud chcete zachytit jakoukoli knihovní funkci, pak by vaše vlastní knihovní funkce měla mít stejný prototyp jako původní knihovní funkce.

Právě jsme pokryli úplně základní věci potřebné k zpětnému inženýrství programu.

Pro ty, kteří by chtěli udělat další krok v reverzním inženýrství, pomůže ve větší míře pochopení formátu souboru ELF a programu Assembly Language.


Linux
  1. Linuxový příkaz mv

  2. Linux du command

  3. Linuxový ip příkaz

  1. Linux cd příkaz

  2. Jak používat příkazy strace a ltrace v Linuxu

  3. Příkaz Linux k nalezení řetězců v binárním nebo jiném souboru než ASCII

  1. 5 nástrojů Rust, které stojí za to vyzkoušet na příkazovém řádku Linuxu

  2. 4 Nástroje Markdown pro příkazový řádek Linuxu

  3. 5 nástrojů příkazového řádku pro rychlé vyhledání souborů v systému Linux