Anjoy SSC338Q libtools.so
Extracted from firmware for Mstar MC800S-V3 camera module.
Rated "the most uses of grep per grep".
Reading /proc/%d/status, the long way around.
fn get_process_ppid(arg1: i32) -> i32
{
let mut var_210: i32 = 0;
let mut var_20c: ();
memset(&var_20c, 0, 0xfc);
let mut var_110: [i8; 0x100];
sprintf(&var_110, "cat /proc/%d/status |grep PPid: |grep -v grep |grep -v sh|awk '{print $2}'", arg1);
let r0_3: *mut FILE = popen(&var_110, "r");
if r0_3 != 0 {
let r0_5: u32 = fread(&var_210, 1, 0x100, r0_3);
let mut r0_7: i32;
let mut r1_1: i32;
let mut r2_1: i32;
r0_7 = pclose(r0_3);
if r0_5 > 0 {
/* tailcall */
return sub_43c28(r0_5, nullptr, &var_210, r0_7, r1_1, r2_1);
}
}
0
}
We have /sbin/login at home.
fn autologin() -> i32
{
let r0: *mut passwd = getpwnam("root");
if r0 == 0 {
puts("get root password info failed!");
return 0xffffffff;
}
let r5_1: uid_t = *(r0.byte_offset(8) as *mut i32);
let r6_1: gid_t = *(r0.byte_offset(0xc) as *mut i32);
let r0_2: *mut i8 = strdup(*(r0.byte_offset(0x14) as *mut i32));
let r0_4: *mut i8 = strdup(*(r0.byte_offset(0x18) as *mut i32));
printf("get root password info ok, dir = %s, shell = %s\n", r0_2, r0_4);
chown("/dev/console", r5_1, r6_1);
chown("/dev/tty", r5_1, r6_1);
setregid(r6_1, r6_1);
setegid(r6_1);
setgid(r6_1);
setreuid(r5_1, r5_1);
seteuid(r5_1);
setuid(r5_1);
setenv("HOME", r0_2, 1);
setenv("SHELL", r0_4, 1);
setenv("USER", "root", 1);
setenv("LOGNAME", "root", 1);
chdir(r0_2);
0
}
Found the ex-Windows developer.
fn GetFileFromFirmware(arg1: i32, arg2: i32, arg3: i32, arg4: i32, arg5: i32 @ r5, arg6: i32) -> i32
{
// ...
}
// immediately after:
fn GetFileFromFirmwareEx(arg1: i32, arg2: *mut c_void, arg3: i32, arg4: *mut i8) -> i32
{
// ...
match arg3 {
1 => {
result = GetFileFromFirmware(result_1, arg4, *(arg2.byte_offset(0x14) as *mut u32),
*(arg2.byte_offset(0x18) as *mut u32), *(arg2.byte_offset(0x1c) as *mut u32));
}
2 => {
result = GetFileFromFirmware(result_1, arg4, *(arg2.byte_offset(0x120) as *mut u32),
*(arg2.byte_offset(0x124) as *mut u32), *(arg2.byte_offset(0x228) as *mut u32));
}
3 | 5 | 6 | 7 | 9 | 0xa | 0xb | 0xc | 0xd | 0xe | 0xf => {
goto 'label_44ef4;
}
4 => {
result = GetFileFromFirmware(result_1, arg4, *(arg2.byte_offset(0x22c) as *mut u32),
*(arg2.byte_offset(0x230) as *mut u32), *(arg2.byte_offset(0x234) as *mut u32));
}
8 => {
result = GetFileFromFirmware(result_1, arg4, *(arg2.byte_offset(0x238) as *mut u32),
*(arg2.byte_offset(0x23c) as *mut u32), *(arg2.byte_offset(0x240) as *mut u32));
}
0x10 => {
let result_2: i32 = GetFileFromFirmware(result_1, arg4, *(arg2.byte_offset(0x244) as *mut u32),
*(arg2.byte_offset(0x248) as *mut u32), *(arg2.byte_offset(0x24c) as *mut u32));
result = result_2;
let result_3: i32 = result_2;
}
}
// ...
}
Why have one ICMP ping implementation if you could have two?
fn icmp_ping_old(arg1: *mut i8) -> i32
{
let r0: i32 = socket(2, 3, 1);
if r0 < 0 {
DebugPrint(8, 7, "[%s:%u]error : socket\n", "icmp_ping_old", 0x14d);
return 0;
}
// ...
}
// earlier:
fn icmp_ping(arg1: *mut i8, arg2: i32) -> pid_t
{
// ...
let r0_6: i32 = socket(2, 3, 1);
if r0_6 < 0 {
DebugPrint(8, 7, "[%s:%u]RAW socket created error\n", "icmp_ping", 0xe5);
perror("RAW socket created error");
*(mutex_ping as *mut u8) = 1;
return 0;
}
// ...
}
But are you sure it will reboot?
fn delay_reboot_thread(arg1: *mut i32) -> i32
{
let entry_r2: i32;
printf("%s: reboot.\n", "delay_reboot_thread", entry_r2, "delay_reboot_thread");
let r6: i32 = *(arg1 as *mut u32);
free(arg1);
DebugPrint(8, 7, "[%s:%u]reboot_thread start, system will reboot after %ds...\n\n", "delay_reboot_thread", 0xc95, r6);
pthread_self();
pthread_detach();
system("killall procman");
system("killall record_server");
system("touch /mnt/nand/soft_reboot.flag");
let mut r0_1: i32;
if r6 < 0x1f {
r0_1 = r6;
} else {
r0_1 = 0xa;
}
SLEEP_SECOND(r0_1);
DebugPrint(8, 7, "[%s:%u]call reboot to reboot system...\n\n", "delay_reboot_thread", 0xca3);
WatchDogForceReset();
system("reboot -f");
system("reboot -f");
system("reboot -f");
system("reboot -f");
system("reboot -f");
system("reboot -f");
free(arg1);
delay_reboot(3);
0
}
Kill it!!! (There is a total of 51 instance of killall <proc> in this library, of which 23 are killall -9 <proc>.)
fn KillProcessForFirmware(arg1: i32, arg2: i32) -> i32
{
mysystem("killall -9 watchdog.sh");
killprocess("procman");
mysystem("killall -9 AlarmProxy");
mysystem("killall -9 AlarmService");
let mut var_50: i32 = 0;
let mut var_4c: ();
memset(&var_4c, 0, 0x3c);
read_file_to_string("/etc/flag.customize", &var_50, 0x40);
if strstr(&var_50, "_CWYY") != 0 {
mysystem_with_param("%s/upgrade_killall.sh", "/tmp/oem");
mysystem("killall skyapp");
mysystem("killall skfactory");
mysystem("killall netevent");
}
if arg2 != 3 {
mysystem("killall web_server");
mysystem("killall h5live_server");
if arg2 != 7 {
mysystem("killall hik_server");
if arg2 != 8 {
mysystem("killall juanvision");
}
}
} else {
mysystem("killall h5live_server");
mysystem("killall hik_server");
mysystem("killall juanvision");
}
mysystem("touch /tmp/not.check.stream.exist");
mysystem("killall media_server");
if access("/opt/ch/lprAttendance", 0) != 0xffffffff {
DebugPrint(8, 7, "[%s:%u]lprAttendance found, kill it!!!\n\n", "KillProcessForFirmware", 0x256);
mysystem("killall lprAttendance");
}
if access("/opt/ch/fire_server_aiwinn", 0) != 0xffffffff {
DebugPrint(8, 7, "[%s:%u]fire_server_aiwinn found, kill it!!!\n\n", "KillProcessForFirmware", 0x25c);
mysystem("killall fire_server_aiwinn");
}
mysystem("killall record_server");
mysystem("sync");
if arg2 != 9 {
mysystem("killall danale_server");
mysystem("killall goolink_server");
mysystem("killall skyworth_server");
mysystem("killall ac18pro_server");
killprocess("p2p_server");
mysystem("killall rtmp_publish");
mysystem("killall rtsp_server");
mysystem("killall hank_server");
mysystem("killall -9 cubo_main");
mysystem("killall -9 qrc_reader");
usleep(0x2dc6c0);
killprocess("media_server");
if access("/opt/ch/mobile_server", 0) != 0xffffffff {
'label_547de:
DebugPrint(8, 7, "[%s:%u]mobile_server found, kill it!!!\n\n", "KillProcessForFirmware", 0x291);
killprocess("mobile_server");
}
if arg2 != 0xc {
goto 'label_5468a;
}
killprocess("comm_server");
killprocess("ipvs_adaptor");
killprocess("gb28181_server");
} else {
mysystem("killall rtmp_publish");
mysystem("killall rtsp_server");
mysystem("killall hank_server");
mysystem("killall -9 cubo_main");
mysystem("killall -9 qrc_reader");
usleep(0x2dc6c0);
killprocess("media_server");
if access("/opt/ch/mobile_server", 0) != 0xffffffff {
goto 'label_547de;
}
'label_5468a:
if access("/opt/ch/anko_adaptor", 0) != 0xffffffff {
DebugPrint(8, 7, "[%s:%u]anko_adaptor found, kill it!!!\n\n", "KillProcessForFirmware", 0x29b);
killprocess("anko_adaptor");
}
if access("/opt/ch/hb_server", 0) != 0xffffffff {
DebugPrint(8, 7, "[%s:%u]hb_server found, kill it!!!\n\n", "KillProcessForFirmware", 0x2a2);
killprocess("hb_server");
}
if arg2 != 6 {
killprocess("comm_server");
if arg2 != 0xa {
killprocess("ipvs_adaptor");
killprocess("gb28181_server");
}
} else {
killprocess("ipvs_adaptor");
killprocess("gb28181_server");
}
}
let r0_13: i32 = get_system_totalmem();
let r0_14: i32 = get_system_freemem();
DebugPrint(8, 7, "[%s:%u]freemem=%d kB, totalmen=%d kB.\n\n", "KillProcessForFirmware", 0x2bc, r0_14, r0_13);
let mut cond:0: bool = r0_14 < 0x3000;
if r0_14 >= 0x3000 {
cond:0 = r0_13 < &__elf_header;
}
if !cond:0 {
return 0;
}
mysystem("echo 3 > /proc/sys/vm/drop_caches");
let r0_18: i32 = get_system_freemem();
usleep(0xf4240);
DebugPrint(8, 7, "[%s:%u]freemem is %d kB\n\n", "KillProcessForFirmware", 0x2c2, r0_18);
0
}
Covering all the bases.
fn GetCrcValue(arg1: *mut i8, arg2: *mut c_void) -> i32 {
// ...
r2 = *((&data_8ba08).byte_offset((r3_1 as u32 ^ r2 as u8 as u32) << 2) as *mut u32) ^ r2 >> 8;
// ...
}
fn GetCrcValue1(arg1: i32, arg2: *mut c_void, arg3: *mut c_void) -> i32 {
// Same polynomial as in `GetCrcValue`.
}
fn GetCrcValue_ex(arg1: *mut i8, arg2: *mut c_void, arg3: i32) -> i32 {
// Same polynomial as in `GetCrcValue`.
}
fn GetCrcValue_exex(arg1: i32, arg2: *mut i8, arg3: *mut c_void) {
// Same polynomial as in `GetCrcValue`.
}
fn GetFileCrc(arg1: i32, arg2: *mut i32) -> i32 {
// Does not call any of the above. Same polynomial.
}
fn GetFileCrc1(arg1: i32, arg2: i32, arg3: i32, arg4: *mut i32) -> i32 {
// Does not call any of the above either. Same polynomial.
}
fn GetFileMd5(arg1: i32, arg2: *mut i8, arg3: u32) -> i32 {
// More secure?
}
Keeping the right things secret.
fn ReadSerialNumber() -> i32
{
/* tailcall */
ReadEncriptData()
}
fn ReadEncriptData(arg1: i32) -> i32
{
let lr: i32;
let var_4: i32 = lr;
let r0: i32 = ReadEncriptDataFromSoft();
// ...
}
fn ReadEncriptDataFromSoft(arg1: i32) -> i32
{
// ...
if flash_sect_rw(0, GET_SN_MTD_BLOCK(), 0x1000, 0x1f, var_c0) == 0 {
// ...
CheckSoftSNData(r4_2.byte_offset(-0x40), 0x20, arg1, 0x10, var_c0)
// ...
}
// ...
}
fn CheckSoftSNData(arg1: *mut i8, arg2: u32, arg3: *mut i32) -> i32
{
let mut var_238: i32;
__builtin_memset(&var_238, 0, 0x28);
let mut var_210: i32;
hexStrToUInt(arg1, arg2, &var_210);
let mut var_214: i32;
let mut i: *mut i8 = ((&var_214).byte_offset(3) as *mut u32);
let mut r1: i32 = 1;
let mut var_204: i32;
do {
i = &i[1];
if *(i as *mut u8) as u32 != 0 {
r1 = 0;
}
} while i != ((&var_204).byte_offset(3) as *mut u32);
if r1 != 1 {
let r3: i32 = var_204;
var_238 = var_210;
let var_20c: i32;
let var_234_1: i32 = var_20c;
let var_208: i32;
let var_230_1: i32 = var_208;
let var_22c_1: i32 = r3;
let mut expanded_key: [u32; 0x40];
crypto_aes_expand_key(&expanded_key, key_soft, 0x20);
let mut var_224: i32;
aes_decrypt_(&expanded_key, &var_224, &var_238);
let mut r3_1: i32 = 'OSJA';
let var_21c: i32;
if var_21c == 'OSJA' {
r3_1 = 'NS';
let var_218: i32;
if var_218 as u16 as u32 == 'NS' {
*(arg3 as *mut u32) = var_224;
let var_220: i32;
arg3[1] = var_220;
arg3[2] = var_21c;
arg3[3] = var_218;
return 0;
}
}
printf("[%s:%d] SN check failed.\n\n", "CheckSoftSNData", 0x1c0, r3_1);
}
0xffffffff
}
The serial number key is, of course, hardcoded; also due to a type mismatch (it's allocated as uint32_t[0x20], but read as uint8_t[0x20]), three quarters of it are zeroes.
key_soft:
be 00 00 00 30 00 00 00 ....0...
41 00 00 00 33 00 00 00 52 00 00 00 91 00 00 00 A...3...R.......
85 00 00 00 99 00 00 00 1c 00 00 00 1f 00 00 00 ................
df 00 00 00 2e 00 00 00 43 00 00 00 50 00 00 00 ........C...P...
23 00 00 00 79 00 00 00 39 00 00 00 29 00 00 00 #...y...9...)...
11 00 00 00 02 00 00 00 3e 00 00 00 45 00 00 00 ........>...E...
59 00 00 00 60 00 00 00 ff 00 00 00 ac 00 00 00 Y...`...........
99 00 00 00 55 00 00 00 66 00 00 00 88 00 00 00 ....U...f.......
3f 00 00 00 fe 00 00 00 ?.......
Every important function must be clearly named and exported. (It does not appear to be referenced anywhere else in the firmware.)
fn GetCopyPrevention__(arg1: i32, arg2: *mut i8, arg3: i32)
{
if arg3 <= 0x20 {
return;
}
let mut var_118: [i8; 0x80];
memset(&var_118, 0, 0x80);
let mut var_98: [i8; 0x84];
memset(&var_98, 0, 0x80);
sprintf(&var_98, "%s%s", arg1, "cham.li@anjvision.com");
our_md5_encode(&var_118, &var_98, strlen(&var_98));
strcpy(arg2, &var_118);
}