daniel_bilar

AUTOMATED REVERSE ENGINEERING: MISTFALL ENGINE

Oct 17th, 2012
326
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.66 KB | None | 0 0
  1. AUTOMATED REVERSE ENGINEERING: MISTFALL ENGINE
  2. ----------------------------------------------
  3.  
  4. (x) 2000 Z0MBiE
  5. xlated from russian in 2001
  6.  
  7. Our efforts are directed to develop such method of executable program
  8. modification, that finding changes will require maximal amount of time.
  9. Modification means addition of the viral code to some specified program,
  10. given in the PE format. It is obvious, that main viral body should be
  11. encrypted, and metamorphic (generated) virus decryptor should be
  12. integrated with program's code.
  13.  
  14. Hence, our task is divided into 3 parts, or 3 global questions: WHAT?,
  15. WHERE? and HOW?
  16. WHAT? - is a question about instructions, those will be inserted into
  17. modifying program. It is explained in the article about metamorphism and
  18. shown in the CODEGEN metamorphic engine.
  19. WHERE? - is a question of finding places in the modifying program, our
  20. instructions should be inserted into. This is a simple task; engine's
  21. caller will resolve it in the different ways.
  22. This article shows HOW to insert our own instruction between two
  23. arbitrary instructions of the modifying program. In other words, how to
  24. decompile, modify and compile the whole program.
  25.  
  26. THEORY
  27.  
  28. So, task is to insert own instructions between PE file's ones. But,
  29. because of instructions are linked between each other, changing
  30. instructions involves change of links. But change of links involves change
  31. of other instructions, and so on, until significant part of code will be
  32. changed.
  33. For example: when inserting some instruction into block of code,
  34. offsets of all the instructions coming after insertion are increased.
  35. This means that mostly all offsets - both relative and absolute, should be
  36. fixed. While fixing relative offsets, some jxx can grow, that means that
  37. all this shit should be repeated from start.
  38.  
  39. As you can see, there are two kinds of offsets: absolute (offsets
  40. itself) and relative (jmp/call arguments). And we should find all these
  41. offsets. Absolute offsets may be found by means of analyzing PE structure,
  42. including fixup table. Finding relative offsets requires partial
  43. disassembly.
  44.  
  45. Hence, we must disassemble file into some easy-modifiable substance,
  46. change this substance, and assemble file back.
  47.  
  48. Algorithm will be formalized as an universal engine, working with PE
  49. files.
  50.  
  51. So, task is divided into 5 general steps:
  52.  
  53. 1. Load PE file into virtual addresses; allocate flag table (DWORD for
  54. each byte), and initialize it with special PE structure - related bits;
  55. in other words, perform initial PE structure analysis.
  56.  
  57. 2. Disassemble file (instruction by instruction), at the same time
  58. filling flag table with new information about instruction offsets. At this
  59. step we can not make a mistake, i.e. to recognize code as data or vise
  60. versa, because such fault is fatal; so, dual situations should be
  61. excluded, and some files may not be processed at all.
  62.  
  63. 3. Convert all known information into list of: instructions,
  64. datablocks, labels and pointers. I.e. to make our information a bit more
  65. high-level, a bit nearer to source. Such list is an easy-modifiable
  66. substance we was speaking about; it is generated because of easy
  67. manipulation with its elements.
  68.  
  69. 4. Call <user's mutator> (external relative to engine), which will
  70. modify instruction list; for example insert additional instructions or
  71. remove existing ones.
  72.  
  73. 5. Assemble file back: recalculate all offsets; fix PE structure;
  74. rebuild fixup table and so on.
  75.  
  76. DISASSEMBLING PROBLEMS
  77.  
  78. Main problem, of course, is disassembling. We have no such resources,
  79. as IDA, for example, - neither memory, neither time, nor signatures;
  80. moreover, our virus is limited to some kilobytes.
  81. And main disassembling problem is duality of some labels: they can
  82. precede both code and data. I.e. if we have some data fixup that points to
  83. some label, and there is no code instruction pointing to the same label,
  84. then we can not correctly decide if this label precedes code or data.
  85. Such misrecognizing causes the following: code, interpreted as data,
  86. will not be fixed, and will fail program when executed, commonly on such
  87. instruction as jmp, jxx or call. Vise versa, incorrectly fixed data (i.e.
  88. data interpreted as code) will with high probability cause fault too.
  89. This means that we must always correctly disitinguish code and data.
  90. Some library functions may be found by singatures, but we can not insert
  91. signature base into virus; also, jmptable-analysis can help, but only
  92. partially.
  93. We can work with only special easy-decompileable files, containing only
  94. code in the CODE section, but such files are real rarity.
  95. So, we will a bit reduce set of processing files (exclude packed ones
  96. and etc.), improve our disassembler as possible, and hope for luck.
  97.  
  98. IMPLEMENTATION
  99.  
  100. Really, there are much more little steps we should perform; and each
  101. step have an influence to the result. Here is a complete list of steps
  102. required for PE file reversing:
  103.  
  104. - check if given PE file is valid; if fixup table is present & etc.
  105. - allocate memory for virtual program image and for flag table
  106. - load into virtual addresses:
  107. 1. dos stub & pe header
  108. 2. sections
  109. - analyze PE header; process pointers; mark phsyical/virtual start/end
  110. of each section & etc.
  111. - process imports
  112. - process exports
  113. - process fixups
  114. - process resources
  115. - search for signatures (such as push ebp/mov ebp,esp) and mark 'em
  116. as for-next-analysis
  117. - mark entry point
  118. - disassemble file, instruction by instruction.
  119. algorithm is described in the article about permutation, but
  120. there are some differences:
  121. when there are no more bytes marked as for-next-analysis, we must
  122. search for labels-pointed-by-data-fixups, and check if they precedes
  123. code instructions. (see below)
  124. - build list of opcodes, labels, pointers & etc.
  125. note, that such list will contain zero-length pointers (labels), i.e.
  126. transfer is performed to higher level of abstraction, nearer to source.
  127. After converting linear arrays into list, linear arrays
  128. (memory & flag table) may be deallocated.
  129. - modify list; while debugging, i was inserting NOPs between instructions.
  130. - recalculate new virtual/physical addresses for each list entry
  131. - recalculate fixup table
  132. - recalculate pointers (i.e. rva's, fixups & relative aguments
  133. of the conditional jmps); if some jmps are grown,
  134. repeat from recalculating virtual addresses.
  135. Note, that here possible some iterations, it is normal.
  136. - assemble list (collect all stuff into single file)
  137. - write file to disk
  138. - copy overlay, if present
  139. - recalculate checksum, if non-zero
  140.  
  141. Phrases, such as "analyzing PE header" or "process imports" means that
  142. we analyzing these structures and marking labels, pointers and other
  143. special objects in the flag table with special bits. For example byte at
  144. the pe_header+28h (28h=EntryPointRva) will be marked as FLAG_DWORD |
  145. FLAG_RVA, and byte it points to will be marked as FLAG_LABEL | FLAG_CREF.
  146.  
  147. Which special objects will be present in the instruction list?
  148.  
  149. 1. Label, i.e. zero-length substance pointed by RVAs and FIXUPs.
  150. 2. RVA, or DWORD pointing to some label.
  151. 3. FIXUP, or DWORD, same as RVA, but increased by IMAGEBASE,
  152. requires to be inserted into fixup table on the final steps.
  153. 4. so-called DELTA, or difference between virtual addresses of two labels.
  154. 5. instruction
  155. 6. datablock
  156.  
  157. Now, about distinguishing code and data when only existing reference
  158. is data-reference. Algorithm: take instruction by instruction; check if it
  159. is not 00 00, FF FF, F4 (hlt), CD (int), and so on -- i.e. fail if such
  160. instruction is never present in the standard PE file. Also fail, if one of
  161. the middle bytes of the instruction is marked as label, data, or other
  162. special object. Also, if instruction is jmp,call,jxx,jecxz and so on, then
  163. it should not point into the middle of other instruction or to data.
  164. Analysis is repeated, until marked-as-code instruction, RET or JMP is
  165. found.
  166.  
  167. But, there are another kind of dual situation. You can think that such
  168. object as label can not exists in the middle of some instruction,
  169. generated by the HLL compiler. But it can. Lets examine typical situation:
  170.  
  171. AVPBASE.DLL:
  172. 100050D3 83E904 sub ecx, 4
  173. 100050D6 720C jb 100050E4
  174. 100050D8 83E003 and eax, 3
  175. 100050DB 03C8 add ecx,eax
  176. 100050DD FF2485F0500010 jmp dword ptr [100050F0+eax*4] (1)
  177. 100050E4 FF248DE8510010 jmp dword ptr [100051E8+ecx*4]
  178. 100050EB 90 nop
  179. 100050EC FF248D6C510010 jmp dword ptr [1000516C+ecx*4] (2)
  180. 100050F3 90 nop
  181. 100050F4 00510010 dd 10005100
  182. 100050F8 2C510010 dd 1000512C
  183.  
  184. As you can see, 100050F0-address, that is XREF-ed by (1), really is
  185. part of the instruction (2). Its not so hard to understand why it is so.
  186. As a result, we can not decide, if (2) is code or data. I.e. it is
  187. impossible to determine if (10050F0 == 100050F4 - 4) or (10050F0 ==
  188. 100050EC + 4). So, pointer at (1) can not be fixed and file can not be
  189. processed.
  190.  
  191. Or, another one example:
  192.  
  193. FAR.EXE:
  194. 004474D8 B8E1C24200 mov eax,0042C2E1 ; ==42C350-6Fh ; (1)
  195. 004474DD 6A00 push 00
  196. 004474DF 6800000100 push 00010000
  197. 004474E4 83C06F add eax, 6F ; ==111
  198. 004474E7 50 push eax
  199. 004474E8 E8AF180100 call 00458D9C
  200. ...
  201. 0042C2DB E8B0450200 call 00450890
  202. 0042C2E0 83C408 add esp, 08 ; (2)
  203. 0042C2E3 8D9500FFFFFF lea edx,[ebp][0FFFFFF00]
  204. ...
  205. 0042C350 55 push ebp
  206. 0042C351 8BEC mov ebp, esp
  207. 0042C353 833D5054460003 cmp d,[000465450], 03
  208.  
  209. And, in addition, another bad thing: parts of 16-bit code, inserted
  210. into 32-bit applications, such as antiviruses, formatters and so on.
  211.  
  212. The ENGINE
  213.  
  214. Though, it works. Engine is called MISTFALL, there is G.Martin's story
  215. named "With morning comes Mistfall".
  216.  
  217. Engine is written in Borland C++, but without classes or other shit.
  218. Main engine() subroutine with fucking lots of parameters comes first,
  219. followed by some internal additional subroutines. All this stuff is called
  220. kernel, and it is located in the engine.cpp & .hpp; code and constants
  221. correspondingly. Kernel calls so-called user's mutator (mutate.cpp), which
  222. must be written by engine's user. Mutator works only with instruction
  223. list. List entries describes labels, opcodes, data blocks and so on.
  224. Moreover, all the engine interface technology is taken from RPME engine.
  225. The only difference is that RPME works with blocks of viral code, and
  226. MISTFALL works with PE files.
  227.  
  228. As a result, simplest file infection is the following:
  229.  
  230. 1. Find any two non-jmp instructions; insert JMP after first instruction
  231. to the second one; insert encrypted viral body after JMP.
  232. 2. The same for decryptor.
  233. 3. The same for commands calling the decryptor.
  234.  
  235. This was only one of lots of possible variants; the more such methods
  236. we will use, the harder it will be to analyze the virus.
  237.  
  238. USAGE
  239.  
  240. Engine uses lots of memory. It can fail on incorrect file; and on
  241. normal one too. Memory allocation, same as in RPME, is provided by
  242. engine's caller. Before calling engine, you must allocate about 32 MB of
  243. memory, and write own malloc() subroutine which will provide to engine
  244. pointers to little parts of this big block. It is just kind of own heap.
  245. Engine's allocation strategy is specific: it will only allocate memory and
  246. never deallocate. But, its ok, 'coz the same big memory block will be used
  247. for each file. Really, engine uses (17*SizeOfImage) bytes of memory for
  248. linear arrays and 40 bytes of memory for each list entry. Engine's code is
  249. permutable, i.e. it satisfies most of demands described in the "Demands to
  250. engines" article. Engine can work in ring-0, but, because of lots of
  251. required memory & time, dont call it there. Time, used by engine is
  252. unpredictable; minimal time for processing one file is some minutes in
  253. realtime priority.
  254.  
  255. Obvious, that such engine cant be invoked on system events, such as
  256. openfile. You should work with file queue, passing file by file to engine
  257. in the individual thread or process.
  258.  
  259. SPECIAL FEATURES
  260.  
  261. Engine works only with standard files. I.e. all the section names must
  262. be known: such as .text, .data and so on. Otherwise file is packed or
  263. whatever, and will not be processed.
  264. For all the standard files it is accepted that code exists only in the
  265. first section; this allows us to increase disassembly quality.
  266.  
  267. SIGNATURE LIBRARY
  268.  
  269. This is another external subroutine, performing 2 tasks:
  270. 1. on start: search for known codeblocks, and
  271. 2. add existing codeblocks into library
  272. I.e. before disassembling signature manager will mark known codeblocks
  273. as CODE, for-next-analysis, and, on exit, it will update signature library
  274. with new information.
  275.  
  276. When applied to viruses, signature library will be created per each
  277. local machine, performing kind of self-education, with each new file.
  278.  
  279. Signature here means some constant code bytes, not containing fixups
  280. and relative offsets.
  281.  
  282. ENGINE INTERFACE
  283.  
  284. // return: 0==success, non-zero error codes listed in ENGINE.HPP
  285. int __cdecl engine(
  286. DWORD user_arg, // user's parameter (whatever)
  287. BYTE* buf, // PE file
  288. DWORD inbufsize, // initial size of file
  289. DWORD* outbufsize, // ptr to resulting size of file
  290. DWORD maxbufsize, // maximal allocated buf size
  291. int __cdecl user_disasm(DWORD,BYTE*), // length-disassembler
  292. void* __cdecl user_malloc(DWORD,DWORD), // memory allocator
  293. DWORD __cdecl user_random(DWORD,DWORD), // randomer
  294. int __cdecl user_mutate( // mutator
  295. DWORD user_arg, // (described below)
  296. PE_HEADER* pe,
  297. PE_OBJENTRY* oe,
  298. hooy* root,
  299. int __cdecl (*)(DWORD user_arg,BYTE*),
  300. void* __cdecl (*)(DWORD user_arg,DWORD),
  301. DWORD __cdecl (*)(DWORD user_arg,DWORD)
  302. ),
  303. int __cdecl user_sigman( // signature manager or NULL
  304. DWORD user_arg,
  305. PE_HEADER* pe,
  306. PE_OBJENTRY* oe,
  307. BYTE* memb,
  308. DWORD* flag,
  309. DWORD action) // 0/1
  310. ); //engine
  311.  
  312. int __cdecl mutate( //mutator
  313. DWORD user_arg, // user's parameter
  314. PE_HEADER* pe, // pe header
  315. PE_OBJENTRY* oe, // pe objtable
  316. hooy* root, // list root
  317. int __cdecl user_disasm(DWORD user_arg,BYTE*),
  318. void* __cdecl user_malloc(DWORD user_arg,DWORD),
  319. DWORD __cdecl user_random(DWORD user_arg,DWORD)
  320. ); //mutate
  321.  
  322. int __cdecl sigman( //signature manager
  323. DWORD user_arg, // user parameter
  324. PE_HEADER* pe, // pe header
  325. PE_OBJENTRY* oe, // pe objtable
  326. BYTE* memb, // virtual memory block to work with
  327. DWORD* flag, // flag table
  328. DWORD action // 0==before(mark), 1==after(update)
  329. ); //sigman
  330.  
  331. This shit shown above... of course it doesnt means, that you should
  332. program in C++ to use engine. Engine may be called both from asm & cpp.
  333.  
  334. EXAMPLES
  335.  
  336. Mistfall engine was used in the Z-10 virus, all sources are published
  337. in the TZ #1 e-zine.
  338.  
  339. * * *
Advertisement
Add Comment
Please, Sign In to add comment