root/tools/finsig_thumb2.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bprintf
  2. add_blankline
  3. write_output
  4. add_prop_hit
  5. get_misc_val
  6. get_misc_val_value
  7. save_misc_val
  8. save_misc_val_blobs
  9. find_saved_sig_index
  10. find_saved_sig
  11. get_saved_sig_val
  12. find_saved_sig_index_by_adr
  13. find_saved_sig_by_val
  14. save_sig
  15. add_func_name
  16. save_sig_with_j
  17. find_next_sig_call
  18. is_sig_call
  19. init_disasm_sig_ref
  20. sig_match_str_r0_call
  21. sig_match_reg_evp
  22. sig_match_reg_evp_table
  23. sig_match_reg_evp_alt2
  24. sig_match_unreg_evp_table
  25. sig_match_evp_table_veneer
  26. sig_match_get_nd_value
  27. sig_match_get_current_exp
  28. sig_match_get_current_nd_value
  29. sig_match_imager_active_callback
  30. sig_match_imager_active
  31. sig_match_screenlock
  32. sig_match_screenunlock
  33. sig_match_log_camera_event
  34. sig_match_physw_misc
  35. sig_match_kbd_read_keys
  36. sig_match_get_kbd_state
  37. sig_match_create_jumptable
  38. sig_match_take_semaphore_strict
  39. sig_match_get_semaphore_value
  40. sig_match_stat
  41. sig_match_open
  42. sig_match_open_gt_57
  43. sig_match_close_gt_57
  44. sig_match_umalloc
  45. sig_match_ufree
  46. sig_match_deletefile_fut
  47. sig_match_closedir
  48. sig_match_strrchr
  49. sig_match_time
  50. sig_match_strncpy
  51. sig_match_strncmp
  52. sig_match_strtolx
  53. sig_match_exec_evp
  54. sig_match_fgets_fut
  55. sig_match_log
  56. sig_match_pow_dry_52
  57. sig_match_pow_dry_gt_52
  58. sig_match_sqrt
  59. sig_match_get_drive_cluster_size
  60. sig_match_mktime_ext
  61. sig_match_rec2pb
  62. sig_match_get_parameter_data
  63. sig_match_prepdir_x
  64. sig_match_prepdir_1
  65. sig_match_prepdir_0
  66. sig_match_mkdir
  67. sig_match_add_ptp_handler
  68. sig_match_qsort
  69. sig_match_deletedirectory_fut
  70. sig_match_set_control_event
  71. sig_match_displaybusyonscreen_52
  72. sig_match_undisplaybusyonscreen_52
  73. sig_match_pt_playsound
  74. sig_match_try_take_sem_dry_gt_58
  75. sig_match_wait_all_eventflag_strict
  76. sig_match_get_num_posted_messages
  77. sig_match_set_hp_timer_after_now
  78. sig_match_transfer_src_overlay
  79. sig_match_exmem_vars
  80. sig_match_zicokick_52
  81. sig_match_zicokick_gt52
  82. sig_match_zicokick_copy
  83. sig_match_zicokick_values
  84. sig_match_enable_hdmi_power
  85. sig_match_disable_hdmi_power
  86. sig_match_levent_table
  87. sig_match_flash_param_table
  88. sig_match_jpeg_count_str
  89. sig_match_misc_flag_named
  90. sig_match_cam_has_iris_diaphragm
  91. sig_match_cam_uncached_bit
  92. sig_match_physw_event_table
  93. sig_match_uiprop_count
  94. sig_match_get_canon_mode_list
  95. sig_match_zoom_busy
  96. sig_match_focus_busy
  97. sig_match_aram_size
  98. sig_match_aram_start
  99. sig_match_aram_start2
  100. sig_match__nrflag
  101. sig_match_var_struct_get
  102. sig_match_rom_ptr_get
  103. find_call_near_str
  104. sig_match_near_str
  105. sig_match_prop_string
  106. is_immediate_ret_sub
  107. sig_match_named_last
  108. sig_match_named_save_sig
  109. sig_match_named
  110. run_sig_rules
  111. add_event_proc
  112. process_reg_eventproc_call
  113. process_eventproc_table_call
  114. process_createtask_call
  115. save_ptp_handler_func
  116. process_add_ptp_handler_call
  117. add_generic_func_match
  118. add_generic_sig_match
  119. find_generic_funcs
  120. find_ctypes
  121. print_misc_val_makefile
  122. output_firmware_vals
  123. print_platform_misc_val_undef
  124. output_platform_vals
  125. output_propcases
  126. output_exmem_types
  127. print_misc_val_comment
  128. get_physw_table_entry
  129. find_physw_table_entry
  130. find_physw_table_max
  131. write_physw_event_table_dump
  132. print_kval
  133. add_kinfo
  134. add_kmval
  135. kinfo_compare
  136. print_kmvals
  137. do_km_vals
  138. output_physw_vals
  139. output_modemap
  140. compare_sig_names
  141. compare_func_addresses
  142. write_funcs
  143. write_func_lists
  144. print_other_stubs_min
  145. print_stubs_min_def
  146. find_other_stubs_min
  147. print_results
  148. write_stubs
  149. main

   1 #include <stdlib.h>
   2 #include <stdio.h>
   3 #include <stdint.h>
   4 #include <string.h>
   5 #include <time.h>
   6 #include <stdarg.h>
   7 
   8 #include <inttypes.h>
   9 
  10 #include <capstone.h>
  11 
  12 
  13 #include "stubs_load.h"
  14 #include "firmware_load_ng.h"
  15 #include "ptp_op_names.h"
  16 
  17 // arbitrary standardized constant for search "near" a string ref etc
  18 // could base on ADR etc reach
  19 #define SEARCH_NEAR_REF_RANGE 1024
  20 
  21 #define SIG_NEAR_OFFSET_MASK    0x00FF
  22 #define SIG_NEAR_COUNT_MASK     0xFF00
  23 #define SIG_NEAR_COUNT_SHIFT    8
  24 #define SIG_NEAR_REV            0x10000
  25 #define SIG_NEAR_INDIRECT       0x20000
  26 #define SIG_NEAR_JMP_SUB        0x40000
  27 #define SIG_NEAR_AFTER(max_insns,n) (((max_insns)&SIG_NEAR_OFFSET_MASK) \
  28                                 | (((n)<<SIG_NEAR_COUNT_SHIFT)&SIG_NEAR_COUNT_MASK))
  29 #define SIG_NEAR_BEFORE(max_insns,n) (SIG_NEAR_AFTER(max_insns,n)|SIG_NEAR_REV)
  30 
  31 /* copied from finsig_dryos.c */
  32 char    out_buf[32*1024] = "";
  33 int     out_len = 0;
  34 char    hdr_buf[32*1024] = "";
  35 int     hdr_len = 0;
  36 int     out_hdr = 1;
  37 
  38 FILE *out_fp;
  39 
  40 void bprintf(char *fmt, ...)
  41 {
  42     va_list argp;
  43     va_start(argp, fmt);
  44 
  45     if (out_hdr)
  46         hdr_len += vsprintf(hdr_buf+hdr_len,fmt,argp);
  47     else
  48         out_len += vsprintf(out_buf+out_len,fmt,argp);
  49 
  50     va_end(argp);
  51 }
  52 
  53 void add_blankline()
  54 {
  55     if (strcmp(hdr_buf+hdr_len-2,"\n\n") != 0)
  56     {
  57         hdr_buf[hdr_len++] = '\n';
  58         hdr_buf[hdr_len] = 0;
  59     }
  60 }
  61 
  62 void write_output()
  63 {
  64     add_blankline();
  65     if (out_fp)
  66     {
  67         fprintf(out_fp,"%s",hdr_buf);
  68         fprintf(out_fp,"%s",out_buf);
  69     }
  70 }
  71 
  72 // Master list of functions / addresses to find
  73 
  74 #define DONT_EXPORT    0x01
  75 #define OPTIONAL       0x02
  76 #define UNUSED         0x04
  77 #define BAD_MATCH      0x08
  78 #define EV_MATCH       0x10
  79 #define LIST_ALWAYS    0x20
  80 // force an arm veneer (NHSTUB2)
  81 #define ARM_STUB       0x80
  82 
  83 typedef struct {
  84     char        *name;
  85     int         flags;
  86     uint32_t    val;
  87 } sig_entry_t;
  88 
  89 int next_sig_entry = 0;
  90 
  91 #define MAX_SIG_ENTRY  5000
  92 
  93 sig_entry_t  sig_names[MAX_SIG_ENTRY] =
  94 {
  95     // Order here currently has no effect on search order, but mostly copied from finsig_dryos which did
  96     { "ExportToEventProcedure_FW", UNUSED|DONT_EXPORT },
  97     { "RegisterEventProcedure", UNUSED|DONT_EXPORT },
  98     { "RegisterEventProcedure_alt1", UNUSED|DONT_EXPORT },
  99     { "RegisterEventProcedure_alt2", UNUSED|DONT_EXPORT },
 100     { "RegisterEventProcTable", UNUSED|DONT_EXPORT },
 101     { "UnRegisterEventProcTable", UNUSED|DONT_EXPORT },
 102     { "UnRegisterEventProcedure", UNUSED|DONT_EXPORT },
 103     { "PrepareDirectory_1", UNUSED|DONT_EXPORT },
 104     { "PrepareDirectory_x", UNUSED|DONT_EXPORT },
 105     { "PrepareDirectory_0", UNUSED|DONT_EXPORT },
 106     { "CreateTaskStrictly", UNUSED|DONT_EXPORT },
 107     { "CreateTaskStrictly_alt", UNUSED|DONT_EXPORT },
 108     { "CreateJumptable", UNUSED },
 109     { "_uartr_req", UNUSED },
 110     { "StartRecModeMenu", UNUSED },
 111     { "LogCameraEvent", UNUSED|DONT_EXPORT },
 112     { "getImageDirName", UNUSED|DONT_EXPORT },
 113 
 114     { "AllocateMemory", UNUSED|LIST_ALWAYS },
 115     { "AllocateUncacheableMemory" },
 116     { "Close" },
 117     { "CreateBinarySemaphore" },
 118     { "CreateCountingSemaphore", UNUSED|LIST_ALWAYS },
 119     { "CreateTask" },
 120     { "DebugAssert", OPTIONAL|LIST_ALWAYS },
 121     { "DeleteDirectory_Fut" },
 122     { "DeleteFile_Fut" },
 123     { "DeleteSemaphore", UNUSED|LIST_ALWAYS },
 124     { "DoAELock" },
 125     { "DoAFLock" },
 126     { "EnterToCompensationEVF" },
 127     { "ExecuteEventProcedure", ARM_STUB },
 128     { "ExitFromCompensationEVF" },
 129     { "ExitTask" },
 130     { "ExpCtrlTool_StartContiAE" },
 131     { "ExpCtrlTool_StopContiAE" },
 132     { "Fclose_Fut" },
 133     { "Feof_Fut" },
 134     { "Fflush_Fut" },
 135     { "Fgets_Fut" },
 136     { "Fopen_Fut" },
 137     { "Fread_Fut" },
 138     { "FreeMemory", UNUSED|LIST_ALWAYS },
 139     { "FreeUncacheableMemory" },
 140     { "Fseek_Fut" },
 141     { "Fwrite_Fut" },
 142     { "GetBatteryTemperature" },
 143     { "GetCCDTemperature" },
 144 
 145     { "GetCurrentAvValue" },
 146     { "GetCurrentShutterSpeed" },
 147     { "GetUsableMaxAv", OPTIONAL },
 148     { "GetUsableMinAv", OPTIONAL },
 149     { "GetUsableAvRange", UNUSED |OPTIONAL },
 150     { "get_nd_value", OPTIONAL },
 151     { "get_current_exp", UNUSED | OPTIONAL }, // helper, underlying function of ShowCurrentExp
 152     { "get_current_nd_value", OPTIONAL },
 153     { "GetDrive_ClusterSize" },
 154     { "GetDrive_FreeClusters" },
 155     { "GetDrive_TotalClusters" },
 156     { "GetFocusLensSubjectDistance" },
 157     { "GetFocusLensSubjectDistanceFromLens" },
 158     { "GetImageFolder", OPTIONAL },
 159     { "GetKbdState" },
 160     { "GetMemInfo" },
 161     { "GetOpticalTemperature" },
 162     { "GetParameterData" },
 163     { "GetPropertyCase" },
 164     { "GetSystemTime" },
 165     { "GetVRAMHPixelsSize" },
 166     { "GetVRAMVPixelsSize" },
 167     { "GetZoomLensCurrentPoint" },
 168     { "GetZoomLensCurrentPosition" },
 169     { "GiveSemaphore", OPTIONAL|LIST_ALWAYS },
 170     { "IsStrobeChargeCompleted" },
 171     { "LEDDrive", OPTIONAL },
 172     { "LocalTime" },
 173     { "LockMainPower" },
 174     { "Lseek", UNUSED|LIST_ALWAYS },
 175     { "MakeDirectory_Fut" },
 176     { "MakeSDCardBootable", OPTIONAL },
 177     { "MoveFocusLensToDistance" },
 178     { "MoveIrisWithAv", OPTIONAL },
 179     { "MoveZoomLensWithPoint" },
 180     { "NewTaskShell", UNUSED },
 181     { "Open" },
 182     { "PB2Rec" },
 183     { "PT_MoveDigitalZoomToWide", OPTIONAL },
 184     { "PT_MoveOpticalZoomAt", OPTIONAL },
 185     { "PT_PlaySound" },
 186     { "PostLogicalEventForNotPowerType" },
 187     { "PostLogicalEventToUI" },
 188     { "PutInNdFilter", OPTIONAL },
 189     { "PutOutNdFilter", OPTIONAL },
 190     { "Read" },
 191     { "ReadFastDir" },
 192     { "Rec2PB" },
 193     { "RefreshPhysicalScreen" },
 194     { "Remove", OPTIONAL|UNUSED },
 195     { "RenameFile_Fut" },
 196     { "Restart" },
 197     { "ScreenLock" },
 198     { "ScreenUnlock" },
 199     { "SetAE_ShutterSpeed" },
 200     { "SetAutoShutdownTime" },
 201     { "SetCurrentCaptureModeType" },
 202     { "SetFileAttributes" },
 203     { "SetFileTimeStamp" },
 204     { "SetLogicalEventActive" },
 205     { "SetParameterData" },
 206     { "SetPropertyCase" },
 207     { "SetScriptMode" },
 208     { "SleepTask" },
 209     { "TakeSemaphore" },
 210     { "TurnOffBackLight" },
 211     { "TurnOnBackLight" },
 212     { "TurnOnDisplay" },
 213     { "TurnOffDisplay" },
 214     { "UIFS_WriteFirmInfoToFile", OPTIONAL|UNUSED},
 215     { "UnlockAE" },
 216     { "UnlockAF" },
 217     { "UnlockMainPower" },
 218     { "UnsetZoomForMovie", OPTIONAL },
 219 //    { "UpdateMBROnFlash" },
 220     { "VbattGet" },
 221     { "Write" },
 222     { "WriteSDCard" },
 223 
 224     { "_log" },
 225     { "_log10" },
 226     { "_pow" },
 227     { "_sqrt" },
 228     { "add_ptp_handler" },
 229     { "apex2us" },
 230     { "close" },
 231     { "displaybusyonscreen", OPTIONAL },
 232     { "err_init_task", OPTIONAL },
 233     { "exmem_alloc" },
 234     { "exmem_free", OPTIONAL|LIST_ALWAYS },
 235     { "free" },
 236 
 237     { "kbd_p1_f" },
 238     { "kbd_p1_f_cont" },
 239     { "kbd_p2_f" },
 240     { "kbd_read_keys" },
 241     { "kbd_read_keys_r2" },
 242 
 243     { "kbd_pwr_off", OPTIONAL },
 244     { "kbd_pwr_on", OPTIONAL },
 245     { "lseek" },
 246     { "malloc" },
 247     { "memcmp" },
 248     { "memcpy" },
 249     { "memset" },
 250 // identical to MakeDirectory_Fut for recent cams
 251 //    { "mkdir" },
 252     { "mktime_ext" },
 253     { "open" },
 254     { "OpenFastDir" },
 255     { "closedir" },
 256     { "get_fstype", OPTIONAL|LIST_ALWAYS },
 257     { "qsort" },
 258     { "rand" },
 259     { "read", UNUSED|OPTIONAL },
 260     { "realloc", OPTIONAL|LIST_ALWAYS },
 261     { "reboot_fw_update" },
 262     { "set_control_event" },
 263     { "srand" },
 264     { "stat" },
 265     { "strcat" },
 266     { "strchr" },
 267     { "strcmp" },
 268     { "strcpy" },
 269     { "strftime" },
 270     { "strlen" },
 271     { "strncmp" },
 272     { "strncpy" },
 273     { "strrchr" },
 274     { "strtol" },
 275     { "strtolx" },
 276 
 277     { "task_CaptSeq" },
 278     { "task_DvlpSeqTask", OPTIONAL },
 279     { "task_ExpDrv" },
 280     { "task_FileWrite", OPTIONAL },
 281     { "task_InitFileModules" },
 282     { "task_MovieRecord" },
 283     { "task_PhySw", OPTIONAL },
 284     { "task_RotaryEncoder", OPTIONAL },
 285     { "task_TouchPanel", OPTIONAL },
 286     { "task_TricInitTask", OPTIONAL },
 287 
 288     { "hook_CreateTask" },
 289 
 290     { "time" },
 291     { "vsprintf" },
 292     { "write", UNUSED|OPTIONAL },
 293     { "undisplaybusyonscreen", OPTIONAL },
 294 
 295     { "EngDrvIn", OPTIONAL|UNUSED|LIST_ALWAYS },
 296     { "EngDrvOut", OPTIONAL|UNUSED|LIST_ALWAYS },
 297     { "EngDrvRead" },
 298     { "EngDrvBits", OPTIONAL|UNUSED|LIST_ALWAYS },
 299 
 300     { "PTM_GetCurrentItem" },
 301     { "PTM_SetCurrentItem", UNUSED|LIST_ALWAYS },
 302     { "PTM_NextItem", OPTIONAL|UNUSED|LIST_ALWAYS },
 303     { "PTM_PrevItem", OPTIONAL|UNUSED|LIST_ALWAYS },
 304     { "PTM_SetPropertyEnable", OPTIONAL|UNUSED|LIST_ALWAYS },
 305 
 306     { "DisableISDriveError", OPTIONAL },
 307 
 308     // OS functions, mostly to aid firmware analysis. Order is important!
 309     { "_GetSystemTime", OPTIONAL|UNUSED|LIST_ALWAYS }, // only for locating timer functions
 310     { "SetTimerAfter", OPTIONAL|UNUSED|LIST_ALWAYS },
 311     { "SetTimerWhen", OPTIONAL|UNUSED|LIST_ALWAYS },
 312     { "CancelTimer", OPTIONAL|UNUSED|LIST_ALWAYS },
 313     { "CancelHPTimer" },
 314     { "SetHPTimerAfterTimeout", OPTIONAL|UNUSED|LIST_ALWAYS },
 315     { "SetHPTimerAfterNow" },
 316     { "CreateTaskStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 317     { "CreateMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 318     { "CreateRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 319     { "GetSemaphoreValue", OPTIONAL|UNUSED|LIST_ALWAYS },
 320     { "TryTakeSemaphore", OPTIONAL|UNUSED|LIST_ALWAYS },
 321     { "CreateMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 322     { "CreateEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 323     { "CreateBinarySemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 324     { "CreateCountingSemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 325     { "CreateRecursiveLockStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 326     { "TakeSemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 327     { "ReceiveMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 328     { "PostMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },    // r23+
 329     { "WaitForAnyEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 330     { "WaitForAllEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 331     { "AcquireRecursiveLockStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 332     { "DeleteMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 333     { "PostMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 334     { "ReceiveMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 335     { "TryReceiveMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 336     { "TryPostMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 337     { "GetNumberOfPostedMessages", OPTIONAL|UNUSED|LIST_ALWAYS },
 338     { "DeleteRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 339     { "AcquireRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 340     { "ReleaseRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 341     { "WaitForAnyEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 342     { "WaitForAllEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 343     { "ClearEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 344     { "SetEventFlag", OPTIONAL|LIST_ALWAYS },
 345     { "GetEventFlagValue", OPTIONAL|UNUSED|LIST_ALWAYS },
 346     { "CreateEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 347     { "DeleteEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 348     { "CheckAnyEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 349     { "CheckAllEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 350     { "RegisterInterruptHandler", OPTIONAL|UNUSED|LIST_ALWAYS },
 351     { "UnregisterInterruptHandler", OPTIONAL|UNUSED|LIST_ALWAYS },
 352     { "GetSRAndDisableInterrupt", OPTIONAL|UNUSED|LIST_ALWAYS }, // disables IRQ, returns a value
 353     { "SetSR", OPTIONAL|UNUSED|LIST_ALWAYS }, // enables IRQ, puts back value returned by GetSR
 354     { "EnableInterrupt", OPTIONAL|UNUSED|LIST_ALWAYS }, // enables IRQ
 355     { "_divmod_signed_int", OPTIONAL|UNUSED|LIST_ALWAYS}, // division for signed integers, remainder is returned in r1
 356     { "_divmod_unsigned_int", OPTIONAL|UNUSED|LIST_ALWAYS}, // division for unsigned integers, remainder is returned in r1
 357     { "_dflt", OPTIONAL|UNUSED|LIST_ALWAYS}, // int -> double
 358     { "_dfltu", OPTIONAL|UNUSED|LIST_ALWAYS}, // uint -> double
 359     { "_dfix", OPTIONAL|UNUSED|LIST_ALWAYS}, // double -> int
 360     { "_dfixu", OPTIONAL|UNUSED|LIST_ALWAYS}, // double -> uint
 361     { "_dmul", OPTIONAL|UNUSED|LIST_ALWAYS}, // double precision float multiplication
 362     { "_ddiv", OPTIONAL|UNUSED|LIST_ALWAYS}, // double precision float division
 363     { "_dadd", OPTIONAL|UNUSED|LIST_ALWAYS}, // addition for doubles
 364     { "_dsub", OPTIONAL|UNUSED|LIST_ALWAYS}, // subtraction for doubles
 365     { "_drsb", OPTIONAL|UNUSED|LIST_ALWAYS}, // reverse subtraction for doubles (?)
 366     { "_dcmp", OPTIONAL|UNUSED|LIST_ALWAYS}, // comparison of 2 doubles, only updates condition flags
 367     { "_dcmp_reverse", OPTIONAL|UNUSED|LIST_ALWAYS}, // like _dcmp, but operands in reverse order, only updates condition flags
 368     { "_safe_sqrt", OPTIONAL|UNUSED|LIST_ALWAYS}, // only calls _sqrt for numbers >= 0
 369     { "_scalbn", OPTIONAL|UNUSED|LIST_ALWAYS}, // double scalbn (double x, long exp), returns x * FLT_RADIX ** exp
 370     { "_fflt", OPTIONAL|UNUSED|LIST_ALWAYS}, // int -> float
 371     { "_ffltu", OPTIONAL|UNUSED|LIST_ALWAYS}, // uint -> float
 372     { "_ffix", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> int
 373     { "_ffixu", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> uint
 374     { "_fmul", OPTIONAL|UNUSED|LIST_ALWAYS}, // single precision float multiplication
 375     { "_fdiv", OPTIONAL|UNUSED|LIST_ALWAYS}, // single precision float division
 376     { "_f2d", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> double
 377     { "DisplayBusyOnScreen", OPTIONAL|UNUSED|LIST_ALWAYS}, // displays full screen "busy" message
 378     { "UndisplayBusyOnScreen", OPTIONAL|UNUSED|LIST_ALWAYS},
 379     { "CreateDialogBox", OPTIONAL|UNUSED|LIST_ALWAYS},
 380     { "DisplayDialogBox", OPTIONAL|UNUSED|LIST_ALWAYS},
 381     { "add_ui_to_dialog", OPTIONAL|UNUSED|LIST_ALWAYS}, // name made up, assigns resources to a dialog
 382     { "get_string_by_id", OPTIONAL|UNUSED|LIST_ALWAYS}, // name made up, retrieves a localised or unlocalised string by its ID
 383     { "malloc_strictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // name made up
 384     { "GetCurrentMachineTime", OPTIONAL|UNUSED|LIST_ALWAYS }, // reads usec counter, name from ixus30
 385     { "HwOcReadICAPCounter", OPTIONAL|UNUSED|LIST_ALWAYS }, // reads usec counter, name from ixus30
 386     { "transfer_src_overlay_helper",UNUSED}, // helper for other related functions
 387     { "transfer_src_overlay" },
 388     { "GraphicSystemCoreFinish_helper", OPTIONAL|UNUSED }, // function that calls GraphicSystemCoreFinish
 389     { "GraphicSystemCoreFinish", OPTIONAL|UNUSED }, // used to identify mzrm message functions
 390     { "mzrm_createmsg", OPTIONAL|UNUSED },
 391     { "mzrm_sendmsg", OPTIONAL|UNUSED },
 392     { "zicokick_start", OPTIONAL|UNUSED }, // used to identify Zico core Xtensa blobs
 393     { "zicokick_copy", OPTIONAL|UNUSED }, // used to identify Zico core Xtensa blobs
 394 
 395     { "createsemaphore_low", OPTIONAL|UNUSED },
 396 //    { "deletesemaphore_low", UNUSED },
 397     { "givesemaphore_low", OPTIONAL|UNUSED}, // OPT_CONSOLE_REDIR_ENABLED
 398     { "takesemaphore_low", OPTIONAL|UNUSED },
 399     { "bzero" }, // 
 400     { "memset32" }, // actually jump to 2nd instruction of bzero 
 401 
 402     // Other stuff needed for finding misc variables - don't export to stubs_entry.S
 403     { "GetSDProtect", UNUSED },
 404     { "DispCon_ShowBitmapColorBar", UNUSED },
 405     { "ResetZoomLens", OPTIONAL|UNUSED },
 406     { "ResetFocusLens", OPTIONAL|UNUSED },
 407     { "NR_GetDarkSubType", OPTIONAL|UNUSED },
 408     { "NR_SetDarkSubType", OPTIONAL|UNUSED },
 409     { "SavePaletteData", OPTIONAL|UNUSED },
 410     { "GUISrv_StartGUISystem", OPTIONAL|UNUSED|LIST_ALWAYS },
 411     { "get_resource_pointer", OPTIONAL|UNUSED|LIST_ALWAYS }, // name made up, gets a pointer to a certain resource (font, dialog, icon)
 412     { "CalcLog10", OPTIONAL|UNUSED|LIST_ALWAYS }, // helper
 413     { "CalcSqrt", OPTIONAL|UNUSED }, // helper
 414     { "dry_memcpy", OPTIONAL|UNUSED }, // helper, memcpy-like function in dryos kernel code
 415     { "get_playrec_mode", OPTIONAL|UNUSED }, // helper, made up name
 416     { "DebugAssert2", OPTIONAL|UNUSED }, // helper, made up name, two arg form of DebugAssert
 417     { "get_canon_mode_list", OPTIONAL|UNUSED }, // helper, made up name
 418     { "taskcreate_LowConsole", OPTIONAL|UNUSED }, // helper, made up name
 419     { "ImagerActivate", OPTIONAL|UNUSED }, // helper
 420     { "imager_active_callback", OPTIONAL|UNUSED }, // helper
 421 
 422     { "MFOn", OPTIONAL },
 423     { "MFOff", OPTIONAL },
 424     { "PT_MFOn", OPTIONAL },
 425     { "PT_MFOff", OPTIONAL },
 426     { "SS_MFOn", OPTIONAL },
 427     { "SS_MFOff", OPTIONAL },
 428 
 429     { "GetAdChValue", OPTIONAL },
 430 
 431     // for HDMI remote support - optional, probably only present on cameras with HDMI
 432     { "EnableHDMIPower", OPTIONAL },
 433     { "DisableHDMIPower", OPTIONAL },
 434 
 435     {0,0,0},
 436 };
 437 
 438 typedef struct {
 439     char*   name;   // name
 440     int     id;     // propcase id, as found
 441     int     use;    // 0: informational only; 1: use for propset guess AND print as #define; 2: use for propset guess
 442     
 443     int     id_ps6; // id in propset 6
 444     int     id_ps7; // id in propset 7
 445     int     id_ps8; // id in propset 8
 446     int     id_ps9; // id in propset 9
 447     int     id_ps10;// id in propset 10
 448 } known_prop_t;
 449 
 450 #define KNOWN_PROPSET_COUNT (10-5)
 451 
 452 known_prop_t knownprops[] =
 453 {   // name                          id  u ps6 ps7 ps8 ps9 ps10
 454     {"PROPCASE_AFSTEP"             , -1, 0                     },
 455     {"PROPCASE_FOCUS_STATE"        , -1, 1, 18, 18, 18, 18,  18},
 456     {"PROPCASE_AV"                 , -1, 1, 23, 23, 23, 23,  23},
 457     {"PROPCASE_BV"                 , -1, 1, 34, 38, 35, 38,  40},
 458     {"PROPCASE_DELTA_DIGITALGAIN"  , -1, 0                     },
 459     {"PROPCASE_DELTA_SV"           , -1, 1, 79, 84, 81, 84,  86},
 460     {"PROPCASE_DELTA_ND"           , -1, 0                     },
 461     {"PROPCASE_EV_CORRECTION_2"    , -1, 1,210,216,213,216, 218},
 462     {"PROPCASE_ORIENTATION_SENSOR" , -1, 1,222,228,225,228, 230},
 463     {"PROPCASE_SV_MARKET"          , -1, 1,249,255,252,255, 257},
 464     {"PROPCASE_SVFIX"              , -1, 0                     },
 465     {"PROPCASE_TV"                 , -1, 1,265,272,269,272, 274},
 466     {0,}
 467 };
 468 
 469 void add_prop_hit(char *name, int id)
 470 {
 471     int n = 0;
 472     while (knownprops[n].name) {
 473         if (strcmp(knownprops[n].name,name) == 0) {
 474             knownprops[n].id = id;
 475             break;
 476         }
 477         n++;
 478     }
 479 }
 480 
 481 #define MISC_BLOB_XTENSA_MAX    5
 482 #define MISC_BLOB_TYPE_NONE     0
 483 #define MISC_BLOB_TYPE_XTENSA   1
 484 typedef struct {
 485     int         type;
 486     uint32_t    rom_adr; // location of data in ROM, if copied
 487     uint32_t    ram_adr; // location of data in RAM
 488     uint32_t    size;
 489 } misc_blob_t;
 490 
 491 // for values that don't get a DEF etc
 492 #define MISC_VAL_NO_STUB    1
 493 // DEF_CONST instead of DEF
 494 #define MISC_VAL_DEF_CONST  2
 495 #define MISC_VAL_OPTIONAL   4
 496 // variables and constants
 497 typedef struct {
 498     char        *name;
 499     int         flags;
 500     uint32_t    val;
 501     // informational values
 502     uint32_t    base; // if stub is found as ptr + offset, record
 503     uint32_t    offset;
 504     uint32_t    ref_adr; // code address near where value found (TODO may want list)
 505     misc_blob_t *blobs; // malloc'd array of blobs if this is a blob type value, terminated with flags = 0
 506 } misc_val_t;
 507 
 508 misc_val_t misc_vals[]={
 509     // stubs_min variables / constants
 510     { "ctypes",             },
 511     { "physw_run",          },
 512     { "physw_sleep_delay",  },
 513     { "physw_status",       },
 514     { "fileio_semaphore",   },
 515     { "levent_table",       },
 516     { "FlashParamsTable",   },
 517     { "playrec_mode",       },
 518     { "jpeg_count_str",     },
 519     { "zoom_busy",          },
 520     { "focus_busy",         },
 521     { "imager_active",      },
 522     { "_nrflag",            MISC_VAL_OPTIONAL},
 523     { "active_bitmap_buffer",MISC_VAL_OPTIONAL},
 524     { "CAM_UNCACHED_BIT",   MISC_VAL_NO_STUB},
 525     { "physw_event_table",  MISC_VAL_NO_STUB},
 526     { "uiprop_count",       MISC_VAL_DEF_CONST},
 527     { "canon_mode_list",    MISC_VAL_NO_STUB},
 528     { "ARAM_HEAP_START",    MISC_VAL_NO_STUB},
 529     { "ARAM_HEAP_SIZE",     MISC_VAL_NO_STUB},
 530     { "zicokick_values",    MISC_VAL_NO_STUB}, // used to identify Zico core Xtensa blobs (dummy for now)
 531     { "CAM_HAS_ND_FILTER",  MISC_VAL_NO_STUB},
 532     { "CAM_IS_ILC",         MISC_VAL_NO_STUB}, // used for finsig code that wants to check for interchangeable lens, not currently used in CHDK
 533     { "CAM_HAS_IRIS_DIAPHRAGM",MISC_VAL_NO_STUB},
 534     { "exmem_alloc_table",  },
 535     { "exmem_types_table",  },
 536     { "exmem_type_count",   MISC_VAL_DEF_CONST},
 537     {0,0,0},
 538 };
 539 
 540 misc_val_t *get_misc_val(const char *name)
 541 {
 542     misc_val_t *p=misc_vals;
 543     while(p->name) {
 544         if(strcmp(name,p->name) == 0) {
 545             return p;
 546         }
 547         p++;
 548     }
 549     return NULL;
 550 }
 551 
 552 // get value of misc val, if set. Name :<
 553 uint32_t get_misc_val_value(const char *name)
 554 {
 555     misc_val_t *p=get_misc_val(name);
 556     if(!p) {
 557         printf("get_misc_val_value: invalid name %s\n",name);
 558         return 0;
 559     }
 560     return p->val;
 561 }
 562 void save_misc_val(const char *name, uint32_t base, uint32_t offset, uint32_t ref_adr)
 563 {
 564     misc_val_t *p=get_misc_val(name);
 565     if(!p) {
 566         printf("save_misc_val: invalid name %s\n",name);
 567         return;
 568     }
 569     p->val = base + offset;
 570     p->base = base;
 571     p->offset = offset;
 572     p->ref_adr = ref_adr;
 573     p->blobs = NULL;
 574 }
 575 void save_misc_val_blobs(const char *name, misc_blob_t *blobs, uint32_t ref_adr)
 576 {
 577     misc_val_t *p=get_misc_val(name);
 578     if(!p) {
 579         printf("save_misc_val: invalid name %s\n",name);
 580         return;
 581     }
 582     p->val = p->base = p->offset = 0;
 583     p->ref_adr = ref_adr;
 584     p->blobs = blobs;
 585 }
 586 
 587 // Return the array index of a named function in the array above
 588 #if 0
 589 int find_saved_sig_index(const char *name)
 590 {
 591     int i;
 592     for (i=0; sig_names[i].name != 0; i++)
 593     {
 594         if (strcmp(name,sig_names[i].name) == 0)
 595         {
 596             return i;
 597         }
 598     }
 599     return -1;
 600 }
 601 #endif
 602 
 603 sig_entry_t * find_saved_sig(const char *name)
 604 {
 605     int i;
 606     for (i=0; sig_names[i].name != 0; i++)
 607     {
 608         if (strcmp(name,sig_names[i].name) == 0)
 609         {
 610             return &sig_names[i];
 611         }
 612     }
 613     return NULL;
 614 }
 615 
 616 // return value of saved sig, or 0 if not found / doesn't exist
 617 uint32_t get_saved_sig_val(const char *name)
 618 {
 619     sig_entry_t *sig=find_saved_sig(name);
 620     if(!sig) {
 621         // printf("get_saved_sig_val: missing %s\n",name);
 622         return 0;
 623     }
 624     return sig->val;
 625 }
 626 
 627 // unused for now
 628 // Return the array index of of function with given address
 629 #if 0
 630 int find_saved_sig_index_by_adr(uint32_t adr)
 631 {
 632     if(!adr) {
 633         return  -1;
 634     }
 635     int i;
 636     for (i=0; sig_names[i].name != 0; i++)
 637     {
 638         if (sig_names[i].val == adr)
 639         {
 640             return i;
 641         }
 642     }
 643     return -1;
 644 }
 645 #endif
 646 #if 0
 647 sig_entry_t* find_saved_sig_by_val(uint32_t val)
 648 {
 649     if(!val) {
 650         return NULL;
 651     }
 652     int i;
 653     for (i=0; sig_names[i].name != 0; i++)
 654     {
 655         if (sig_names[i].val == val)
 656         {
 657             return &sig_names[i];
 658         }
 659     }
 660     return NULL;
 661 }
 662 #endif
 663 
 664 // Save the address value found for a function in the above array
 665 void save_sig(const char *name, uint32_t val)
 666 {
 667     sig_entry_t *sig = find_saved_sig(name);
 668     if (!sig)
 669     {
 670         printf("save_sig: refusing to save unknown name %s\n",name);
 671         return;
 672     }
 673     if(sig->val && sig->val != val) {
 674         printf("save_sig: duplicate name %s existing 0x%08x != new 0x%08x\n",name,sig->val,val);
 675     }
 676     sig->val = val;
 677 }
 678 
 679 void add_func_name(char *n, uint32_t eadr, char *suffix)
 680 {
 681     int k;
 682 
 683     char *s = n;
 684     int mallocd = 0;
 685     if (suffix != 0)
 686     {
 687         s = malloc(strlen(n) + strlen(suffix) + 1);
 688         sprintf(s, "%s%s", n, suffix);
 689         mallocd = 1;
 690     }
 691 
 692     for (k=0; sig_names[k].name != 0; k++)
 693     {
 694         if (strcmp(sig_names[k].name, s) == 0)
 695         {
 696             if (sig_names[k].val == 0)             // same name, no address
 697             {
 698                 sig_names[k].val = eadr;
 699                 sig_names[k].flags |= EV_MATCH;
 700                 if (mallocd)
 701                     free(s);
 702                 return;
 703             }
 704             else if (sig_names[k].val == eadr)     // same name, same address
 705             {
 706                 if (mallocd)
 707                     free(s);
 708                 return;
 709             }
 710             else // same name, different address
 711             {
 712                 printf("add_func_name: duplicate name %s existing 0x%08x != new 0x%08x\n",s, sig_names[k].val, eadr);
 713             }
 714         }
 715     }
 716 
 717     sig_names[next_sig_entry].name = s;
 718     sig_names[next_sig_entry].flags = OPTIONAL|UNUSED;
 719     sig_names[next_sig_entry].val = eadr;
 720     next_sig_entry++;
 721     sig_names[next_sig_entry].name = 0;
 722 }
 723 
 724 // save sig, with up to one level veneer added as j_...
 725 int save_sig_with_j(firmware *fw, char *name, uint32_t adr)
 726 {
 727     if(!adr) {
 728         printf("save_sig_with_j: %s null adr\n",name);
 729         return 0;
 730     }
 731     // attempt to disassemble target
 732     if(!fw_disasm_iter_single(fw,adr)) {
 733         printf("save_sig_with_j: %s disassembly failed at 0x%08x\n",name,adr);
 734         return 0;
 735     }
 736     // handle functions that immediately jump
 737     // only one level of jump for now, doesn't check for conditionals, but first insn shouldn't be conditional
 738     //uint32_t b_adr=B_target(fw,fw->is->insn);
 739     uint32_t b_adr=get_direct_jump_target(fw,fw->is);
 740     if(b_adr) {
 741         char *buf=malloc(strlen(name)+6);
 742         sprintf(buf,"j_%s",name);
 743         add_func_name(buf,adr,NULL); // this is the orignal named address
 744 //        adr=b_adr | fw->is->thumb; // thumb bit from iter state
 745         adr=b_adr; // thumb bit already handled by get_direct...
 746     }
 747     save_sig(name,adr);
 748     return 1;
 749 }
 750 
 751 // find next call to func named "name" or j_name, up to max_offset form the current is address
 752 // TODO should have a way of dealing with more than one veneer
 753 // TODO max_offset is in bytes, unlike insn search functions that use insn counts
 754 int find_next_sig_call(firmware *fw, iter_state_t *is, uint32_t max_offset, const char *name)
 755 {
 756     uint32_t adr=get_saved_sig_val(name);
 757 
 758     if(!adr) {
 759         printf("find_next_sig_call: missing %s\n",name);
 760         return 0;
 761     }
 762 
 763     search_calls_multi_data_t match_fns[3];
 764 
 765     match_fns[0].adr=adr;
 766     match_fns[0].fn=search_calls_multi_end;
 767     char veneer[128];
 768     sprintf(veneer,"j_%s",name);
 769     adr=get_saved_sig_val(veneer);
 770     if(!adr) {
 771         match_fns[1].adr=0;
 772     } else {
 773         match_fns[1].adr=adr;
 774         match_fns[1].fn=search_calls_multi_end;
 775         match_fns[2].adr=0;
 776     }
 777     return fw_search_insn(fw,is,search_disasm_calls_multi,0,match_fns,is->adr + max_offset);
 778 }
 779 // is the insn pointed to by is a call to "name" or one of it's veneers?
 780 // note: inefficient, should not be used for large searches
 781 int is_sig_call(firmware *fw, iter_state_t *is, const char *name)
 782 {
 783     uint32_t adr=get_branch_call_insn_target(fw,is);
 784     // not a call at all
 785     // TODO could check if unknown veneer
 786     if(!adr) {
 787         return 0;
 788     }
 789     uint32_t sig_adr=get_saved_sig_val(name);
 790     osig* ostub2 = find_sig(fw->sv->stubs,name);
 791     if (ostub2 && ostub2->val)
 792         sig_adr = ostub2->val;
 793     if(!sig_adr) {
 794         printf("is_sig_call: missing %s\n",name);
 795         return 0;
 796     }
 797     if(adr == sig_adr) {
 798         return 1;
 799     }
 800     char veneer[128];
 801     sprintf(veneer,"j_%s",name);
 802     sig_adr=get_saved_sig_val(veneer);
 803     if(!sig_adr) {
 804         return 0;
 805     }
 806     return (adr == sig_adr);
 807 }
 808 
 809 typedef struct sig_rule_s sig_rule_t;
 810 typedef int (*sig_match_fn)(firmware *fw, iter_state_t *is, sig_rule_t *rule);
 811 // signature matching structure
 812 struct sig_rule_s {
 813     sig_match_fn    match_fn;       // function to locate function
 814     char        *name;              // function name used in CHDK
 815     char        *ref_name;          // event / other name to match in the firmware
 816     int         param;              // function specific param/offset
 817     int         dryos_min;          // minimum dryos rel (0 = any)
 818     int         dryos_max;          // max dryos rel to apply this sig to (0 = any)
 819     // DryOS version specific params / offsets - not used yet
 820     /*
 821     int         dryos52_param; // ***** UPDATE for new DryOS version *****
 822     int         dryos54_param;
 823     int         dryos55_param;
 824     int         dryos57_param;
 825     int         dryos58_param;
 826     */
 827 };
 828 
 829 // Get DryOS version specific param
 830 /*
 831 int dryos_param(firmware *fw, sig_rule_t *sig)
 832 {
 833     switch (fw->dryos_ver)
 834     {
 835     case 52:    return sig->dryos52_param;
 836     case 54:    return sig->dryos54_param;
 837     case 55:    return sig->dryos55_param;
 838     case 57:    return sig->dryos57_param;
 839     case 58:    return sig->dryos58_param;
 840     }
 841     return 0;
 842 }
 843 */
 844 
 845 // initialize iter state using address from ref_name, print error and return 0 if not found
 846 int init_disasm_sig_ref(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 847 {
 848     if(!rule->ref_name) {
 849         printf("init_disasm_sig_ref: %s missing ref_name\n",rule->name);
 850         return 0;
 851     }
 852     uint32_t adr=get_saved_sig_val(rule->ref_name);
 853     if(!adr) {
 854         printf("init_disasm_sig_ref: %s missing %s\n",rule->name,rule->ref_name);
 855         return 0;
 856     }
 857     if(!disasm_iter_init(fw,is,adr)) {
 858         printf("init_disasm_sig_ref: %s bad address 0x%08x for %s\n",rule->name,adr,rule->ref_name);
 859         return 0;
 860     }
 861     return 1;
 862 }
 863 
 864 int sig_match_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule);
 865 
 866 // match 
 867 // r0=ref value
 868 //...
 869 // bl=<our func>
 870 int sig_match_str_r0_call(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 871 {
 872     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
 873     if(!str_adr) {
 874         printf("sig_match_str_r0_call: %s failed to find ref %s\n",rule->name,rule->ref_name);
 875         return  0;
 876     }
 877 
 878 //    printf("sig_match_str_r0_call: %s ref str %s 0x%08x\n",rule->name,rule->ref_name,str_adr);
 879 
 880     // TODO should handle multiple instances of string
 881     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
 882     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
 883         if(is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
 884             // printf("sig_match_str_r0_call: %s ref str %s ref 0x%"PRIx64"\n",rule->name,rule->ref_name,is->insn->address);
 885             // TODO should check if intervening insn nuke r0
 886             if(insn_match_find_next(fw,is,4,match_b_bl_blximm)) {
 887                 uint32_t adr=get_branch_call_insn_target(fw,is);
 888                 // printf("sig_match_str_r0_call: thumb %s call 0x%08x\n",rule->name,adr);
 889                 return save_sig_with_j(fw,rule->name,adr);
 890             }
 891         }
 892     }
 893     return 0;
 894 }
 895 
 896 // find RegisterEventProcedure
 897 int sig_match_reg_evp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 898 {
 899     const insn_match_t reg_evp_match[]={
 900         {MATCH_INS(MOV,   2),  {MATCH_OP_REG(R2),  MATCH_OP_REG(R1)}},
 901         {MATCH_INS(LDR,   2),  {MATCH_OP_REG(R1),  MATCH_OP_MEM_ANY}},
 902         {MATCH_INS(B,     MATCH_OPCOUNT_IGNORE)},
 903         {ARM_INS_ENDING}
 904     };
 905 
 906     uint32_t e_to_evp=get_saved_sig_val("ExportToEventProcedure_FW");
 907     if(!e_to_evp) {
 908         printf("sig_match_reg_evp: failed to find ExportToEventProcedure, giving up\n");
 909         return 0;
 910     }
 911 
 912     //look for the underlying RegisterEventProcedure function (not currently used)
 913     uint32_t reg_evp=0;
 914     // start at identified Export..
 915     disasm_iter_init(fw,is,e_to_evp);
 916     if(insn_match_seq(fw,is,reg_evp_match)) {
 917         reg_evp=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
 918         //printf("RegisterEventProcedure found 0x%08x at %"PRIx64"\n",reg_evp,is->insn->address);
 919         save_sig("RegisterEventProcedure",reg_evp);
 920     }
 921     return (reg_evp != 0);
 922 }
 923 
 924 // find event proc table registration, and some other stuff
 925 // TODO this should be broken up to some generic parts
 926 int sig_match_reg_evp_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 927 {
 928     // ref to find RegisterEventProcTable
 929     uint32_t str_adr = find_str_bytes(fw,rule->ref_name); // note this string may appear more than once, assuming want first
 930     if(!str_adr) {
 931         printf("sig_match_reg_evp_table: failed to find %s\n",rule->ref_name);
 932         return 0;
 933     }
 934     //printf("sig_match_reg_evp_table: DispDev_EnableEventProc 0x%08x\n",str_adr);
 935     uint32_t reg_evp_alt1=0;
 936     uint32_t reg_evp_tbl=0;
 937     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
 938     uint32_t dd_enable_p=0;
 939     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
 940         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
 941             continue;
 942         }
 943         if(!insn_match_find_next(fw,is,2,match_b_bl)) {
 944             continue;
 945         }
 946         reg_evp_alt1=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
 947         //printf("RegisterEventProcedure_alt1 found 0x%08x at %"PRIx64"\n",reg_evp_alt1,is->insn->address);
 948         save_sig("RegisterEventProcedure_alt1",reg_evp_alt1);
 949 
 950         uint32_t regs[4];
 951 
 952         // get r0 and r1, backtracking up to 4 instructions
 953         if((get_call_const_args(fw,is,4,regs)&3)==3) {
 954             // sanity check, arg0 was the original thing we were looking for
 955             if(regs[0]==str_adr) {
 956                 dd_enable_p=regs[1]; // constant value should already have correct ARM/THUMB bit
 957                 //printf("DispDev_EnableEventProc found 0x%08x at %"PRIx64"\n",dd_enable_p,is->insn->address);
 958                 add_func_name("DispDev_EnableEventProc",dd_enable_p,NULL);
 959                 break;
 960             }
 961         }
 962     } 
 963     // found candidate function
 964     if(dd_enable_p) {
 965         disasm_iter_init(fw,is,dd_enable_p); // start at found func
 966         if(insn_match_find_next(fw,is,4,match_b_bl)) { // find the first bl
 967             // sanity check, make sure we get a const in r0
 968             uint32_t regs[4];
 969             if(get_call_const_args(fw,is,4,regs)&1) {
 970                 reg_evp_tbl=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
 971                 // printf("RegisterEventProcTable found 0x%08x at %"PRIx64"\n",reg_evp_tbl,is->insn->address);
 972                 save_sig("RegisterEventProcTable",reg_evp_tbl);
 973             }
 974         }
 975     }
 976     return (reg_evp_tbl != 0);
 977 }
 978 
 979 // find an alternate eventproc registration call
 980 int sig_match_reg_evp_alt2(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 981 {
 982     uint32_t reg_evp_alt2=0;
 983     // TODO could make this a param for different fw variants
 984     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
 985     if(!str_adr) {
 986         printf("sig_match_reg_evp_alt2: failed to find %s\n",rule->ref_name);
 987         return 0;
 988     }
 989     //printf("sig_match_reg_evp_alt2: EngApp.Delete 0x%08x\n",str_adr);
 990     uint32_t reg_evp_alt1=get_saved_sig_val("RegisterEventProcedure_alt1");
 991 
 992     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
 993     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
 994         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
 995             continue;
 996         }
 997         if(!insn_match_find_next(fw,is,3,match_b_bl)) {
 998             continue;
 999         }
1000         uint32_t regs[4];
1001         // sanity check, constants in r0, r1 and r0 was the original thing we were looking for
1002         if((get_call_const_args(fw,is,4,regs)&3)==3) {
1003             if(regs[0]==str_adr) {
1004                 reg_evp_alt2=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
1005                 // TODO could keep looking
1006                 if(reg_evp_alt2 == reg_evp_alt1) {
1007                     printf("RegisterEventProcedure_alt2 == _alt1 at %"PRIx64"\n",is->insn->address);
1008                     reg_evp_alt2=0;
1009                 } else {
1010                     save_sig("RegisterEventProcedure_alt2",reg_evp_alt2);
1011                    // printf("RegisterEventProcedure_alt2 found 0x%08x at %"PRIx64"\n",reg_evp_alt2,is->insn->address);
1012                     // TODO could follow alt2 and make sure it matches expected mov r2,0, bl register..
1013                 }
1014                 break;
1015             }
1016         }
1017     }
1018     return (reg_evp_alt2 != 0);
1019 }
1020 
1021 // find UnRegisterEventProc (made up name) for reference, finding tables missed by reg_event_proc_table
1022 int sig_match_unreg_evp_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1023 {
1024     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1025     if(!str_adr) {
1026         printf("sig_match_unreg_evp_table: failed to find %s\n",rule->ref_name);
1027         return 0;
1028     }
1029     // for checks
1030     uint32_t reg_evp_alt1=get_saved_sig_val("RegisterEventProcedure_alt1");
1031     uint32_t reg_evp_alt2=get_saved_sig_val("RegisterEventProcedure_alt2");
1032 
1033     uint32_t mecha_unreg=0;
1034     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1035     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1036         //printf("sig_match_unreg_evp_table: found ref 0x%"PRIx64"\n",is->insn->address);
1037         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
1038             continue;
1039         }
1040         if(!insn_match_find_next(fw,is,3,match_b_bl)) {
1041             continue;
1042         }
1043         uint32_t reg_call=get_branch_call_insn_target(fw,is);
1044         // wasn't a registration call (could be unreg)
1045         // TODO could check veneers
1046         if(!reg_call || (reg_call != reg_evp_alt1 && reg_call != reg_evp_alt2)) {
1047             continue;
1048         }
1049         uint32_t regs[4];
1050         if((get_call_const_args(fw,is,4,regs)&3)==3) {
1051             // sanity check we got the right string
1052             if(regs[0]==str_adr) {
1053                 mecha_unreg=ADR_SET_THUMB(regs[1]);
1054                 break;
1055             }
1056         }
1057     }
1058     if(!mecha_unreg) {
1059         return 0;
1060     }
1061     // follow
1062     disasm_iter_init(fw,is,mecha_unreg);
1063     // find first func call
1064     if(!insn_match_find_next(fw,is,7,match_b_bl)) {
1065         return 0;
1066     }
1067     // now find next ldr pc. Could follow above func, but this way picks up veneer on many fw
1068     const insn_match_t match_ldr_r0[]={
1069         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1070         {ARM_INS_ENDING}
1071     };
1072     if(!insn_match_find_next(fw,is,18,match_ldr_r0)) {
1073         return 0;
1074     }
1075     uint32_t tbl=LDR_PC2val(fw,is->insn);
1076     if(!tbl) {
1077         return 0;
1078     }
1079     if(!disasm_iter(fw,is)) {
1080         printf("sig_match_unreg_evp_table: disasm failed\n");
1081         return 0;
1082     }
1083     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1084 }
1085 // find some veneers of RegisterEventProcTable/UnRegisterEventProcTable
1086 // expects b <something else>, b <ref_name>
1087 // TODO should be a generic find veneer near?
1088 int sig_match_evp_table_veneer(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1089 {
1090     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
1091     int prevb = 0;
1092     uint32_t cadr;
1093     // following should probably be done using fw_search_insn
1094     // both veneers consist of a single b instruction, always preceded by another b instruction
1095     disasm_iter_init(fw,is,ref_adr); // start at our known function
1096     while (is->adr < (ref_adr+0x800)) {
1097         cadr = is->adr;
1098         if (!disasm_iter(fw,is)) {
1099             disasm_iter_set(fw,is,(is->adr+2) | fw->thumb_default);
1100         }
1101         else {
1102             if (is->insn->id == ARM_INS_B) {
1103                 uint32_t b_adr = get_branch_call_insn_target(fw,is);
1104                 if (prevb && (b_adr == ref_adr)) {
1105                     // this doesn't use _with_j since we want identify the veneer
1106                     add_func_name(rule->name,cadr | is->thumb,NULL);
1107                     return 1;
1108                 }
1109                 prevb = 1;
1110             }
1111         }
1112     }
1113     return 0;
1114 }
1115 
1116 int sig_match_get_nd_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1117 {
1118     // this match is only valid for cameras with both ND and Iris
1119     if(!get_misc_val_value("CAM_HAS_ND_FILTER") || !get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
1120         return 0;
1121     }
1122 
1123     if(!init_disasm_sig_ref(fw,is,rule)) {
1124         return 0;
1125     }
1126     if(!find_next_sig_call(fw,is,16,"ClearEventFlag")) {
1127         printf("sig_match_get_nd_value: no match ClearEventFlag\n");
1128         return 0;
1129     }
1130     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1131         printf("sig_match_get_nd_value: bl match 1 failed\n");
1132         return 0;
1133     }
1134     // follow
1135     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1136     disasm_iter(fw,is);
1137     if (B_target(fw,is->insn))
1138         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1139     // first call should be either get_nd_value or GetUsableAvRange
1140     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
1141         printf("sig_match_get_nd_value: bl match 2 failed\n");
1142         return 0;
1143     }
1144     uint32_t addr=get_branch_call_insn_target(fw,is);
1145     if(addr == get_saved_sig_val("GetUsableAvRange")) {
1146         printf("sig_match_get_nd_value: found GetUsableAvRange, iris or ND only?\n");
1147         return 0;
1148     }
1149     return save_sig_with_j(fw,rule->name,addr);
1150 }
1151 
1152 int sig_match_get_current_exp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1153 {
1154     if(!init_disasm_sig_ref(fw,is,rule)) {
1155         return 0;
1156     }
1157     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
1158         printf("sig_match_get_current_exp: bl match 1 failed\n");
1159         return 0;
1160     }
1161     // follow
1162     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1163     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1164         printf("sig_match_get_current_exp: bl match 2 failed\n");
1165         return 0;
1166     }
1167     // follow
1168     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1169     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1170         printf("sig_match_get_current_exp: bl match 3 failed\n");
1171         return 0;
1172     }
1173     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1174 }
1175 
1176 int sig_match_get_current_nd_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1177 {
1178     // this match is only valid for cameras with both ND and Iris
1179     if(!get_misc_val_value("CAM_HAS_ND_FILTER") || !get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
1180         return 0;
1181     }
1182     if(!init_disasm_sig_ref(fw,is,rule)) {
1183         return 0;
1184     }
1185     if(!find_next_sig_call(fw,is,36,"GetCurrentShutterSpeed_FW")) {
1186         printf("sig_match_get_current_nd_value: no match GetCurrentShutterSpeed_FW\n");
1187         return 0;
1188     }
1189     // bl <func we want>
1190     // strh r0, [rN,8]
1191     const insn_match_t match_bl_strh[]={
1192         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1193         {MATCH_INS(STRH,2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(INVALID,INVALID,0x8)}},
1194         {ARM_INS_ENDING}
1195     };
1196     if(!insn_match_find_next_seq(fw,is,10,match_bl_strh)) {
1197         printf("sig_match_get_current_nd_value: match failed\n");
1198         return 0;
1199     }
1200     // rewind one for call
1201     disasm_iter_init(fw,is,adr_hist_get(&is->ah,1));
1202     disasm_iter(fw,is);
1203     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1204 }
1205 
1206 int sig_match_imager_active_callback(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1207 {
1208     if(!init_disasm_sig_ref(fw,is,rule)) {
1209         return 0;
1210     }
1211     const insn_match_t match_ldr_bl_mov_pop[]={
1212         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1213         {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
1214         {MATCH_INS(MOV,2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
1215         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1216         {ARM_INS_ENDING}
1217     };
1218 
1219     if(!insn_match_find_next_seq(fw,is,28,match_ldr_bl_mov_pop)) {
1220         printf("sig_match_imager_active_callback: match failed\n");
1221         return 0;
1222     }
1223     // rewind to LDR r0,...
1224     disasm_iter_init(fw,is,adr_hist_get(&is->ah,3));
1225     // get LDR value
1226     disasm_iter(fw,is);
1227     uint32_t f1=LDR_PC2val(fw,is->insn);
1228 //    printf("f1 0x%08x\n",f1);
1229     // thumb bit should be set correctly
1230     return save_sig_with_j(fw,rule->name,f1);
1231 }
1232 int sig_match_imager_active(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1233 {
1234     if(!init_disasm_sig_ref(fw,is,rule)) {
1235         return 0;
1236     }
1237 
1238     const insn_match_t match_ldr_mov_str_pop[]={
1239         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_BASE(PC)}},
1240         {MATCH_INS(MOV,2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM(1)}},
1241         {MATCH_INS(STR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
1242         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1243         {ARM_INS_ENDING}
1244     };
1245 
1246     int backtrack=3;
1247     if(!insn_match_find_next_seq(fw,is,10,match_ldr_mov_str_pop)) {
1248         // re-init and try reverse mov/ldr order
1249         init_disasm_sig_ref(fw,is,rule);
1250         const insn_match_t match_mov_ldr_str_pop[]={
1251             {MATCH_INS(MOV,2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM(1)}},
1252             {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_BASE(PC)}},
1253             {MATCH_INS(STR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
1254             {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1255             {ARM_INS_ENDING}
1256         };
1257         if(!insn_match_find_next_seq(fw,is,10,match_mov_ldr_str_pop)) {
1258             printf("sig_match_imager_active: match failed\n");
1259             return 0;
1260         }
1261         backtrack=2;
1262     }
1263     // rewind to LDR
1264     disasm_iter_init(fw,is,adr_hist_get(&is->ah,backtrack));
1265     disasm_iter(fw,is);
1266     uint32_t base=LDR_PC2val(fw,is->insn);
1267     uint32_t reg=is->insn->detail->arm.operands[0].reg;
1268 //    printf("base 0x%08x @0x%08x\n",base,(uint32_t)is->insn->address);
1269     // skip mov if after LDR
1270     if(backtrack == 3) {
1271         disasm_iter(fw,is);
1272     }
1273     disasm_iter(fw,is);
1274     // sanity check base is the same as LDR'd to
1275     if(is->insn->detail->arm.operands[1].mem.base != reg) {
1276         printf("sig_match_imager_active: reg mismatch\n");
1277         return 0;
1278     }
1279     uint32_t off=is->insn->detail->arm.operands[1].mem.disp;
1280 //    printf("off 0x%08x @0x%08x\n",off,(uint32_t)is->insn->address);
1281     save_misc_val("imager_active",base,off,(uint32_t)is->insn->address);
1282     return 1;
1283 }
1284 
1285 
1286 int sig_match_screenlock(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1287 {
1288     if(!init_disasm_sig_ref(fw,is,rule)) {
1289         return 0;
1290     }
1291     // match specific instruction sequence instead of first call because g3x, g5x have different code
1292     // not by dryos rev, sx710 has same code as earlier cams
1293     const insn_match_t match_cmp_bne_bl[]={
1294         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
1295         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
1296         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1297         {ARM_INS_ENDING}
1298     };
1299     if(!insn_match_find_next_seq(fw,is,6,match_cmp_bne_bl)) {
1300         // printf("sig_match_screenlock: match failed\n");
1301         return 0;
1302     }
1303     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1304 }
1305 
1306 int sig_match_screenunlock(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1307 {
1308     if(!init_disasm_sig_ref(fw,is,rule)) {
1309         return 0;
1310     }
1311     // look for ScreenLock call to verify right version of UIFS_DisplayFirmUpdateView
1312     if(!find_next_sig_call(fw,is,14,"ScreenLock")) {
1313         // printf("sig_match_screenunlock: no ScreenLock\n");
1314         return 0;
1315     }
1316     
1317     // expect tail call to ScreenUnlock
1318     const insn_match_t match_end[]={
1319         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1320         {MATCH_INS_CC(B,AL,MATCH_OPCOUNT_IGNORE)},
1321         {ARM_INS_ENDING}
1322     };
1323     if(!insn_match_find_next_seq(fw,is,38,match_end)) {
1324         // printf("sig_match_screenunlock: match failed\n");
1325         return 0;
1326     }
1327     // TODO would be nice to have some validation
1328     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1329 }
1330 
1331 // look for f(0x60,"_Simage") at start of task_StartupImage
1332 int sig_match_log_camera_event(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1333 {
1334     if(!init_disasm_sig_ref(fw,is,rule)) {
1335         return 0;
1336     }
1337     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1338         // printf("sig_match_log_camera_event: bl match failed\n");
1339         return 0;
1340     }
1341     uint32_t regs[4];
1342     if((get_call_const_args(fw,is,4,regs)&3)!=3) {
1343         // printf("sig_match_log_camera_event: get args failed\n");
1344         return 0;
1345     }
1346     if(regs[0] != 0x60) {
1347         // printf("sig_match_log_camera_event: bad r0 0x%x\n",regs[0]);
1348         return 0;
1349     }
1350     const char *str=(char *)adr2ptr(fw,regs[1]);
1351     if(!str || strcmp(str,"_SImage") != 0) {
1352         // printf("sig_match_log_camera_event: bad r1 0x%x\n",regs[1]);
1353         return 0;
1354     }
1355     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1356 }
1357 
1358 // TODO this finds multiple values in PhySwTask main function
1359 int sig_match_physw_misc(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1360 {
1361     if(!init_disasm_sig_ref(fw,is,rule)) {
1362         osig* ostub2 = find_sig(fw->sv->stubs,rule->ref_name);
1363         if (ostub2 && ostub2->val)
1364         {
1365             disasm_iter_init(fw,is,ostub2->val);
1366         }
1367         else
1368         {
1369             return 0;
1370         }
1371     }
1372     int i;
1373     uint32_t physw_run=0;
1374     for(i=0; i<3; i++) {
1375         if(!disasm_iter(fw,is)) {
1376             printf("sig_match_physw_misc: disasm failed\n");
1377             return 0;
1378         }
1379         physw_run=LDR_PC2val(fw,is->insn);
1380         if(physw_run) {
1381             if(adr_is_var(fw,physw_run)) {
1382                 save_misc_val("physw_run",physw_run,0,(uint32_t)is->insn->address);
1383                 break;
1384             } else {
1385                 printf("sig_match_physw_misc: adr not data? 0x%08x\n",physw_run);
1386                 return 0;
1387             }
1388         }
1389     }
1390     if(!physw_run) {
1391         return 0;
1392     }
1393 
1394     // look for physw_sleep_delay, offset from physw_run, loaded before SleepTask
1395     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
1396         return 0;
1397     }
1398     uint32_t sleeptask=get_saved_sig_val("SleepTask");
1399     if(!sleeptask) {
1400         printf("sig_match_physw_misc: missing SleepTask\n");
1401         return 0;
1402     }
1403     uint32_t f=get_branch_call_insn_target(fw,is);
1404 
1405     // call wasn't direct, check for veneer
1406     if(f != sleeptask) {
1407         fw_disasm_iter_single(fw,f);
1408         uint32_t f2=get_direct_jump_target(fw,fw->is);
1409         if(f2 != sleeptask) {
1410             return 0;
1411         }
1412         // sleeptask veneer is useful for xref
1413         // re-adding existing won't hurt
1414         save_sig_with_j(fw,"SleepTask",f);
1415     }
1416     // rewind 1 for r0
1417     disasm_iter_init(fw,is,adr_hist_get(&is->ah,1));
1418     if(!disasm_iter(fw,is)) {
1419         printf("sig_match_physw_misc: disasm failed\n");
1420         return 0;
1421     }
1422     // TODO could check base is same reg physw_run was loaded into
1423     if(is->insn->id != ARM_INS_LDR
1424         || is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
1425         return 0;
1426     }
1427     save_misc_val("physw_sleep_delay",physw_run,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
1428     // skip over sleeptask to next insn
1429     if(!disasm_iter(fw,is)) {
1430         printf("sig_match_physw_misc: disasm failed\n");
1431         return 0;
1432     }
1433     
1434     // look for kbd_p1_f
1435     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
1436         return 0;
1437     }
1438     save_sig("kbd_p1_f",get_branch_call_insn_target(fw,is));
1439 
1440     // look for kbd_p2_f
1441     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1442         return 0;
1443     }
1444     save_sig("kbd_p2_f",get_branch_call_insn_target(fw,is));
1445     return 1;
1446 }
1447 // TODO also finds p1_f_cont, physw_status
1448 int sig_match_kbd_read_keys(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1449 {
1450     if(!init_disasm_sig_ref(fw,is,rule)) {
1451         return 0;
1452     }
1453     // look for kbd_read_keys
1454     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1455         return 0;
1456     }
1457     save_sig("kbd_read_keys",get_branch_call_insn_target(fw,is));
1458     if(!disasm_iter(fw,is)) {
1459         printf("sig_match_kbd_read_keys: disasm failed\n");
1460         return 0;
1461     }
1462     uint32_t physw_status=LDR_PC2val(fw,is->insn);
1463     if(physw_status) {
1464         save_misc_val("physw_status",physw_status,0,(uint32_t)is->insn->address);
1465         save_sig("kbd_p1_f_cont",(uint32_t)(is->insn->address) | is->thumb);
1466         return 1;
1467     }
1468     return 0;
1469 }
1470 
1471 // TODO also finds kbd_read_keys_r2
1472 int sig_match_get_kbd_state(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1473 {
1474     if(!init_disasm_sig_ref(fw,is,rule)) {
1475         return 0;
1476     }
1477     // instructions that zero out physw_status
1478     insn_match_t match[]={
1479         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1480         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1481         {ARM_INS_ENDING}
1482     };
1483 
1484     if(!insn_match_find_next_seq(fw,is,11,match)) {
1485         return 0;
1486     }
1487     save_sig_with_j(fw,"GetKbdState",get_branch_call_insn_target(fw,is));
1488     // look for kbd_read_keys_r2
1489     if(!insn_match_find_next(fw,is,5,match_b_bl_blximm)) {
1490         return 0;
1491     }
1492     save_sig_with_j(fw,"kbd_read_keys_r2",get_branch_call_insn_target(fw,is));
1493     return 1;
1494 }
1495 
1496 int sig_match_create_jumptable(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1497 {
1498     if(!init_disasm_sig_ref(fw,is,rule)) {
1499         return 0;
1500     }
1501     // find second function call
1502     if(!insn_match_find_nth(fw,is,20,2,match_bl_blximm)) {
1503         return 0;
1504     }
1505     // follow
1506     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1507     if(!insn_match_find_next(fw,is,15,match_bl_blximm)) {
1508         return 0;
1509     }
1510     // TODO could verify it looks right (version string)
1511     save_sig("CreateJumptable",get_branch_call_insn_target(fw,is));
1512     return 1;
1513 }
1514 
1515 // TODO this actually finds a bunch of different stuff
1516 int sig_match_take_semaphore_strict(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1517 {
1518     if(!init_disasm_sig_ref(fw,is,rule)) {
1519         return 0;
1520     }
1521     // find first function call
1522     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1523         return 0;
1524     }
1525     // follow
1526     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1527     // find second call
1528     if(!insn_match_find_nth(fw,is,10,2,match_bl_blximm)) {
1529         return 0;
1530     }
1531     // follow
1532     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1533     // skip two calls, next should be DebugAssert
1534     if(!insn_match_find_nth(fw,is,20,3,match_bl_blximm)) {
1535         return 0;
1536     }
1537     save_sig_with_j(fw,"DebugAssert",get_branch_call_insn_target(fw,is));
1538 
1539     // next should be TakeSemaphoreStrictly
1540     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
1541         return 0;
1542     }
1543     save_sig_with_j(fw,"TakeSemaphoreStrictly",get_branch_call_insn_target(fw,is));
1544     arm_reg ptr_reg = ARM_REG_INVALID;
1545     uint32_t sem_adr=0;
1546     int i;
1547     // iterate backwards looking for the value put in r0
1548     for(i=1; i<7; i++) {
1549         fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
1550         cs_insn *insn=fw->is->insn;
1551         if(insn->id != ARM_INS_LDR) {
1552             continue;
1553         }
1554         if(ptr_reg == ARM_REG_INVALID
1555             && insn->detail->arm.operands[0].reg == ARM_REG_R0
1556             && insn->detail->arm.operands[1].mem.base != ARM_REG_PC) {
1557             ptr_reg = insn->detail->arm.operands[1].mem.base;
1558             continue;
1559         }
1560         if(ptr_reg == ARM_REG_INVALID || !isLDR_PC(insn) || insn->detail->arm.operands[0].reg != ptr_reg) {
1561             continue;
1562         }
1563         sem_adr=LDR_PC2val(fw,insn);
1564         if(sem_adr) {
1565             break;
1566         }
1567     }
1568     if(!sem_adr) {
1569         return 0;
1570     }
1571     save_misc_val("fileio_semaphore",sem_adr,0,(uint32_t)is->insn->address);
1572     // look for next call: GetDrive_FreeClusters
1573     if(!insn_match_find_next(fw,is,10,match_bl_blximm)) {
1574         return 0;
1575     }
1576     return save_sig_with_j(fw,"GetDrive_FreeClusters",get_branch_call_insn_target(fw,is));
1577 }
1578 
1579 int sig_match_get_semaphore_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1580 {
1581     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1582     if(!str_adr) {
1583         printf("sig_get_semaphore_value: failed to find ref %s\n",rule->ref_name);
1584         return 0;
1585     }
1586 
1587     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1588     // assume first / only ref
1589     if(!fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1590         // printf("sig_get_semaphore_value: failed to find code ref to %s\n",rule->ref_name);
1591         return 0;
1592     }
1593     // search backwards for func call
1594     uint32_t fadr=0;
1595     int i;
1596     for(i=1; i<=5; i++) {
1597         if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
1598             // printf("sig_get_semaphore_value: disasm failed\n");
1599             return 0;
1600         }
1601         if(insn_match_any(fw->is->insn,match_bl_blximm)){
1602             fadr=get_branch_call_insn_target(fw,fw->is);
1603             break;
1604         }
1605     }
1606     if(!fadr) {
1607         // printf("sig_get_semaphore_value: failed to find bl 1\n");
1608         return 0;
1609     }
1610     // follow
1611     disasm_iter_init(fw,is,fadr);
1612     // look for first call
1613     if(!insn_match_find_next(fw,is,9,match_bl_blximm)) {
1614         // printf("sig_get_semaphore_value: failed to find bl 2\n");
1615         return 0;
1616     }
1617     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1618 }
1619 // similar to sig_match_str_r0_call, but string also appears with Fopen_Fut
1620 int sig_match_stat(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1621 {
1622     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1623     if(!str_adr) {
1624         printf("sig_match_stat: %s failed to find ref %s\n",rule->name,rule->ref_name);
1625         return  0;
1626     }
1627 
1628     // TODO should handle multiple instances of string
1629     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1630     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1631         if(is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
1632             if(insn_match_find_next(fw,is,2,match_bl_blximm)) {
1633                 uint32_t adr=get_branch_call_insn_target(fw,is);
1634                 // same string ref'd by Fopen
1635                 if(is_sig_call(fw,is,"Fopen_Fut_FW")) {
1636                     continue;
1637                 }
1638                 // TODO could check r1 not a const
1639                 return save_sig_with_j(fw,rule->name,adr);
1640             }
1641         }
1642     }
1643     return 0;
1644 }
1645 static const insn_match_t match_open_mov_call[]={
1646     // 3 reg / reg movs, followed by a call
1647     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1648     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1649     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1650     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1651     {ARM_INS_ENDING}
1652 };
1653 
1654 
1655 // find low level open
1656 int sig_match_open(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1657 {
1658     if(!init_disasm_sig_ref(fw,is,rule)) {
1659         return 0;
1660     }
1661     if(!insn_match_find_next_seq(fw,is,48,match_open_mov_call)) {
1662         return 0;
1663     }
1664     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1665 }
1666 // find low level open for dryos >= 58
1667 // TODO not verified it works as expected
1668 int sig_match_open_gt_57(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1669 {
1670     if(!init_disasm_sig_ref(fw,is,rule)) {
1671         return 0;
1672     }
1673     if(!find_next_sig_call(fw,is,38,"TakeSemaphoreStrictly")) {
1674         return 0;
1675     }
1676     // looking for next call
1677     // this should be equivalent to previous versions Open, without the extra semaphore wrapper
1678     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
1679         return 0;
1680     }
1681     // follow
1682     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1683     // match same pattern as normal
1684     if(!insn_match_find_next_seq(fw,is,48,match_open_mov_call)) {
1685         return 0;
1686     }
1687     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1688 }
1689 
1690 // find low level close for dryos >= 58
1691 // TODO not verified it works as expected
1692 int sig_match_close_gt_57(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1693 {
1694     if(!init_disasm_sig_ref(fw,is,rule)) {
1695         return 0;
1696     }
1697     if(!find_next_sig_call(fw,is,34,"TakeSemaphoreStrictly")) {
1698         return 0;
1699     }
1700     // looking for next call
1701     // this should be equivalent to previous versions Close, without the extra semaphore wrapper
1702     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
1703         return 0;
1704     }
1705     // follow
1706     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1707     // first call
1708     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
1709         return 0;
1710     }
1711     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1712 }
1713 
1714 
1715 // AllocateUncacheableMemory
1716 int sig_match_umalloc(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1717 {
1718     if(!init_disasm_sig_ref(fw,is,rule)) {
1719         return 0;
1720     }
1721     // looking for 3rd call
1722     if(!insn_match_find_nth(fw,is,15,3,match_bl_blximm)) {
1723         return 0;
1724     }
1725     //follow
1726     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1727     // looking for 3rd call again
1728     if(!insn_match_find_nth(fw,is,14,3,match_bl_blximm)) {
1729         return 0;
1730     }
1731     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1732 }
1733 
1734 // FreeUncacheableMemory
1735 int sig_match_ufree(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1736 {
1737     if(!init_disasm_sig_ref(fw,is,rule)) {
1738         return 0;
1739     }
1740     // find the first call to strcpy
1741     if(!find_next_sig_call(fw,is,60,"strcpy_FW")) {
1742         return 0;
1743     }
1744     // find 3rd call
1745     if(!insn_match_find_nth(fw,is,12,3,match_bl_blximm)) {
1746         return 0;
1747     }
1748     // follow
1749     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1750     // Look for Close call
1751     if(!find_next_sig_call(fw,is,40,"Close_FW")) {
1752         return 0;
1753     }
1754     // next call should be FreeUncacheableMemory
1755     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1756         return 0;
1757     }
1758     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1759 }
1760 
1761 int sig_match_deletefile_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1762 {
1763     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1764     if(!str_adr) {
1765         printf("sig_match_deletefile_fut: %s failed to find ref %s\n",rule->name,rule->ref_name);
1766         return  0;
1767     }
1768     // TODO should handle multiple instances of string
1769     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1770     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1771         // next call should be DeleteFile_Fut
1772         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1773             continue;
1774         }
1775         // follow
1776         uint32_t adr=get_branch_call_insn_target(fw,is);
1777         if(!fw_disasm_iter_single(fw,adr)) {
1778             printf("sig_match_deletefile_fut: disasm failed\n");
1779             return 0;
1780         }
1781         const insn_match_t match_mov_r1[]={
1782             {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
1783 #if CS_API_MAJOR < 4
1784             {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
1785 #endif
1786             {ARM_INS_ENDING}
1787         };
1788 
1789         if(!insn_match_any(fw->is->insn,match_mov_r1)){
1790             continue;
1791         }
1792         return save_sig_with_j(fw,rule->name,adr);
1793     }
1794     return 0;
1795 }
1796 
1797 uint32_t find_call_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule);
1798 
1799 int sig_match_closedir(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1800 {
1801     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1802     if(!str_adr) {
1803         printf("sig_match_closedir: %s failed to find ref %s\n",rule->name,rule->ref_name);
1804         return  0;
1805     }
1806     // TODO should handle multiple instances of string
1807     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1808     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1809         if(!find_next_sig_call(fw,is,60,"sprintf_FW")) {
1810             continue;
1811         }
1812         if(insn_match_find_nth(fw,is,7,2,match_bl_blximm)) {
1813             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1814         }
1815     }
1816 
1817     uint32_t call_adr = find_call_near_str(fw,is,rule);
1818     if(call_adr) {
1819         disasm_iter_init(fw,is,call_adr); // reset to a bit before where the string was found
1820         const insn_match_t match_closedir[]={
1821             {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1822             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG_ANY}},
1823             {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1824             {ARM_INS_ENDING}
1825         };
1826         if(insn_match_seq(fw,is,match_closedir)){
1827             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1828         }
1829     }
1830 
1831     return 0;
1832 }
1833 
1834 int sig_match_strrchr(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1835 {
1836     uint32_t sig_adr=get_saved_sig_val(rule->name);
1837     if (sig_adr == 0)
1838     {
1839         uint32_t call_adr = find_call_near_str(fw,is,rule);
1840         if(call_adr) {
1841             disasm_iter_init(fw,is,call_adr-4); // reset to a bit before where the string was found
1842             const insn_match_t match_mov_r1_imm[]={
1843                 {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
1844                 {ARM_INS_ENDING}
1845             };
1846             if(insn_match_find_next(fw,is,2,match_mov_r1_imm)){
1847                 disasm_iter_init(fw,is,call_adr);
1848                 disasm_iter(fw,is);
1849                 return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1850             }
1851         }
1852     }
1853     return 0;
1854 }
1855 
1856 int sig_match_time(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1857 {
1858     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1859     if(!str_adr) {
1860         printf("sig_match_time: %s failed to find ref %s\n",rule->name,rule->ref_name);
1861         return  0;
1862     }
1863     uint32_t fadr=0;
1864     // TODO should handle multiple instances of string
1865     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1866     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1867         // find second func after str ref
1868         if(insn_match_find_nth(fw,is,6,2,match_bl_blximm)) {
1869             fadr=get_branch_call_insn_target(fw,is);
1870             break;
1871         }
1872     }
1873     if(!fadr) {
1874         return 0;
1875     }
1876     // follow found func
1877     disasm_iter_init(fw,is,fadr);
1878     // find second call
1879     if(insn_match_find_nth(fw,is,11,2,match_bl_blximm)) {
1880         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1881     }
1882     return 0;
1883 }
1884 
1885 int sig_match_strncpy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1886 {
1887     if(!init_disasm_sig_ref(fw,is,rule)) {
1888         return 0;
1889     }
1890     if(!find_next_sig_call(fw,is,60,"strcpy_FW")) {
1891         return 0;
1892     }
1893     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1894         return 0;
1895     }
1896     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1897 }
1898 
1899 int sig_match_strncmp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1900 {
1901     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1902     if(!str_adr) {
1903         printf("sig_match_strncmp: failed to find ref %s\n",rule->ref_name);
1904         return  0;
1905     }
1906     // TODO should handle multiple instances of string
1907     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1908     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1909         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
1910             continue;
1911         }
1912         uint32_t regs[4];
1913         if((get_call_const_args(fw,is,4,regs)&6)==6) {
1914             // sanity check we got the right string
1915             if(regs[1]==str_adr &&  regs[2] == strlen(rule->ref_name)) {
1916                 return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1917             }
1918         }
1919     }
1920     return 0;
1921 }
1922 
1923 int sig_match_strtolx(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1924 {
1925     if(!init_disasm_sig_ref(fw,is,rule)) {
1926         return 0;
1927     }
1928     if(!find_next_sig_call(fw,is,130,"strncpy")) {
1929         return 0;
1930     }
1931     // find first call after strncpy
1932     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1933         return 0;
1934     }
1935     uint32_t adr=get_branch_call_insn_target(fw,is);
1936     if(!adr) {
1937         return 0;
1938     }
1939     // follow
1940     disasm_iter_init(fw,is,adr);
1941     if(!disasm_iter(fw,is)) {
1942         printf("sig_match_strtolx: disasm failed\n");
1943         return 0;
1944     }
1945     // expect
1946     // mov r3, #0
1947     // b ...
1948     const insn_match_t match_mov_r3_imm[]={
1949         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R3),  MATCH_OP_IMM_ANY}},
1950         {ARM_INS_ENDING}
1951     };
1952     if(!insn_match(is->insn,match_mov_r3_imm)){
1953         return 0;
1954     }
1955     if(!disasm_iter(fw,is)) {
1956         printf("sig_match_strtolx: disasm failed\n");
1957         return 0;
1958     }
1959     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1960 }
1961 
1962 // find the version of ExecuteEventProcedure that doesn't assert evp isn't reg'd
1963 int sig_match_exec_evp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1964 {
1965     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
1966     if(!str_adr) {
1967         printf("sig_match_exec_evp: failed to find ref %s\n",rule->ref_name);
1968         return  0;
1969     }
1970     // TODO should handle multiple instances of string
1971     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1972     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1973         // search backwards for push {r0,...}
1974         int i;
1975         for(i=1; i<=18; i++) {
1976             if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
1977                 break;
1978             }
1979             if(fw->is->insn->id == ARM_INS_PUSH && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
1980                 // push should be start of func
1981                 uint32_t adr=(uint32_t)(fw->is->insn->address) | is->thumb;
1982                 // search forward in original iter_state for DebugAssert. If found, in the wrong ExecuteEventProcedure
1983                 if(find_next_sig_call(fw,is,28,"DebugAssert")) {
1984                     break;
1985                 }
1986                 return save_sig_with_j(fw,rule->name,adr);
1987             }
1988         }
1989     }
1990     return 0;
1991 }
1992 
1993 int sig_match_fgets_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1994 {
1995     if(!init_disasm_sig_ref(fw,is,rule)) {
1996         return 0;
1997     }
1998     if(!find_next_sig_call(fw,is,16,"Fopen_Fut_FW")) {
1999         return 0;
2000     }
2001     disasm_iter(fw,is);
2002     disasm_iter(fw,is);
2003     if (B_target(fw,is->insn) && (is->insn->detail->arm.cc == ARM_CC_NE)) {
2004         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2005     } else {
2006         if (B_target(fw,is->insn) && (is->insn->detail->arm.cc == ARM_CC_NE)) {
2007             disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2008         }
2009     }
2010     if(!insn_match_find_nth(fw,is,20,1,match_bl_blximm)) {
2011         return 0;
2012     }
2013     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2014 }
2015 
2016 int sig_match_log(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2017 {
2018     if(!init_disasm_sig_ref(fw,is,rule)) {
2019         return 0;
2020     }
2021     const insn_match_t match_pop6[]={
2022         {MATCH_INS(POP, 6), {MATCH_OP_REST_ANY}},
2023         {ARM_INS_ENDING}
2024     };
2025     // skip forward through 3x pop     {r4, r5, r6, r7, r8, lr}
2026     if(!insn_match_find_nth(fw,is,38,3,match_pop6)) {
2027         return 0;
2028     }
2029     // third call
2030     if(!insn_match_find_nth(fw,is,24,3,match_bl_blximm)) {
2031         return 0;
2032     }
2033     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2034 }
2035 
2036 // this only works on DryOS r52 cams
2037 int sig_match_pow_dry_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2038 {
2039     if (fw->dryos_ver != 52) {
2040         return 0;
2041     }
2042     if(!init_disasm_sig_ref(fw,is,rule)) {
2043         return 0;
2044     }
2045     const insn_match_t match_ldrd_r0_r1[]={
2046         {MATCH_INS(LDRD,    3), {MATCH_OP_REG(R0), MATCH_OP_REG(R1),    MATCH_OP_ANY}},
2047         {ARM_INS_ENDING}
2048     };
2049     // skip forward to first ldrd    r0, r1, [r...]
2050     if(!insn_match_find_next(fw,is,50,match_ldrd_r0_r1)) {
2051         return 0;
2052     }
2053     // prevent false positive
2054     if(is->insn->detail->arm.operands[2].mem.base == ARM_REG_SP) {
2055         return 0;
2056     }
2057     if(!disasm_iter(fw,is)) {
2058         printf("sig_match_pow: disasm failed\n");
2059         return 0;
2060     }
2061     uint32_t adr=get_branch_call_insn_target(fw,is);
2062     if(!adr) {
2063         return 0;
2064     }
2065     return save_sig_with_j(fw,rule->name,adr);
2066 }
2067 
2068 // match for dryos > r52 cams
2069 int sig_match_pow_dry_gt_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2070 {
2071     if (fw->dryos_ver <= 52) {
2072         return 0;
2073     }
2074     if(!init_disasm_sig_ref(fw,is,rule)) {
2075         return 0;
2076     }
2077     const insn_match_t match1a[]={
2078         {MATCH_INS(LDRSH,   2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(SP,INVALID,0x12)}},
2079         {MATCH_INS(LDRD,    3), {MATCH_OP_REG(R2),  MATCH_OP_REG(R3), MATCH_OP_MEM(SP,INVALID,0x20)}},
2080         {MATCH_INS(STR,     2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM(SP,INVALID,0)}},
2081         {MATCH_INS(BL,      MATCH_OPCOUNT_IGNORE)},
2082         {ARM_INS_ENDING}
2083     };
2084     const insn_match_t match1b[]={
2085         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R2),  MATCH_OP_REG(R7)}},
2086         {MATCH_INS(LDRSH,   2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(SP,INVALID,0x12)}},
2087         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R3),  MATCH_OP_REG(R6)}},
2088         {MATCH_INS(STR,     2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM(SP,INVALID,0)}},
2089         {MATCH_INS(BL,      MATCH_OPCOUNT_IGNORE)},
2090         {ARM_INS_ENDING}
2091     };
2092     const insn_match_t* match1[]={ match1a, match1b };
2093     int idx;
2094     for (idx = 0; idx < 2; idx += 1)
2095     {
2096         // match above sequence
2097         if(insn_match_find_next_seq(fw,is,50,match1[idx]))
2098             break;
2099         init_disasm_sig_ref(fw,is,rule);
2100     }
2101     // check for match
2102     if (idx >= 2)
2103         return 0;
2104     // match above sequence
2105     uint32_t adr=get_branch_call_insn_target(fw,is);
2106     if(!adr) {
2107         return 0;
2108     }
2109     // follow bl
2110     disasm_iter_init(fw,is,adr);
2111     const insn_match_t match2a[]={
2112         {MATCH_INS(LDRD,3), {MATCH_OP_REG(R0),  MATCH_OP_REG(R1),   MATCH_OP_MEM_ANY}},
2113         {MATCH_INS(BLX, 1), {MATCH_OP_IMM_ANY}},
2114         {ARM_INS_ENDING}
2115     };
2116     const insn_match_t match2b[]={
2117         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R2),  MATCH_OP_REG(R0)}},
2118         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R3),  MATCH_OP_REG(R1)}},
2119         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R4),  MATCH_OP_IMM(0x40000000)}},
2120         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
2121         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_REG(R4)}},
2122         {MATCH_INS(BL,  1), {MATCH_OP_IMM_ANY}},
2123         {ARM_INS_ENDING}
2124     };
2125     const insn_match_t* match2[]={ match2a, match2b };
2126     // match above sequence
2127     if(!insn_match_find_next_seq(fw,is,15,match2[idx])) {
2128         return 0;
2129     }
2130     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2131 }
2132 
2133 int sig_match_sqrt(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2134 {
2135     if(!init_disasm_sig_ref(fw,is,rule)) {
2136         return 0;
2137     }
2138     // third call
2139     if(!insn_match_find_nth(fw,is,12,3,match_bl_blximm)) {
2140         return 0;
2141     }
2142     // follow
2143     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2144     if(!disasm_iter(fw,is)) {
2145         printf("sig_match_sqrt: disasm failed\n");
2146         return 0;
2147     }
2148     uint32_t j_tgt=get_direct_jump_target(fw,is);
2149     // veneer?
2150     if(j_tgt) {
2151         // follow
2152         disasm_iter_init(fw,is,j_tgt);
2153         if(!disasm_iter(fw,is)) {
2154             printf("sig_match_sqrt: disasm failed\n");
2155             return 0;
2156         }
2157     }
2158     // second call/branch (seems to be bhs)
2159     if(!insn_match_find_nth(fw,is,12,2,match_b_bl_blximm)) {
2160         return 0;
2161     }
2162     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2163 }
2164 int sig_match_get_drive_cluster_size(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2165 {
2166     if(!init_disasm_sig_ref(fw,is,rule)) {
2167         return 0;
2168     }
2169     // only handle first match, don't expect multiple refs to string
2170     if(fw_search_insn(fw,is,search_disasm_str_ref,0,"A/OpLogErr.txt",(uint32_t)is->adr+260)) {
2171         // find first call after string ref
2172         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
2173             // printf("sig_match_get_drive_cluster_size: bl not found\n");
2174             return 0;
2175         }
2176         // follow
2177         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2178         // find second call
2179         if(!insn_match_find_nth(fw,is,13,2,match_bl_blximm)) {
2180             // printf("sig_match_get_drive_cluster_size: call 1 not found\n");
2181             return 0;
2182         }
2183         // follow
2184         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2185         disasm_iter(fw,is);
2186         if (B_target(fw, is->insn))
2187             disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2188         // find next call
2189         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2190             // printf("sig_match_get_drive_cluster_size: call 2 not found\n");
2191             return 0;
2192         }
2193         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2194     }
2195     return 0;
2196 }
2197 
2198 int sig_match_mktime_ext(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2199 {
2200     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2201     if(!str_adr) {
2202         printf("sig_match_mktime_ext: failed to find ref %s\n",rule->ref_name);
2203         return  0;
2204     }
2205     // TODO should handle multiple instances of string
2206     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2207     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2208         // expect sscanf after str
2209         if(!find_next_sig_call(fw,is,12,"sscanf_FW")) {
2210             // printf("sig_match_mktime_ext: no sscanf\n");
2211             return 0;
2212         }
2213         // find next call
2214         if(!insn_match_find_next(fw,is,22,match_bl_blximm)) {
2215             // printf("sig_match_mktime_ext: no call\n");
2216             return 0;
2217         }
2218         // follow
2219         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2220         if(!disasm_iter(fw,is)) {
2221             printf("sig_match_mktime_ext: disasm failed\n");
2222             return 0;
2223         }
2224         uint32_t j_tgt=get_direct_jump_target(fw,is);
2225         // veneer?
2226         if(j_tgt) {
2227             // follow
2228             disasm_iter_init(fw,is,j_tgt);
2229             if(!disasm_iter(fw,is)) {
2230                 printf("sig_match_mktime_ext: disasm failed\n");
2231                 return 0;
2232             }
2233         }
2234         const insn_match_t match_pop4[]={
2235             {MATCH_INS(POP, 4), {MATCH_OP_REST_ANY}},
2236             {MATCH_INS(POP, 6), {MATCH_OP_REST_ANY}},
2237             {ARM_INS_ENDING}
2238         };
2239 
2240         // find pop
2241         if(!insn_match_find_next(fw,is,54,match_pop4)) {
2242             // printf("sig_match_mktime_ext: no pop\n");
2243             return 0;
2244         }
2245         if(!insn_match_find_next(fw,is,1,match_b)) {
2246             // printf("sig_match_mktime_ext: no b\n");
2247             return 0;
2248         }
2249         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2250     }
2251     return 0;
2252 }
2253 
2254 // match call after ref to "_EnrySRec" because "AC:Rec2PB" ref before push in function, hard to be sure of start
2255 int sig_match_rec2pb(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2256 {
2257     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2258     if(!str_adr) {
2259         printf("sig_match_mktime_ext: failed to find ref %s\n",rule->ref_name);
2260         return  0;
2261     }
2262     // TODO should handle multiple instances of string
2263     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2264     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2265         const insn_match_t match_ldr_cbnz_r0[]={
2266             {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0), MATCH_OP_MEM_ANY}},
2267             {MATCH_INS(CBNZ, 2), {MATCH_OP_REG(R0), MATCH_OP_IMM_ANY}},
2268             {ARM_INS_ENDING}
2269         };
2270         if(!insn_match_find_next_seq(fw,is,10,match_ldr_cbnz_r0)) {
2271             // printf("sig_match_rec2pb: no cbnz\n");
2272             continue;
2273         }
2274         // follow
2275         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2276         if(!insn_match_find_next(fw,is,3,match_b_bl_blximm)) {
2277             // printf("sig_match_rec2pb: no call\n");
2278             // followed branch, doesn't make sense to keep searching
2279             return 0;
2280         }
2281         uint32_t adr=(uint32_t)is->insn->address | is->thumb;
2282         // follow for sanity check
2283         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2284         if(!find_next_sig_call(fw,is,16,"LogCameraEvent")) {
2285             // printf("sig_match_rec2pb: no LogCameraEvent call\n");
2286             return 0;
2287         }
2288         uint32_t regs[4];
2289         if((get_call_const_args(fw,is,4,regs)&3)!=3) {
2290             // printf("sig_match_rec2pb: failed to get args\n");
2291             return 0;
2292         }
2293         // sanity check starts with LogCameraEvent with expected number and string
2294         if(regs[0]==0x60 && adr2ptr(fw,regs[1]) && (strcmp((const char *)adr2ptr(fw,regs[1]),"AC:Rec2PB")==0)) {
2295             return save_sig_with_j(fw,rule->name,adr);
2296         } else {
2297             // printf("sig_match_rec2pb: bad args\n");
2298             return 0;
2299         }
2300     }
2301     return 0;
2302 }
2303 
2304 // could just do sig_match_named, 3rd b, but want more validation
2305 int sig_match_get_parameter_data(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2306 {
2307     if(!init_disasm_sig_ref(fw,is,rule)) {
2308         return 0;
2309     }
2310     const insn_match_t match_cmp_bhs[]={
2311         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY, MATCH_OP_IMM_ANY}},
2312         {MATCH_INS_CC(B,HS,MATCH_OPCOUNT_IGNORE)},
2313         {ARM_INS_ENDING}
2314     };
2315     if(!insn_match_find_next_seq(fw,is,4,match_cmp_bhs)) {
2316         // printf("sig_match_get_parameter_data: no match cmp, bhs\n");
2317         return 0;
2318     }
2319     // follow
2320     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2321     if(!insn_match_find_next(fw,is,1,match_b)) {
2322         // printf("sig_match_get_parameter_data: no match b\n");
2323         return 0;
2324     }
2325     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2326 }
2327 
2328 // PrepareDirectory (via string ref) points at something like
2329 // mov r1, 1
2330 // b PrepareDirectory_x
2331 int sig_match_prepdir_x(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2332 {
2333     if(!init_disasm_sig_ref(fw,is,rule)) {
2334         return 0;
2335     }
2336     const insn_match_t match_mov_r1_1[]={
2337         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(1)}},
2338 #if CS_API_MAJOR < 4
2339         {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(1)}},
2340 #endif
2341         {ARM_INS_ENDING}
2342     };
2343     if(!insn_match_find_next(fw,is,1,match_mov_r1_1)) {
2344         //printf("sig_match_prepdir_x: no match mov\n");
2345         return 0;
2346     }
2347     if(!insn_match_find_next(fw,is,1,match_b)) {
2348         //printf("sig_match_prepdir_x: no match b\n");
2349         return 0;
2350     }
2351     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2352 }
2353 
2354 // PrepareDirectory (via string ref) points at something like
2355 // mov r1, 1
2356 // b PrepareDirectory_x
2357 int sig_match_prepdir_1(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2358 {
2359     uint32_t call_adr = find_call_near_str(fw,is,rule);
2360     if(call_adr) {
2361         disasm_iter_init(fw,is,call_adr);
2362         disasm_iter(fw,is);
2363         disasm_iter(fw,is);
2364         if (!CBx_target(fw,is->insn))
2365         {
2366             rule->param = SIG_NEAR_BEFORE(20,5);
2367             call_adr = find_call_near_str(fw,is,rule);
2368             if(!call_adr) {
2369                 return 0;
2370             }
2371             disasm_iter_init(fw,is,call_adr);
2372             disasm_iter(fw,is);
2373             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2374         }
2375     }
2376 
2377     rule->param = SIG_NEAR_BEFORE(7,2);
2378     call_adr = find_call_near_str(fw,is,rule);
2379     if(!call_adr) {
2380         return 0;
2381     }
2382     disasm_iter_init(fw,is,call_adr);
2383     disasm_iter(fw,is);
2384     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2385 }
2386 // assume this function is directly after the 2 instructions of ref
2387 int sig_match_prepdir_0(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2388 {
2389     if(!init_disasm_sig_ref(fw,is,rule)) {
2390         return 0;
2391     }
2392     uint32_t ref_pdx=get_saved_sig_val("PrepareDirectory_x");
2393     if(!ref_pdx) {
2394         printf("sig_match_prepdir_0: missing PrepareDirectory_x\n");
2395         return 0;
2396     }
2397     // skip over, assume validated previously
2398     disasm_iter(fw,is);
2399     disasm_iter(fw,is);
2400     // this should be the start address of our function
2401     uint32_t adr=(uint32_t)is->adr|is->thumb;
2402     const insn_match_t match_mov_r1_1[]={
2403         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(0)}},
2404 #if CS_API_MAJOR < 4
2405         {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(0)}},
2406 #endif
2407         {ARM_INS_ENDING}
2408     };
2409     if(!insn_match_find_next(fw,is,1,match_mov_r1_1)) {
2410         //printf("sig_match_prepdir_0: no match mov\n");
2411         return 0;
2412     }
2413     if(!insn_match_find_next(fw,is,1,match_b)) {
2414         //printf("sig_match_prepdir_0: no match b\n");
2415         return 0;
2416     }
2417     uint32_t pdx=get_branch_call_insn_target(fw,is);
2418     if(pdx != ref_pdx) {
2419         //printf("sig_match_prepdir_0: target 0x%08x != 0x%08x\n",pdx,ref_pdx);
2420         return 0;
2421     }
2422     return save_sig_with_j(fw,rule->name,adr);
2423 }
2424 int sig_match_mkdir(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2425 {
2426     if(!init_disasm_sig_ref(fw,is,rule)) {
2427         return 0;
2428     }
2429     const insn_match_t match[]={
2430         {MATCH_INS(STRB,2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
2431         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG(SP)}},
2432         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R1),  MATCH_OP_MEM_BASE(SP)}},
2433         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
2434         {ARM_INS_ENDING}
2435     };
2436     if(insn_match_find_next_seq(fw,is,148,match)) {
2437         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2438     }
2439 
2440     init_disasm_sig_ref(fw,is,rule);
2441     const insn_match_t match2[]={
2442         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_REG_ANY}},
2443         {MATCH_INS(STRB,2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
2444         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG(SP)}},
2445         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
2446         {ARM_INS_ENDING}
2447     };
2448     if(!insn_match_find_next_seq(fw,is,148,match2)) {
2449         //printf("sig_match_mkdir: no match\n");
2450         return 0;
2451     }
2452     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2453 }
2454 
2455 int sig_match_add_ptp_handler(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2456 {
2457     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2458     if(!str_adr) {
2459         printf("sig_match_add_ptp_handler: failed to find ref %s\n",rule->ref_name);
2460         return  0;
2461     }
2462     // TODO should handle multiple instances of string
2463     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2464     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2465         // expect CreateTaskStrictly
2466         if(!find_next_sig_call(fw,is,8,"CreateTaskStrictly")) {
2467             // printf("sig_match_add_ptp_handler: no CreateTaskStrictly\n");
2468             continue;
2469         }
2470         // expect add_ptp_handler is 3rd call after CreateTask
2471         if(!insn_match_find_nth(fw,is,13,3,match_bl_blximm)) {
2472             // printf("sig_match_add_ptp_handler: no match bl\n");
2473             return 0;
2474         }
2475         // sanity check, expect opcode, func, 0
2476         uint32_t regs[4];
2477         if((get_call_const_args(fw,is,5,regs)&7)!=7) {
2478             // printf("sig_match_add_ptp_handler: failed to get args\n");
2479             return 0;
2480         }
2481         if(regs[0] < 0x9000 || regs[0] > 0x10000 || !adr2ptr(fw,regs[1]) || regs[2] != 0) {
2482             // printf("sig_match_add_ptp_handler: bad args 0x%08x 0x%08x 0x%08x\n",regs[0],regs[1],regs[2]);
2483             return 0;
2484         }
2485         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2486     }
2487     return 0;
2488 }
2489 int sig_match_qsort(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2490 {
2491     if(!init_disasm_sig_ref(fw,is,rule)) {
2492         return 0;
2493     }
2494     if(!find_next_sig_call(fw,is,90,"DebugAssert")) {
2495         // printf("sig_match_qsort: no DebugAssert\n");
2496         return 0;
2497     }
2498     if(!insn_match_find_nth(fw,is,38,3,match_bl_blximm)) {
2499         // printf("sig_match_qsort: no match bl\n");
2500         return 0;
2501     }
2502     // follow
2503     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2504     // if call in first 4 insn, follow again (newer cams have an extra sub)
2505     if(insn_match_find_next(fw,is,4,match_bl_blximm)) {
2506         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2507     }
2508     if(!insn_match_find_next(fw,is,14,match_bl_blximm)) {
2509         // printf("sig_match_qsort: no match bl (qsort)\n");
2510         return 0;
2511     }
2512     // sanity check, expect r1-r3 to be const
2513     uint32_t regs[4];
2514     if((get_call_const_args(fw,is,5,regs)&0xe)!=0xe) {
2515         // printf("sig_match_qsort: failed to get args\n");
2516         return 0;
2517     }
2518     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2519 }
2520 // looks for sequence of calls near ref string RedEyeController.c
2521 // DeleteFile_Fut
2522 // ...
2523 // strcpy
2524 // ...
2525 // strchr
2526 // ...
2527 // DeleteDirectory_Fut
2528 int sig_match_deletedirectory_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2529 {
2530     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2531     if(!str_adr) {
2532         printf("sig_match_deletedirectory_fut: failed to find ref %s\n",rule->ref_name);
2533         return  0;
2534     }
2535     // TODO using larger than default "near" range, needed for sx710
2536     // not looking for ref to string, just code near where the actual string is
2537     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - 2048) | fw->thumb_default); // reset to a bit before where the string was found
2538     uint32_t end_adr = ADR_ALIGN4(str_adr) + 2048;
2539     while(find_next_sig_call(fw,is,end_adr - (uint32_t)is->adr,"DeleteFile_Fut")) {
2540         if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2541             // printf("sig_match_deletedirectory_fut: no match bl strcpy\n");
2542             continue;
2543         }
2544         if(!is_sig_call(fw,is,"strcpy")) {
2545             // printf("sig_match_deletedirectory_fut: bl not strcpy at 0x%"PRIx64"\n",is->insn->address);
2546             continue;
2547         }
2548         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2549             // printf("sig_match_deletedirectory_fut: no match bl strrchr at 0x%"PRIx64"\n",is->insn->address);
2550             continue;
2551         }
2552         if(!is_sig_call(fw,is,"strrchr")) {
2553             // printf("sig_match_deletedirectory_fut: bl not strrchr at 0x%"PRIx64"\n",is->insn->address);
2554             continue;
2555         }
2556         // verify that arg1 to strrch is /
2557         uint32_t regs[4];
2558         if((get_call_const_args(fw,is,2,regs)&0x2)!=0x2) {
2559             // printf("sig_match_deletedirectory_fut: failed to get strrchr r1 at 0x%"PRIx64"\n",is->insn->address);
2560             continue;
2561         }
2562         if(regs[1] != '/') {
2563             // printf("sig_match_deletedirectory_fut: strrchr r1 not '/' at 0x%"PRIx64"\n",is->insn->address);
2564             continue;
2565         }
2566         if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
2567             // printf("sig_match_deletedirectory_fut: no match bl at 0x%"PRIx64"\n",is->insn->address);
2568             continue;
2569         }
2570         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2571     }
2572     return 0;
2573 }
2574 
2575 /*
2576 match
2577    ref "LogicalEvent:0x%04x:adr:%p,Para:%ld" (or "LogicalEvent:0x%08x:adr:%p,Para:%ld")
2578    bl      LogCameraEvent
2579    mov     r0, rN
2580    bl      <some func>
2581    bl      set_control_event (or veneer)
2582 same string is used elsewhere, so match specific sequence
2583 */
2584 int sig_match_set_control_event(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2585 {
2586     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2587     if(!str_adr) {
2588         // not logged, two different ref strings so failing one is normal
2589         // printf("sig_match_set_control_event: failed to find ref %s\n",rule->ref_name);
2590         return  0;
2591     }
2592     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2593     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2594         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2595             // printf("sig_match_set_control_event: no match bl at 0x%"PRIx64"\n",is->insn->address);
2596             continue;
2597         }
2598         if(!is_sig_call(fw,is,"LogCameraEvent")) {
2599             // printf("sig_match_set_control_event: not LogCameraEvent at 0x%"PRIx64"\n",is->insn->address);
2600             continue;
2601         }
2602         const insn_match_t match_seq[]={
2603             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG_ANY,}},
2604             {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
2605             {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
2606             {ARM_INS_ENDING}
2607         };
2608         if(!insn_match_find_next_seq(fw,is,1,match_seq)) {
2609             // printf("sig_match_set_control_event: no match seq\n");
2610             continue;
2611         }
2612         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2613     }
2614     return 0;
2615 }
2616 // find displaybusyonscreen for r52 cams, later uses different code
2617 int sig_match_displaybusyonscreen_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2618 {
2619     if (fw->dryos_ver != 52) {
2620         return 0;
2621     }
2622     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2623     if(!str_adr) {
2624         printf("sig_match_displaybusyonscreen: failed to find ref %s\n",rule->ref_name);
2625         return  0;
2626     }
2627     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2628     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2629         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
2630             // printf("sig_match_displaybusyonscreen: no match bl at 0x%"PRIx64"\n",is->insn->address);
2631             continue;
2632         }
2633         if(!is_sig_call(fw,is,"LogCameraEvent")) {
2634             // printf("sig_match_displaybusyonscreen: not LogCameraEvent at 0x%"PRIx64"\n",is->insn->address);
2635             continue;
2636         }
2637         if(!find_next_sig_call(fw,is,4,"GUISrv_StartGUISystem_FW")) {
2638             // printf("sig_match_displaybusyonscreen: no match GUISrv_StartGUISystem_FW\n");
2639             continue;
2640         }
2641         if(!insn_match_find_nth(fw,is,5,2,match_bl_blximm)) {
2642             // printf("sig_match_displaybusyonscreen: no match bl 0x%"PRIx64"\n",is->insn->address);
2643             continue;
2644         }
2645         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2646     }
2647     return 0;
2648 }
2649 // find undisplaybusyonscreen for r52 cams, later uses different code
2650 int sig_match_undisplaybusyonscreen_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2651 {
2652     if (fw->dryos_ver != 52) {
2653         return 0;
2654     }
2655     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2656     if(!str_adr) {
2657         printf("sig_match_undisplaybusyonscreen: failed to find ref %s\n",rule->ref_name);
2658         return  0;
2659     }
2660     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2661     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2662         // assume same function display* was found in, skip to that
2663         if(!find_next_sig_call(fw,is,24,"displaybusyonscreen")) {
2664             // printf("sig_match_undisplaybusyonscreen: no match displaybusyonscreen\n");
2665             continue;
2666         }
2667         if(!find_next_sig_call(fw,is,12,"GUISrv_StartGUISystem_FW")) {
2668             // printf("sig_match_undisplaybusyonscreen: no match GUISrv_StartGUISystem_FW\n");
2669             continue;
2670         }
2671         if(!insn_match_find_nth(fw,is,6,3,match_bl_blximm)) {
2672             // printf("sig_match_undisplaybusyonscreen: no match bl 0x%"PRIx64"\n",is->insn->address);
2673             continue;
2674         }
2675         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2676     }
2677     return 0;
2678 }
2679 int sig_match_pt_playsound(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2680 {
2681     // function looks different on dry >= 57, may be OK but will need wrapper changes
2682     if (fw->dryos_ver >= 57) {
2683         return 0;
2684     }
2685     return sig_match_near_str(fw,is,rule);
2686 }
2687 
2688 int sig_match_try_take_sem_dry_gt_58(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2689 {
2690     if(!init_disasm_sig_ref(fw,is,rule)) {
2691         return 0;
2692     }
2693     if(!find_next_sig_call(fw,is,24,"ReceiveMessageQueue")) {
2694         printf("sig_match_try_take_sem_dry_gt_58: failed to find ReceiveMessageQueue\n");
2695         return 0;
2696     }
2697     if(!find_next_sig_call(fw,is,60,"bzero")) {
2698         printf("sig_match_try_take_sem_dry_gt_58: failed to find bzero\n");
2699         return 0;
2700     }
2701     if(insn_match_find_next(fw,is,3,match_bl_blximm)) {
2702         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2703     }
2704     printf("sig_match_try_take_sem_dry_gt_58: no match\n");
2705     return 0;
2706 }
2707 
2708 int sig_match_wait_all_eventflag_strict(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2709 {
2710     if(!init_disasm_sig_ref(fw,is,rule)) {
2711         return 0;
2712     }
2713     uint32_t str_adr = find_str_bytes(fw,"EFTool.c");
2714     if(!str_adr) {
2715         printf("sig_match_wait_all_eventflag_strict: failed to find ref EFTool.c\n");
2716         return 0;
2717     }
2718     if(!find_next_sig_call(fw,is,60,"SleepTask")) {
2719         printf("sig_match_wait_all_eventflag_strict: failed to find SleepTask\n");
2720         return 0;
2721     }
2722 
2723     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,is->adr + 60)) {
2724         if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2725             printf("sig_match_wait_all_eventflag_strict: no match bl 0x%"PRIx64"\n",is->insn->address);
2726             return 0;
2727         }
2728         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2729     }
2730     return 0;
2731 }
2732 
2733 int sig_match_get_num_posted_messages(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2734 {
2735     if(!init_disasm_sig_ref(fw,is,rule)) {
2736         return 0;
2737     }
2738     if(!find_next_sig_call(fw,is,50,"TakeSemaphore")) {
2739         printf("sig_match_get_num_posted_messages: failed to find TakeSemaphore\n");
2740         return 0;
2741     }
2742     // find next call
2743     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
2744         printf("sig_match_get_num_posted_messages:  no match bl 0x%"PRIx64"\n",is->insn->address);
2745         return 0;
2746     }
2747     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2748 }
2749 
2750 int sig_match_set_hp_timer_after_now(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2751 {
2752     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2753     if(!str_adr) {
2754         printf("sig_match_set_hp_timer_after_now: failed to find ref %s\n",rule->ref_name);
2755         return 0;
2756     }
2757     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2758     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2759         if(!find_next_sig_call(fw,is,20,"ClearEventFlag")) {
2760             // printf("sig_match_set_hp_timer_after_now: failed to find ClearEventFlag\n");
2761             continue;
2762         }
2763         // find 3rd call
2764         if(!insn_match_find_nth(fw,is,13,3,match_bl_blximm)) {
2765             // printf("sig_match_set_hp_timer_after_now: no match bl 0x%"PRIx64"\n",is->insn->address);
2766             continue;
2767         }
2768         // check call args, expect r0 = 70000
2769         uint32_t regs[4];
2770         if((get_call_const_args(fw,is,6,regs)&0x1)!=0x1) {
2771             // printf("sig_match_set_hp_timer_after_now: failed to get args 0x%"PRIx64"\n",is->insn->address);
2772             continue;
2773         }
2774         // r1, r2 should be func pointers but may involve reg-reg moves that get_call_const_args doesn't track
2775         if(regs[0] != 70000) {
2776             // printf("sig_match_set_hp_timer_after_now: args mismatch 0x%08x 0x%08x 0x%"PRIx64"\n",regs[0],regs[1],is->insn->address);
2777             continue;
2778         }
2779         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2780     }
2781     return 0;
2782 }
2783 int sig_match_transfer_src_overlay(firmware *fw, iter_state_t *is, sig_rule_t *rule) {
2784     if(!init_disasm_sig_ref(fw,is,rule)) {
2785         return 0;
2786     }
2787     // skip to debugassert
2788     if(!find_next_sig_call(fw,is,32,"DebugAssert")) {
2789         printf("sig_match_transfer_src_overlay: no match DebugAssert\n");
2790         return 0;
2791     }
2792     var_ldr_desc_t desc;
2793     if(!find_and_get_var_ldr(fw, is, 20, ARM_REG_R0, &desc)) {
2794         printf("sig_match_transfer_src_overlay: no match ldr\n");
2795         return 0;
2796     }
2797     // following should be call
2798     if(!insn_match_find_next(fw,is,1,match_bl_blximm)) {
2799         printf("sig_match_transfer_src_overlay: no match bl 0x%"PRIx64"\n",is->insn->address);
2800         return 0;
2801     }
2802     // adding active_bitmap_buffer here
2803     // note 4 bytes after value used on many ports, but the value normally sent to transfer_src_overlay
2804     save_misc_val("active_bitmap_buffer",desc.adr_adj,desc.off,(uint32_t)is->insn->address);
2805     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2806 }
2807 
2808 // find exmem related stuff
2809 int sig_match_exmem_vars(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2810 {
2811     uint32_t adr[2], fnd[2];
2812     if(!init_disasm_sig_ref(fw,is,rule)) {
2813         printf("sig_match_exmem_vars: missing ref\n");
2814         return 0;
2815     }
2816     // expect first LDR pc
2817     if(!insn_match_find_next(fw,is,15,match_ldr_pc)) {
2818         printf("sig_match_exmem_vars: match LDR PC failed\n");
2819         return 0;
2820     }
2821     adr[0]=LDR_PC2val(fw,is->insn);
2822     fnd[0]=(uint32_t)is->insn->address;
2823     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
2824         printf("sig_match_exmem_vars: 2nd match LDR PC failed\n");
2825         return 0;
2826     }
2827     adr[1]=LDR_PC2val(fw,is->insn);
2828     fnd[1]=(uint32_t)is->insn->address;
2829     //printf("sig_match_exmem_vars: %x, %x\n",adr[0], adr[1]);
2830     int n;
2831     for (n=0; n<2; n++) {
2832         if (adr[n] < fw->data_start+fw->data_len) {
2833             uint32_t ladr = adr[n]-fw->data_start+fw->data_init_start;
2834             save_misc_val("exmem_types_table",ladr,0,fnd[n]);
2835             int m;
2836             int exm_typ_cnt = 0;
2837             for (m=0; m<42; m++) {
2838                 if ( (fw_u32(fw,ladr+m*4)!=0) && isASCIIstring(fw, fw_u32(fw,ladr+m*4)) )
2839                 {
2840                     char *extyp = (char*)adr2ptr(fw, fw_u32(fw,ladr+m*4));
2841                     if ( strncmp(extyp,"EXMEM",5)==0 )
2842                     {
2843                         exm_typ_cnt++;
2844                     }
2845                 }
2846                 else
2847                 {
2848                     break;
2849                 }
2850             }
2851             save_misc_val("exmem_type_count",exm_typ_cnt,0,ladr);
2852         }
2853         else if (adr[n] < fw->memisostart) {
2854             save_misc_val("exmem_alloc_table",adr[n],0,fnd[n]);
2855         }
2856     }
2857     return 1;
2858 }
2859 
2860 // find function that copies Zico Xtensa blobs to their destination (dryos 52)
2861 int sig_match_zicokick_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2862 {
2863     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2864     if(!str_adr) {
2865         printf("sig_match_zicokick_52: failed to find ref %s\n",rule->ref_name);
2866         return  0;
2867     }
2868     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2869     
2870     // search for string ref
2871     if(!fw_search_insn(fw,is,search_disasm_str_ref,0,rule->ref_name,(uint32_t)is->adr+SEARCH_NEAR_REF_RANGE)) {
2872         printf("sig_match_zicokick_52: failed to find insn ref %s\n",rule->ref_name);
2873         return 0;
2874     }
2875     // check preceding instruction
2876     if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,1))) {
2877         printf("sig_match_zicokick_52: disasm failed\n");
2878         return 0;
2879     }
2880     if (!(isLDR_PC(fw->is->insn) && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0)) {
2881         printf("sig_match_zicokick_52: match ldr r0 failed\n");
2882         return 0;
2883     }
2884     // save backtracked address
2885     uint32_t adr=(uint32_t)(fw->is->insn->address) | is->thumb;
2886     // step forward one from string ref
2887     if(!disasm_iter(fw,is)) {
2888         printf("sig_match_zicokick_52: disasm failed\n");
2889         return 0;
2890     }
2891     if (is->insn->id == ARM_INS_PUSH && is->insn->detail->arm.operands[0].reg == ARM_REG_R4) {
2892         return save_sig_with_j(fw,rule->name,adr);
2893     }
2894     return 0;
2895 }
2896 // find function that copies Zico Xtensa blobs to their destination (dryos >52)
2897 int sig_match_zicokick_gt52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2898 {
2899     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
2900     if(!str_adr) {
2901         printf("sig_match_zicokick_gt52: failed to find ref %s\n",rule->ref_name);
2902         return  0;
2903     }
2904     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2905     
2906     // search for string ref
2907     if(!fw_search_insn(fw,is,search_disasm_str_ref,0,rule->ref_name,(uint32_t)is->adr+SEARCH_NEAR_REF_RANGE)) {
2908         printf("sig_match_zicokick_gt52: failed to find insn ref %s\n",rule->ref_name);
2909         return 0;
2910     }
2911     int i;
2912     // search backward for
2913     // ldr r0,...
2914     // push r4,...
2915     for(i=1; i<=8; i++) {
2916         if (!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
2917             printf("sig_match_zicokick_gt52: disasm failed\n");
2918             return 0;
2919         }
2920         if (fw->is->insn->id == ARM_INS_PUSH && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R4) {
2921             if (!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i+1))) {
2922                 printf("sig_match_zicokick_gt52: disasm failed\n");
2923                 return 0;
2924             }
2925             if (isLDR_PC(fw->is->insn) && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
2926                 return save_sig_with_j(fw,rule->name,(uint32_t)(fw->is->insn->address) | is->thumb);
2927             }
2928             return 0;
2929         }
2930     }
2931     return 0;
2932 }
2933 int sig_match_zicokick_copy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2934 {
2935     if(!init_disasm_sig_ref(fw,is,rule)) {
2936         return 0;
2937     }
2938     // TODO could be less strict on regs, 5 LDRs in a row is rare
2939     const insn_match_t match_ldrs_bl[]={
2940         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
2941         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R1),  MATCH_OP_MEM_BASE(PC)}},
2942         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R2),  MATCH_OP_MEM_BASE(R0)}},
2943         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
2944         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(R0)}},
2945         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
2946         {ARM_INS_ENDING}
2947     };
2948     if(!insn_match_find_next_seq(fw,is,30,match_ldrs_bl)) {
2949         printf("sig_match_zicokick_copy no match ldr\n");
2950         return 0;
2951     }
2952     // TODO could sanity check bl target
2953     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2954 }
2955 
2956 int sig_match_zicokick_values(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2957 {
2958     if(!init_disasm_sig_ref(fw,is,rule)) {
2959         return 0;
2960     }
2961 // get_call_const_args doesn't currently handle ldr sequence
2962 #if 0
2963     // first call is further from function start
2964     if(!find_next_sig_call(fw,is,64,"zicokick_copy")) {
2965         printf("sig_match_zicokick_values: no zicokick_copy 1\n");
2966         return 0;
2967     }
2968     while(1) {
2969         uint32_t regs[4];
2970         if((get_call_const_args(fw,is,7,regs)&0x7)==0x7) {
2971             printf("xtensa blob @ 0x%08x, loads to 0x%08x, size 0x%08x\n",regs[1],regs[0],regs[2]);
2972         } else {
2973             printf("sig_match_zicokick_values: failed to get regs\n");
2974         }
2975         if(!find_next_sig_call(fw,is,8,"zicokick_copy")) {
2976             break;
2977         }
2978     }
2979     return 1;
2980 #endif
2981     int i;
2982     uint32_t uv[3] = {0,0,0};
2983     int uvi = 0;
2984     misc_blob_t *blobs=malloc((MISC_BLOB_XTENSA_MAX + 1)*sizeof(misc_blob_t));
2985     int n_blobs = 0;
2986 
2987     for(i=1; i<=64; i++) {
2988         if (!disasm_iter(fw,is)) {
2989             free(blobs);
2990             return 0;
2991         }
2992         if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].type == ARM_OP_MEM) {
2993             uint32_t u = LDR_PC2val(fw,is->insn);
2994             if ((u<fw->base+fw->size8) && (u>fw->rom_code_search_max_adr)) {
2995                 // address outside the main fw
2996                 if (uvi<3) {
2997                     uv[uvi] = u;
2998                     uvi++;
2999                 }
3000             }
3001         }
3002         else if (is->insn->id == ARM_INS_BL) {
3003             if (uvi==3) {
3004                 // func call, all 3 addresses are in collection
3005                 uint32_t bsize, bloadedto, badr, u;
3006                 int j;
3007                 badr = MAX(MAX(uv[0],uv[1]),uv[2]);
3008                 for (j=0; j<3; j++) {
3009                     if (uv[j]!=badr) {
3010                         u = fw_u32(fw, uv[j]);
3011                         if (u<1024*1024*2) {
3012                             bsize = u;
3013                         }
3014                         else {
3015                             bloadedto = u;
3016                         }
3017                     }
3018                 }
3019                 if (bsize) {
3020                     if(n_blobs == MISC_BLOB_XTENSA_MAX) {
3021                         printf("sig_match_zicokick_values: ignoring xtensa blobs > %d\n",MISC_BLOB_XTENSA_MAX);
3022                         blobs[n_blobs].type = MISC_BLOB_TYPE_NONE;
3023                         break;
3024                     }
3025                     // printf("xtensa blob @ 0x%08x, loads to 0x%08x, size 0x%08x\n",badr,bloadedto,bsize);
3026                     blobs[n_blobs].type = MISC_BLOB_TYPE_XTENSA;
3027                     blobs[n_blobs].rom_adr = badr;
3028                     blobs[n_blobs].ram_adr = bloadedto;
3029                     blobs[n_blobs].size = bsize;
3030                     n_blobs++;
3031                 }
3032             }
3033             uvi = 0;
3034         }
3035         else if (is->insn->id == ARM_INS_POP) {
3036             break;
3037         }
3038     }
3039     if(n_blobs > 0) {
3040         blobs[n_blobs].type = MISC_BLOB_TYPE_NONE;
3041         save_misc_val_blobs("zicokick_values",blobs,0);
3042         return 1;
3043     } else {
3044         free(blobs);
3045         return 0;
3046     }
3047 }
3048 
3049 int sig_match_enable_hdmi_power(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3050 {
3051     if(!init_disasm_sig_ref(fw,is,rule)) {
3052         return 0;
3053     }
3054     if(!find_next_sig_call(fw,is,14,"CreateEventFlagStrictly")) {
3055         printf("sig_match_enable_hdmi_power: no match CreateEventFlagStrictly\n");
3056         return 0;
3057     }
3058     const insn_match_t match_seq[]={
3059         {MATCH_INS(BL,   MATCH_OPCOUNT_IGNORE)},
3060         {MATCH_INS(CBNZ, MATCH_OPCOUNT_IGNORE)},
3061         {ARM_INS_ENDING}
3062     };
3063     if(!insn_match_find_next_seq(fw,is,4,match_seq)) {
3064         printf("sig_match_enable_hdmi_power: no match bl seq cbnz 0x%"PRIx64"\n",is->insn->address);
3065         return 0;
3066     }
3067     // function should be next call
3068     if (!disasm_iter(fw,is)) {
3069         return 0;
3070     }
3071     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3072 }
3073 
3074 int sig_match_disable_hdmi_power(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3075 {
3076     if(!init_disasm_sig_ref(fw,is,rule)) {
3077         return 0;
3078     }
3079     if(!find_next_sig_call(fw,is,24,"EnableHDMIPower")) {
3080         printf("sig_match_disable_hdmi_power: no match EnableHDMIPower\n");
3081         return 0;
3082     }
3083     if(!find_next_sig_call(fw,is,22,"ClearEventFlag")) {
3084         printf("sig_match_disable_hdmi_power: no match ClearEventFlag\n");
3085         return 0;
3086     }
3087     const insn_match_t match_seq[]={
3088         {MATCH_INS(BL,   MATCH_OPCOUNT_IGNORE)},
3089         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3090         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
3091         {ARM_INS_ENDING}
3092     };
3093     if(!insn_match_find_next_seq(fw,is,12,match_seq)) {
3094         printf("sig_match_disable_hdmi_power: no match seq bl movs pop 0x%"PRIx64"\n",is->insn->address);
3095         return 0;
3096     }
3097     // bl matched above should be func
3098     disasm_iter_init(fw,is,adr_hist_get(&is->ah,2));
3099     if (!disasm_iter(fw,is)) {
3100         return 0;
3101     }
3102     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3103 }
3104 
3105 int sig_match_levent_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3106 {
3107     if(!init_disasm_sig_ref(fw,is,rule)) {
3108         return 0;
3109     }
3110     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
3111         // printf("sig_match_levent_table: no match bl 0x%"PRIx64"\n",is->insn->address);
3112         return 0;
3113     }
3114     // follow
3115     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3116 
3117     // find first call of next function
3118     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
3119         // printf("sig_match_levent_table: no match bl 0x%"PRIx64"\n",is->insn->address);
3120         return 0;
3121     }
3122     
3123     // follow
3124     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3125 
3126     // first instruction should load address
3127     disasm_iter(fw,is);
3128     uint32_t adr=LDR_PC2val(fw,is->insn);
3129     if(!adr) {
3130         // printf("sig_match_levent_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3131         return  0;
3132     }
3133     uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
3134     if(!p) {
3135         printf("sig_match_levent_table: 0x%08x not a ROM adr 0x%"PRIx64"\n",adr,is->insn->address);
3136         return  0;
3137     }
3138     if(*(p+1) != 0x800) {
3139         printf("sig_match_levent_table: expected 0x800 not 0x%x at 0x%08x ref 0x%"PRIx64"\n",*(p+1),adr,is->insn->address);
3140         return  0;
3141     }
3142     // TODO saving the function might be useful for analysis
3143     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3144     return 1;
3145 }
3146 int sig_match_flash_param_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3147 {
3148     if(!init_disasm_sig_ref(fw,is,rule)) {
3149         return 0;
3150     }
3151     // expect 3 asserts
3152     if(!insn_match_find_next(fw,is,14,match_bl_blximm)) {
3153         // printf("sig_match_flash_param_table: no match bl 1\n");
3154         return 0;
3155     }
3156     if(!is_sig_call(fw,is,"DebugAssert")) {
3157         // printf("sig_match_flash_param_table: bl 1 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3158         return 0;
3159     }
3160     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
3161         // printf("sig_match_flash_param_table: no match bl 2\n");
3162         return 0;
3163     }
3164     if(!is_sig_call(fw,is,"DebugAssert")) {
3165         // printf("sig_match_flash_param_table: bl 2 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3166         return 0;
3167     }
3168     if(!insn_match_find_next(fw,is,8,match_bl_blximm)) {
3169         // printf("sig_match_flash_param_table: no match bl 3\n");
3170         return 0;
3171     }
3172     if(!is_sig_call(fw,is,"DebugAssert")) {
3173         // printf("sig_match_flash_param_table: bl 3 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3174         return 0;
3175     }
3176     // expect AcquireRecursiveLockStrictly, func
3177     if(!insn_match_find_nth(fw,is,14,2,match_bl_blximm)) {
3178         // printf("sig_match_flash_param_table: no match sub 1\n");
3179         return 0;
3180     }
3181     // follow
3182     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3183 
3184     // first call
3185     if(!insn_match_find_next(fw,is,8,match_bl_blximm)) {
3186         // printf("sig_match_flash_param_table: no match sub 1 bl\n");
3187         return 0;
3188     }
3189     
3190     // follow
3191     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3192     // first instruction should load address
3193     disasm_iter(fw,is);
3194     uint32_t adr=LDR_PC2val(fw,is->insn);
3195     if(!adr) {
3196         // printf("sig_match_flash_param_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3197         return  0;
3198     }
3199     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3200     return 1;
3201 }
3202 int sig_match_jpeg_count_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3203 {
3204     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
3205     if(!str_adr) {
3206         printf("sig_match_jpeg_count_str: failed to find ref %s\n",rule->ref_name);
3207         return  0;
3208     }
3209     // TODO should handle multiple instances of string
3210     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3211     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
3212         // printf("sig_match_jpeg_count_str: str match 0x%"PRIx64"\n",is->insn->address);
3213         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
3214             // printf("sig_match_jpeg_count_str: no match bl\n");
3215             continue;
3216         }
3217         if(!is_sig_call(fw,is,"sprintf_FW")) {
3218             // printf("sig_match_jpeg_count_str: not sprintf_FW at 0x%"PRIx64"\n",is->insn->address);
3219             continue;
3220         }
3221         // expect ptr in r0, str in r1
3222         uint32_t regs[4];
3223         if((get_call_const_args(fw,is,5,regs)&0x3)!=0x3) {
3224             // printf("sig_match_jpeg_count_str: failed to get sprintf args 0x%"PRIx64"\n",is->insn->address);
3225             continue;
3226         }
3227         if(regs[1] != str_adr) {
3228             // printf("sig_match_jpeg_count_str: expected r1 == 0x%08x not 0x%08x at 0x%"PRIx64"\n",str_adr, regs[1],is->insn->address);
3229             return 0;
3230         }
3231         if(!adr_is_var(fw,regs[0])) {
3232             // printf("sig_match_jpeg_count_str: r0 == 0x%08x not var ptr at 0x%"PRIx64"\n",regs[0],is->insn->address);
3233             return 0;
3234         }
3235         save_misc_val(rule->name,regs[0],0,(uint32_t)is->insn->address);
3236         return 1;
3237     }
3238     return 0;
3239 }
3240 
3241 // set a boolean misc val if ref is present
3242 int sig_match_misc_flag_named(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3243 {
3244     uint32_t ref=get_saved_sig_val(rule->ref_name);
3245     save_misc_val(rule->name,(ref)?1:0,0,ref);
3246     return 1;
3247 }
3248 
3249 int sig_match_cam_has_iris_diaphragm(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3250 {
3251     uint32_t v;
3252     uint32_t ref=0;get_saved_sig_val(rule->ref_name);
3253     // ILC assumed to have iris
3254     if(get_misc_val_value("CAM_IS_ILC")) {
3255         v=1;
3256     } else {
3257         ref=get_saved_sig_val(rule->ref_name);
3258         v=(ref)?1:0;
3259     }
3260     save_misc_val(rule->name,v,0,ref);
3261     return 1;
3262 }
3263 
3264 int sig_match_cam_uncached_bit(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3265 {
3266     if(!init_disasm_sig_ref(fw,is,rule)) {
3267         return 0;
3268     }
3269     const insn_match_t match_bic_r0[]={
3270         {MATCH_INS(BIC, 3), {MATCH_OP_REG(R0),  MATCH_OP_REG(R0),   MATCH_OP_IMM_ANY}},
3271         {ARM_INS_ENDING}
3272     };
3273     if(insn_match_find_next(fw,is,4,match_bic_r0)) {
3274         save_misc_val(rule->name,is->insn->detail->arm.operands[2].imm,0,(uint32_t)is->insn->address);
3275         return 1;
3276     }
3277     return 0;
3278 }
3279 
3280 int sig_match_physw_event_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3281 {
3282     if(!init_disasm_sig_ref(fw,is,rule)) {
3283         return 0;
3284     }
3285     // expect first LDR pc
3286     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3287         printf("sig_match_physw_event_table: match LDR PC failed\n");
3288         return 0;
3289     }
3290     uint32_t adr=LDR_PC2val(fw,is->insn);
3291     if(!adr) {
3292         printf("sig_match_physw_event_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3293         return 0;
3294     }
3295     if(!adr2ptr(fw,adr)) {
3296         printf("sig_match_physw_event_table: adr not ROM 0x%08x at 0x%"PRIx64"\n",adr,is->insn->address);
3297         return 0;
3298     }
3299     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3300     return 1;
3301 }
3302 int sig_match_uiprop_count(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3303 {
3304     if(!init_disasm_sig_ref(fw,is,rule)) {
3305         return 0;
3306     }
3307     if(!find_next_sig_call(fw,is,38,"DebugAssert")) {
3308         // printf("sig_match_uiprop_count: no DebugAssert 1\n");
3309         return 0;
3310     }
3311     if(!find_next_sig_call(fw,is,14,"DebugAssert")) {
3312         // printf("sig_match_uiprop_count: no DebugAssert 2\n");
3313         return 0;
3314     }
3315     const insn_match_t match_bic_cmp[]={
3316         {MATCH_INS(BIC, 3), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY,   MATCH_OP_IMM(0x8000)}},
3317         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY,  MATCH_OP_ANY}},
3318         {ARM_INS_ENDING}
3319     };
3320     if(!insn_match_find_next_seq(fw,is,3,match_bic_cmp)) {
3321         // printf("sig_match_uiprop_count: no bic,cmp\n");
3322         return 0;
3323     }
3324     save_misc_val(rule->name,is->insn->detail->arm.operands[1].imm,0,(uint32_t)is->insn->address);
3325     return 1;
3326 }
3327 
3328 int sig_match_get_canon_mode_list(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3329 {
3330     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
3331     if(!str_adr) {
3332         printf("sig_match_get_canon_mode_list: failed to find ref %s\n",rule->ref_name);
3333         return  0;
3334     }
3335     uint32_t adr=0;
3336     // TODO should handle multiple instances of string
3337     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3338     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
3339         // printf("sig_match_get_canon_mode_list: str match 0x%"PRIx64"\n",is->insn->address);
3340         if(!find_next_sig_call(fw,is,4,"LogCameraEvent")) {
3341             // printf("sig_match_get_canon_mode_list: no LogCameraEvent\n");
3342             continue;
3343         }
3344         // some cameras have a mov and an extra call
3345         if(!disasm_iter(fw,is)) {
3346             // printf("sig_match_get_canon_mode_list: disasm failed\n");
3347             return 0;
3348         }
3349         const insn_match_t match_mov_r0_1[]={
3350 #if CS_API_MAJOR < 4
3351             {MATCH_INS(MOVS, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3352 #endif
3353             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3354             {ARM_INS_ENDING}
3355         };
3356         if(insn_match_any(is->insn,match_mov_r0_1)) {
3357             if(!insn_match_find_nth(fw,is,2,2,match_bl_blximm)) {
3358                 // printf("sig_match_get_canon_mode_list: no match bl 1x\n");
3359                 continue;
3360             }
3361         } else {
3362             if(!insn_match_any(is->insn,match_bl_blximm)) {
3363                 // printf("sig_match_get_canon_mode_list: no match bl 1\n");
3364                 continue;
3365             }
3366         }
3367         // found something to follow, break
3368         adr=get_branch_call_insn_target(fw,is);
3369         break;
3370     }
3371     if(!adr) {
3372         return 0;
3373     }
3374     // printf("sig_match_get_canon_mode_list: sub 1 0x%08x\n",adr);
3375     disasm_iter_init(fw,is,adr);
3376     if(!find_next_sig_call(fw,is,40,"TakeSemaphoreStrictly")) {
3377         // printf("sig_match_get_canon_mode_list: no TakeSemaphoreStrictly\n");
3378         return 0;
3379     }
3380     // match second call
3381     if(!insn_match_find_nth(fw,is,12,2,match_bl_blximm)) {
3382         // printf("sig_match_get_canon_mode_list: no match bl 2\n");
3383         return 0;
3384     }
3385     // follow
3386     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3387     const insn_match_t match_loop[]={
3388         {MATCH_INS(ADD, 3), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY,   MATCH_OP_IMM(1)}},
3389         {MATCH_INS(UXTH, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
3390         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM_ANY}},
3391         {MATCH_INS_CC(B,LO,MATCH_OPCOUNT_IGNORE)},
3392         {ARM_INS_ENDING}
3393     };
3394     if(!insn_match_find_next_seq(fw,is,40,match_loop)) {
3395         // printf("sig_match_get_canon_mode_list: match 1 failed\n");
3396         return 0;
3397     }
3398     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
3399         // printf("sig_match_get_canon_mode_list: no match bl 3\n");
3400         return 0;
3401     }
3402     // should be func
3403     adr=get_branch_call_insn_target(fw,is);
3404     // sanity check
3405     disasm_iter_init(fw,is,adr);
3406     const insn_match_t match_ldr_r0_ret[]={
3407         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
3408         {MATCH_INS(BX, 1),   {MATCH_OP_REG(LR)}},
3409         {ARM_INS_ENDING}
3410     };
3411     if(!insn_match_find_next_seq(fw,is,1,match_ldr_r0_ret)) {
3412         // printf("sig_match_get_canon_mode_list: match 2 failed\n");
3413         return 0;
3414     }
3415     return save_sig_with_j(fw,rule->name,adr);
3416 } 
3417 
3418 int sig_match_zoom_busy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3419 {
3420     if(!init_disasm_sig_ref(fw,is,rule)) {
3421         return 0;
3422     }
3423     // first call
3424     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
3425         // printf("sig_match_zoom_busy: no match bl\n");
3426         return 0;
3427     }
3428     // follow
3429     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3430     // get base address from first LDR PC
3431     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3432         // printf("sig_match_zoom_busy: match LDR PC failed\n");
3433         return 0;
3434     }
3435     uint32_t base=LDR_PC2val(fw,is->insn);
3436     arm_reg rb=is->insn->detail->arm.operands[0].reg;
3437     
3438     // look for first TakeSemaphoreStrictly
3439     if(!find_next_sig_call(fw,is,40,"TakeSemaphoreStrictly")) {
3440         // printf("sig_match_zoom_busy: no match TakeSemaphoreStrictly\n");
3441         return 0;
3442     }
3443     if(!disasm_iter(fw,is)) {
3444         // printf("sig_match_zoom_busy: disasm failed\n");
3445         return 0;
3446     }
3447     // assume next instruction is ldr
3448     if(is->insn->id != ARM_INS_LDR 
3449         || is->insn->detail->arm.operands[0].reg != ARM_REG_R0
3450         || is->insn->detail->arm.operands[1].mem.base != rb) {
3451         // printf("sig_match_zoom_busy: no match LDR\n");
3452         return 0;
3453     }
3454     save_misc_val(rule->name,base,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
3455     return 1;
3456 }
3457 
3458 int sig_match_focus_busy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3459 {
3460     if(!init_disasm_sig_ref(fw,is,rule)) {
3461         return 0;
3462     }
3463     // look for first TakeSemaphore
3464     if(!find_next_sig_call(fw,is,40,"TakeSemaphore")) {
3465         // printf("sig_match_focus_busy: no match TakeSemaphore\n");
3466         return 0;
3467     }
3468     // next call
3469     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
3470         // printf("sig_match_focus_busy: no match bl\n");
3471         return 0;
3472     }
3473     // follow
3474     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3475     // get base address from first LDR PC
3476     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3477         // printf("sig_match_focus_busy: match LDR PC failed\n");
3478         return 0;
3479     }
3480     uint32_t base=LDR_PC2val(fw,is->insn);
3481     arm_reg rb=is->insn->detail->arm.operands[0].reg;
3482     
3483     // look for first TakeSemaphoreStrictly
3484     if(!find_next_sig_call(fw,is,50,"TakeSemaphoreStrictly")) {
3485         // printf("sig_match_focus_busy: no match TakeSemaphoreStrictly\n");
3486         return 0;
3487     }
3488     const insn_match_t match_ldr[]={
3489         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0), MATCH_OP_MEM_ANY}},
3490         {ARM_INS_ENDING}
3491     };
3492     if(!insn_match_find_next(fw,is,7,match_ldr)) {
3493         // printf("sig_match_focus_busy: no match LDR\n");
3494         return 0;
3495     }
3496 
3497     // check LDR 
3498     if(is->insn->detail->arm.operands[1].mem.base != rb) {
3499         // printf("sig_match_focus_busy: no match LDR base\n");
3500         return 0;
3501     }
3502     save_misc_val(rule->name,base,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
3503     return 1;
3504 }
3505 int sig_match_aram_size(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3506 {
3507     if(!init_disasm_sig_ref(fw,is,rule)) {
3508         printf("sig_match_aram_size: missing ref\n");
3509         return 0;
3510     }
3511     const insn_match_t match_ldr_r0_sp_cmp[]={
3512         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),MATCH_OP_MEM(SP,INVALID,0xc)}},
3513         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),MATCH_OP_IMM_ANY}},
3514         {ARM_INS_ENDING}
3515     };
3516     if(!insn_match_find_next_seq(fw,is,15,match_ldr_r0_sp_cmp)) {
3517         printf("sig_match_aram_size: no match LDR\n");
3518         return 0;
3519     }
3520     uint32_t val=is->insn->detail->arm.operands[1].imm;
3521     if(val != 0x22000 && val != 0x32000) {
3522         printf("sig_match_aram_size: unexpected ARAM size 0x%08x\n",val);
3523     }
3524     save_misc_val(rule->name,val,0,(uint32_t)is->insn->address);
3525     return 1;
3526 }
3527 
3528 int sig_match_aram_start(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3529 {
3530     if(!init_disasm_sig_ref(fw,is,rule)) {
3531         printf("sig_match_aram_start: missing ref\n");
3532         return 0;
3533     }
3534     if(!find_next_sig_call(fw,is,50,"DebugAssert")) {
3535         printf("sig_aram_start: no match DebugAssert\n");
3536         return 0;
3537     }
3538     const insn_match_t match_cmp_bne_ldr[]={
3539         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R1),MATCH_OP_IMM(0)}},
3540         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
3541         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(PC)}},
3542         {ARM_INS_ENDING}
3543     };
3544     if(!insn_match_find_next_seq(fw,is,15,match_cmp_bne_ldr)) {
3545         printf("sig_match_aram_start: no match CMP\n");
3546         return 0;
3547     }
3548     uint32_t adr=LDR_PC2val(fw,is->insn);
3549     if(!adr) {
3550         printf("sig_match_aram_start: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3551         return 0;
3552     }
3553     // could sanity check that it looks like a RAM address
3554     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3555     return 1;
3556 }
3557 
3558 int sig_match_aram_start2(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3559 {
3560     if (get_misc_val_value("ARAM_HEAP_START"))
3561         return 0;
3562 
3563     if(!init_disasm_sig_ref(fw,is,rule)) {
3564         printf("sig_match_aram_start: missing ref\n");
3565         return 0;
3566     }
3567     if(!find_next_sig_call(fw,is,50,"DebugAssert")) {
3568         printf("sig_aram_start2: no match DebugAssert\n");
3569         return 0;
3570     }
3571     const insn_match_t match_cmp_bne_ldr[]={
3572         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R1),MATCH_OP_IMM(0)}},
3573         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
3574         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(SP)}},
3575         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(PC)}},
3576         {ARM_INS_ENDING}
3577     };
3578     if(!insn_match_find_next_seq(fw,is,15,match_cmp_bne_ldr)) {
3579         printf("sig_match_aram_start2: no match CMP\n");
3580         return 0;
3581     }
3582     uint32_t adr=LDR_PC2val(fw,is->insn);
3583     if(!adr) {
3584         printf("sig_match_aram_start2: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3585         return 0;
3586     }
3587     // could sanity check that it looks like a RAM address
3588     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3589     return 1;
3590 }
3591 
3592 int sig_match__nrflag(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3593 {
3594     if(!init_disasm_sig_ref(fw,is,rule)) {
3595         return 0;
3596     }
3597     uint32_t fadr=is->adr;
3598     // find range check on input arg
3599     const insn_match_t match_cmp_b[]={
3600         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),MATCH_OP_IMM_ANY}},
3601         {MATCH_INS(B,MATCH_OPCOUNT_IGNORE)}, // blo or blt may be used, so don't include cond
3602         {ARM_INS_ENDING}
3603     };
3604     if(!insn_match_find_next_seq(fw,is,4,match_cmp_b) || is->insn->detail->arm.cc == ARM_CC_AL) {
3605         printf("sig_match__nrflag: no match CMP\n");
3606         return 0;
3607     }
3608     // follow
3609     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3610     if(!disasm_iter(fw,is)) {
3611         printf("sig_match__nrflag: disasm failed\n");
3612         return 0;
3613     }
3614     // assume next is base addres
3615     uint32_t adr=LDR_PC2val(fw,is->insn);
3616     if(!adr) {
3617         printf("sig_match__nrflag: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3618         return 0;
3619     }
3620     arm_reg reg_base = is->insn->detail->arm.operands[0].reg; // reg value was loaded into
3621     if(!disasm_iter(fw,is)) {
3622         printf("sig_match__nrflag: disasm failed\n");
3623         return 0;
3624     }
3625     // firmware may use add/sub to get actual firmware base address
3626     if(isADDx_imm(is->insn) || isSUBx_imm(is->insn)) {
3627         if(is->insn->detail->arm.operands[0].reg != reg_base) {
3628             printf("sig_match__nrflag: no match ADD/SUB\n");
3629             return 0;
3630         }
3631         if(isADDx_imm(is->insn)) {
3632             adr+=is->insn->detail->arm.operands[1].imm;
3633         } else {
3634             adr-=is->insn->detail->arm.operands[1].imm;
3635         }
3636         if(!disasm_iter(fw,is)) {
3637             printf("sig_match__nrflag: disasm failed\n");
3638             return 0;
3639         }
3640     }
3641     if(is->insn->id != ARM_INS_STR || is->insn->detail->arm.operands[1].reg != reg_base) {
3642         printf("sig_match__nrflag: no match STR\n");
3643         return 0;
3644     }
3645     uint32_t disp = is->insn->detail->arm.operands[1].mem.disp;
3646     save_misc_val(rule->name,adr,disp,fadr);
3647     return 1;
3648 }
3649 // get the address used by a function that does something like
3650 // ldr rx =base
3651 // ldr r0 [rx, offset]
3652 // bx lr
3653 int sig_match_var_struct_get(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3654 {
3655     if(!init_disasm_sig_ref(fw,is,rule)) {
3656         return 0;
3657     }
3658     uint32_t fadr=is->adr;
3659     var_ldr_desc_t desc;
3660     if(!find_and_get_var_ldr(fw, is, 1, ARM_REG_R0, &desc)) {
3661         printf("sig_match_var_struct_get: no match ldr\n");
3662         return 0;
3663     }
3664     if(!disasm_iter(fw,is)) {
3665         printf("sig_match_var_struct_get: disasm failed\n");
3666         return 0;
3667     }
3668     // TODO could check for other RET type instructions
3669     if(!insn_match(is->insn,match_bxlr)) {
3670         printf("sig_match_var_struct_get: no match BX LR\n");
3671         return 0;
3672     }
3673     save_misc_val(rule->name,desc.adr_adj,desc.off,fadr);
3674     return 1;
3675 }
3676 
3677 int sig_match_rom_ptr_get(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3678 {
3679     if(!init_disasm_sig_ref(fw,is,rule)) {
3680         return 0;
3681     }
3682     uint32_t fadr=is->adr;
3683     if(!disasm_iter(fw,is)) {
3684         printf("sig_match_rom_ptr_get: disasm failed\n");
3685         return 0;
3686     }
3687     uint32_t adr=LDR_PC2val(fw,is->insn);
3688     if(!adr) {
3689         printf("sig_match_rom_ptr_get: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3690         return  0;
3691     }
3692     if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
3693         printf("sig_match_rom_ptr_get: not R0\n");
3694         return 0;
3695     }
3696     if(!disasm_iter(fw,is)) {
3697         printf("sig_match_rom_ptr_get: disasm failed\n");
3698         return 0;
3699     }
3700     // TODO could check for other RET type instructions
3701     if(!insn_match(is->insn,match_bxlr)) {
3702         printf("sig_match_rom_ptr_get: no match BX LR\n");
3703         return 0;
3704     }
3705     save_misc_val(rule->name,adr,0,fadr);
3706     return 1;
3707 }
3708 
3709 // find Nth function call within max_insns ins of string ref, 
3710 // returns address w/thumb bit set according to current state of call instruction
3711 // modifies is and potentially fw->is
3712 uint32_t find_call_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3713 {
3714     uint32_t str_adr = find_str_bytes(fw,rule->ref_name);
3715     if(!str_adr) {
3716         printf("find_call_near_str: %s failed to find ref %s\n",rule->name,rule->ref_name);
3717         return 0;
3718     }
3719     uint32_t search_adr = str_adr;
3720     // looking for ref to ptr to string, not ref to string
3721     // TODO only looks for first ptr
3722     if(rule->param & SIG_NEAR_INDIRECT) {
3723         // printf("find_call_near_str: %s str 0x%08x\n",rule->name,str_adr);
3724         search_adr=find_u32_adr(fw,str_adr,fw->base);
3725         if(!search_adr) {
3726             printf("find_call_near_str: %s failed to find indirect ref %s\n",rule->name,rule->ref_name);
3727             return 0;
3728         }
3729         // printf("find_call_near_str: %s indirect 0x%08x\n",rule->name,search_adr);
3730     }
3731     const insn_match_t *insn_match;
3732     if(rule->param & SIG_NEAR_JMP_SUB) {
3733         insn_match = match_b_bl_blximm;
3734     } else {
3735         insn_match = match_bl_blximm;
3736     }
3737 
3738     int max_insns=rule->param&SIG_NEAR_OFFSET_MASK;
3739     int n=(rule->param&SIG_NEAR_COUNT_MASK)>>SIG_NEAR_COUNT_SHIFT;
3740     //printf("find_call_near_str: %s max_insns %d n %d %s\n",rule->name,max_insns,n,(rule->param & SIG_NEAR_REV)?"rev":"fwd");
3741     // TODO should handle multiple instances of string
3742     disasm_iter_init(fw,is,(ADR_ALIGN4(search_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3743     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,search_adr+SEARCH_NEAR_REF_RANGE)) {
3744         // bactrack looking for preceding call
3745         if(rule->param & SIG_NEAR_REV) {
3746             int i;
3747             int n_calls=0;
3748             for(i=1; i<=max_insns; i++) {
3749                 fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
3750                 if(insn_match_any(fw->is->insn,insn_match)) {
3751                     n_calls++;
3752                 }
3753                 if(n_calls == n) {
3754                     return (uint32_t)fw->is->insn->address | fw->is->thumb;
3755                 }
3756             }
3757         } else {
3758             if(insn_match_find_nth(fw,is,max_insns,n,insn_match)) {
3759                 return (uint32_t)is->insn->address | is->thumb;
3760             }
3761         }
3762     }
3763     printf("find_call_near_str: no match %s\n",rule->name);
3764     return 0;
3765 }
3766                                 
3767 // find Nth function call within max_insns ins of string ref
3768 int sig_match_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3769 {
3770     if (!get_saved_sig_val(rule->name))
3771     {
3772         uint32_t call_adr = find_call_near_str(fw,is,rule);
3773         if(call_adr) {
3774             disasm_iter_init(fw,is,call_adr); // reset to a bit before where the string was found
3775             disasm_iter(fw,is);
3776             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3777         }
3778     }
3779     return 0;
3780 }
3781 
3782 
3783 int sig_match_prop_string(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3784 {
3785     uint32_t call_adr = find_call_near_str(fw, is, rule);
3786 
3787     if (call_adr == 0)
3788         return 0;
3789 
3790     // initialize to found address
3791     disasm_iter_init(fw,is,call_adr);
3792     disasm_iter(fw,is);
3793 
3794     uint32_t myreg;
3795 
3796     if (is_sig_call(fw,is,"GetPropertyCase")) {
3797         // looking for r0
3798         myreg = 0;
3799     }
3800     else {
3801         // semaphore version of GetPropertyCase, looking for r1
3802         myreg = 1;
3803     }
3804     
3805     // re-init 'is' to current address minus at least 8 insts
3806     const int hl = 8;
3807     disasm_iter_init(fw,is,call_adr - hl*4);
3808     // history needs to be made
3809     while (is->adr < call_adr) {
3810         if (!disasm_iter(fw,is))
3811             disasm_iter_init(fw,is,(is->adr | is->thumb)+2);
3812     }
3813     uint32_t regs[4];
3814     // get r0 or r1, backtracking up to 8 instructions
3815     if ((get_call_const_args(fw,is,hl,regs)&(1<<myreg))==(1<<myreg)) {
3816         add_prop_hit(rule->name,(int)regs[myreg]);
3817         return 1;
3818     }
3819     return 0;
3820 }
3821 
3822 // check if func is a nullsub or mov r0, x ; ret
3823 // to prevent sig_named* matches from going off the end of dummy funcs
3824 int is_immediate_ret_sub(firmware *fw,iter_state_t *is_init)
3825 {
3826     fw_disasm_iter_single(fw,is_init->adr | is_init->thumb);
3827     const insn_match_t match_mov_r0_imm[]={
3828         {MATCH_INS(MOV,   2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
3829 #if CS_API_MAJOR < 4
3830         {MATCH_INS(MOVS,  2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
3831 #endif
3832         {ARM_INS_ENDING}
3833     };
3834     // if it's a MOV, check if next is ret
3835     if(insn_match_any(fw->is->insn,match_mov_r0_imm)) {
3836         fw_disasm_iter(fw);
3837     }
3838     if(isRETx(fw->is->insn)) {
3839         return 1;
3840     }
3841     return 0;
3842 }
3843 
3844 // match last function called by already matched sig, 
3845 // either the last bl/blximmg before pop {... pc}
3846 // or b after pop {... lr}
3847 // param defines min and max number of insns
3848 // doesn't work on functions that don't push/pop since can't tell if unconditional branch is last
3849 // TODO should probably be split into a general "find last call of current func"
3850 #define SIG_NAMED_LAST_MAX_MASK     0x00000FFF
3851 #define SIG_NAMED_LAST_MIN_MASK     0x00FFF000
3852 #define SIG_NAMED_LAST_MIN_SHIFT    12
3853 #define SIG_NAMED_LAST_RANGE(min,max)   ((SIG_NAMED_LAST_MIN_MASK&((min)<<SIG_NAMED_LAST_MIN_SHIFT)) \
3854                                          | (SIG_NAMED_LAST_MAX_MASK&(max)))
3855 
3856 int sig_match_named_last(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3857 {
3858     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
3859     int min = (rule->param&SIG_NAMED_LAST_MIN_MASK)>>SIG_NAMED_LAST_MIN_SHIFT;
3860     int max = (rule->param&SIG_NAMED_LAST_MAX_MASK);
3861     if(!ref_adr) {
3862         printf("sig_match_named_last: %s missing %s\n",rule->name,rule->ref_name);
3863         return 0;
3864     }
3865     disasm_iter_init(fw,is,ref_adr);
3866     if(is_immediate_ret_sub(fw,is)) {
3867         printf("sig_match_named_last: immediate return %s\n",rule->name);
3868         return 0;
3869     }
3870     int push_found=0;
3871     uint32_t last_adr=0;
3872     int count;
3873     for(count=0; count < max; count++) {
3874         if(!disasm_iter(fw,is)) {
3875             printf("sig_match_named_last: disasm failed %s 0x%"PRIx64"\n",rule->name,is->adr);
3876             return 0;
3877         }
3878         if(isPUSH_LR(is->insn)) {
3879             // already found a PUSH LR, probably in new function
3880             if(push_found) {
3881                 return 0;
3882             }
3883             push_found=1;
3884             continue;
3885         }
3886         // ignore everything before push (could be some mov/ldr, shoudln't be any calls)
3887         if(!push_found) {
3888             continue;
3889         }
3890         // found a potential call, store
3891         if(insn_match_any(is->insn,match_bl_blximm) && count >= min) {
3892             last_adr=get_branch_call_insn_target(fw,is);
3893             continue;
3894         }
3895         // found pop PC, can only be stored call if present
3896         if(isPOP_PC(is->insn)) {
3897             if(last_adr) {
3898                 return save_sig_with_j(fw,rule->name,last_adr);
3899             }
3900             // no call found, or not found within min
3901             return 0;
3902         }
3903         // found pop LR, check if next is unconditional B
3904         if(isPOP_LR(is->insn)) {
3905             // hit func end with less than min, no match
3906             if(count < min) {
3907                 return 0;
3908             }
3909             if(!disasm_iter(fw,is)) {
3910                 printf("sig_match_named_last: disasm failed %s 0x%"PRIx64"\n",rule->name,is->adr);
3911                 return 0;
3912             }
3913             if(is->insn->id == ARM_INS_B && is->insn->detail->arm.cc == ARM_CC_AL) {
3914                 return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3915             }
3916             // doen't go more than one insn after pop (could be more, but uncommon)
3917             return 0;
3918         }
3919         // found another kind of ret, give up
3920         if(isRETx(is->insn)) {
3921             return 0;
3922         }
3923     }
3924     return 0;
3925 }
3926 
3927 // default - use the named firmware function
3928 #define SIG_NAMED_ASIS          0x00000000
3929 // use the target of the first B, BX, BL, BLX etc
3930 #define SIG_NAMED_JMP_SUB       0x00000001
3931 // use the target of the first BL, BLX
3932 #define SIG_NAMED_SUB           0x00000002
3933 // match address of Nth instruction in named sub
3934 #define SIG_NAMED_INSN          0x00000003
3935 #define SIG_NAMED_TYPE_MASK     0x0000000F
3936 
3937 #define SIG_NAMED_CLEARTHUMB    0x00000010
3938 #define SIG_NAMED_FLAG_MASK     0x000000F0
3939 
3940 #define SIG_NAMED_NTH_MASK      0x00000F00
3941 #define SIG_NAMED_NTH_SHIFT     8
3942 
3943 //#define SIG_NAMED_NTH(n,type)   ((SIG_NAMED_NTH_MASK&((n)<<SIG_NAMED_NTH_SHIFT)) | ((SIG_NAMED_##type)&SIG_NAME_TYPE_MASK))
3944 #define SIG_NAMED_NTH(n,type)   ((SIG_NAMED_NTH_MASK&((n)<<SIG_NAMED_NTH_SHIFT)) | (SIG_NAMED_##type))
3945 
3946 void sig_match_named_save_sig(const char *name, uint32_t adr, uint32_t flags)
3947 {
3948     if(flags & SIG_NAMED_CLEARTHUMB) {
3949         adr = ADR_CLEAR_THUMB(adr);
3950     }
3951     save_sig(name,adr);
3952 }
3953 // match already identified function found by name
3954 // if offset is 1, match the first called function with 20 insn instead (e.g. to avoid eventproc arg handling)
3955 // initial direct jumps (j_foo) assumed to have been handled
3956 int sig_match_named(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3957 {
3958     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
3959     if(!ref_adr) {
3960         printf("sig_match_named: missing %s\n",rule->ref_name);
3961         return 0;
3962     }
3963     uint32_t sig_type = rule->param & SIG_NAMED_TYPE_MASK;
3964     uint32_t sig_flags = rule->param & SIG_NAMED_FLAG_MASK;
3965     uint32_t sig_nth = (rule->param & SIG_NAMED_NTH_MASK)>>SIG_NAMED_NTH_SHIFT;
3966     if(!sig_nth) {
3967         sig_nth=1;
3968     }
3969     // no offset, just save match as is
3970     // TODO might want to validate anyway
3971     if(sig_type == SIG_NAMED_ASIS) {
3972         sig_match_named_save_sig(rule->name,ref_adr,sig_flags); 
3973         return 1;
3974     }
3975     const insn_match_t *insn_match;
3976     if(sig_type == SIG_NAMED_JMP_SUB) {
3977         insn_match = match_b_bl_blximm;
3978     } else if(sig_type == SIG_NAMED_SUB) {
3979         insn_match = match_bl_blximm;
3980     } else if(sig_type == SIG_NAMED_INSN) {
3981         insn_match = NULL;
3982     } else {
3983         printf("sig_match_named: %s invalid type %d\n",rule->ref_name,sig_type);
3984         return 0;
3985     }
3986 
3987     disasm_iter_init(fw,is,ref_adr);
3988     // TODO for eventprocs, may just want to use the original
3989     if(is_immediate_ret_sub(fw,is)) {
3990         printf("sig_match_named: immediate return %s\n",rule->name);
3991         return 0;
3992     }
3993     if(sig_type == SIG_NAMED_INSN) {
3994         int i;
3995         // iter starts on the address given to init
3996         for(i=0;i<=sig_nth;i++) {
3997             if(!disasm_iter(fw,is)) {
3998                 printf("sig_match_named: disasm failed %s 0x%08x\n",rule->name,(uint32_t)is->insn->address);
3999                 return 0;
4000             }
4001         }
4002         sig_match_named_save_sig(rule->name,(uint32_t)is->insn->address | is->thumb,sig_flags); 
4003         return 1;
4004     }
4005 
4006     // TODO max search is hardcoded
4007     if(insn_match_find_nth(fw,is,15 + 5*sig_nth,sig_nth,insn_match)) {
4008         uint32_t adr = B_BL_BLXimm_target(fw,is->insn);
4009         if(adr) {
4010             // BLX, set thumb bit 
4011             if(is->insn->id == ARM_INS_BLX) {
4012                 // curently not thumb, set in target
4013                 if(!is->thumb) {
4014                     adr=ADR_SET_THUMB(adr);
4015                 }
4016             } else {
4017                 // preserve current state
4018                 adr |= is->thumb;
4019             }
4020             disasm_iter_set(fw,is,adr);
4021             if(disasm_iter(fw,is)) {
4022                 // TODO only checks one level
4023                 uint32_t j_adr=get_direct_jump_target(fw,is);
4024                 if(j_adr) {
4025                     char *buf=malloc(strlen(rule->name)+3);
4026                     // add j_ for cross referencing
4027                     sprintf(buf,"j_%s",rule->name);
4028                     add_func_name(buf,adr,NULL); // add the previous address as j_...
4029                     adr=j_adr;
4030                 }
4031             } else {
4032                 printf("sig_match_named: disasm failed in j_ check at %s 0x%08x\n",rule->name,adr);
4033             }
4034             sig_match_named_save_sig(rule->name,adr,sig_flags); 
4035             return 1;
4036         } else {
4037             printf("sig_match_named: %s invalid branch target 0x%08x\n",rule->ref_name,adr);
4038         }
4039     } else {
4040         printf("sig_match_named: %s branch not found 0x%08x\n",rule->ref_name,ref_adr);
4041     }
4042     return 0;
4043 }
4044 
4045 #define SIG_DRY_MIN(min_rel) (min_rel),0
4046 #define SIG_DRY_MAX(max_rel) 0,(max_rel)
4047 #define SIG_DRY_RANGE(min_rel,max_rel) (min_rel),(max_rel)
4048 // bootstrap sigs:
4049 // Used to find the minimum needed to for find_generic_funcs to get generic task and eventproc matches 
4050 // order is important
4051 sig_rule_t sig_rules_initial[]={
4052 // function         CHDK name                   ref name/string         func param          dry rel
4053 // NOTE _FW is in the CHDK column, because that's how it is in sig_names
4054 {sig_match_str_r0_call, "ExportToEventProcedure_FW","ExportToEventProcedure"},
4055 {sig_match_reg_evp,     "RegisterEventProcedure",},
4056 {sig_match_reg_evp_table, "RegisterEventProcTable","DispDev_EnableEventProc"},
4057 {sig_match_reg_evp_alt2, "RegisterEventProcedure_alt2","EngApp.Delete"},
4058 {sig_match_unreg_evp_table,"UnRegisterEventProcTable","MechaUnRegisterEventProcedure"},
4059 {sig_match_evp_table_veneer,"RegisterEventProcTable_alt","RegisterEventProcTable"},
4060 {sig_match_evp_table_veneer,"UnRegisterEventProcTable_alt","UnRegisterEventProcTable"},
4061 {sig_match_str_r0_call,"CreateTaskStrictly",    "LowConsole",},
4062 {sig_match_str_r0_call,"CreateTaskStrictly_alt","HdmiCecTask",          0,                  SIG_DRY_MIN(59)},
4063 {sig_match_str_r0_call,"CreateTask",            "EvShel",},
4064 {sig_match_near_str,   "dry_memcpy",            "EP Slot%d",            SIG_NEAR_BEFORE(4,1)},
4065 {sig_match_add_ptp_handler,"add_ptp_handler",   "PTPtoFAPI_EventProcTask_Try",},
4066 {NULL},
4067 };
4068 
4069 // main sigs:
4070 // Run after find_generic_funcs. Order is important
4071 sig_rule_t sig_rules_main[]={
4072 // function         CHDK name                   ref name/string         func param          dry rel
4073 {sig_match_named,   "ExitTask",                 "ExitTask_FW",},
4074 {sig_match_named,   "EngDrvRead",               "EngDrvRead_FW",        SIG_NAMED_JMP_SUB},
4075 {sig_match_named,   "CalcLog10",                "CalcLog10_FW",         SIG_NAMED_JMP_SUB},
4076 {sig_match_named,   "CalcSqrt",                 "CalcSqrt_FW",          SIG_NAMED_JMP_SUB},
4077 {sig_match_named,   "Close",                    "Close_FW",},
4078 {sig_match_named,   "close",                    "Close",                SIG_NAMED_SUB,      SIG_DRY_MAX(57)},
4079 {sig_match_named,   "DoAELock",                 "SS.DoAELock_FW",       SIG_NAMED_JMP_SUB},
4080 {sig_match_named,   "DoAFLock",                 "SS.DoAFLock_FW",       SIG_NAMED_JMP_SUB},
4081 {sig_match_named,   "Fclose_Fut",               "Fclose_Fut_FW",},
4082 {sig_match_named,   "Fopen_Fut",                "Fopen_Fut_FW",},
4083 {sig_match_named,   "Fread_Fut",                "Fread_Fut_FW",},
4084 {sig_match_named,   "Fseek_Fut",                "Fseek_Fut_FW",},
4085 {sig_match_named,   "Fwrite_Fut",               "Fwrite_Fut_FW",},
4086 {sig_match_named,   "GetAdChValue",             "GetAdChValue_FW",},
4087 {sig_match_named,   "GetCurrentAvValue",        "GetCurrentAvValue_FW",},
4088 {sig_match_named,   "GetCurrentShutterSpeed",   "GetCurrentShutterSpeed_FW",},
4089 {sig_match_named,   "GetBatteryTemperature",    "GetBatteryTemperature_FW",},
4090 {sig_match_named,   "GetCCDTemperature",        "GetCCDTemperature_FW",},
4091 {sig_match_named,   "GetFocusLensSubjectDistance","GetFocusLensSubjectDistance_FW",SIG_NAMED_JMP_SUB},
4092 {sig_match_named,   "GetOpticalTemperature",    "GetOpticalTemperature_FW",},
4093 {sig_match_named,   "GetPropertyCase",          "GetPropertyCase_FW",   SIG_NAMED_SUB},
4094 {sig_match_named,   "GetSystemTime",            "GetSystemTime_FW",},
4095 {sig_match_named,   "GetUsableMaxAv",           "GetUsableMaxAv_FW",},
4096 {sig_match_named,   "GetUsableMinAv",           "GetUsableMinAv_FW",},
4097 // a different match would be needed for older, ND only cams maybe based on "AE Result Tv Setting "
4098 {sig_match_named,   "GetUsableAvRange",         "GetUsableMinAv", SIG_NAMED_SUB},
4099 {sig_match_named,   "GetVRAMHPixelsSize",       "GetVRAMHPixelsSize_FW",},
4100 {sig_match_named,   "GetVRAMVPixelsSize",       "GetVRAMVPixelsSize_FW",},
4101 {sig_match_named,   "GetZoomLensCurrentPoint",  "GetZoomLensCurrentPoint_FW",},
4102 {sig_match_named,   "GetZoomLensCurrentPosition","GetZoomLensCurrentPosition_FW",},
4103 {sig_match_named,   "GiveSemaphore",            "GiveSemaphore_FW",},
4104 {sig_match_named,   "IsStrobeChargeCompleted",  "EF.IsChargeFull_FW",},
4105 {sig_match_named,   "Read",                     "Read_FW",},
4106 {sig_match_named,   "LEDDrive",                 "LEDDrive_FW",},
4107 {sig_match_named,   "LockMainPower",            "LockMainPower_FW",},
4108 {sig_match_named,   "MoveFocusLensToDistance",  "MoveFocusLensToDistance_FW",},
4109 {sig_match_named,   "MoveIrisWithAv",           "MoveIrisWithAv_FW",},
4110 {sig_match_named,   "MoveZoomLensWithPoint",    "MoveZoomLensWithPoint_FW",},
4111 {sig_match_named,   "Open",                     "Open_FW",},
4112 {sig_match_named,   "PostLogicalEventForNotPowerType",  "PostLogicalEventForNotPowerType_FW",},
4113 {sig_match_named,   "PostLogicalEventToUI",     "PostLogicalEventToUI_FW",},
4114 {sig_match_named,   "PT_MFOn",                  "SS.MFOn_FW",           SIG_NAMED_JMP_SUB},
4115 {sig_match_named,   "PT_MFOff",                 "SS.MFOff_FW",          SIG_NAMED_JMP_SUB},
4116 {sig_match_named,   "PT_MoveDigitalZoomToWide", "SS.MoveDigitalZoomToWide_FW", SIG_NAMED_JMP_SUB},
4117 {sig_match_named,   "PT_MoveOpticalZoomAt",     "SS.MoveOpticalZoomAt_FW",},
4118 {sig_match_named,   "PutInNdFilter",            "PutInNdFilter_FW",},
4119 {sig_match_named,   "PutOutNdFilter",           "PutOutNdFilter_FW",},
4120 {sig_match_named,   "SetAE_ShutterSpeed",       "SetAE_ShutterSpeed_FW",},
4121 {sig_match_named,   "SetAutoShutdownTime",      "SetAutoShutdownTime_FW",},
4122 {sig_match_named,   "SetCurrentCaptureModeType","SetCurrentCaptureModeType_FW",},
4123 {sig_match_named,   "SetLogicalEventActive",    "UiEvnt_SetLogicalEventActive_FW",},
4124 {sig_match_named,   "SetScriptMode",            "SetScriptMode_FW",},
4125 {sig_match_named,   "SleepTask",                "SleepTask_FW",},
4126 {sig_match_named,   "SetPropertyCase",          "SetPropertyCase_FW",   SIG_NAMED_SUB},
4127 {sig_match_named,   "TakeSemaphore",            "TakeSemaphore_FW",},
4128 {sig_match_named,   "TurnOnDisplay",            "DispCon_TurnOnDisplay_FW",SIG_NAMED_SUB},
4129 {sig_match_named,   "TurnOffDisplay",           "DispCon_TurnOffDisplay_FW",SIG_NAMED_SUB},
4130 {sig_match_named,   "TurnOnBackLight",          "DispCon_TurnOnBackLight_FW",SIG_NAMED_SUB},
4131 {sig_match_named,   "TurnOffBackLight",         "DispCon_TurnOffBackLight_FW",SIG_NAMED_SUB},
4132 {sig_match_named,   "UIFS_WriteFirmInfoToFile", "UIFS_WriteFirmInfoToFile_FW",},
4133 {sig_match_named,   "UnlockAE",                 "SS.UnlockAE_FW",       SIG_NAMED_JMP_SUB},
4134 {sig_match_named,   "UnlockAF",                 "SS.UnlockAF_FW",       SIG_NAMED_JMP_SUB},
4135 {sig_match_named,   "UnlockMainPower",          "UnlockMainPower_FW",},
4136 {sig_match_named,   "UnRegisterEventProcedure", "UnRegisterEventProcTable", SIG_NAMED_SUB},
4137 //{sig_match_named,   "UnsetZoomForMovie",        "UnsetZoomForMovie_FW",},
4138 {sig_match_named,   "VbattGet",                 "VbattGet_FW",},
4139 {sig_match_named,   "Write",                    "Write_FW",},
4140 {sig_match_named,   "bzero",                    "exec_FW",              SIG_NAMED_SUB},
4141 {sig_match_named,   "exmem_free",               "ExMem.FreeCacheable_FW",SIG_NAMED_JMP_SUB},
4142 {sig_match_named,   "exmem_alloc",              "ExMem.AllocCacheable_FW",SIG_NAMED_JMP_SUB},
4143 {sig_match_named,   "free",                     "FreeMemory_FW",        SIG_NAMED_JMP_SUB},
4144 {sig_match_named,   "lseek",                    "Lseek_FW",},
4145 {sig_match_named,   "_log10",                   "CalcLog10",            SIG_NAMED_NTH(2,SUB)},
4146 {sig_match_named,   "malloc",                   "AllocateMemory_FW",    SIG_NAMED_JMP_SUB},
4147 {sig_match_named,   "memcmp",                   "memcmp_FW",},
4148 {sig_match_named,   "memcpy",                   "memcpy_FW",},
4149 {sig_match_named,   "memset",                   "memset_FW",},
4150 {sig_match_named,   "strcmp",                   "strcmp_FW",},
4151 {sig_match_named,   "strcpy",                   "strcpy_FW",},
4152 {sig_match_named,   "strlen",                   "strlen_FW",},
4153 {sig_match_named,   "task_CaptSeq",             "task_CaptSeqTask",},
4154 {sig_match_named,   "task_ExpDrv",              "task_ExpDrvTask",},
4155 {sig_match_named,   "task_FileWrite",           "task_FileWriteTask",},
4156 //{sig_match_named,   "task_MovieRecord",         "task_MovieRecord",},
4157 //{sig_match_named,   "task_PhySw",               "task_PhySw",},
4158 {sig_match_named,   "vsprintf",                 "sprintf_FW",           SIG_NAMED_SUB},
4159 {sig_match_named,   "PTM_GetCurrentItem",       "PTM_GetCurrentItem_FW",},
4160 {sig_match_named,   "DisableISDriveError",      "DisableISDriveError_FW",},
4161 // TODO assumes CreateTask is in RAM, doesn't currently check
4162 {sig_match_named,   "hook_CreateTask",          "CreateTask",           SIG_NAMED_CLEARTHUMB},
4163 {sig_match_named,   "malloc_strictly",          "task_EvShel",          SIG_NAMED_NTH(2,SUB)},
4164 {sig_match_named,   "DebugAssert2",             "malloc_strictly",      SIG_NAMED_NTH(3,SUB)},
4165 {sig_match_named,   "AcquireRecursiveLockStrictly","StartWDT_FW",       SIG_NAMED_NTH(1,SUB)},
4166 {sig_match_named,   "CheckAllEventFlag",        "ChargeStrobeForFA_FW", SIG_NAMED_SUB},
4167 {sig_match_named,   "ClearEventFlag",           "GetAEIntegralValueWithFix_FW",SIG_NAMED_SUB},
4168 {sig_match_named,   "CheckAnyEventFlag",        "task_SynchTask",       SIG_NAMED_NTH(2,SUB)},
4169 {sig_match_named,   "taskcreate_LowConsole",    "task_EvShel",          SIG_NAMED_SUB},
4170 {sig_match_named,   "CreateMessageQueueStrictly","taskcreate_LowConsole",SIG_NAMED_SUB},
4171 {sig_match_named,   "CreateBinarySemaphoreStrictly","taskcreate_LowConsole",SIG_NAMED_NTH(2,SUB)},
4172 {sig_match_named,   "PostMessageQueue",         "GetCh_FW",             SIG_NAMED_NTH(2,SUB)},
4173 {sig_match_named,   "CreateEventFlagStrictly",  "InitializeDigicon_FW", SIG_NAMED_SUB},
4174 {sig_match_named,   "WaitForAnyEventFlag",      "task_DPOFTask",        SIG_NAMED_SUB},
4175 {sig_match_named,   "GetEventFlagValue",        "task_DPOFTask",        SIG_NAMED_NTH(2,SUB)},
4176 {sig_match_named,   "CreateBinarySemaphore",    "task_UartLog",         SIG_NAMED_SUB},
4177 {sig_match_named,   "PostMessageQueueStrictly", "EF.IsChargeFull_FW",   SIG_NAMED_SUB},
4178 {sig_match_named,   "SetEventFlag",             "StopStrobeChargeForFA_FW",SIG_NAMED_SUB},
4179 {sig_match_named,   "TryReceiveMessageQueue",   "task_DvlpSeqTask",     SIG_NAMED_NTH(3,SUB)},
4180 // Semaphore funcs found by eventproc match, but want veneers. Will warn if mismatched
4181 {sig_match_named,   "TakeSemaphore",            "task_Bye",             SIG_NAMED_SUB},
4182 {sig_match_named_last,"GiveSemaphore",          "TurnOnVideoOutMode_FW",SIG_NAMED_LAST_RANGE(10,24)},
4183 // TODO finding through veneers would be better for disassembly
4184 {sig_match_named,   "givesemaphore_low",        "GiveSemaphore",        SIG_NAMED_SUB,      SIG_DRY_MAX(52)}, // first call on dry <=52
4185 {sig_match_named,   "givesemaphore_low",        "GiveSemaphore",        SIG_NAMED_NTH(2,SUB),SIG_DRY_MIN(53)}, // 2nd call on dry >52
4186 
4187 // can't use last because func has early return POP
4188 {sig_match_named,   "ReleaseRecursiveLock",     "StartWDT_FW",          SIG_NAMED_NTH(2,SUB)},
4189 // alternate match because "exec" lands near a literal pool on some cams
4190 {sig_match_near_str,"bzero",                    "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(8,2)|SIG_NEAR_INDIRECT},
4191 //{sig_match_near_str,"bzero",                    "FromDate",             SIG_NEAR_BEFORE(2,1)},
4192 {sig_match_named,   "memset32",                 "bzero",                SIG_NAMED_NTH(1,INSN)},
4193 {sig_match_misc_flag_named,"CAM_IS_ILC",        "task_EFLensComTask",},
4194 {sig_match_misc_flag_named,"CAM_HAS_ND_FILTER", "task_Nd",},
4195 {sig_match_cam_has_iris_diaphragm,"CAM_HAS_IRIS_DIAPHRAGM","task_IrisEvent",},
4196 {sig_match_near_str,"ImagerActivate",           "Fail ImagerActivate(ErrorCode:%x)\r",SIG_NEAR_BEFORE(6,1)},
4197 //{sig_match_named,   "ScreenLock",               "UIFS_DisplayFirmUpdateView_FW",SIG_NAMED_SUB},
4198 {sig_match_screenlock,"ScreenLock",             "UIFS_DisplayFirmUpdateView_FW"},
4199 {sig_match_screenunlock,"ScreenUnlock",         "UIFS_DisplayFirmUpdateView_FW"},
4200 {sig_match_log_camera_event,"LogCameraEvent",   "task_StartupImage",},
4201 {sig_match_physw_misc, "physw_misc",            "task_PhySw"},
4202 {sig_match_kbd_read_keys, "kbd_read_keys",      "kbd_p1_f"},
4203 {sig_match_get_kbd_state, "GetKbdState",        "kbd_read_keys"},
4204 {sig_match_create_jumptable, "CreateJumptable", "InitializeAdjustmentSystem_FW"},
4205 {sig_match_take_semaphore_strict, "TakeSemaphoreStrictly","Fopen_Fut"},
4206 {sig_match_get_semaphore_value,"GetSemaphoreValue","\tRaw[%i]"},
4207 {sig_match_stat,    "stat",                     "A/uartr.req"},
4208 {sig_match_open,    "open",                     "Open_FW",              0,              SIG_DRY_MAX(57)},
4209 {sig_match_open_gt_57,"open",                   "Open_FW",              0,              SIG_DRY_MIN(58)},
4210 // match close for dryos 58 and later
4211 {sig_match_close_gt_57,"close",                 "Close_FW",             0,              SIG_DRY_MIN(58)},
4212 {sig_match_umalloc, "AllocateUncacheableMemory","Fopen_Fut_FW"},
4213 {sig_match_ufree,   "FreeUncacheableMemory",    "Fclose_Fut_FW"},
4214 {sig_match_cam_uncached_bit,"CAM_UNCACHED_BIT", "FreeUncacheableMemory"},
4215 {sig_match_deletefile_fut,"DeleteFile_Fut",     "Get Err TempPath"},
4216 {sig_match_near_str,"createsemaphore_low",      "intr_sem",             SIG_NEAR_AFTER(3,1)},
4217 // probably only matched on cameras with wifi code in firmware, but catches veneers
4218 {sig_match_near_str,"takesemaphore_low",        "sem_test_callback",    SIG_NEAR_AFTER(12,2)},
4219 // not using Strictly, to pick up veneers
4220 {sig_match_near_str,"AcquireRecursiveLock",     "not executed\n",SIG_NEAR_BEFORE(20,3)},
4221 {sig_match_near_str,"CreateCountingSemaphoreStrictly","DvlpSeqTask",    SIG_NEAR_BEFORE(18,3)},
4222 {sig_match_near_str,"CreateMessageQueue",       "CreateMessageQueue:%ld",SIG_NEAR_BEFORE(7,1)},
4223 {sig_match_near_str,"CreateEventFlag",          "CreateEventFlag:%ld",  SIG_NEAR_BEFORE(7,1)},
4224 {sig_match_near_str,"CreateRecursiveLock",      "WdtInt",               SIG_NEAR_BEFORE(9,1)},
4225 {sig_match_near_str,"CreateRecursiveLockStrictly","LoadedScript",       SIG_NEAR_AFTER(6,2)},
4226 {sig_match_near_str,"DeleteMessageQueue",       "DeleteMessageQueue(%d) is FAILURE",SIG_NEAR_BEFORE(10,1)},
4227 {sig_match_near_str,"DeleteEventFlag",          "DeleteEventFlag(%d) is FAILURE",SIG_NEAR_BEFORE(10,1)},
4228 {sig_match_near_str,"ReceiveMessageQueue",      "ReceiveMessageQue:%d", SIG_NEAR_BEFORE(9,1)},
4229 {sig_match_near_str,"RegisterInterruptHandler", "WdtInt",               SIG_NEAR_AFTER(3,1)},
4230 {sig_match_near_str,"TryPostMessageQueue",      "TryPostMessageQueue(%d)\n",SIG_NEAR_BEFORE(9,1)},
4231 // different string on cams newer than sx280
4232 {sig_match_near_str,"TryPostMessageQueue",      "[CWS]TryPostMessageQueue(%d) Failed\n",SIG_NEAR_BEFORE(9,1)},
4233 {sig_match_near_str,"TryTakeSemaphore",         "FileScheduleTask",     SIG_NEAR_AFTER(10,2),SIG_DRY_MAX(58)},
4234 {sig_match_try_take_sem_dry_gt_58,"TryTakeSemaphore","task_ImageStoreTask",0,SIG_DRY_MIN(59)},
4235 // pick up takesemaphore_low from TryTakeSemaphore in case not matched by earlier rule
4236 {sig_match_named,   "takesemaphore_low",        "TryTakeSemaphore",     SIG_NAMED_SUB},
4237 {sig_match_near_str,"WaitForAllEventFlag",      "Error WaitEvent PREPARE_TESTREC_EXECUTED.", SIG_NEAR_BEFORE(5,1)},
4238 {sig_match_near_str,"WaitForAnyEventFlagStrictly","_imageSensorTask",   SIG_NEAR_AFTER(10,2)},
4239 {sig_match_wait_all_eventflag_strict,"WaitForAllEventFlagStrictly","EF.StartInternalMainFlash_FW"},
4240 {sig_match_near_str,"DeleteSemaphore",          "DeleteSemaphore passed",SIG_NEAR_BEFORE(3,1)},
4241 {sig_match_get_num_posted_messages,"GetNumberOfPostedMessages","task_CtgTotalTask"},
4242 {sig_match_near_str,"LocalTime",                "%Y-%m-%dT%H:%M:%S",    SIG_NEAR_BEFORE(5,1)},
4243 {sig_match_near_str,"LocalTime",                "%Y.%m.%d %H:%M:%S",    SIG_NEAR_BEFORE(5,1)},
4244 {sig_match_near_str,"strftime",                 "%Y/%m/%d %H:%M:%S",    SIG_NEAR_AFTER(3,1)},
4245 {sig_match_near_str,"OpenFastDir",              "OpenFastDir_ERROR\n",  SIG_NEAR_BEFORE(5,1)},
4246 {sig_match_near_str,"ReadFastDir",              "ReadFast_ERROR\n",     SIG_NEAR_BEFORE(5,1)},
4247 // this matches using sig_match_near_str, but function for dryos >=57 takes additional param so disabling for those versions
4248 {sig_match_pt_playsound,"PT_PlaySound",         "BufAccBeep",           SIG_NEAR_AFTER(7,2)|SIG_NEAR_JMP_SUB, SIG_DRY_MAX(56)},
4249 {sig_match_closedir,"closedir",                 "ReadFast_ERROR\n",     SIG_NEAR_AFTER(1,1)},
4250 {sig_match_strrchr,"strrchr",                   "ReadFast_ERROR\n",     SIG_NEAR_AFTER(9,2)},
4251 {sig_match_strrchr,"strrchr",                   "ReadFast_ERROR\n",     SIG_NEAR_BEFORE(18,4)},
4252 {sig_match_time,    "time",                     "<UseAreaSize> DataWidth : %d , DataHeight : %d\r\n",},
4253 {sig_match_near_str,"strcat",                   "String can't be displayed; no more space in buffer",SIG_NEAR_AFTER(5,2)},
4254 {sig_match_near_str,"strchr",                   "-._~",SIG_NEAR_AFTER(4,1)},
4255 {sig_match_strncpy, "strncpy",                  "UnRegisterEventProcedure",},
4256 {sig_match_strncmp, "strncmp",                  "EXFAT   ",},
4257 {sig_match_strtolx, "strtolx",                  "CheckSumAll_FW",},
4258 {sig_match_near_str,"strtol",                   "prio <task ID> <priority>\n",SIG_NEAR_AFTER(7,1)},
4259 {sig_match_exec_evp,"ExecuteEventProcedure",    "Can not Execute "},
4260 {sig_match_fgets_fut,"Fgets_Fut",               "CheckSumAll_FW",},
4261 {sig_match_log,     "_log",                     "_log10",},
4262 {sig_match_pow_dry_52,"_pow",                   "GetDefectTvAdj_FW",    0,                  SIG_DRY_MAX(52)},
4263 {sig_match_pow_dry_gt_52,"_pow",                "GetDefectTvAdj_FW",    0,                  SIG_DRY_MIN(53)},
4264 {sig_match_sqrt,    "_sqrt",                    "CalcSqrt",},
4265 {sig_match_named,   "get_fstype",               "OpenFastDir",          SIG_NAMED_NTH(2,SUB)},
4266 {sig_match_near_str,"GetMemInfo",               " -- refusing to print malloc information.\n",SIG_NEAR_AFTER(7,2)},
4267 {sig_match_get_drive_cluster_size,"GetDrive_ClusterSize","OpLog.WriteToSD_FW",},
4268 {sig_match_mktime_ext,"mktime_ext",             "%04d%02d%02dT%02d%02d%02d.%01d",},
4269 {sig_match_near_str,"PB2Rec",                   "AC:ActionP2R Fail",     SIG_NEAR_BEFORE(6,1)},
4270 {sig_match_rec2pb,  "Rec2PB",                   "_EnrySRec",},
4271 //{sig_match_named,   "GetParameterData",         "PTM_RestoreUIProperty_FW",          SIG_NAMED_NTH(3,JMP_SUB)},
4272 {sig_match_get_parameter_data,"GetParameterData","PTM_RestoreUIProperty_FW",},
4273 {sig_match_prepdir_1,"PrepareDirectory_1",      "<OpenFileWithDir> PrepareDirectory NG\r\n",SIG_NEAR_BEFORE(7,1)},
4274 {sig_match_prepdir_x,"PrepareDirectory_x",      "PrepareDirectory_1",},
4275 {sig_match_prepdir_0,"PrepareDirectory_0",      "PrepareDirectory_1",},
4276 {sig_match_mkdir,   "MakeDirectory_Fut",        "PrepareDirectory_x",},
4277 // moved to sig_rules_initial to allow auto-detecting handlers
4278 //{sig_match_add_ptp_handler,"add_ptp_handler",   "PTPtoFAPI_EventProcTask_Try",},
4279 {sig_match_qsort,   "qsort",                    "task_MetaCtg",},
4280 {sig_match_deletedirectory_fut,"DeleteDirectory_Fut","RedEyeController.c",},
4281 {sig_match_set_control_event,"set_control_event","LogicalEvent:0x%04x:adr:%p,Para:%ld",},
4282 // newer cams use %08x
4283 {sig_match_set_control_event,"set_control_event","LogicalEvent:0x%08x:adr:%p,Para:%ld",},
4284 {sig_match_displaybusyonscreen_52,"displaybusyonscreen","_PBBusyScrn",  0,                  SIG_DRY_MAX(52)},
4285 {sig_match_undisplaybusyonscreen_52,"undisplaybusyonscreen","_PBBusyScrn",0,                SIG_DRY_MAX(52)},
4286 {sig_match_near_str,"srand",                    "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(14,4)|SIG_NEAR_INDIRECT},
4287 {sig_match_near_str,"rand",                     "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(15,5)|SIG_NEAR_INDIRECT},
4288 {sig_match_set_hp_timer_after_now,"SetHPTimerAfterNow","MechaNC.c",},
4289 {sig_match_levent_table,"levent_table",         "ShowLogicalEventName_FW",},
4290 {sig_match_flash_param_table,"FlashParamsTable","GetParameterData",},
4291 {sig_match_named,   "get_playrec_mode",         "task_SsStartupTask",   SIG_NAMED_SUB},
4292 {sig_match_var_struct_get,"playrec_mode",       "get_playrec_mode",},
4293 {sig_match_jpeg_count_str,"jpeg_count_str",     "9999",},
4294 {sig_match_physw_event_table,"physw_event_table","kbd_read_keys_r2",},
4295 {sig_match_uiprop_count,"uiprop_count",         "PTM_SetCurrentItem_FW",},
4296 {sig_match_get_canon_mode_list,"get_canon_mode_list","AC:PTM_Init",},
4297 {sig_match_rom_ptr_get,"canon_mode_list",       "get_canon_mode_list",},
4298 {sig_match_zoom_busy,"zoom_busy",               "ResetZoomLens_FW",},
4299 {sig_match_focus_busy,"focus_busy",             "MoveFocusLensToTerminate_FW",},
4300 {sig_match_aram_size,"ARAM_HEAP_SIZE",          "AdditionAgentRAM_FW",},
4301 {sig_match_aram_start,"ARAM_HEAP_START",        "AdditionAgentRAM_FW",},
4302 {sig_match_aram_start2,"ARAM_HEAP_START",       "AdditionAgentRAM_FW",},
4303 {sig_match__nrflag,"_nrflag",                   "NRTBL.SetDarkSubType_FW",},
4304 {sig_match_near_str,"transfer_src_overlay_helper","Window_EmergencyRefreshPhysicalScreen",SIG_NEAR_BEFORE(6,1)},
4305 {sig_match_transfer_src_overlay,"transfer_src_overlay","transfer_src_overlay_helper",},
4306 {sig_match_named,"GraphicSystemCoreFinish_helper","transfer_src_overlay",SIG_NAMED_NTH(3,SUB),SIG_DRY_MAX(52)},
4307 {sig_match_named,"GraphicSystemCoreFinish_helper","transfer_src_overlay",SIG_NAMED_NTH(4,SUB),SIG_DRY_RANGE(53,57)},// VTMReduuce fails on M10
4308 {sig_match_near_str,"GraphicSystemCoreFinish_helper","VTMReduuce"/*sic*/,SIG_NEAR_BEFORE(6,1),SIG_DRY_MIN(58)}, 
4309 {sig_match_named,"GraphicSystemCoreFinish","GraphicSystemCoreFinish_helper",SIG_NAMED_SUB},
4310 {sig_match_named,"mzrm_createmsg","GraphicSystemCoreFinish",SIG_NAMED_SUB},
4311 {sig_match_named_last,"mzrm_sendmsg","GraphicSystemCoreFinish",SIG_NAMED_LAST_RANGE(10,16)},
4312 {sig_match_zicokick_52,"zicokick_start",        "ZicoKick Start\n",0,SIG_DRY_MAX(52)},
4313 {sig_match_zicokick_gt52,"zicokick_start",      "ZicoKick Start\n",0,SIG_DRY_MIN(53)},
4314 {sig_match_zicokick_copy,"zicokick_copy",       "zicokick_start"},
4315 {sig_match_zicokick_values,"zicokick_values",   "zicokick_start"},
4316 {sig_match_enable_hdmi_power,"EnableHDMIPower", "HecHdmiCecPhysicalCheckForScript_FW"},
4317 {sig_match_disable_hdmi_power,"DisableHDMIPower","HecHdmiCecPhysicalCheckForScript_FW"},
4318 {sig_match_get_nd_value,"get_nd_value",         "PutInNdFilter",},
4319 {sig_match_get_current_exp,"get_current_exp","ShowCurrentExp_FW",},
4320 {sig_match_get_current_nd_value,"get_current_nd_value","get_current_exp",},
4321 {sig_match_imager_active_callback,"imager_active_callback","ImagerActivate",},
4322 {sig_match_imager_active,"imager_active","imager_active_callback",},
4323 {sig_match_prop_string,"PROPCASE_AFSTEP", "\n\rError : GetAFStepResult",SIG_NEAR_BEFORE(7,1)},
4324 {sig_match_prop_string,"PROPCASE_FOCUS_STATE", "\n\rError : GetAFResult",SIG_NEAR_BEFORE(7,1)},
4325 {sig_match_prop_string,"PROPCASE_AV", "\n\rError : GetAvResult",SIG_NEAR_BEFORE(7,1)},
4326 {sig_match_prop_string,"PROPCASE_BV", "\n\rError : GetBvResult",SIG_NEAR_BEFORE(7,1)},
4327 {sig_match_prop_string,"PROPCASE_DELTA_DIGITALGAIN", "\n\rError : GetDeltaDigitalResult",SIG_NEAR_BEFORE(7,1)},
4328 {sig_match_prop_string,"PROPCASE_DELTA_SV", "\n\rError : GetDeltaGainResult",SIG_NEAR_BEFORE(7,1)},
4329 {sig_match_prop_string,"PROPCASE_DELTA_ND", "\n\rError : GetDeltaNdResult",SIG_NEAR_BEFORE(7,1)},
4330 {sig_match_prop_string,"PROPCASE_EV_CORRECTION_2", "\n\rError : GetRealExposureCompensationResult",SIG_NEAR_BEFORE(7,1)},
4331 {sig_match_prop_string,"PROPCASE_ORIENTATION_SENSOR", "\n\rError : GetRotationAngleResult",SIG_NEAR_BEFORE(7,1)},
4332 {sig_match_prop_string,"PROPCASE_SV_MARKET", "\n\rError : GetSvResult",SIG_NEAR_BEFORE(7,1)},
4333 {sig_match_prop_string,"PROPCASE_SVFIX", "\n\rError : GetSvFixResult",SIG_NEAR_BEFORE(7,1)},
4334 {sig_match_prop_string,"PROPCASE_TV", "\n\rError : GetTvResult",SIG_NEAR_BEFORE(7,1)},
4335 {sig_match_exmem_vars,"exmem_types_table", "ExMem.View_FW"},
4336 {NULL},
4337 };
4338 
4339 void run_sig_rules(firmware *fw, sig_rule_t *sig_rules)
4340 {
4341     sig_rule_t *rule=sig_rules;
4342     // for convenience, pass an iter_state to match fns so they don't have to manage
4343     iter_state_t *is=disasm_iter_new(fw,0);
4344     while(rule->match_fn) {
4345         if((rule->dryos_min && fw->dryos_ver < rule->dryos_min)
4346             || (rule->dryos_max && fw->dryos_ver > rule->dryos_max)) {
4347             rule++;
4348             continue;
4349         }
4350 //        printf("rule: %s ",rule->name);
4351         //int r=rule->match_fn(fw,is,rule);
4352         rule->match_fn(fw,is,rule);
4353 //        printf("%d\n",r);
4354         rule++;
4355     }
4356     disasm_iter_free(is);
4357 }
4358 
4359 void add_event_proc(firmware *fw, char *name, uint32_t adr)
4360 {
4361     // TODO - no ARM eventprocs seen so far, warn
4362     if(!ADR_IS_THUMB(adr)) {
4363         printf("add_event_proc: %s is ARM 0x%08x\n",name,adr);
4364     }
4365     // attempt to disassemble target
4366     if(!fw_disasm_iter_single(fw,adr)) {
4367         printf("add_event_proc: %s disassembly failed at 0x%08x\n",name,adr);
4368         return;
4369     }
4370     // handle functions that immediately jump
4371     // only one level of jump for now, doesn't check for conditionals, but first insn shouldn't be conditional
4372     //uint32_t b_adr=B_target(fw,fw->is->insn);
4373     uint32_t b_adr=get_direct_jump_target(fw,fw->is);
4374     if(b_adr) {
4375         char *buf=malloc(strlen(name)+6);
4376         sprintf(buf,"j_%s_FW",name);
4377         add_func_name(buf,adr,NULL); // this is the orignal named address
4378 //        adr=b_adr | fw->is->thumb; // thumb bit from iter state
4379         adr=b_adr; // thumb bit already handled by get_direct...
4380     }
4381     add_func_name(name,adr,"_FW");
4382 }
4383 
4384 // process a call to an 2 arg event proc registration function
4385 int process_reg_eventproc_call(firmware *fw, iter_state_t *is,uint32_t unused) {
4386     uint32_t regs[4];
4387     // get r0, r1, backtracking up to 4 instructions
4388     if((get_call_const_args(fw,is,4,regs)&3)==3) {
4389         // TODO follow ptr to verify code, pick up underlying functions
4390         if(isASCIIstring(fw,regs[0])) {
4391             char *nm=(char *)adr2ptr(fw,regs[0]);
4392             add_event_proc(fw,nm,regs[1]);
4393             //add_func_name(nm,regs[1],NULL);
4394             //printf("eventproc found %s 0x%08x at 0x%"PRIx64"\n",nm,regs[1],is->insn->address);
4395         } else {
4396             printf("eventproc name not string at 0x%"PRIx64"\n",is->insn->address);
4397         }
4398     } else {
4399         // check for special case: one of the 2 arg eventprocs is used in loop to register a table
4400 
4401         // using the existing 'is' iterator
4402         // first, address is backed up
4403         uint64_t adr = is->insn->address;
4404         uint32_t adr_thumb = is->thumb;
4405         uint32_t tbla = 0;
4406         int ar = -1;
4407         int found = 0;
4408         // go back a 10 instructions
4409         disasm_iter_init(fw,is,adr_hist_get(&is->ah,10));
4410         // search for ldr reg, =address where address is higher in ROM (supposed to be the eventproc table)
4411         while(1) {
4412             if (!disasm_iter(fw,is)) break;
4413             if (is->insn->address >= adr) break;
4414             if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].type == ARM_OP_MEM) {
4415                 uint32_t u = LDR_PC2val(fw,is->insn);
4416                 if ((u<fw->base+fw->size8) && (u>adr) && (!isASCIIstring(fw,u))) {
4417                     ar = is->insn->detail->arm.operands[0].reg;
4418                     tbla = u;
4419                     break;
4420                 }
4421             }
4422         }
4423         // search for found register appearing later in an add instruction
4424         while(ar >= 0) {
4425             if (!disasm_iter(fw,is)) break;
4426             if (is->insn->address >= adr) break;
4427             if (is->insn->id == ARM_INS_ADD && is->insn->detail->arm.operands[1].reg == ar) {
4428                 found = 1;
4429                 //printf("found loop eventproc table at 0x%"PRIx64"\n",is->insn->address);
4430                 break;
4431             }
4432         }
4433         if (found) {
4434             // following is taken from process_eventproc_table_call
4435             uint32_t *p=(uint32_t*)adr2ptr_with_data(fw,tbla);
4436             if(p) {
4437                 while(*p) {
4438                     uint32_t nm_adr=*p;
4439                     // NULL name = end of table
4440                     if (!nm_adr) break;
4441                     if(!isASCIIstring(fw,nm_adr)) {
4442                         printf("eventproc name not string tbl2 0x%08x 0x%08x\n",tbla,nm_adr);
4443                         break;
4444                     }
4445                     char *nm=(char *)adr2ptr(fw,nm_adr);
4446                     p++;
4447                     uint32_t fn=*p;
4448                     p++;
4449                     add_event_proc(fw,nm,fn);
4450                 }
4451             } else {
4452                 printf("eventproc tbl2 not table 0x%08x\n",tbla);
4453             }
4454         }
4455         else {
4456             printf("failed to get export/register eventproc args at 0x%"PRIx64"\n",adr);
4457         }
4458         // restore address in 'is' to avoid infinite loop
4459         disasm_iter_init(fw,is,adr | adr_thumb);
4460         disasm_iter(fw,is);
4461     }
4462     return 0; // always keep looking
4463 }
4464 
4465 // process a call to event proc table registration
4466 int process_eventproc_table_call(firmware *fw, iter_state_t *is,uint32_t unused) {
4467     uint32_t regs[4];
4468     int foundr0 = 0;
4469     // get r0, backtracking up to 4 instructions
4470     foundr0 = get_call_const_args(fw,is,4,regs) & 1;
4471     if (!foundr0) {
4472         // case 1: table memcpy'd onto stack
4473         uint32_t ca = is->insn->address | is->thumb;
4474         uint32_t sa = adr_hist_get(&is->ah,2);
4475         uint32_t ta = adr_hist_get(&is->ah,8);
4476         disasm_iter_set(fw,is,ta);
4477         int n = 0;
4478         while(++n<=(8-2))
4479         {
4480             disasm_iter(fw,is);
4481         }
4482         fw_disasm_iter_single(fw,sa);
4483         uint32_t adr1 = get_saved_sig_val("j_dry_memcpy");
4484         uint32_t adr2 = get_branch_call_insn_target(fw,fw->is);
4485         if (fw->is->insn->id == ARM_INS_BLX && adr1 == adr2) {
4486             foundr0 = get_call_const_args(fw,is,8-2,regs) & 2;
4487             if (foundr0) {
4488                 regs[0] = regs[1];
4489                 // printf("eventproc table case1 0x%x found table 0x%x\n",ca,regs[1]);
4490             }
4491         }
4492         // restore iter address
4493         disasm_iter_init(fw,is,ca);
4494         disasm_iter(fw,is);
4495     }
4496     if(foundr0) {
4497         // include tables in RAM data
4498         uint32_t *p=(uint32_t*)adr2ptr_with_data(fw,regs[0]);
4499         //printf("found eventproc table 0x%08x\n",regs[0]);
4500         // if it was a valid address
4501         if(p) {
4502             while(*p) {
4503                 uint32_t nm_adr=*p;
4504                 if(!isASCIIstring(fw,nm_adr)) {
4505                     printf("eventproc name not string tbl 0x%08x 0x%08x\n",regs[0],nm_adr);
4506                     break;
4507                 }
4508                 char *nm=(char *)adr2ptr(fw,nm_adr);
4509                 p++;
4510                 uint32_t fn=*p;
4511                 p++;
4512                 //printf("found %s 0x%08x\n",nm,fn);
4513                 add_event_proc(fw,nm,fn);
4514                 //add_func_name(nm,fn,NULL);
4515             }
4516         } else {
4517             printf("failed to get *EventProcTable arg 0x%08x at 0x%"PRIx64"\n",regs[0],is->insn->address);
4518         }
4519     } else {
4520         printf("failed to get *EventProcTable r0 at 0x%"PRIx64"\n",is->insn->address);
4521     }
4522     return 0;
4523 }
4524 
4525 int process_createtask_call(firmware *fw, iter_state_t *is,uint32_t unused) {
4526     //printf("CreateTask call at %"PRIx64"\n",is->insn->address);
4527     uint32_t regs[4];
4528     // get r0 (name) and r3 (entry), backtracking up to 10 instructions
4529     if((get_call_const_args(fw,is,10,regs)&9)==9) {
4530         if(isASCIIstring(fw,regs[0])) {
4531             // TODO
4532             char *buf=malloc(64);
4533             char *nm=(char *)adr2ptr(fw,regs[0]);
4534             sprintf(buf,"task_%s",nm);
4535             //printf("found %s 0x%08x at 0x%"PRIx64"\n",buf,regs[3],is->insn->address);
4536             add_func_name(buf,regs[3],NULL);
4537         } else {
4538             printf("task name name not string at 0x%"PRIx64"\n",is->insn->address);
4539         }
4540     } else {
4541         printf("failed to get CreateTask args at 0x%"PRIx64"\n",is->insn->address);
4542     }
4543     return 0;
4544 }
4545 
4546 int save_ptp_handler_func(uint32_t op,uint32_t handler) {
4547     if((op >= 0x9000 && op < 0x10000) || (op >= 0x1000 && op < 0x2000)) {
4548         char *buf=malloc(64);
4549         const char *nm=get_ptp_op_name(op);
4550         if(nm) {
4551             sprintf(buf,"handle_%s",nm);
4552         } else {
4553             sprintf(buf,"handle_PTP_OC_0x%04x",op);
4554         }
4555         // TODO Canon sometimes uses the same handler for multiple opcodes
4556         add_func_name(buf,handler,NULL);
4557     } else {
4558         return 0;
4559     }
4560     return 1;
4561 }
4562 int process_add_ptp_handler_call(firmware *fw, iter_state_t *is,uint32_t unused) {
4563     uint32_t regs[4];
4564     // get r0 (opcode) and r1 (handler), backtracking up to 8 instructions
4565     if((get_call_const_args(fw,is,8,regs)&3)==3) {
4566         //uint32_t op=regs[0];
4567         if(!save_ptp_handler_func(regs[0],regs[1])) {
4568             printf("add_ptp_handler op 0x%08x out of range 0x%"PRIx64"\n",regs[0],is->insn->address);
4569         }
4570         return 0;
4571     } else {
4572         // if above failed, check for opcode table
4573         arm_reg ptr_reg = ARM_REG_INVALID;
4574         int i;
4575         // backtrack until we get to ldrh r0, ...
4576         for(i=1; i<6; i++) {
4577             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
4578             cs_insn *insn=fw->is->insn;
4579             if(insn->id != ARM_INS_LDRH) {
4580                 continue;
4581             }
4582             if(insn->detail->arm.operands[0].reg != ARM_REG_R0
4583                 || insn->detail->arm.operands[1].mem.base == ARM_REG_PC
4584                 // shift isn't set correctly under capstone 3, not required for current cams
4585                 /*|| insn->detail->arm.operands[1].shift.value != 3*/) { 
4586                 continue;
4587             }
4588             ptr_reg = insn->detail->arm.operands[1].mem.base;
4589             //printf("add_ptp_handler ptr_reg %d at 0x%"PRIx64"\n",ptr_reg,insn->address);
4590             break;
4591         }
4592         // didn't find args or anything that looks like table load
4593         if(ptr_reg == ARM_REG_INVALID) {
4594             printf("failed to get add_ptp_handler args at 0x%"PRIx64"\n",is->insn->address);
4595             return 0;
4596         }
4597         uint32_t op_table=0;
4598         // backtrack looking for LDR into ptr_reg
4599         // starting from previous i
4600         for(; i<20; i++) {
4601             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
4602             cs_insn *insn=fw->is->insn;
4603             if(!isLDR_PC(insn)) {
4604                 continue;
4605             }
4606             if(insn->detail->arm.operands[0].reg != ptr_reg) {
4607                 continue;
4608             }
4609             // printf("add_ptp_handler LDR PC 0x%08x at 0x%"PRIx64"\n",LDR_PC2val(fw,insn),insn->address);
4610             uint32_t adr=LDR_PC2val(fw,insn);
4611             // check loaded address points to expected value (OC GetStorageIDs)
4612             if(fw_u32(fw,adr) == 0x1004) {
4613                 op_table=adr;
4614             }
4615             break;
4616         }
4617         if(!op_table) {
4618             printf("failed to get ptp handler table adr at 0x%"PRIx64"\n",is->insn->address);
4619             return 0;
4620         }
4621         // TODO canon firmware has count in loop that calls add_ptp_handler,
4622         // but for simplicity just checking for valid opcode with hardcoded max
4623         for(i=0; i<64; i++) {
4624             uint32_t op=fw_u32(fw,op_table+i*8);
4625             uint32_t handler=fw_u32(fw,op_table+i*8+4);
4626             // fails on op out of range
4627             if(!save_ptp_handler_func(op,handler)) {
4628                 break;
4629