http://bugs.winehq.org/show_bug.cgi?id=10273
--- Comment #18 from Anastasius Focht focht@gmx.net 2007-11-05 15:46:21 --- Hello,
--- quote --- I don't think we can drop -fPIC, some distros don't allow text relocations, and we can't guarantee that the specified load address will always be free. --- quote ---
Pity .. so we have to think about other solutions.
--- quote --- And changing the location produces even worse c2 results for gdi32. --- quote ---
Thats something I wondered about too. I disassembled and compared the entry code of the first checked exports - it did not change at all: same stack frame, same location of __i686.get_pc_thunk.bx(). Usually within 8 instruction range, which makes it "bad guy". So c2 must include additional information from somewhere.
--- quote --- So it seems it is the fact that there is a call at all in these first 8 instructions that safedisc considers "bad". --- quote ---
True, any instruction which transfers execution flow within that range seems to be considered bad by analyzer (jmp, jcond, call ...) - regardless of the destination (within own .text section other outside). Though "outside" destinations seem to have "modifier" effect on c3.
They probably analyzed windows API entry code and made up some statistics how many instructions are needed to safely detect any "hostile" execution flow transfer (jump trampolines) without having too much false positives. Besides there are other ways to trace/hook system calls without the need to overwrite opcodes on entry code ;-)
I did some more testing with my custom "backdoor" dll and presented the analyzer some lengthy code sequences (which do just nothing but waste instructions + cpu cycles).
"t_x" = testcase x "exp" = exports
t_1: 10 exp, seq len: 20 instr, standard stack frame (ebp), register math t_2: 9 exp, seq len: 20 instr, standard stack frame (ebp) 1 exp, seq len: 6 instr, standard stack frame (ebp) t_3: 5 exp, seq len: 20 instr, standard stack frame (ebp) 5 exp, seq len: 6 instr, standard stack frame (ebp) t_4: 10 exp, seq len: 10 instr, standard stack frame (ebp), 9 x nop + 1 x ret
t1 t2 t3 t4 condition (cx < th) -------------------------------------------------------------------- c1: 0x00 0x0A 0x32 0x00 0x5F c2: 0x00 0x01 0x19 0x00 0x3C c3: 0x00 0x00 0x00 0x00 0x5A
The instruction classes used seem to have no influence. The disassembler only treats any control transfer instructions special (including ret).
So if we pad the entry code at least 8-9 instructions long with NOP or other padding opcodes we should get around these idiotic checks.
For testing I abused the wine tools and wine's relay thunk feature. Seemed to me quickest solution for testing, sorry for that ;-)
--- snip ---
diff --git a/tools/winebuild/spec32.c b/tools/winebuild/spec32.c index 8df5b85..9164827 100644 --- a/tools/winebuild/spec32.c +++ b/tools/winebuild/spec32.c @@ -124,7 +124,16 @@ static void output_relay_debug( DLLSPEC *spec )
output( "\t.align %d\n", get_alignment(4) ); output( ".L__wine_spec_relay_entry_point_%d:\n", i ); - + + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + output( "\tnop\n" ); + if (odp->flags & FLAG_REGISTER) output( "\tpushl %%eax\n" ); else --- snip ---
(rebuild)
The results seem very positive to the current state of affair. With the "hacked" relay thunk (first 8 bytes in thunk are NOP) the following stats are generated:
*** Note: you have to run the target with +relay to let this test work ***
kernel32 user32 gdi32 condition (cx < threshold) -------------------------------------------------------------------- c1: 0x12 0x04 0x14 0x5F c2: 0x02 0x00 0x04 0x3C c3: 0x00 0x00 0x00 0x5A
So if wine could make default thunks/entry code with just enough no-op opcode padding before jumping to real thing we can keep -fPIC. This "feature" should be tested thoroughly, maybe some other brain damaged PE protectors/virii might find this entry code somewhat suspicious.
Though this kind of entry code is not uncommon, Micro$oft has such kind of API entry padding too to support "hot patching" technique (their infamous "mov edi, edi").
Regards