First, some real stats with explanations:
Code:
266237 - 74 // JZ short
270527 - C0 // frequent modr/m byte, like for xor eax,eax
287474 - FE // eg. 8B8DE4FEFFFF mov ecx,[ebp][-0000011C]
295194 - 0F // extension byte, eg. 0FB7C0 movzx eax,ax
317843 - 85 // 85D2 test edx,edx (test for zero)
326395 - 4D // modr/m with ecx and ebp: 8B4DF8 mov ecx,[ebp][-08] (likely VS specific)
351547 - 04
400960 - 6A // push byte: 6A01 push 001
407908 - 50 // push eax
407956 - 83 // arithmetics: 83E010 and eax,010; 83C820 or eax,020; also add,sub,cmp
437390 - 08
481820 - 8D // 8D45CC lea eax,[ebp][-34]
489930 - 45 // modr/m with eax and ebp: 89450C mov [ebp][0C],eax
538288 - 02
562981 - E8 // call
656140 - 89 // mov mem,reg: 894610 mov [esi][10],eax
838642 - 01
839087 - 90 // nop
1864004 - FF
1950362 - 8B // mov reg,reg: 8BF9 mov edi,ecx
4492195 - 00 // FE,FF,00,01,02,04,08 are mostly from constants, not really code-specific
31752798 - total
90185 - 6A 01 // 6A01 push 001
90282 - 89 45 // 89450C mov [ebp][0C],eax
90326 - FA 02 // sample-specific
91292 - 5D C2 // 5D C20400 pop ebp; retn 00004
92554 - 83 C4 // 83C414 add esp,014
92807 - 85 C0 // 85C0 test eax,eax
96856 - 90 8B
97879 - C7 45 // C745D401000000 mov d,[ebp][-2C],000000001
113417 - 00 90
119613 - 8B 45 // 8B4508 mov eax,[ebp][08]
132747 - 02 00
143732 - 45 FC // [ebp-4]: 8B45FC mov eax,[ebp][-04]
173247 - 8B 4D // 8B4D0C mov ecx,[ebp][0C] (VS specific; ebp stack frame + ecx="this")
184642 - FF 8B // C745FCFFFFFFFF 8BC6 mov d,[ebp][-04],0FFFFFFFF mov eax,esi
207634 - 01 00
242310 - 00 8B // 8B9098000000 8BCE mov edx,[eax][00000098] mov ecx,esi
599218 - 90 90 // nop nop
797362 - FF FF
2456286 - 00 00
31752798 - total
> what's the better ways to detect x86 code?
There's no perfect way even with complex reverse-engineering tools,
but for a rough estimation it seems reasonable to parse the section
layouts in exe headers - at least it allows to discard data sections.
> in particular how to find exact boundaries of code,
> not aligned to 16 kb chunks?
Why, by disassembling - x86 parsing can be done by a simple
state machine, so its not really slow or anything.
> now i think about searching in boundary chunks for
> first/last E8 instruction with argument of form 0x00...
> or 0xFF...
There're also function prologs/epilogs and alignment paddings
(with "nops" of various lengths).
E8 is actually much less necessary for the code - there's
FF15 for import calls, so its not unlikely to encounter
a case with lots of (eg.unrolled) code and no E8.