In assembly there is only one explicit reference to the target function, and that's where its address is moved to the variable at point 1 (you can see that the reference is marked 'o' (offset) and not 'p' ([procedure] call)). While IDA could in theory track stack var assignments and add xrefs also at the points the variable is used for an indirect call (2 and 3), this:
- would increase analysis time for negligible benefit (an experienced user can easily see that
var_4 is called twice)
- is prone to false positives (e.g. if there's a function call before initialization and use of the variable, you can't be sure that it's not overwritten by the side-effects of the function)
- is easily defeatable (e.g. do some simple math on the pointer and IDA will be fooled).
So it's better to add only explicit cross-references and let the human decide if extra ones need to be added manually.
That said, IDA does some limited amount of register tracking to handle the most common situations, e.g.:
mov esi, ds:__imp__sprintf
lea edx, [esp+0BCh+szOffset]
push offset Format ; "%08X"
push edx ; Dest
call esi ; __imp__sprintf
You can see that it added a comment at the
call esi instruction with the actual destination loaded earlier into
esi. (it also adds a call xref in such case).
the reference? The application stores the address of the function in
[ebp+var_4]and then calls whatever is at
[ebp+var_4]2 times. In this case it's the function it moved there. The original code could have been something like that: pastebin.com/0S83zEhp
sub_4011C0function has only one reference. In reference I mean IDA only recognizes one call to the function which is from
DATA XREF: sub_4011D0+5o