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