I ran dtruss on a process that launches another one: the League of Legends Launcher starts the main game process with certain arguments that I can't seem to pass via the command line.

What caught my attention was this line of dtruss output:

PID/THRD  RELATIVE  ELAPSD    CPU SYSCALL(args)          = return
9386/0x47dac:     19625    3013   1805 posix_spawn(0x2A634FC, 0x38A2A00, 0xB06A56E0)         = 0 0

I looked up the man page for posix_spawn, and it is supposed to accept 6 arguments. They are, in order, PID, /path/to/file, file_actions, aatrp, argv and envp.

I was stepping through GDB at the same time as running dtruss, so I was able to inspect the memory in question.

  • The first argument pointed to 0x000024d2, which was in the PID.
  • The second argument pointed to a char array: the executable path for LeagueofLegends
  • The third argument always points to the same thing, of which the unintelligible string representation is \026l<?

I don't know what to make of this. Ultimately I want to be able to launch this process myself via C, with my custom arguments, but I think the first step to do that is to understand how it's called by the system, and what that third argument is.

My questions are: what more can I do to find out what that third argument is (?) and is what I'm trying to do (circumventing the launcher) even possible?

It is possible directly on the Windows command-line via,

 @start "" "League of Legends.exe" "8394" "LoLLauncher.exe" "" "spectator fspectate.op.gg:4081 tjJbtRLQ/HMV7HuAxWV0XsXoRB4OmFBr 1391881421 NA1"

but on Mac this just relaunches the launcher.

For context, I've already written up a lengthy (and unanswered) question describing everything I've done.

Please let me know how I can improve this question. I've spent over 50 hours trying to figure this out on my own, brute forcing through dtruss output, and I'm just about ready to give up.


If you read the source code for dtruss:

cat `which dtruss`

You'll discover that the number of arguments is hard-coded.


 /(OPT_command && pid == $target) || 
  (OPT_pid && pid == PID) ||
  (OPT_name && NAME == strstr(NAME, execname)) ||
  (OPT_name && execname == strstr(execname, NAME)) ||
    /* set start details */
    self->start = timestamp;
    self->vstart = vtimestamp;
    self->arg0 = arg0;
    self->arg1 = arg1;
    self->arg2 = arg2;

    /* count occurances */
    OPT_counts == 1 ? @Counts[probefunc] = count() : 1;


 /* print 3 arg output - default */
    /* calculate elapsed time */
    this->elapsed = timestamp - self->start;
    self->start = 0;
    this->cpu = vtimestamp - self->vstart;
    self->vstart = 0;
    self->code = errno == 0 ? "" : "Err#";

    /* print optional fields */
    /* OPT_printid  ? printf("%5d/%d:  ",pid,tid) : 1; */
    OPT_printid  ? printf("%5d/0x%x:  ",pid,tid) : 1;
    OPT_relative ? printf("%8d ",vtimestamp/1000) : 1;
    OPT_elapsed  ? printf("%7d ",this->elapsed/1000) : 1;
    OPT_cpu      ? printf("%6d ",this->cpu/1000) : 1;

    /* print main data */
    printf("%s(0x%X, 0x%X, 0x%X)\t\t = %d %s%d\n",probefunc,self->arg0,
    OPT_stack ? ustack()    : 1;
    OPT_stack ? trace("\n") : 1;
    self->arg0 = 0;
    self->arg1 = 0;
    self->arg2 = 0;

Certain syscalls have bespoke handling (/* mmap has 6 arguments */).

I made a copy of the script and copy-pasted in a few more self->arg* and , 0x%X.

I was able to change the default to 6 args, achieving output like this:

posix_spawn(0x700003AA66B4, 0x7FF7B215BF10, 0x700003AA6570, 0x700003AA6610, 0x700003AA6720, 0x0)         = 0 0

As for why we have to copy-paste args instead of just incrementing a counter: DTrace does not support loops. I think because it's unacceptable for tracing to introduce the possibility of an infinite loop inside the kernel.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.