ie I would have expected to see 'ptrace', 'scanf' and 'printf' in the strings output, but they must be obfuscated in some way (otherwise I guess there's no point using the dlopen/dlsym trick at all.)
I only see one call to dlsym (at 8048506), so it seems to me the program is doing something tricky to build each symbol name string and then calling a routine there to dlsym() it.
That's about where my x86-fu fails me, though, and I remember I should be working on other things. :/
80484e0: 83 f2 55 xor $0x55,%edx
>>> def ascii_to_xored_hex(s, xorval):
... return ''.join(['%02x' % (ord(c) ^ xorval) for c in s])
>>> ascii_to_xored_hex('ptrace', 0x55)
>>> ascii_to_xored_hex('printf', 0x55)
mrj10@mjlap:~/Downloads$ xxd hackme | grep 2521
0000680: 008d 7600 2521 2734 3630 0090 2636 343b ..v.%!'460..&64;
mrj10@mjlap:~/Downloads$ xxd hackme | grep 2527
0000690: 3300 6690 2527 3c3b 2133 0090 6afb 4c8d 3.f.%'<;!3..j.L.
To the disassembler, these strings look like and-xor sequences. e.g., for 'ptrace':
8048684: 25 21 27 34 36 and $0x36342721,%eax
8048689: 30 00 xor %al,(%eax)
I have a hardcopy of Paul Carter's "PC Assembly Language" book at home that I started reading once but never finished. Some day... :/