1. Modifying ESP directly: Don't zero out ESP! ESP is a *pointer* to stack memory. You want to leave it wherever it was when your process started (the loader will allocate your stack memory for you before your code starts). You'll get one of a variety of hardware exceptions (#GP, #SS, or #PF) if you try to push anything after setting ESP to zero.
The usual things you can do with ESP are:
a. Save and restore it by moving it to/from EBP ("setting up a stack frame")
b. Subtract and add from it (reserving local variables or cleaning up pushed arguments after a cdecl call)
c. Push/pop with it (implied use of ESP).
2. You can push a string literal onto the stack like you're trying to do, BUT you need to do it in reverse, since each time you push, the stack address *decreases*.
a. Push your string 4 bytes at a time from the end towards the beginning, including zeros for null termination. If your string is not a multiple of 4 bytes, you can use zeros to pad the first pushed value (last portion of the string). Otherwise, your method of pushing a zeroed eax will work.
b. You're already handling the little endian layout of the pushed constants properly, so that's fine.
c. Generally you have to push things in 'reverse' order even with C-style function call parameters (you push the rightmost argument first and work your way left in the argument list). Since it appears the function you're calling uses registers only, this won't matter in this case.
3. Alvaro's worry about the string being in stack memory should be OK, since it looks like your function call is passing the char* correctly in ECX. I haven't used linux for assembly programming, but this looks a lot like how calls are made in old-school DOS programming when making system calls - all of the actual function parameters are passed via registers. If you wanted to do the same thing, but call a C library function instead, you would likely need to pass your parameters on the stack, including pushing a pointer to your string onto the stack itself (no calling convention I'm aware of lets you pass strings or arrays without a pointer).
4. Hexdump's "h" character between your strings are the opcode for the "PUSH imm32" (0x68) instruction. 0x68 is the same as the lowercase 'h' in ASCII.
a. Each instruction has several different versions which may have a different opcode byte (or multiple bytes!) for each version.
b. PUSH is one of the most common instructions, so Intel reserved a lot of single-byte opcodes for it.
c. "imm32" means the instruction uses an "immediate" operand - a value stored after the opcode, in the code stream itself. imm32 means it's a 32-bit value stored in the code stream.
Edited by Nypyren, 01 September 2012 - 08:15 PM.