Arquivo para categoria Dica

Running 64bit binaries on 32bit userland with 64bit kernel

The problem

Some time ago I wrote about how my Gentoo Linux is installed (blogpost in pt_BR) on my laptop. Long story short: I have a hybrid system, 32bit userland running on a 64bit kernel. I opted to do this type of install because I still have an impression that a full 64bit system still has some key problems (in my past adventures flash was one of them). Anyway, this setup works very well. I can use all 8GB (not by one same process) for example. But not long after the installation I discovered one thing that got me confused.

How come even with a 64bit kernel I still coudn’t run any 64bit ELF binary?

I thought that just having a 64bit kernel was enough to run 32 and 64bit binaries, in fact it is! But is not so trivial do run a 64bit binary in a 32bit userland.

The first attempt, no luck

The first thing I tried was obviously call the binary directly on the command line:

daltonmatos@jetta ~/src [158]$ ./m64
-bash: ./m64: No such file or directory
daltonmatos@jetta ~/src [159]$

This is, indeed, a very cryptic error message.

Enter the dynamic loader

All binaries (at least the massive majority) on your system is a dynamically linked ELF. And you can check this with the file command.

daltonmatos@jetta ~/src [179]$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), 
for GNU/Linux 2.6.9, stripped
daltonmatos@jetta ~/src [180]$

Remember, 32bit userland. A bit further and we will see this for 64 bit binaries.

By dynamic linked you can understand that all libraries that the binary depends on are loaded at runtime. You can check which are these libraries with the ldd command.

daltonmatos@jetta ~/src [180]$ ldd /bin/ls
 linux-gate.so.1 => (0xffffe000)
 librt.so.1 => /lib/librt.so.1 (0xf776c000)
 libacl.so.1 => /lib/libacl.so.1 (0xf7763000)
 libc.so.6 => /lib/libc.so.6 (0xf7605000)
 libpthread.so.0 => /lib/libpthread.so.0 (0xf75eb000)
 /lib/ld-linux.so.2 (0xf7796000)
 libattr.so.1 => /lib/libattr.so.1 (0xf75e4000)
daltonmatos@jetta ~/src [181]$

Note: Thee ldd command is just a wrapper around the real dynamic loader, usually /lib/ld.so or /lib/ld-<version>.so. I’m running glibc-2.13 so here I have /lib/ld-2.13.so. Running ldd <some-binary> is the same as running /lib/ld-2.13.so --list <some-binary>. So for now on we will be using the second.

Here begins the differencies. The 32 bit ld.so (or the ldd wrapper) tool does not know how to handle a 64 bit binary, as we can see:

daltonmatos@jetta ~/src [229]$ /lib/ld-2.13.so --list ./m64
./m64: error while loading shared libraries: ./m64: wrong ELF class: ELFCLASS64
daltonmatos@jetta ~/src [230]$

And the ldd command:

 
daltonmatos@jetta ~/src [190]$ ldd ./m64
 not a dynamic executable

which is clearly not true, since I compiled m64 myself using a regular gcc that outputs 64bit binaries. So we need a 64bit-capable ld.so. Since I have two compilers (one that generates 32bit binaries and another that generates 64bit) I need to have two complete tool chains (basically: binutils, gcc, gdb, glibc). So my second tool chain has what I need.

Specifically on Gentoo Linux, the cross compiling infrastructure creates /usr/<arch>-pc-linux-gnu folder and stores all files under it so I have a /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so, that is, the dynamic loader for 64bit binaries. Yes, I have a different version of the glibc for 64bit (2.14.1).

So let’s re-check our binaries, but now with this new tool:

daltonmatos@jetta ~/src [191]$ file ./m64 
./m64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), 
for GNU/Linux 2.6.9, not stripped
daltonmatos@jetta ~/src [192]$

Alright, our brand new 64bit program. Now, the shared libraries:

daltonmatos@jetta ~/src [194]$ /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so --list ./m64
 linux-vdso.so.1 => (0x00007fffa90fb000)
 libc.so.6 => /usr/x86_64-pc-linux-gnu/lib/libc.so.6 (0x00007f103f367000)
 /lib64/ld-linux-x86-64.so.2 => /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so (0x00007f103f6f7000)
daltonmatos@jetta ~/src [195]$

That’s awesome! You maybe asking yourself: “How is it possible to run the ld.so binary directly but not other 64bit binaries?” And you’re right, because the ld.so itself is a 64bit binary:

daltonmatos@jetta ~/src [197]$ file /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so 
/usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), 
dynamically linked, stripped
daltonmatos@jetta ~/src [198]$

If you know this, let me know because I don’t! =) I just discovered that the dynamic loader itself is runnable directly from the command line and can be used to run other binaries.

daltonmatos@jetta ~/src [200]$ file ./m64
./m64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), 
for GNU/Linux 2.6.9, not stripped
daltonmatos@jetta ~/src [201]$ /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so ./m64 
Hello World
daltonmatos@jetta ~/src [202]$

Like magic! =)

If you tried these exact steps I  just described you probably got an error like this one:

daltonmatos@jetta ~/src [202]$ /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so ./m64 
./m64: error while loading shared libraries: libc.so.6: wrong ELF class: ELFCLASS32
daltonmatos@jetta ~/src [203]$

What does this mean? Remember that we have two glibc’s installed? Both binaries depend on a shared library with the name libc.so.6. So shouldn’t the SO handle this automatically? Yes, but it doesn’t and now I have to tell you that I’ve been hiding one piece of important information from you. Is the second key to be able to run 64bit binaries, being the first the discovery of ld.so as a generic binary runner.

Go back in the output of the ld.so --list command and see how the libc.so.6‘s path is resolved in both cases. See that they have different paths? 32 bit resloves to /lib/libc.so.6 and 64bit resolves to /usr/x86_64-pc-linux-gnu/lib/libc.so.6. So how do we handle this?

It turns out that we can change where the dynamic loader looks for shared objects with the LD_LIBRARY_PATH environment variable. So all commands using the 64bit loader need to be prepended with LD_LIBRARY_PATH=<new-path>, like this:

daltonmatos@jetta ~/src [203]$ LD_LIBRARY_PATH=/usr/x86_64-pc-linux-gnu/lib /usr/x86_64-pc-linux-gnu/lib/ld-2.14.1.so ./m64 
Hello World
daltonmatos@jetta ~/src [204]$

One of the key advantages of having the ability to do this is that you can compile some programs to 64bit code and this program will be able to use more that 4GB of RAM, if needed.

An easier, but not optimal way

There is another way to do this that will not require tweaking the library loading path or anythin. You can compile all your 64bit binaries as statically linked ELF’s, like this:

daltonmatos@jetta ~/src [218]$ x86_64-pc-linux-gnu-gcc -static -o m64s main.c
daltonmatos@jetta ~/src [219]$ file m64s 
m64s: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.9, not stripped
daltonmatos@jetta ~/src [220]$

This way you won’t need the dynamic loader and then you can run your binary directly from the command line:

daltonmatos@jetta ~/src [220]$ ./m64s
Hello World
daltonmatos@jetta ~/src [221]$

But this comes with a cost. Extra disk space:

daltonmatos@jetta ~/src [222]$ file m64*
m64: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
m64s: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.9, not stripped
daltonmatos@jetta ~/src [223]$ ls -lh m64*
-rwxr-xr-x 1 daltonmatos daltonmatos 7,7K Abr 8 17:15 m64
-rwxr-xr-x 1 daltonmatos daltonmatos 771K Abr 10 20:32 m64s
daltonmatos@jetta ~/src [224]$

The statically linked binary is 100 times bigger than the dynamic one. This is because you have all that’s needed bundled into the static binary. You can reduce the final size using the strip command but you won’t get a significant reduction.

That’s it! Thanks for reading!

, , , ,

6 Comentários

SSH bruteforce break attempts made to my personal server

This is just a quick post to show you why you must at least try to configure properly you ssh servers. Today, reading my server logs I realized a big number of login failures into my server and I thought about collecting some numbers about these attempts.

All login attempts have the same signature on the logs, it’s something like this:

Apr 19 23:33:20 li292-237 sshd[25190]: Invalid user <username> from <IP Address>

Each entry with a different username and possibly a different origin IP. A quick wc -l on the string Invalid user returns 197243 lines.

daltonmatos@jetta ~/ [10]$ grep -i "invalid user" messages | wc -l
197243
daltonmatos@jetta ~/ [11]$

From all these, there are 32175 unique username

daltonmatos@jetta ~/ [13]$ grep -i "invalid user" messages | awk '{print $8}' | sort | uniq | wc -l
32175
daltonmatos@jetta ~/ [14]$

and 669 unique IP Addresses.

daltonmatos@jetta ~/ [16]$ grep -i "invalid user" messages | awk '{print $10}' | sort | uniq | wc -l
669
daltonmatos@jetta ~/ [17]$

The 10 most common usernames were:

daltonmatos@jetta ~/ [25]$ grep -i "invalid user" messages | awk '{print $8}' | ./count.py | sort -k 2 -n -r | head -10
test 3099
oracle 2901
admin 2869
guest 1596
nagios 1459
postgres 1229
user 1174
mysql 1030
ftp 754
temp 592
daltonmatos@jetta ~/ [26]$

p.s. Here is the code for count.py, if you are curious. =) https://gist.github.com/2333049

The same approach cane be applied to know the IP’s involved in the attacks and here are the top 10 (just the number of hits from each IP):

daltonmatos@jetta ~/ [27]$ grep -i "invalid user" messages | awk '{print $10}' | ./count.py | sort -k 2 -n -r | head -10
19332
16498
10844
9109
7283
5860
5288
4482
4343
4270
daltonmatos@jetta ~/ [28]$

Running a whois on the IP’s show me that the attacks comes from many different places including: China, Japan, Nigeria and US.

All attempts were made between April 2011 and April 2012. What I want to show you is that you need to take care of your ssh server configuration because one of these attempts could be successful. The first thing I do with any new ssh server is to disable login/password authentication and only accept ssh keys authentication. This is already a good start. =)

Thanks for reading.

Update: My great friend Denilson just pointed out that count.py has the very same effect of uniq -c. Thanks for the tip!

, , , , ,

1 comentário

Gentoo com kernel land 64 bits e user land 32 bits

O problema

Recentemente comprei um novo  notebook e por isso tive a oportunidade de instalar um novo S.O do zero, afinal já estava com uma mesma instalação desde 2008 (quando comprei meu primeiro notebook). Até poderia transferir uma instalação de um notebook para o outro, mas daria um trabalho parecido (ou maior) do que instalar novamente.

Nesse momento surgiu a mesma dúvida de sempre: Instalar a versão 64 bits ou 32 bits? Em 2008 tentei instalar uma versão totalmente 64 bits e tive problemas com plugin do flash, cheguei a ter um firefox 32 bits só para poder ver vídeos usando flash. Pouco tempo depois desisti do sistema 64 bits por esses e outros motivos e acabei instalando 32 bits mesmo.

Dessa vez a mesma dúvida. Achei que 3 anos depois as coisas estariam melhores, mas já de cara tive problemas com o driver de vídeo. A tela simplesmente não dava refresh corretamente. Um ls no terminal não retornava nenhum resultado, a não ser que a janela do terminal fosse movida de lugar, aí sim acontecia o refresh e o resultado do ls (já executado) aparecia. Isso significa que estava simplesmente impossível de usar o sistema.

Percebi que no live-DVD que estava usando, o driver de vídeo funcionava perfeitamente, então comecei a estudar o que o sistema do live-DVD tinha de diferente. Percebi que ele era userland 32 bits, apesar de reconhecer os 4GB do novo notebook. Nesse momento tive a idéia de tentar uma instalação mista: Kernel 64 bits e userland 32 bits.

Crosscompiling

O termo crosscompiling é usado quando temos um compilador rodando em um sistema e gerando código para outro. Por exemplo, você tem um notebook (intel x86) e consegue compilar um código em C que roda no seu celular (processador ARM, por exemplo). Mas o crosscompiling não serve apenas para gerar código entre máquinas diferentes, pode ser usado também para gerar código para a mesma máquinas, apenas escolhendo se o resultado será um binário de 32 bits ou 64 bits.

Foi exatamente isso que fiz nesse caso. Para fazer o que estava querendo precisava ter dois compiladores. Primeiro usei o gcc que é instalado no gentoo para compilar um outro gcc, esse último gerando código 64 bits. Ou seja, tenho agora um compilador que roda em um ambiente 32 bits mas que gera código 64 bits. E esse compilador foi gerado por um outro compilador que roda em 32 bits e também gera código 32 bits, entendeu? Ainda comigo? Ok, vamos lá.

Toda a mágica desse processo é feita pelo pacote sys-devel/crossdev, que faz parte da árvore principal do Gentoo. O que esse script faz é construir todo o toolkit necessário para gerar o cross compiler que precisamos. O que precisamos fazer é muito simples:

# emerge crosdevv
# crossdev -t x86_64-pc-linux-gnu

E isso é suficiente. Quando vi que realmente funcionava lembrei de 2004, quando ainda estava usando slackware e tive que fazer isso “na unha. Cheguei até a postar no twitter um agradecimento a quem quer que tenha escrito esse script. Merecido!

Compilando o novo kernel

Agora, com tudo pronto, temos dois compiladores: O que gera código 32 bits, que podemos chamar apenas digitando gcc, e o que gera código 64 bits que podemos chamar com o comando x86_64-pc-linux-gnu-gcc. O que temos que fazer agora é configurar nosso kernel para que use o compilador correto. Felizmente alguém já precisou fazer o que estou fazendo e existe uma maneira fácil de escolher o compilador que vai gerar nosso kernel.

Escolhendo compilador customizado para nosso kernel

Para isso basta irmos ao menu General setup/ e escolher o valor correto para a opção Cross-compiler tool prefix. Isso fará com que nosso novo compilador seja usado no momento de compilar nosso kernel. Agora o que temos que fazer é compilar nosso kernel como fazemos normalmente, o de sempre:

# make
# make modules
# make modules_install

Agora é só copiar nosso novo 64 bit kernel para o lugar certo, adicionar uma nova entrada no boot loader e bootar nosso novo kernel. E isso é o que temos depois de um boot com sucesso:

Kernel 64 bits com userland 32 bits

Exatamente o que queríamos:

  • Kernel 64 bits (X86_64)
  • Userland 32 bits (todos os binários são ELF 32-bit)
  • Os 4 GB RAM corretamente sendo usados
Agora tenho o driver de vídeo funcionando perfeitamente, com aceleração 3D e o novo gnome3 rodando sem nenhum problema! Perfeito!

, ,

1 comentário

Gentoo com partição raiz encriptada

A idéia desse post é apenas mostrar o caminho para instalar um Gentoo tendo a partição raiz (e todas as outras) encriptadas. Não é um texto muito detalhado, é para quem já tem costume com a instalação de gentoo. Mas mesmo que você não tenha familiaridade com esta distribuição, pode ler mesmo assim, apenas por curiosidade. =)

Um ponto negativo da abordagem que vou apresentar é que fica praticamente impossível recuperar seu laptop com programas como preyproject. A idéia nesse caso é proteger seus dados, não o aparelho. Esse último, bem ou mal, pode-se juntar dinheiro (mesmo que por muito tempo) e comprar outro, mas seus dados pessoais em mãos erradas, pode ser fatal. Lembrem-se sempre de manter seus backups atualizados. Bem, chega de papo e vamos ao que interessa.

Preparando as partições

Antes de começar a instalação, precisamos particionar nosso disco de forma correta. Repare que o que teremos encriptado será somente a partição raiz onde o S.O estará inslatado, tudo bem que isso corresponderá a quase 100% do disco, mas precisamos dexar claro que uma pequena parte do disco permanecerá sem encriptação, e você entenderá porque mais a frente.

Essas são as partções originais do notebook onde fiz essa instalação:

Particoes originais

Preciasmos, basciamente, criar duas partições:

  • Uma onde guardaremos as imagens dos kernels que vamos usar (/boot, 256MB)
  • Outra contendo o restande do disco, onde o S.O estará instalado

O motivo pelo qual temos que ter essa partição não encriptada é que nosso boot loader (grub) não é capaz de ler uma partição que esteja encriptada, se isso fosse possível poderíamos encriptar 100% do disco, inclusive a partição onde estão os kernels. Feito isso, nosso disco agora tem apenas duas partições:

Novas particoes criadas

Encriptando a partição principal

Agora o que precisamos fazer é efetivamente encriptar a partição onde vamos instalar o gentoo, para isso usaremos o cryptsetup:

Formatando com cryptsetup luksformat

Agora temos que fazer com que essa partição encriptada fique disponível como um block device.

Destravando crypt root

A partir desse momento já poderíamos formatar esse block device (mke2fs -j), mas como vamos usar LVM2 em cima da encriptação, precisamos fazer mais algumas coisas antes de começar a instalação. O comando acima cria um block device em /dev/mapper/crypt-root.

Configurando o LVM2

Agora que já temos toda a partição encripada é hora de configurar o LVM2. Vamos criar apenas um volume físico (physical volume) que ocupará toda a partição que acabamos de encriptar. Depois criaremos um Volume Group e dois Logical Volumes.

Criando volumes lvm2

Agora é hora de formatar os volumes que acabamos de criar:

Formatando volume lógico que será a partição raiz do S.O

Formatando o volume lógico que será o /home

Agora que temos dois volumes prontos, podemos começar a instalar o gentoo. Basta montar o block device /dev/vg01/root em /mnt/gentoo (como recomenda o gentoo handbook) e podemos continuar a partir do passo 5.

Configurando o kernel

No momento de configurar o kernel (handbook, passo 7) que usaremos, precisamos adicionar opções especiais para que tudo dê certo. Basicamente essas opções são:

  • Suporte a Device mapper, com suporte a encripatação
  • Suporte ao algoritmo de encriptação usado

Kernel option: device mapper

Kernel Option: Algoritmo de encriptação

Kernel Option: Sha 256 support

Essa última opção só foi necessária pois usamos –cipher twofish-cbc-essiv:sha256 no momento de encriptar a partição /dev/sda2.

Para podermos compilar esse kernel, o melhor é usarmos o utilitário genkernel. Basta instalar usando emerge normal. Antes de efetivamente compilar esse kernel, precisamos instalar dois pacotes. Atenção para as USE flags específicas de cada um deles:

  • sys-fs/lvm2 USE: +static
  • sys-fs/cryptsetup USE: +static-libs

Isso é necessário pois o ramdisk que será criado daqui a pouco precisará deles. O genkernel é uma mão na roda e é quem fará o trabalho duro de adicionar suporte para que seja possível bootar esse sistema com do disco encriptado. Como o genkernel já salva automaticamente os arquivos em /boot, temos que montar nossa partição (aquela que não é encriptada) no lugar certo:

# mount /dev/sda1 /boot

Agora poderemos usar o genkernel para preparar tudo:

# genkernel --menuconfig --luks --lvm all

Isso vai fazer com que o menu de configuração do kernel seja chamado (onde teremos que adicionar as opções citadas acima) e depois que salvarmos a configuração já começará a compilar nosso novo kernel. No final da compilação, teremos em /boot dois arquivos: O kernel e o initramfs criados pelo genkernel.

Configurando o boot loader

Nesse momento, o que falta é configurar o boot loader, nesse caso o Grub.

# emerge grub
# grub
grub> root (hd0,0)
gurb> setup (hd0)
quit

Repare que estamos instalando o grub no MBR, isso é válido pois nesse caso temos apenas um S.O instalado. Se estivéssemos fazendo dual-boot e já tivéssemos um outro S.O rodando, teríamos que instalar o grub na partição sda1, nesse caso em (hd0,0).

Como estamos preparando um sistema e um kernel especiais, precisamos passar opções especiais para o Grub, só assim ele poderá carregar nosso novo S.O. As linhas do /boot/grub/menu.lst ficam assim:

title Gentoo Linux 2.6.38-gentoo-r6
root (hd0,0)
kernel /boot/kernel-genkernel-x86_64-2.6.38-gentoo-r6 dolvm root=/dev/ram0 crypt_root=/dev/sda2 real_root=/dev/vg01/root
initrd /boot/initramfs-genkernel-x86_64-2.6.38-gentoo-r6

Ajustando o /etc/fstab

O último passo é ajustar o arquivo /etc/fstab para que as partições corretas sejam montadas. Temos que adicionar o /boot e o /, ficando assim:

Como fica o /etc/fstab

Nesse arquivo temos o /home, que no meu caso foi necessário pois sempre deixo a home separada. Não esqueça de retirar a linha do SWAP, pois não criamos uma.

Agora é dar reboot e curtir o novo sistema com os dados totalmente encriptados. Como estamos usando LVM2, qualquer partição adicional que você criar no futuro, já estará encriptada.

Considerações finais

É bem verdade que pelo fato de ter seu disco encriptado você terá uma perda de performance. Eu sei disso. A questão é que já uso essa configuração há 3 anos (usei desde o primeiro notebook) e como nunca tive um notebook sem ter o disco encriptado acabo não sabendo qual é a real perda de performance.

Usei o primeiro notebook durante todo o tempo, sem achar que ele estava lento por qualquer motivo. O notebook novo já é naturalmente mais rápido (Processador melhor, disco melhor, etc) então mesmo tendo novamente o disco encriptado, já percebi uma melhoria considerável na performance do novo aparelho. Então, diante disso, tá tudo bem! =)

1 comentário

Acessando os sites que você quiser no trabalho

Esse post mostra uma (dentre várias) formas de burlar os diversos bloqueios impostos por algumas empresas no que diz respeito a acesso à internet. Veremos uma forma bem simples e eficiente de navegar em sites que supostamente não poderíamos estar vendo. =)

Leia o resto deste post »

, ,

14 Comentários

Como aplicar formatação nos textos que você escreve no google talk

Update: Apenas mudança na formação. Texto justificado.

Nem todo mundo sabe, mas é possível aplicar efeitos aos textos enviados pelo google talk. Esses efeitos são:

  • Itálico
  • Negrito
  • Strike through

O que menos gente ainda sabe é como aplicar esses efeitos. =) Eu descobri como fazer por acaso (e não foi lendo o manual do google talk). Por várias vezes, lendo emails de listas de discussão de projetos vi emails usando caracteres especias para dar algum destaque às palavras do texto. Um caractere clássico é o *, usado quando queremos dar alguma ênfase ao termo escrito.

Leia o resto deste post »

Deixe um comentário

xargs: A ponte entre entrada padrão e a lista de parâmetros

Update: Apenas mudança na formatação. Texto justificado. Mudança no formato das figuras (jpg)

O xargs é um programa de linha de comando que transforma o conteúdo de sua entrada padrão na lista de parâmetros de um segundo comando, à sua escolha. Muito útil quando você tem um texto qualquer e, com cada parte desse texto, deseja executar um comando. Um exemplo clássico é o redimensionamento de uma quantidade muito grande imagens, ou a necessidade de baixar da rede uma lista de arquivos.

Leia o resto deste post »

, , ,

1 comentário