@@ -246,4 +246,147 @@ class Subs(Instruction):
246246### C6.2.307 RET (page 2203) (11010110010111110000000000000000)
247247
248248class Ret (Instruction ):
249- encoding = [RetEncoding ]
249+ encoding = [RetEncoding ]
250+
251+
252+
253+ class MultipleInstr (object ):
254+ INSTRUCTION_SIZE = 4
255+
256+ def __init__ (self , init_instrs = ()):
257+ self .instrs = {}
258+ self .labels = {}
259+ self .expected_labels = {}
260+ # List of all labeled jump already resolved
261+ # Will be used for 'relocation'
262+ self .computed_jump = []
263+ self .size = 0
264+ for i in init_instrs :
265+ self += i
266+
267+ def get_code (self ):
268+ if self .expected_labels :
269+ raise ValueError ("Unresolved labels: {0}" .format (self .expected_labels .keys ()))
270+ return b"" .join ([x [1 ].get_code () for x in sorted (self .instrs .items ())])
271+
272+ def add_instruction (self , instruction ):
273+ # if isinstance(instruction, Label):
274+ # return self.add_label(instruction)
275+ # # Change DelayedJump to LabeledJump ?
276+ # if isinstance(instruction, DelayedJump):
277+ # return self.add_delayed_jump(instruction)
278+ if isinstance (instruction , Instruction ):
279+ self .instrs [self .size ] = instruction
280+ self .size += self .INSTRUCTION_SIZE
281+ return
282+ raise ValueError ("Don't know what to do with {0} of type {1}" .format (instruction , type (instruction )))
283+
284+ def add_label (self , label ):
285+ if label .name not in self .expected_labels :
286+ # Label that have no jump before definition
287+ # Just registed the address of the label
288+ self .labels [label .name ] = self .size
289+ return
290+ # Label with jmp before definition
291+ # Lot of stuff todo:
292+ # Find all delayed jump that refer to this jump
293+ # Replace them with real jump
294+ # If size of jump < JUMP_SIZE: relocate everything we can
295+ # Update expected_labels
296+ for jump_to_label in self .expected_labels [label .name ]:
297+ if jump_to_label .offset in self .instrs :
298+ raise ValueError ("WTF REPLACE EXISTING INSTR..." )
299+ distance = self .size - jump_to_label .offset
300+ real_jump = jump_to_label .type (distance )
301+ self .instrs [jump_to_label .offset ] = real_jump
302+ self .computed_jump .append ((jump_to_label .offset , self .size ))
303+ for i in range (self .JUMP_SIZE - len (real_jump .get_code ())):
304+ self .instrs [jump_to_label .offset + len (real_jump .get_code ()) + i ] = _NopArtifact ()
305+ del self .expected_labels [label .name ]
306+ self .labels [label .name ] = self .size
307+ if not self .expected_labels :
308+ # No more un-resolved label (for now): time to reduce the shellcode
309+ self ._reduce_shellcode ()
310+
311+ def add_delayed_jump (self , jump ):
312+ dst = jump .label
313+ if dst in self .labels :
314+ # Jump to already defined labels
315+ # Nothing fancy: get offset of label and jump to it !
316+ distance = self .size - self .labels [dst ]
317+ jump_instruction = jump .type (- distance )
318+ self .computed_jump .append ((self .size , self .labels [dst ]))
319+ return self .add_instruction (jump_instruction )
320+ # Jump to undefined label
321+ # Add label to expected ones
322+ # Add jump info -> offset of jump | type
323+ # Reserve space for call !
324+ jump .offset = self .size
325+ self .expected_labels .setdefault (dst , []).append (jump )
326+ self .size += self .JUMP_SIZE
327+ return
328+
329+ def merge_shellcode (self , other ):
330+ shared_labels = set (self .labels ) & set (other .labels )
331+ if shared_labels :
332+ raise ValueError ("Cannot merge shellcode: shared labels {0}" .format (shared_labels ))
333+ for offset , instr in sorted (other .instrs .items ()):
334+ for label_name in [name for name , label_offset in other .labels .items () if label_offset == offset ]:
335+ self .add_instruction (Label (label_name ))
336+ self .add_instruction (instr )
337+
338+ def __iadd__ (self , other ):
339+ if isinstance (other , MultipleInstr ):
340+ self .merge_shellcode (other )
341+ elif isinstance (other , basestring ):
342+ self .assemble (other )
343+ else :
344+ self .add_instruction (other )
345+ return self
346+
347+ def assemble (self , code ):
348+ for instr in assemble_instructions_generator (code ):
349+ self .add_instruction (instr )
350+
351+
352+ def split_in_instruction (str ):
353+ for line in str .split ("\n " ):
354+ if not line :
355+ continue
356+ for instr in line .split (";" ):
357+ if not instr :
358+ continue
359+ yield instr .strip ()
360+
361+ def assemble_instructions_generator (str ):
362+ for instr in split_in_instruction (str ):
363+ data = instr .split (" " , 1 )
364+ mnemo , args_raw = data [0 ], data [1 :]
365+ try :
366+ instr_object = globals ()[mnemo .capitalize ()]
367+ except :
368+ raise ValueError ("Unknow mnemonic <{0}>" .format (mnemo ))
369+
370+ # if issubclass(instr_object, Raw):
371+ # # Raw should received the raw buffer as it expect encoded hex
372+ # # The transformation may transform 'raw 9090' (nopnop) as 0n9090
373+ # # If other fake-instr need this : make a class attribute
374+ # yield instr_object(*args_raw)
375+ # continue
376+
377+ args = []
378+ if args_raw :
379+ for arg in args_raw [0 ].split ("," ):
380+ arg = arg .strip ()
381+ try :
382+ arg = int (arg , 0 )
383+ except ValueError :
384+ pass
385+ args .append (arg )
386+ yield instr_object (* args )
387+
388+ def assemble (str ):
389+ """Play test"""
390+ shellcode = MultipleInstr ()
391+ shellcode += str
392+ return shellcode .get_code ()
0 commit comments