77from pathlib import Path
88from subprocess import check_output
99from random import randint
10+ import keystone
1011
1112import click
1213
@@ -234,7 +235,7 @@ def _symlink_modify_guestfs(g, file_path, file):
234235 g .chmod (0o777 , linkpath )
235236
236237
237- def _modify_guestfs (g , file_path , file , project_dir ):
238+ def _modify_guestfs (g , file_path , file , project_dir , config ):
238239 """
239240 Given a guestfs handle, a file path, and a file dict, perform the specified action on the guestfs filesystem.
240241 If the action is unsupported or fails, we'll print details and raise an exception.
@@ -282,7 +283,7 @@ def _modify_guestfs(g, file_path, file, project_dir):
282283 new_file = file
283284 new_file ["host_path" ] = m
284285 new_file_path = str (Path (folder , Path (m ).name ))
285- _modify_guestfs (g , new_file_path , new_file , project_dir )
286+ _modify_guestfs (g , new_file_path , new_file , project_dir , config )
286287 return
287288
288289 try :
@@ -389,12 +390,56 @@ def _modify_guestfs(g, file_path, file, project_dir):
389390 elif action == "binary_patch" :
390391 file_offset = file .get ("file_offset" )
391392 bytes_hex = file .get ("hex_bytes" )
393+ asm = file .get ("asm" )
394+
395+ if bool (bytes_hex ) == bool (asm ):
396+ raise ValueError ("Exactly one of 'hex_bytes' or 'asm' must be specified for binary_patch" )
397+
398+ if asm :
399+ arch_map = {
400+ "armel" : getattr (keystone , "KS_ARCH_ARM" ),
401+ "aarch64" : getattr (keystone , "KS_ARCH_ARM64" ),
402+ "mipsel" : getattr (keystone , "KS_ARCH_MIPS" ),
403+ "mipseb" : getattr (keystone , "KS_ARCH_MIPS" ),
404+ "mips64el" : getattr (keystone , "KS_ARCH_MIPS" ),
405+ "mips64eb" : getattr (keystone , "KS_ARCH_MIPS" ),
406+ "intel64" : getattr (keystone , "KS_ARCH_X86" ),
407+ }
408+ mode_map = {
409+ "aarch64" : getattr (keystone , "KS_MODE_LITTLE_ENDIAN" ) | getattr (keystone , "KS_MODE_64" ),
410+ "mipsel" : getattr (keystone , "KS_MODE_MIPS32" ) | getattr (keystone , "KS_MODE_LITTLE_ENDIAN" ),
411+ "mipseb" : getattr (keystone , "KS_MODE_MIPS32" ) | getattr (keystone , "KS_MODE_BIG_ENDIAN" ),
412+ "mips64el" : getattr (keystone , "KS_MODE_MIPS64" ) | getattr (keystone , "KS_MODE_LITTLE_ENDIAN" ),
413+ "mips64eb" : getattr (keystone , "KS_MODE_MIPS64" ) | getattr (keystone , "KS_MODE_BIG_ENDIAN" ),
414+ "intel64" : getattr (keystone , "KS_MODE_64" ),
415+ }
416+ arch = config ["core" ]["arch" ]
417+ ks_arch = arch_map .get (arch )
418+ if ks_arch is None :
419+ raise ValueError (f"Unsupported arch: { arch } " )
420+
421+ # Handle ARM mode selection
422+ if arch == "armel" :
423+ user_mode = file .get ("mode" , "arm" )
424+ if user_mode == "thumb" :
425+ ks_mode = getattr (keystone , "KS_MODE_THUMB" ) | getattr (keystone , "KS_MODE_LITTLE_ENDIAN" )
426+ else :
427+ ks_mode = getattr (keystone , "KS_MODE_ARM" ) | getattr (keystone , "KS_MODE_LITTLE_ENDIAN" )
428+ else :
429+ ks_mode = mode_map .get (arch )
430+ if ks_mode is None :
431+ raise ValueError (f"Unsupported mode for arch: { arch } " )
392432
393- # Convert hex string to bytes
394- patch_bytes = bytes .fromhex (bytes_hex .replace (" " , "" ))
395- target_path = file_path
433+ ks = keystone .Ks (ks_arch , ks_mode )
434+ encoding , _ = ks .asm (asm )
435+ patch_bytes = bytes (encoding )
436+ else :
437+ patch_bytes = bytes .fromhex (bytes_hex .replace (" " , "" ))
396438
397- # Open the file and patch it
439+ if file_offset is None :
440+ raise ValueError ("binary_patch requires file_offset" )
441+
442+ target_path = file_path
398443 p = g .adjust_path (target_path )
399444 if not os .path .isfile (p ):
400445 raise FileNotFoundError (f"Target file for binary_patch not found: { target_path } " )
@@ -477,15 +522,15 @@ def resolve_symlink_path(g, path):
477522 # resolved_file_path = resolve_symlink_path(g, file_path)
478523 # resolved_file_path = os.path.dirname(resolved_file_path) + '/' + os.path.basename(file_path)
479524 if resolved_file_path := file_path :
480- _modify_guestfs (g , resolved_file_path , file , project_dir )
525+ _modify_guestfs (g , resolved_file_path , file , project_dir , config )
481526
482527 # Next, we'll do any move_from operations
483528 move_from_files = {k : v for k , v in files .items () if v ["type" ] == "move_from" }
484529 sorted_move_from_files = sorted (
485530 move_from_files .items (), key = lambda x : len (files [x [0 ]])
486531 )
487532 for file_path , file in sorted_move_from_files :
488- _modify_guestfs (g , file_path , file , project_dir )
533+ _modify_guestfs (g , file_path , file , project_dir , config )
489534
490535 # Now we'll do everything, except symlinks
491536 sorted_files = {
@@ -502,7 +547,7 @@ def resolve_symlink_path(g, path):
502547 # resolved_file_path = file_path
503548 # if resolved_file_path != file_path:
504549 # print(f"WARNING: Resolved file path {file_path} to {resolved_file_path}")
505- _modify_guestfs (g , resolved_file_path , file , project_dir )
550+ _modify_guestfs (g , resolved_file_path , file , project_dir , config )
506551
507552 # Create symlinks after everything else because guestfs requires destination to exist
508553 # move_from_files = {k: v for k, v in files.items() if v["type"] == "symlink"}
@@ -513,7 +558,7 @@ def resolve_symlink_path(g, path):
513558 move_from_files .items (), key = lambda x : len (files [x [0 ]]["target" ])
514559 )
515560 for file_path , file in sorted_move_from_files :
516- _modify_guestfs (g , file_path , file , project_dir )
561+ _modify_guestfs (g , file_path , file , project_dir , config )
517562
518563 # Sanity checks. Does guest still have a /bin/sh? Is there a /igloo directory?
519564 if (
0 commit comments