Обновление 2020.1.14 не требует установки джейлбрейка
Эта статья представляет собой руководство по реверсированию двоичного файла WeChat для реализации пересылки коротких видеороликов в Moments, от первоначального кода сборки до окончательной установки повторной подписи и других операций. Она шаг за шагом научит вас, как играть с WeChat! После того, как вы это изучите, вам будет легко перепроектировать другие функции WeChat.
Эта статья разделена на две части из-за ее длины. В первой части объясняется обратная работа, то есть как найти соответствующие функции и методы для реализации. Вторая часть объясняет, как повторно подписать установку на машинах без джейлбрейка. Установка твика на машинах с джейлбрейком Подробный процесс.
Во второй части текста также представлен код WeChat для автоматического захвата красных конвертов и изменения количества шагов WeChat. Их можно найти шаг за шагом, следуя процедуре, описанной в этой статье, и они не будут повторяться здесь.
Прежде чем практиковаться, вам необходимо подготовить взломанный мобильный телефон, а затем установить все перечисленные ниже инструменты. IDA и Reveal — взломанные версии. Подлинная версия IDA стоит более 2000 долларов. Такой потрясающий инструмент для обратного проектирования действительно стоит своих денег. Однако, если вы не являетесь компанией, специализирующейся на обратном проектировании, нет. нужно использовать подлинную версию. Следующая взломанная версия Windows будет в порядке, я пока не могу найти ее на Mac. Вы можете использовать hopper для замены IDA на Mac, который также является очень мощным инструментом реверс-инжиниринга. Без лишних слов, давайте начнем!
Примечание. Двоичный файл WeChat, рассмотренный в этой статье, — это версия 6.3.28. Если это другая версия WeChat, базовый адрес в двоичном файле также будет другим.
Обратная среда для MacOS + iPhone5S 9.1 с джейлбрейком <br> Сначала используйте dumpdecrypted, чтобы разбить оболочку WeChat (если вы не знаете, как это сделать, прочтите написанное мной руководство), получите файл WeChat.decrypted и сначала закиньте этот файл в IDA для анализа (двоичный файл размером около 60 МБ). , анализ End занимает у IDA почти 40 минут), используйте дамп классов для экспорта всех заголовочных файлов.
LeonLei-MBP:~ gaoshilei$ class-dump -S -s -H /Users/gaoshilei/Desktop/reverse/binary_for_class-dump/WeChat.decrypted -o /Users/gaoshilei/Desktop/reverse/binary_for_class-dump/class-Header/WeChat
У меня есть свекровь! Всего существует 8000 заголовочных файлов, и у WeChat действительно огромный объем работы! Успокойте свои эмоции, соберитесь с мыслями и продолжайте. Чтобы получить ссылку для скачивания короткого видео, найдите представление, в котором воспроизводится видео, и следуйте подсказкам, чтобы найти URL-адрес короткого видео. Используйте Reveal для просмотра окна воспроизведения короткого видео. Вы можете видеть, что объект WCContentItemViewTemplateNewSigh является окном воспроизведения короткого видео. Его подпредставления включают WCSightView, SightView и SightPlayerView. Эти классы являются нашей точкой входа. При сохранении видео в избранное нажмите и удерживайте видео, чтобы открыть эту опцию, поэтому в классе WCContentItemViewTemplateNewSight могут быть методы, связанные с жестами. Найдите подсказки в только что экспортированном файле заголовка.
- (void)onLongTouch;
- (void)onLongPressedWCSight:(id)arg1;
- (void)onLongPressedWCSightFullScreenWindow:(id)arg1;
Эти методы связаны с жестом длительного нажатия. Найдите эти функции в IDA и просмотрите их одну за другой. onLongPressedWCSight и onLongPressedWCSightFullScreenWindow относительно просты, onLongTouch относительно длинный, и я обнаружил, что метод Favorites_Add вызывается внутренне, потому что при длительном нажатии на видео появляется опция «Избранное», и я видел этот вызов функции
ADRP X8, #selRef_sightVideoPath@PAGE
LDR X1, [X8,#selRef_sightVideoPath@PAGEOFF]
Я получил адрес короткого видео здесь. Можно предположить, что эта функция связана с коллекцией. Давайте разберемся и проверим ее.
(lldb) im li -o -f
[ 0] 0x000000000003c000 /var/mobile/Containers/Bundle/Application/2F1D52EC-C57E-4F95-B715-EF04351232E8/WeChat.app/WeChat(0x000000010003c000)
Вы можете видеть, что ASLR WeChat — 0x3c000. Найдите базовые адреса этих трех функций в IDA и установите точки останова соответственно.
(lldb) br s -a 0x1020D3A10+0x3c000
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol110094$$WeChat + 28, address = 0x000000010210fa10
(lldb) br s -a 0x1020D3370+0x3c000
Breakpoint 2: where = WeChat`___lldb_unnamed_symbol110091$$WeChat + 8, address = 0x000000010210f370
(lldb) br s -a 0x1020D33E4+0x3c000
Breakpoint 3: where = WeChat`___lldb_unnamed_symbol110092$$WeChat + 12, address = 0x000000010210f3e4
Вернитесь в WeChat и нажмите и удерживайте короткое видео, чтобы увидеть, как срабатывает точка останова.
Process 3721 stopped
* thread #1: tid = 0x658fc, 0x000000010210f370 WeChat`___lldb_unnamed_symbol110091$$WeChat + 8, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x000000010210f370 WeChat`___lldb_unnamed_symbol110091$$WeChat + 8
WeChat`___lldb_unnamed_symbol110091$$WeChat:
-> 0x10210f370 <+8>: add x29, sp, #16 ; =16
0x10210f374 <+12>: mov x19, x0
0x10210f378 <+16>: adrp x8, 4968
0x10210f37c <+20>: ldr x0, [x8, #744]
(lldb) c
Process 3721 resuming
Process 3721 stopped
* thread #1: tid = 0x658fc, 0x000000010210fa10 WeChat`___lldb_unnamed_symbol110094$$WeChat + 28, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010210fa10 WeChat`___lldb_unnamed_symbol110094$$WeChat + 28
WeChat`___lldb_unnamed_symbol110094$$WeChat:
-> 0x10210fa10 <+28>: add x29, sp, #96 ; =96
0x10210fa14 <+32>: sub sp, sp, #96 ; =96
0x10210fa18 <+36>: mov x19, x0
0x10210fa1c <+40>: adrp x8, 4863
……
Было обнаружено, что сначала сработала точка останова 2, затем сработала точка останова 1, а затем сработали точки останова 2 и 1, когда каждая точка останова 3 была очень тихой. Вы можете исключить связь между onLongPressedWCSightFullScreenWindow и коллекцией коротких видеороликов. Следы коротких видеороликов необходимо найти оставшимися двумя способами. Найдите от C до V и найдите M, следуя проверенным подсказкам! Используйте cycript для внедрения WeChat и получения контроллера, в котором находится представление, воспроизводящее короткое видео.
cy# [#0x138c18030 nextResponder]
#"<WCTimeLineCellView: 0x138c34620; frame = (0 0; 319 249); tag = 1048577; layer = <CALayer: 0x138362ba0>>"
cy# [#0x138c34620 nextResponder]
#"<UITableViewCellContentView: 0x138223c70; frame = (0 0; 320 256); gestureRecognizers = <NSArray: 0x1384ec480>; layer = <CALayer: 0x138081dc0>>"
cy# [#0x138223c70 nextResponder]
#"<MMTableViewCell: 0x138c9f930; baseClass = UITableViewCell; frame = (0 307; 320 256); autoresize = W; layer = <CALayer: 0x1382dcd10>>"
cy# [#0x138c9f930 nextResponder]
#"<UITableViewWrapperView: 0x137b57800; frame = (0 0; 320 504); gestureRecognizers = <NSArray: 0x1383db660>; layer = <CALayer: 0x138af20c0>; contentOffset: {0, 0}; contentSize: {320, 504}>"
cy# [#0x137b57800 nextResponder]
#"<MMTableView: 0x137b8ae00; baseClass = UITableView; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x138adb590>; layer = <CALayer: 0x138956890>; contentOffset: {0, 99.5}; contentSize: {320, 3193}>"
cy# [#0x137b8ae00 nextResponder]
#"<UIView: 0x138ade5c0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x138ac9990>>"
cy# [#0x138ade5c0 nextResponder]
#"<WCTimeLineViewController: 0x1379eb000>"
Найдите контроллер, которому принадлежит WCContentItemViewTemplateNewSight, через цепочку ответчика, и это WCTimeLineViewController. В заголовочном файле этого класса не было обнаружено никаких ценных подсказок, но мы заметили, что представление, в котором находится короткое видео, принадлежит MMTableVIewCell (см. диаграмму анализа Reveal выше), который является наиболее знакомым TableView и данными ячеек для каждой iOS. Он назначается через прокси-метод UITableViewDataSource - tableView:cellForRowAtIndexPath:
С помощью этого метода вы точно можете узнать тень M. Найдите [WCTimeLineViewController tableView:cellForRowAtIndexPath:]
в IDA и найдите базовый адрес 0x10128B6B0:
__text:000000010128B6B0 ADRP X8, #selRef_genNormalCell_indexPath_@PAGE
Функция здесь — это метод генерации ячеек в WCTimeLineViewController. Помимо этого метода, в этом классе есть еще три метода генерации ячеек:
- (void)genABTestTipCell:(id)arg1 indexPath:(id)arg2;
- (void)genRedHeartCell:(id)arg1 indexPath:(id)arg2;
- (void)genUploadFailCell:(id)arg1 indexPath:(id)arg2;
Из буквального значения можно догадаться, что нормальным должен быть метод генерации небольших видеоячеек. Продолжайте искать подсказки в IDA.
__text:0000000101287CC8 ADRP X8, #selRef_getTimelineDataItemOfIndex_@PAGE
Вышеупомянутый метод находится в методе genNormalCell:IndexPath:
:. Вы можете смело догадаться, что этот метод является методом получения данных TimeLine (моментов) также должны быть получены с помощью этого метода, и IDA может видеть их. вызовите этот метод. Метод под названием selRef_getTimelineDataItemOfIndex_
, получение DataItem, похоже, является источником данных ячейки! Затем используйте LLDB, чтобы установить точки останова и проверить гипотезу. Базовый адрес, соответствующий этому методу, можно найти через IDA: 0x101287CE4. Сначала напечатайте смещение ASLR для запуска WeChat.
LeonLei-MBP:~ gaoshilei$ lldb
(lldb) process connect connect://localhost:1234
(lldb) im li -o -f
[0] 0x0000000000050000 /var/mobile/Containers/Bundle/Application/2DCE8F30-9B6B-4652-901C-37EB1FF2A40D/WeChat.app/WeChat(0x0000000100050000)
Итак, точка останова находится по адресу 0x50000+0x101287CE4.
(lldb) br s -a 0x50000+0x101287CE4
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol63721$$WeChat + 252, address = 0x00000001012d7ce4
Распечатайте значение x0
(lldb) po $x0
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>
Получите объект WCDataItem. Значение x0 здесь — это возвращаемое значение после выполнения selRef_getTimelineDataItemOfIndex_
, а затем измените значение x0.
(lldb) register write $x0 0
(lldb) c
В этот момент вы обнаружите, что содержимое небольшого видео, которое мы хотим обновить, пусто.
На данный момент мы нашли метод получения исходных данных короткого видео. Вопрос в том, как нам получить этот WCDataItem? Продолжайте смотреть на статус вызова функции анализа IDA:
WCTimeLineViewController - (void)genNormalCell: (id) indexPath: (id)
__text:0000000101287BCC STP X28, X27, [SP,#var_60]!
__text:0000000101287BD0 STP X26, X25, [SP,#0x60+var_50]
__text:0000000101287BD4 STP X24, X23, [SP,#0x60+var_40]
__text:0000000101287BD8 STP X22, X21, [SP,#0x60+var_30]
__text:0000000101287BDC STP X20, X19, [SP,#0x60+var_20]
__text:0000000101287BE0 STP X29, X30, [SP,#0x60+var_10]
__text:0000000101287BE4 ADD X29, SP, #0x60+var_10
__text:0000000101287BE8 SUB SP, SP, #0x80
__text:0000000101287BEC MOV X19, X3
__text:0000000101287BF0 MOV X22, X0
__text:0000000101287BF4 MOV W25, #0x100000
__text:0000000101287BF8 MOVK W25, #1
__text:0000000101287BFC MOV X0, X2
__text:0000000101287C00 BL _objc_retain
__text:0000000101287C04 MOV X28, X0
__text:0000000101287C08 MOV X0, X19
__text:0000000101287C0C BL _objc_retain
__text:0000000101287C10 MOV X20, X0
__text:0000000101287C14 STR X20, [SP,#0xE0+var_98]
__text:0000000101287C18 ADRP X8, #selRef_row@PAGE
__text:0000000101287C1C LDR X1, [X8,#selRef_row@PAGEOFF]
__text:0000000101287C20 BL _objc_msgSend
__text:0000000101287C24 MOV X26, X0
__text:0000000101287C28 ADRP X8, #selRef_section@PAGE
__text:0000000101287C2C LDR X19, [X8,#selRef_section@PAGEOFF]
__text:0000000101287C30 MOV X0, X20
__text:0000000101287C34 MOV X1, X19
__text:0000000101287C38 BL _objc_msgSend
__text:0000000101287C3C STR X0, [SP,#0xE0+var_A8]
__text:0000000101287C40 MOV X0, X20
__text:0000000101287C44 MOV X1, X19
__text:0000000101287C48 BL _objc_msgSend
__text:0000000101287C4C MOV X2, X0
__text:0000000101287C50 ADRP X8, #selRef_calcDataItemIndex_@PAGE
__text:0000000101287C54 LDR X1, [X8,#selRef_calcDataItemIndex_@PAGEOFF]
__text:0000000101287C58 MOV X0, X22
__text:0000000101287C5C BL _objc_msgSend
__text:0000000101287C60 MOV X21, X0
__text:0000000101287C64 STR X21, [SP,#0xE0+var_C0]
__text:0000000101287C68 ADRP X8, #classRef_MMServiceCenter@PAGE
__text:0000000101287C6C LDR X0, [X8,#classRef_MMServiceCenter@PAGEOFF]
__text:0000000101287C70 ADRP X8, #selRef_defaultCenter@PAGE
__text:0000000101287C74 LDR X1, [X8,#selRef_defaultCenter@PAGEOFF]
__text:0000000101287C78 STR X1, [SP,#0xE0+var_B8]
__text:0000000101287C7C BL _objc_msgSend
__text:0000000101287C80 MOV X29, X29
__text:0000000101287C84 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287C88 MOV X19, X0
__text:0000000101287C8C ADRP X8, #classRef_WCFacade@PAGE
__text:0000000101287C90 LDR X0, [X8,#classRef_WCFacade@PAGEOFF]
__text:0000000101287C94 ADRP X8, #selRef_class@PAGE
__text:0000000101287C98 LDR X1, [X8,#selRef_class@PAGEOFF]
__text:0000000101287C9C STR X1, [SP,#0xE0+var_B0]
__text:0000000101287CA0 BL _objc_msgSend
__text:0000000101287CA4 MOV X2, X0
__text:0000000101287CA8 ADRP X8, #selRef_getService_@PAGE
__text:0000000101287CAC LDR X1, [X8,#selRef_getService_@PAGEOFF]
__text:0000000101287CB0 STR X1, [SP,#0xE0+var_A0]
__text:0000000101287CB4 MOV X0, X19
__text:0000000101287CB8 BL _objc_msgSend
__text:0000000101287CBC MOV X29, X29
__text:0000000101287CC0 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287CC4 MOV X20, X0
__text:0000000101287CC8 ADRP X8, #selRef_getTimelineDataItemOfIndex_@PAGE
__text:0000000101287CCC LDR X1, [X8,#selRef_getTimelineDataItemOfIndex_@PAGEOFF]
__text:0000000101287CD0 STR X1, [SP,#0xE0+var_C8]
__text:0000000101287CD4 MOV X2, X21
__text:0000000101287CD8 BL _objc_msgSend
__text:0000000101287CDC MOV X29, X29
__text:0000000101287CE0 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287CE4 MOV X21, X0
__text:0000000101287CE8 MOV X0, X20
......
Параметр, передаваемый в selRef_getTimelineDataItemOfIndex_
равен x2. Вы можете видеть, что x21, передаваемый в x2, является возвращаемым значением функции selRef_calcDataItemIndex_
, которая представляет собой беззнаковый длинный тип данных. Продолжая анализ, вызывающей стороной функции selRef_getTimelineDataItemOfIndex_
является возвращаемое значение selRef_getService_
на предыдущем шаге. После анализа точки останова обнаруживается, что это объект WCFacade
. Разберемся с вызовами selRef_getTimelineDataItemOfIndex_
:
Вызывающий объект — это возвращаемое значение selRef_getService_
, параметр — это возвращаемое значение selRef_calcDataItemIndex_
<br> Давайте обратим внимание на эти две функции и воспользуемся тем же принципом, чтобы проанализировать, как они реализуют вызов.
selRef_getService_
:MMServiceCenter
. При поиске x19 назначается в позиции 0x101287C88. Результат очень очевиден. — это возврат значения [MMServiceCenter defaultCenter]
[WCFacade class]
.selRef_calcDataItemIndex_
:WCTimeLineViewController
вызывающую программу x0 по адресу 0x101287C58.selRef_section
0x101287C4C обнаруживается, что входящий selRef_section
происходит от selRef_section
или равен x3.WCTimeLineViewController - (void)genNormalCell:(id) indexPath:(id)
, поэтому параметр selRef_calcDataItemIndex_
равен [IndexPath section]
.getTimelineDataItemOfIndex:
может передать [[MMServiceCenter defaultCenter] getService:[WCFacade class]]
Для получения его параметры можно получить с помощью следующей функции
[WCTimeLineViewController calcDataItemIndex:[indexPath section]]
Всегда чувствуете, что чего-то не хватает? Мы еще не получили indexPath! Следующий шаг — получить indexPath. Это относительно просто, поскольку мы находимся в [WCContentItemViewTemplateNewSight onLongTouch]
и можем последовательно получить MMTableViewCell, MMTableView и WCTimeLineViewController через [self nextResponder]
, а затем получить indexPath через [MMTableView indexPathForCell:MMTableViewCell]
.
После этого мы получили объект WCDataItem. Следующий этап должен быть сосредоточен на WCDataItem и, наконец, получить нужное короткое видео. Ищите подсказки в заголовочном файле этого класса. Поскольку видео можно воспроизводить только после загрузки, путь к видео должен быть получен здесь, поэтому обратите внимание на атрибуты или методы, связанные с URL-адресом и путем, а затем найдите следующее. подозреваемые.
@property(retain, nonatomic) NSString *sourceUrl2;
@property(retain, nonatomic) NSString *sourceUrl;
- (id)descriptionForKeyPaths;
- (id)keyPaths;
Вернитесь в LLDB и распечатайте эти значения с точками останова, чтобы посмотреть, что там.
(lldb) po [$x0 keyPaths]
<__NSArrayI 0x15f74e9d0>(
tid,
username,
createtime,
commentUsers,
contentObj
)
(lldb) po [$x0 descriptionForKeyPaths]
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>
(lldb) po [$x0 sourceUrl]
nil
(lldb) po [$x0 sourceUrl2]
nil
Ценных подсказок нет, но я заметил, что в WCDataItem есть WCContentItem. Кажется, единственный способ начать — здесь. Давайте посмотрим на заголовочный файл.
@property(retain, nonatomic) NSString *linkUrl;
@property(retain, nonatomic) NSString *linkUrl2;
@property(retain, nonatomic) NSMutableArray *mediaList;
Распечатайте это в LLDB
(lldb) po [[$x0 valueForKey:@"contentObj"] linkUrl]
https://support.weixin.qq.com/cgi-bin/mmsupport-bin/readtemplate?t=page/common_page__upgrade&v=1
(lldb) po [[$x0 valueForKey:@"contentObj"] linkUrl2]
nil
(lldb) po [[$x0 valueForKey:@"contentObj"] mediaList]
<__NSArrayM 0x15f985e10>(
<WCMediaItem: 0x15dfebdf0>
)
В массиве mediaList есть объект WCMediaItem. Медиа обычно используется для представления видео и аудио. Я бы смело предположил, что это он! Быстро найдите файл заголовка и выполните поиск по нему.
@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForData;
- (id)pathForSightData;
- (id)pathForTempAttachVideoData;
- (id)videoStreamForData;
Среди приведенных выше атрибутов и методов pathForSightData
с наибольшей вероятностью получит короткий путь к видео. Продолжить проверку.
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] dataUrl]
type[1], url[http://vweixinf.tc.qq.com/102/20202/snsvideodownload?filekey=30270201010420301e020166040253480410d14adcddf086f4e131d11a5b1cca1bdf0203039fa00400&bizid=1023&hy=SH&fileparam=302c0201010425302302040fde55e20204580ebd3602024eea02031e8d7d02030f42400204d970370a0201000400], enckey[0], encIdx[-1], token[]
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForData]
/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForSightData]
/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45.mp4
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForAttachVideoData]
nil
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] videoStreamForData]
nil
Получил сетевой URL-адрес и локальный путь короткого видео! Здесь вы можете использовать iFunBox или scp, чтобы скопировать этот файл из песочницы и посмотреть, является ли это коротким видео, которое должна воспроизводить эта ячейка.
LeonLei-MBP:~ gaoshilei$ scp [email protected]:/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45.mp4 Desktop/
8fb0cdd77208de5b56169fb3458b45.mp4 100% 232KB 231.9KB/s 00:00
Откройте его с помощью QuickTime и обнаружите, что это действительно то короткое видео, которое мы ищем. Еще раз проверьте правильность URL-адреса. Откройте указанный выше URL-адрес данных в браузере и обнаружите, что это также короткое видео. Анализируя этот класс, можно сделать следующие выводы:
На этом анализ пути и метода получения короткого видео завершен. Чтобы добиться пересылки, нам необходимо продолжить анализ выпуска WeChat Moments.
Этот раздел представляет собой обходной путь, который я сделал, когда искал функцию пересылки короткого видео. В конце концов, я не нашел способа ее реализовать. Однако в нем также представлены некоторые часто используемые идеи и методы обратного проектирования. Не хотите его читать, можете перейти ко второму разделу.
Откройте интерфейс съемки короткого видео и внедрите в него cycript. Нам нужно выяснить, какой метод используется для публикации короткого видео, а затем проверить, какие окна находятся в текущем окне (потому что съемка короткого видео не производится. в ключевом окне UIApplication)
cy# [UIApp windows].toString()
(
"<iConsoleWindow: 0x125f75e20; baseClass = UIWindow; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x125f77b70>; layer = <UIWindowLayer: 0x125df4810>>",
"<SvrErrorTipWindow: 0x127414d40; baseClass = UIWindow; frame = (0 64; 320 45); hidden = YES; gestureRecognizers = <NSArray: 0x12740d930>; layer = <UIWindowLayer: 0x1274030b0>>",
"<MMUIWindow: 0x127796440; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x1278083c0>; layer = <UIWindowLayer: 0x127796750>>",
"<UITextEffectsWindow: 0x1270e0d40; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x1270b4ba0>>",
"<NewYearActionSheet: 0x127797e10; baseClass = UIWindow; frame = (0 0; 320 568); hidden = YES; userInteractionEnabled = NO; layer = <UIWindowLayer: 0x1277d5490>>"
)
Обнаружено, что на текущей странице всего 5 окон, среди которых MMUIWindow — это окно, в котором было снято короткое видео. Распечатайте древовидную структуру пользовательского интерфейса.
cy# [#0x127796440 recursiveDescription]
Распечатанный результат довольно длинный, поэтому выкладывать его не буду. Найдите эту кнопку, которая предназначена для съемки коротких видеороликов.
| | | | | | <UIButton: 0x1277a9d70; frame = (89.5 368.827; 141 141); opaque = NO; gestureRecognizers = <NSArray: 0x1277aaeb0>; layer = <CALayer: 0x1277a9600>>
| | | | | | | <UIView: 0x1277aa0a0; frame = (0 0; 141 141); userInteractionEnabled = NO; tag = 252707333; layer = <CALayer: 0x1277aa210>>
| | | | | | | | <UIImageView: 0x1277aa2e0; frame = (0 0; 141 141); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1277aa490>>
а затем выполнить
cy# [#0x1277a9d70 setHidden:YES]
Я обнаружил, что кнопка стрельбы исчезла, что подтвердило мои подозрения. Чтобы найти событие ответа кнопки, вы можете найти его через target
cy# [#0x1277a9d70 allTargets]
[NSSet setWithArray:@[#"<MainFrameSightViewController: 0x1269a4600>"]]]
cy# [#0x1277a9d70 allControlEvents]
193
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:193]
null
Я обнаружил, что у кнопки нет соответствующего действия, что странно! UIButton должен иметь цель и действие, иначе кнопка не сможет реагировать на события. Давайте попробуем другие ControlEvents
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchDown]
@["btnPress"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpOutside]
@["btnRelease"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpInside]
@["btnRelease"]
Оказывается, этим трем ControlEvents соответствуют соответствующие действия. Давайте посмотрим на значения этих трёх перечислений.
typedef enum UIControlEvents : NSUInteger {
UIControlEventTouchDown = 1 << 0,
UIControlEventTouchDownRepeat = 1 << 1,
UIControlEventTouchDragInside = 1 << 2,
UIControlEventTouchDragOutside = 1 << 3,
UIControlEventTouchDragEnter = 1 << 4,
UIControlEventTouchDragExit = 1 << 5,
UIControlEventTouchUpInside = 1 << 6,
UIControlEventTouchUpOutside = 1 << 7,
UIControlEventTouchCancel = 1 << 8,
......
} UIControlEvents;
Видно, что UIControlEventTouchDown соответствует 1, UIControlEventTouchUpInside соответствует 128, UIControlEventTouchUpOutside соответствует 64, а сумма трех равна ровно 193! Оказывается, при вызове [#0x1277a9d70 allControlEvents]
возвращаемое значение должно быть перечислением. Если перечислений несколько, сложите их значения. Не правда ли, это немного сбивает с толку? Я чувствую то же самое! Только что мы распечатали действия, соответствующие трем событиям ControlEvent, и продолжили использовать LLDB+IDA для динамического анализа.
Поскольку нам нужно найти способ публиковать короткие видеоролики, нас не интересует соответствующая функция btnPress
. Мы сосредоточиваемся на btnRelease
— методе, который будет вызываться после отпускания кнопки съемки. Найдите этот метод в IDA и установите следующую точку останова после его обнаружения.
(lldb) br s -a 0xac000+0x10209369C
Breakpoint 4: where = WeChat`___lldb_unnamed_symbol108894$$WeChat + 32, address = 0x000000010213f69c
Process 3813 stopped
* thread #1: tid = 0xf1ef0, 0x000000010213f69c WeChat`___lldb_unnamed_symbol108894$$WeChat + 32, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
frame #0: 0x000000010213f69c WeChat`___lldb_unnamed_symbol108894$$WeChat + 32
WeChat`___lldb_unnamed_symbol108894$$WeChat:
-> 0x10213f69c <+32>: bl 0x1028d0b60 ; symbol stub for: objc_msgSend
0x10213f6a0 <+36>: cmp w0, #2 ; =2
0x10213f6a4 <+40>: b.ne 0x10213f6dc ; <+96>
0x10213f6a8 <+44>: adrp x8, 5489
Снимите короткое видео на свой мобильный телефон, а затем отпустите его, активируя точку останова, которая показывает, что наше предположение верно. Продолжая анализ, мы обнаружили, что код находится с правой стороны картинки выше. Посмотрев на него, нет возможности перейти к выпуску видео. Однако, если внимательно посмотреть, есть блок, который и есть. системный блок задержки и расположен по адресу 0x102093760. Затем мы следуем за точкой останова и переходим к адресу, хранящемуся в x16 по адресу 0x1028255A0.
(lldb) si
Process 3873 stopped
* thread #1: tid = 0xf62c4, 0x00000001028d9598 WeChat`dispatch_after, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x00000001028d9598 WeChat`dispatch_after
WeChat`dispatch_after:
-> 0x1028d9598 <+0>: adrp x16, 1655
0x1028d959c <+4>: ldr x16, [x16, #1056]
0x1028d95a0 <+8>: br x16
WeChat`dispatch_apply:
0x1028d95a4 <+0>: adrp x16, 1655
(lldb) po $x2
<__NSStackBlock__: 0x16fd49f88>
Обнаружено, что переданный параметр x2 является блоком. Давайте еще раз рассмотрим функцию send_after.
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
Эта функция имеет три параметра, а именно диспетчер_время_t, диспетчер_очередь_т и диспетчер_блок_т, напечатанный здесь, — это блок, который нужно передать, поэтому мы предполагаем, что после съемки короткого видео будет задержка, а затем выполните только что переданный блок. поэтому x2 должно быть. Если есть вызовы других методов, следующим шагом будет узнать местоположение этого блока.
(lldb) memory read --size 8 --format x 0x16fd49f88
0x16fd49f88: 0x000000019f8fd218 0x00000000c2000000
0x16fd49f98: 0x000000010214777c 0x0000000102fb0e60
0x16fd49fa8: 0x000000015da32600 0x000000015ea1a430
0x16fd49fb8: 0x000000015cf5fee0 0x000000016fd49ff0
0x000000010214777c — это местоположение блока. Разумеется, нужно вычесть текущее смещение ASLR WeChat. Конечный адрес в IDA — 0x10209377C. Неожиданно обнаруживается, что это подпрограмма btnRelease
sub_10209377C. Эта подпрограмма очень проста и имеет только один метод selRef_logicCheckState_
, который может быть нашей целью. Давайте сначала посмотрим, кто вызвал этот метод
(lldb) br s -a 0xb4000+0x1020937BC
......
Process 3873 stopped
* thread #1: tid = 0xf62c4, 0x00000001021477bc WeChat`___lldb_unnamed_symbol108895$$WeChat + 64, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x00000001021477bc WeChat`___lldb_unnamed_symbol108895$$WeChat + 64
WeChat`___lldb_unnamed_symbol108895$$WeChat:
-> 0x1021477bc <+64>: adrp x8, 5489
0x1021477c0 <+68>: ldr x1, [x8, #1552]
0x1021477c4 <+72>: orr w2, wzr, #0x1
0x1021477c8 <+76>: ldp x29, x30, [sp, #16]
(lldb) po $x0
<MainFrameSightViewController: 0x15d1f0c00>
Я обнаружил, что он все еще вызывается объектом MainFrameSightViewController. Тогда selRef_logicCheckState_
также должен быть в заголовочном файле этого класса. Я искал и нашел его.
- (void)logicCheckState:(int)arg1;
Найдите [MainFrameSightViewController logCheckState:] в левом окне IDA и обнаружите, что этот метод очень сложен и содержит слишком много логики, поэтому не волнуйтесь и выполняйте его медленно. Мы обнаружили переход по адресу 0x102094D6C, и идея была очень ясна. Нам нужно только найти строку, где было снято короткое видео, и посмотреть вниз, чтобы увидеть эту строку. Установите точку останова по адресу 0x102094D6C. Эта точка останова будет срабатывать несколько раз при съемке коротких видеороликов. Вы можете отключить точку останова перед съемкой, включить ее перед отпусканием и в это время распечатать значение x8.
(lldb) p/x $x8
(unsigned long) $38 = 0x0000000102174e10
x8 — это указатель, и адрес, на который он указывает, — 0x102174e10. Используйте этот адрес за вычетом текущего смещения ASLR, чтобы найти базовый адрес в IDA. Он равен 0x102094E10. Логическая строка обработки для завершения съемки найдена и выполняется. полностью. После позиции 0x102094E24 перейдите к 0x1020951C4. Эта ветка содержит меньше контента и содержит три функции.
loc_1020951C4
ADRP X8, #selRef_hideTips@PAGE
LDR X1, [X8,#selRef_hideTips@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
ADRP X8, #selRef_finishWriter@PAGE
LDR X1, [X8,#selRef_finishWriter@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
ADRP X8, #selRef_turnCancelBtnForFinishRecording@PAGE
LDR X1, [X8,#selRef_turnCancelBtnForFinishRecording@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
B loc_102095288
Среди них следует сосредоточить внимание на selRef_finishWriter
и selRef_turnCancelBtnForFinishRecording
. Эти два метода, похоже, означают конец короткой видеозаписи. Подсказки, скорее всего, кроются в этих двух функциях. Посмотрев на вызывающую сторону, мы обнаружили, что эти два метода принадлежат MainFrameSightViewController. Продолжайте проверять эти два метода в IDA. Я нашел метод f_switchToSendingPanel
в конце selRef_finishWriter
по адресу 0x102094248, установил точку останова, а затем снял видео и обнаружил, что этот метод не срабатывает. Интерфейс публикации не должен вызываться через этот метод, поэтому продолжайте возвращаться к методу selRef_finishWriter
; вызывайте метод selRef_stopRecording
по адресу 0x1020941DC, и вызывающая сторона, которая его печатает, обнаруживает, что этот метод принадлежит SightFacade
, и продолжает искать реализация этого метода в IDA. selRef_stopRecord
вызывается снова в позиции 0x101F9BED4 этого метода. Вызывающий также обнаруживает, что этот метод принадлежит SightCaptureLogicF4. Это немного похоже на чистку лука и продолжает искать реализацию этого метода. selRef_finishWriting
снова вызывается в позиции 0x101A98778 внутри этого метода. Используя тот же принцип, обнаруживается, что этот метод принадлежит SightMovieWriter. Три слоя отклеились, продолжим:
Две строки разделены в позиции 0x10261D004 в SightMovieWriter - (void)finishWriting
В этой позиции устанавливается точка останова, а затем точка останова срабатывает после съемки короткого видео, и печатается значение x19.
(lldb) po $x19
<OS_dispatch_queue: CAPTURE.CALLBACK[0x13610bcd0] = { xrefcnt = 0x4, refcnt = 0x4, suspend_cnt = 0x0, locked = 1, target = com.apple.root.default-qos.overcommit[0x1a0aa3700], width = 0x0, running = 0x0, barrier = 1 }>
Следовательно, код не перейдет к loc_10261D054, а пойдет влево. В коде слева обнаружено, что блок включен. Этот блок представляет собой подпрограмму sub_10261D0AC и имеет адрес 0x10261D0AC. Найдите этот адрес и его структуру. как показано ниже:
Видно, что он в основном разделен на две строки. Мы устанавливаем точку останова в конце первого поля, то есть 0x10261D108. После того, как точка останова срабатывает после завершения стрельбы, значение x0 печатается как 1. ассемблерный код вот
__text:000000010261D104 CMP X0, #2
__text:000000010261D108 B.EQ loc_10261D234
B.EQ перейдет на loc_10261D234 только тогда, когда результат предыдущего шага равен 0, но результат здесь не равен 0. Измените значение x0 на 2, чтобы результат предыдущего шага был равен 0.
(lldb) po $x0
1
(lldb) register write $x0 2
(lldb) po $x0
2
В это время отпустите точку останова и подождите, чтобы перейти к интерфейсу публикации короткого видео. В результате он зависает в этом интерфейсе без какого-либо ответа, поэтому я думаю, что логика реализации перехода должна быть в строке справа. и продолжаем поиск по строке справа: Было обнаружено, что следующий метод был вызван в правой позиции 0x10261D3AC
- (void)finishWritingWithCompletionHandler:(void (^)(void))handler;
Этот метод представляет собой метод AVAssetWriter, предоставляемый системой. Это операция, которую необходимо выполнить после завершения записи видео. Здесь передается блок. Поскольку существует только один параметр, соответствующая переменная равна x2 и ее значение. x2 печатается.
(lldb) po $x2
<__NSStackBlock__: 0x16e086c78>
(lldb) memory read --size 8 --format x 0x16e086c78
0x16e086c78: 0x00000001a0aa5218 0x00000000c2000000
0x16e086c88: 0x00000001026d94b0 0x0000000102fc98c0
0x16e086c98: 0x0000000136229fd0 0x000000016e086d00
0x16e086ca8: 0x00000001997f5318 0xfffffffec9e882ff
И находим позицию блока 0x10261D4B0 через память стека (смещение ASLR нужно вычесть)
sub_10261D4B0
var_20= -0x20
var_10= -0x10
STP X20, X19, [SP,#var_20]!
STP X29, X30, [SP,#0x20+var_10]
ADD X29, SP, #0x20+var_10
MOV X19, X0
LDR X0, [X19,#0x20]
ADRP X8, #selRef_stopAmr@PAGE
LDR X1, [X8,#selRef_stopAmr@PAGEOFF]
BL _objc_msgSend
LDR X0, [X19,#0x20]
ADRP X8, #selRef_compressAudio@PAGE
LDR X1, [X8,#selRef_compressAudio@PAGEOFF]
LDP X29, X30, [SP,#0x20+var_10]
LDP X20, X19, [SP+0x20+var_20],#0x20
B _objc_msgSend
; End of function sub_10261D4B0
Вызываются только два метода: один — selRef_stopAmr
для остановки amr (аудиоформат), а другой — selRef_compressAudio
для сжатия звука. Следующая операция после завершения съемки не должна помещаться в эти два метода. Я ее искал. так долго, и я до сих пор понятия не имею. Эта дорога кажется мертвой, не попадайте в беду, стратегически отступайте и ищите другие входы.
Радость от обратного пути – это испытать радость успеха на пути к поиску истины. Вы также можете пойти не в том направлении и уходить все дальше и дальше от истины. Не расстраивайтесь, корректируйте направление и продолжайте движение. двигаемся вперед!
(Поскольку WeChat был тайно обновлен в фоновом режиме, следующий контент представляет собой ASLR версии WeChat 6.3.30, а приведенный выше анализ основан на версии 6.3.28)
Обратите внимание, что когда вы нажимаете кнопку камеры в правом верхнем углу круга друзей, внизу появляется лист. Первый — это короткое видео Sight. Начните здесь и используйте cycript, чтобы увидеть, какому событию соответствует кнопка Sight. к.
iPhone-5S:~ root# cycript -p "WeChat"
cy# [UIApp windows].toString()
`(
"<iConsoleWindow: 0x14d6ccc00; baseClass = UIWindow; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x14d7df110>; layer = <UIWindowLayer: 0x14d7d6f60>>",
"<SvrErrorTipWindow: 0x14eaa5800; baseClass = UIWindow; frame = (0 0; 320 45); hidden = YES; gestureRecognizers = <NSArray: 0x14e9e8950>; layer = <UIWindowLayer: 0x14e9e6510>>",
"<UITextEffectsWindow: 0x14ec38ba0; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x14ec39360>>",
"<UITextEffectsWindow: 0x14e9c67a0; frame = (0 0; 320 568); layer = <UIWindowLayer: 0x14d683ff0>>",
"<UIRemoteKeyboardWindow: 0x14f226e40; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x14d6f4de0>>",
"<NewYearActionSheet: 0x14f1704a0; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x14ef9bf90>; layer = <UIWindowLayer: 0x14ef61a20>>"
)`
cy# [#0x14f1704a0 recursiveDescription].toString()
Лист внизу — это NewYearActionSheet, а затем распечатайте диаграмму древовидной структуры пользовательского интерфейса NewYearActionSheet (она слишком длинная, поэтому я не буду ее публиковать). Затем обнаружите, что UIButton, соответствующий Sight, имеет номер 0x14f36d470.
cy# [#0x14f36d470 allTargets]
[NSSet setWithArray:@[#"<NewYearActionSheet: 0x14f1704a0; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x14ef9bf90>; layer = <UIWindowLayer: 0x14ef61a20>>"]]]
cy# [#0x14f36d470 allControlEvents]
64
cy# [#0x14f36d470 actionsForTarget:#0x14f1704a0 forControlEvent:64]
@["OnDefaultButtonTapped:"]
Событие, привязанное к кнопке, можно найти через actionsForTarget:forControlEvent:
UIControl. Метод триггера кнопки Sight — OnDefaultButtonTapped:
Вернитесь в IDA и найдите этот метод в NewYearActionSheet. Продолжаем анализировать только этот метод selRef_dismissWithClickedButtonIndex_animated
, распечатав вызывающий объект, мы обнаруживаем, что это по-прежнему NewYearActionSheet. Продолжайте нажимать, чтобы найти метод newYearActionSheet_clickedButtonAtIndex
. Похоже, что это не сам NewYearActionSheet. Распечатывая вызывающий объект x0, мы обнаруживаем, что он принадлежит классу WCTimeLineViewController. Следуйте за точкой останова и вызовите метод #selRef_showSightWindowForMomentWithMask_byViewController_scene
в позиции 0x1012B78CC. Путем наблюдения мы обнаружили, что вызывающим объектом этого метода является возвращаемое значение x0 в позиции 0x1012B78AC. Я предполагаю, что этот метод находится в SightFacade I. пошел к заголовочному файлу, чтобы найти этот метод.
- (void)showSightWindowForMomentWithMask:(id)arg1 byViewController:(id)arg2 scene:(int)arg3;
Этот метод должен быть методом перехода к небольшому видеоинтерфейсу. Распечатайте его параметры отдельно ниже
(lldb) po $x2
<UIImage: 0x14f046660>, {320, 568}
(lldb) po $x3
<WCTimeLineViewController: 0x14e214800>
(lldb) po $x4
2
(lldb) po $x0
<SightFacade: 0x14f124b40>
Среди них x2, x3 и x4 соответствуют трем параметрам соответственно. x0 — это вызывающая сторона. Перейдите внутрь этого метода, чтобы узнать, как его реализовать. Обнаружено, что в этом методе инициализируется интерфейс съемки короткого видео: сначала инициализируется MainFrameSightViewController, затем создается UINavigationController для помещения в него MainFrameSightViewController, а затем инициализируется MMWindowController для вызова.
- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;
Метод помещает UINavigationController и завершает всю работу по созданию пользовательского интерфейса для интерфейса съемки короткого видео. После завершения съемки войдите в интерфейс публикации. В это время с помощью cycript определите, что текущий контроллер — SightMomentEditViewController. Отсюда у меня возникла идея, не достаточно ли пропустить предыдущий интерфейс съемки и напрямую. войти в интерфейс публикации? Мы сами создаем SightMomentEditViewController и помещаем его в UINavigationController, а затем помещаем этот навигационный контроллер в MMWindowController. **(Я написал настройку здесь для проверки, а конкретные идеи настройки будут написаны позже)** В результате интерфейс публикации действительно может появиться, но NavgationBar панели навигации закрывает оригинал и весь интерфейс. прозрачен, он уродлив, и после завершения публикации весь MMWindowController не может быть уничтожен, и он все равно остается в интерфейсе публикации. Это не тот результат, который нам нужен, но мы много выигрываем. По крайней мере, мы можем напрямую вызывать интерфейс публикации, и небольшие видеоролики можно нормально пересылать. Лично я предполагаю, что причина, по которой текущий интерфейс не может быть уничтожен, заключается в том, что MMWindowController создал новое окно, которое отличается от keyWindow, в котором находится TimeLine. Метод запуска кнопки SightMomentEditViewController не может уничтожить это окно, поэтому у меня есть окно. смелое предположение. Могу ли я просто отобразить SightMomentEditViewController непосредственно в текущем WCTimeLineViewController?
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
Разве не было бы неплохо показать это вот так? Однако, наблюдая за файлом заголовка SightMomentEditViewController в сочетании с элементами интерфейса при выпуске короткого видео, можно предположить, что для создания этого контроллера требуется как минимум два атрибута: один — это путь к короткому видео, а другой — миниатюра короткого видео. Эти два ключа. Если атрибут присвоен SightMomentEditViewController, он должен отображаться нормально.
SightMomentEditViewController *editSightVC = [[%c(SightMomentEditViewController) alloc] init];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UIImage *image = [[self valueForKey:@"_sightView"] getImage];
[editSightVC setRealMoviePath:localPath];
[editSightVC setMoviePath:localPath];
[editSightVC setRealThumbImage:image];
[editSightVC setThumbImage:image];
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
Интерфейс публикации короткого видео может отображаться нормально, и все функции могут использоваться нормально. Единственная проблема заключается в том, что кнопка возврата не действует и SightMomentEditViewController не может быть уничтожен. Используйте cycript, чтобы проверить actionEvent левой кнопки и обнаружить, что ее функция ответа - (void)popSelf;
Нажатие левой кнопки для возврата запускает метод pop, но этого контроллера нет в navgationController, поэтому мы недействительны. нужно переделать этот метод.
- (void)popSelf
{
[self dismissViewControllerAnimated:YES completion:^{
}];
}
В это время нажмите кнопку возврата, чтобы выйти в обычном режиме. Кроме того, в WCContentItemViewTemplateNewSight был обнаружен метод - (void)sendSightToFriend;
который может напрямую пересылать короткие видеоролики друзьям. .
Пересылка коротких видеороликов поддерживает 4 функции: пересылка в «Моменты», пересылка друзьям, сохранение в локальный фотоальбом и копирование ссылки на короткое видео на монтажный стол. Если короткое видео не загружено, при длительном нажатии будет показана только URL-ссылка короткого видео.
Здесь нам нужно подключить два класса, а именно WCContentItemViewTemplateNewSight и SightMomentEditViewController. Подключите метод onLongTouch в WCContentItemViewTemplateNewSight, а затем добавьте всплывающее меню и по очереди добавьте методы ответа. Конкретный код выглядит следующим образом:
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UISaveVideoAtPathToSavedPhotosAlbum(localPath, nil, nil, nil);
}
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UISaveVideoAtPathToSavedPhotosAlbum(localPath, nil, nil, nil);
SightMomentEditViewController *editSightVC = [[%c(SightMomentEditViewController) alloc] init];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UIImage *image = [[self valueForKey:@"_sightView"] getImage];
[editSightVC setRealMoviePath:localPath];
[editSightVC setMoviePath:localPath];
[editSightVC setRealThumbImage:image];
[editSightVC setThumbImage:image];
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
[self sendSightToFriend];
UIMenuController *menuController = [UIMenuController sharedMenuController];
if (menuController.isMenuVisible) return;//防止出现menu闪屏的情况
[self becomeFirstResponder];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
BOOL isExist =[[NSFileManager defaultManager] fileExistsAtPath:localPath];
UIMenuItem *retweetMenuItem = [[UIMenuItem alloc] initWithTitle:@"朋友圈" action:@selector(SLRetweetSight)];
UIMenuItem *saveToDiskMenuItem = [[UIMenuItem alloc] initWithTitle:@"保存到相册" action:@selector(SLSightSaveToDisk)];
UIMenuItem *sendToFriendsMenuItem = [[UIMenuItem alloc] initWithTitle:@"好友" action:@selector(SLSightSendToFriends)];
UIMenuItem *copyURLMenuItem = [[UIMenuItem alloc] initWithTitle:@"复制链接" action:@selector(SLSightCopyUrl)];
if(isExist){
[menuController setMenuItems:@[retweetMenuItem,sendToFriendsMenuItem,saveToDiskMenuItem,copyURLMenuItem]];
}else{
[menuController setMenuItems:@[copyURLMenuItem]];
}
[menuController setTargetRect:CGRectZero inView:self];
[menuController setMenuVisible:YES animated:YES];
Я разместил конкретный файл настройки на github WCSightRetweet.
@interface WCUrl : NSObject
@property(retain, nonatomic) NSString *url;
@end
@interface WCContentItem : NSObject
@property(retain, nonatomic) NSMutableArray *mediaList;
@end
@interface WCDataItem : NSObject
@property(retain, nonatomic) WCContentItem *contentObj;
@end
@interface WCMediaItem : NSObject
@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForSightData;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface WCFacade : NSObject
- (id)getTimelineDataItemOfIndex:(long long)arg1;
@end
@interface WCSightView : UIView
- (id)getImage;
@end
@interface WCContentItemViewTemplateNewSight : UIView{
WCSightView *_sightView;
}
- (WCMediaItem *)iOSREMediaItemFromSight;
- (void)iOSREOnSaveToDisk;
- (void)iOSREOnCopyURL;
- (void)sendSightToFriend;
@end
@interface SightMomentEditViewController : UIViewController
@property(retain, nonatomic) NSString *moviePath;
@property(retain, nonatomic) NSString *realMoviePath;
@property(retain, nonatomic) UIImage *thumbImage;
@property(retain, nonatomic) UIImage *realThumbImage;
- (void)makeInputController;
@end
@interface MMWindowController : NSObject
- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;
- (void)showWindowAnimated:(_Bool)arg1;
@end
@interface WCTimeLineViewController : UIViewController
- (long long)calcDataItemIndex:(long long)arg1;
@end
@interface MMTableViewCell : UIView
@end
@interface MMTableView : UIView
- (id)indexPathForCell:(id)cell;
@end
THEOS_DEVICE_IP = 192.168.0.115//手机所在的IP
include $(THEOS)/makefiles/common.mk
ARCHS = arm64//支持的CPU架构
TWEAK_NAME = WCTimelineSightRetweet
WCTimelineSightRetweet_FILES = Tweak.xm
WCTimelineSightRetweet_FRAMEWORKS = UIKit CoreGraphics//导入系统的framework
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 WeChat"//安装完成杀掉的进程
Управляющий файл не нужно изменять, затем выполните команду make package install
чтобы установить его на мобильный телефон, а затем снова откройте WeChat. Добавлена функция пересылки коротких видеороликов.
Установите макпорты (для процесса установки требуется VPN-соединение, иначе установка не пройдет успешно)
После установки MacPorts откройте терминал и введите sudo port -v selfupdate
чтобы обновить MacPorts до последней версии, что может занять много времени.
После обновления MacPorts установите файл DPKG и введите sudo port -f install dpkg
в терминале.
Загрузите и установите iOSOpendev. Если установка не удалась, вы можете использовать Command + L
чтобы проверить наличие проблем во время установки.
PackageKit: Install Failed: Error Domain=PKInstallErrorDomain Code=112 "运行软件包“iOSOpenDev-1.6-2.pkg”的脚本时出错。" UserInfo={NSFilePath=./postinstall, NSURL=file://localhost/Users/ice/Downloads/iOSOpenDev-1.6-2.pkg#iodsetup.pkg, PKInstallPackageIdentifier=com.iosopendev.iosopendev162.iod-setup.pkg, NSLocalizedDescription=运行软件包“iOSOpenDev-1.6-2.pkg”的脚本时出错。} {
NSFilePath = "./postinstall";
NSLocalizedDescription = "U8fd0U884cU8f6fU4ef6U5305U201ciOSOpenDev-1.6-2.pkgU201dU7684U811aU672cU65f6U51faU9519U3002";
NSURL = "file://localhost/Users/ice/Downloads/iOSOpenDev-1.6-2.pkg#iodsetup.pkg";
PKInstallPackageIdentifier = "com.iosopendev.iosopendev162.iod-setup.pkg";
}
Вот решение: загрузите папку «Спецификации» в iOSOpenDevInstallSolve.
5. Чтобы устранить проблему с ошибкой установки, откройте папку «Спецификации», загруженную на шаге 4. В ней должно быть 8 файлов. Если у вас установлено несколько xcodes, поместите их в соответствующие xcodes.
(1) Четыре файла, начинающиеся с iPhoneOS, помещаются в папку /Application/Xcode/Content/Developer/Platforms/IphoneOS.platform/Developer/Library/Xcode/Specifications (если нет, создайте папку «Спецификации» самостоятельно).
(2) Остальные четыре файла, начиная с iPhone Simulator, помещаются в папку /Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications (если нет, также создайте ее).
(3) Создайте папку usr в папке /Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/ и создайте папку с именем bin в папке usr.
Примечание. Иногда появляется сообщение о том, что установка не удалась. Откройте новый проект в Xcode. Если в меню параметров проекта есть iOSOpenDev, это означает, что установка прошла успешно.
Для установки пакета ipa вы также можете использовать такие инструменты, как itool, но ideviceinstaller может видеть процесс установки, что облегчает нам поиск причины ошибки.
выполнить команду
brew install ideviceinstaller
Если вам будет предложено сообщить, что команда Brew не найдена, это означает, что Homebrew не установлен на вашем Mac.
Распространенные сообщения об ошибках:
ERROR: Could not connect to lockdownd, error code -5
На данный момент просто переустановите libimobiledevice (поскольку ideviceinstaller использует множество других плагинов)
Выполните следующую команду:
$ brew uninstall libimobiledevice
$ brew install --HEAD libimobiledevice
Загрузите инструмент повторной подписи iOS App Signer* (сохраните множество операций командной строки, переподписайте одним щелчком мыши!)*
(3) Загрузите взломанное приложение WeChat.
Поскольку пакет AppStore зашифрован (оболочка) и не может быть повторно подписан, вам придется использовать защищенный пакет. Вы можете использовать dumpdecrypted для самостоятельного дампа оболочки или напрямую использовать помощника PP или itool Assistant для загрузки версии с джейлбрейком. приложения WeChat, которое подверглось обстрелу.
(4) Установите йолиб
yololib может внедрить dylib в двоичный файл WeChat, чтобы ваш хук мог работать эффективно. После загрузки скомпилируйте и получите yololib.
#####(1) Создайте статическую библиотеку. iOSOpendev был установлен на предыдущем шаге. Теперь откройте новый проект в Xcode. Проект iOSOpendev появится в интерфейсе выбора проекта. Здесь нам нужно выбрать проект CaptainHook Tweak. Существует только один новый файл проекта.mm, нам нужно только записать в этот файл все методы перехвата.
Поскольку машины без джейлбрейка не могут устанавливать плагины настройки для подключения оригинальных приложений, таких как машины с джейлбрейком, CaptainHook использует механизм Runtime для реализации таких функций, как определение класса и замена метода с помощью макрокоманд. Вот краткое введение в его использование:
CHDeclareClass(WCContentItemViewTemplateNewSight);
CHDeclareClass(ClassName)
указывает, какой класс необходимо перехватить, и обычно записывается в начале операции над этим классом.
CHDeclareMethod0(id, WCContentItemViewTemplateNewSight, SLSightDataItem){......}
CHDeclareMethod(count, return_type, class_type, name1, arg1, ....)
означает создание нового метода, count означает количество параметров этого метода, return_type означает тип возвращаемого значения, class_type заполняет имя класса этого метода, name1 означает имя метода, arg1 представляет первый параметр, если параметра нет, оставьте его пустым и так далее.
CHOptimizedMethod0(self, void, WCContentItemViewTemplateNewSight, onLongTouch){
CHSuper(0, className, Method);//可选
......
}
CHOptimizedMethod(count, optimization, return_type, class_type, name1, type1, arg1)
означает перехват исходного метода (если CHSuper(0, className, Method)
означает копирование исходного метода, CHSuper означает вызов исходного метода в текущей позиции), count означает количество параметров метода перехвата, оптимизация обычно заполняет себя, return_type — это тип возвращаемого значения метода, class_type заполняет имя класса текущий класс, name1 — имя метода, arg1 — параметр, если параметров нет, заполняем arg и так далее.
CHConstructor
{
@autoreleasepool
{
CHLoadLateClass(WCContentItemViewTemplateNewSight);
CHHook(0, WCContentItemViewTemplateNewSight, onLongTouch);
}
}
Это входная функция CaptainHook. Все подключенные классы должны быть объявлены здесь для загрузки, а методы класса должны быть объявлены здесь.
Затем вы можете написать код в классах и методах. Код слишком длинный, поэтому я не буду его выкладывать на github с помощью MMPlugin.
Этот проект включает в себя функции пересылки коротких видеороликов, автоматического захвата красных конвертов и изменения шагов упражнений WeChat. Функции автоматического захвата красных конвертов и изменения шагов упражнений WeChat можно отключить вручную.
ПРИМЕЧАНИЕ. Если вы используете системные классы, не забудьте импортировать соответствующую библиотеку классов (например, UIKIT) и файлы заголовка, в противном случае ошибка будет сообщена во время компиляции.
После успеха компиляции вы можете найти скомпилированную статическую библиотеку в папке продуктов.
Найдите его в Finder и скопируйте его для последующего использования.
Материалы, которые вы должны иметь в этот момент, включают:
Найдите бинарный файл WeChat из исходного приложения WeChat и скопируйте его для последующего использования.
Выполните следующую команду, чтобы ввести mmplugin.dylib в двоичный файл WeChat.
LeonLei-MBP:WeChat gaoshilei$ ./yololib WeChat MMPlugin.dylib
При выполнении этой команды убедитесь, что Yololib, WeChat и WeChat.App находятся в том же каталоге.
После завершения скопируют mmplugin.dylib и weChat в оригинальный weChat.app, перезаписывая оригинальный файл weChat.
Откройте подписавчик приложения iOS и выберите различные параметры, как показано ниже:
Я выбрал сертификат на уровне предприятия.
Подключитесь к мобильному телефону и выполните следующую команду, чтобы проверить, подключен ли Ideviceinstaller к мобильному телефону:
LeonLei-MBP:WeChat gaoshilei$ ideviceinfo
Если много информации о мобильном телефоне распечатано, это означает, что соединение успешно, и вы можете установить пакет IPA. Выполнить следующую команду для установки:
LeonLei-MBP:WeChat gaoshilei$ ideviceinstaller -i WeChat.ipa
WARNING: could not locate iTunesMetadata.plist in archive !
WARNING: could not locate Payload/WeChat.app/SC_Info/WeChat.sinf in archive !
Copying ' WeChat.ipa ' to device... DONE.
Installing ' com.xxxxxxxxxxxx '
- CreatingStagingDirectory (5%)
- ExtractingPackage (15%)
- InspectingPackage (20%)
- TakingInstallLock (20%)
- PreflightingApplication (30%)
- InstallingEmbeddedProfile (30%)
- VerifyingApplication (40%)
- CreatingContainer (50%)
- InstallingApplication (60%)
- PostflightingApplication (70%)
- SandboxingApplication (80%)
- GeneratingApplicationMap (90%)
- Complete
После завершения установки откройте WeChat на вашем телефоне, чтобы попробовать новые функции, которые мы добавили! Если застряла определенная ссылка, сообщается об ошибке. Пожалуйста, смотрите визуализации: