Really, this should be fixed in the application itself. And such applications should be open source, so that fixing the issue in the app itself should be an option. A security related application which makes this kind of mistake might make other mistakes as well, so I wouldn't trust it.
Simple interposer
But you were asking for a different way, so here is one:
#define _GNU_SOURCE #include <dlfcn.h> int __libc_start_main( int (*main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end) ) { int (*next)( int (*main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end) ) = dlsym(RTLD_NEXT, "__libc_start_main"); ubp_av[argc - 1] = "secret password"; return next(main, argc, ubp_av, init, fini, rtld_fini, stack_end); }
Compile this with
gcc -O2 -fPIC -shared -o injectpassword.so injectpassword.c -ldl
then run your process with
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start fakepasshrase
The interposer library will run this code before the main function from your application gets executed. It will replace the last command line argument by the actual password in the call to main. The command line as printed in /proc/*/cmdline (and therefore seen by tools such as ps) will still contain the fake argument, though. Obviously you'd have to make the source code and the library you compile from it readable only to yourself, so best operate in a chmod 0700 directory. And since the password isn't part of the command invocation, your bash history is safe as well.
More advanced interposer
If you want to do anything more elaborate, you should keep in mind that __libc_start_main gets executed before the runtime library has been properly initialized. So I'd suggest avoiding any function calls unless they are absolutely essential. If you want to be able to call functions to your heart's content, make sure you do so just before main itself gets invoked, after all initialization is done. For the following example I have to thank Grubermensch who pointed out how to hide a password passed as command line argument which brought getpass to my attention.
#define _GNU_SOURCE #include <dlfcn.h> #include <unistd.h> static int (*real_main) (int, char * *, char * *); static int my_main(int argc, char * * argv, char * * env) { char *pass = getpass(argv[argc - 1]); if (pass == NULL) return 1; argv[argc - 1] = pass; return real_main(argc, argv, env); } int __libc_start_main( int (*main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end) ) { int (*next)( int (*main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end) ) = dlsym(RTLD_NEXT, "__libc_start_main"); real_main = main; return next(my_main, argc, ubp_av, init, fini, rtld_fini, stack_end); }
This prompts for the password, so you no longer have to keep the interposer library a secret. The placeholder argument is reused as password prompt, so invoke this like
LD_PRELOAD=$PWD/injectpassword.so darkcoind masternode start "Password: "
Another alternative would read the password from a file descriptor (like e.g. gpg --passphrase-fd does), or from x11-ssh-askpass, or whatever.