-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathreverse-engineering-ios-facebook-messenger-chat.html
325 lines (281 loc) · 16.9 KB
/
reverse-engineering-ios-facebook-messenger-chat.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://use.fontawesome.com/afd448ce82.js"></script>
<!-- Meta Tag -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- SEO -->
<meta name="author" content="Bruno Rocha">
<meta name="keywords" content="Software, Engineering, Blog, Posts, iOS, Xcode, Swift, Articles, Tutorials, OBJ-C, Objective-C, Apple">
<meta name="description" content="Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools.">
<meta name="title" content="iOS Security: Reverse Engineering Messenger's Chat Bubbles">
<meta name="url" content="https://swiftrocks.com/reverse-engineering-ios-facebook-messenger-chat">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="iOS Security: Reverse Engineering Messenger's Chat Bubbles"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta property="og:description" content="Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/reverse-engineering-ios-facebook-messenger-chat"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb.jpg?4"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="iOS Security: Reverse Engineering Messenger's Chat Bubbles"/>
<meta name="twitter:description" content="Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools."/>
<meta name="twitter:site" content="@rockbruno_"/>
<!-- Favicon -->
<link rel="icon" type="image/png" href="images/favicon/iconsmall2.png" sizes="32x32" />
<link rel="apple-touch-icon" href="images/favicon/iconsmall2.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap" rel="stylesheet">
<!-- Bootstrap CSS Plugins -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
<!-- Prism CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/prism4.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style48.css">
<link rel="stylesheet" type="text/css" href="css/sponsor4.css">
<!-- HTML5 shiv and Respond.js support IE8 or Older for HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://swiftrocks.com/reverse-engineering-ios-facebook-messenger-chat"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb.jpg"
],
"datePublished": "2016-11-07T13:42:07+00:00",
"dateModified": "2020-04-12T14:00:00+02:00",
"author": {
"@type": "Person",
"name": "Bruno Rocha"
},
"publisher": {
"@type": "Organization",
"name": "SwiftRocks",
"logo": {
"@type": "ImageObject",
"url": "https://swiftrocks.com/images/thumbs/thumb.jpg"
}
},
"headline": "iOS Security: Reverse Engineering Messenger's Chat Bubbles",
"abstract": "Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools."
}
</script>
</head>
<body>
<div id="main">
<!-- Blog Header -->
<!-- Blog Post (Right Sidebar) Start -->
<div class="container">
<div class="col-xs-12">
<div class="page-body">
<div class="row">
<div><a href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2light.png">
</a>
<div class="menu-large">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-large">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
<div class="menu-small">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-1">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-2">
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
</div>
<div class="content-page" id="WRITEIT_DYNAMIC_CONTENT">
<!--WRITEIT_POST_NAME=iOS Security: Reverse Engineering Messenger's Chat Bubbles-->
<!--WRITEIT_POST_HTML_NAME=reverse-engineering-ios-facebook-messenger-chat-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2016-11-07T13:42:07+00:00-->
<!--Add here the additional properties that you want each page to possess.-->
<!--These properties can be used to change content in the template page or in the page itself as shown here.-->
<!--Properties must start with 'WRITE_IT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools.-->
<title>iOS Security: Reverse Engineering Messenger's Chat Bubbles</title>
<div class="blog-post">
<div class="post-title-index">
<h1>iOS Security: Reverse Engineering Messenger's Chat Bubbles</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 07 Nov 2016
</div>
</div>
<blockquote class="margin-top-40 margin-bottom-40">
<p>This article is SwiftRocks's slimmed down version of my original <a href="https://medium.com/@brunorochaesilva/how-i-hacked-messengers-ios-app-custom-chat-bubble-colors-12f1ac7f070c">2016 Medium article.</a> The Medium article is badly written and contains mistakes, but it has more information on how to reproduce each step.</p>
<p>Update: As of May 3, 2018, this sadly doesn't seem to work anymore. My web chats returned to the blue color and the custom changes only apply locally. I guess color changes are validated on the backend now. Damn you Zuckerberg!</p>
</blockquote>
<p>Due do the messaging nature of the Objective-C runtime, a great amount of information about your code can be extracted and manipulated during runtime by external tools. The <b>Selectors</b> you reference everyday are nothing more than exposed strings in your code, despite what Swift's <b>#selector</b> abstraction might imply. With the right tools, any production app can be messed with just like it's Xcode debugging counterpart.</p>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Facebook's Messenger app is an interesting app to test this kind of concept. It contains many minigames and features that change the app's layout globally - as in, they are not client side changes, everyone can see what you do both in the app and the website. These features contain zero security measures, after all, it's not like you can cause damage with them. That makes them great targets for learning iOS security concepts.</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/QoQW4zL.jpg" alt="">
</div>
<p>The feature I'll inspect in this article is the chat bubble color selector. It allows you to change the color of a chat (duh) to a few pre-determined colors. But what if I want to use my own custom color instead?</p>
<p><a href="http://www.cycript.org/">Cycript</a> is a debugger with a twist: you can easily print, create and call Objective-C methods without any of the complexity or pain you might experience while using <b>gdb or lldb</b>. Combined with a jailbroken iPad with OpenSSH installed, you can treat app like it's source code was available right in front of you.</p>
<pre class="command-line language-bash" data-host="MyiPad" , data-prompt="cy#" data-output="1-2"><code>ssh [email protected]
cycript -p Messenger
var root = [UIApplication sharedApplication].keyWindow.rootViewController
MNModalHostViewController</code></pre>
<p>The iPad currently has the color selection action sheet opened. If Facebook's engineers are good with naming conventions, this means that following down <b>root</b>'s hierarchy will eventually lead us to a view whose's name might contain some combination of the the words "Color", "Selector", "Chat" and "Bubble". In Cycript, you can see a view's properties by putting a <b>*</b> before it.</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/u4Ne4KF.png" alt="">
<p>The second item of the <b>childViewControllers</b> array is a <b>MNActionSheetViewController</b>. The color selection screen is the only thing opened on the iPad, so that must be it.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/546IYgV.png" alt="">
<p>The actionSheet's (created with <b>var actionSheet = root.childViewControllers[1]</b>) childViewController is a <b>MNThreadCustomizationPickerViewController</b>. No idea what it means, but the name is promising.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/x6jr7pT.png" alt="">
<p>The PickerViewController contains an internal <b>pickerView</b>, as expected. There are two here, but (spoilers) the right one is the <b>FBPickerView</b>.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/Ww6NlIS.png" alt="">
<p>The pickerView contains a lookup table with 15 elements (we have 15 colors), which is how I suppose they know which colors to show in the action sheet. It also contains a collectionView. In this case, the easier way to manipulate the colors ended up accessing the cells directly.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/B1mdxu9.png" alt="">
<p>As expected, the collectionView contains 15 cells.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/uNVBUVq.png" alt="">
<p>Here, I picked a random cell (calling it blueCell). It seems that each cell has a <b>button (FBPickerViewButton)</b> property.</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/iTblMYO.png" alt="">
<p>And each FBPickerViewButton contains an <b>item (FBPickerViewItem)</b></p>
</div>
<p>Unfortunately, it turns out that a FBPickerViewItem's properties don't contain anything useful involving chat colors. The actual color information ended up being in it's <b>init</b> method.</p>
<p>Let's pretend I didn't know that. To print a class's methods, we can use of the following snippet:</p>
<pre class="language-js"><code>function printMethods(className) {
var count = new new Type(“I”);
var methodsArray = [];
for(var i = 0; i < *count; i++) {
var method = methods[i];
methodsArray.push({selector:method_getName(method)});
}
free(methods);
return methodsArray;
}</code></pre>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/dGGjDkV.png" alt="">
<p>The result of calling <b>printMethods("FBPickerViewItem")</b>. There it is!</p>
</div>
<p>In order to intercept the initialization of this class, we can use <a href="http://nshipster.com/method-swizzling/">Method Swizzling</a>. Cydia Substrate can be installed on a jailbroken device to provide helpers specific for this purpose.</p>
<pre class="language-js"><code>@import com.saurik.substrate.MS
var _setColor_pointer = {};
MS.hookMessage(FBPickerItem, @selector(initWithColor:accessibilityTitle:accessibilityHint:isSelected:isSelectable:), function(arg0) {
return _setColor_pointer->call(this,[UIColor blackColor],”a”,”b”,false,true);
}, _setColor_pointer);</code></pre>
<p>Basically, our swizzled method calls the original implementation, but with other arguments. In this case, I force the color to be black. If I close the action sheet and open it again, the result is...</p>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/IWIRUiy.jpg" alt="">
<p>The colors changed, but will it actually work?</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/Ow2DDZu.png" alt="">
<p>It works locally, but will it work if I open Messenger somewhere else?</p>
</div>
<div class="post-image margin-top-40 margin-bottom-40">
<img src="https://i.imgur.com/0W3zbIH.png" alt="">
<p>Yes! If they have analytic events for these colors, someone at Facebook will be very confused trying to figure out why there's some random guy with a black chat.</p>
</div>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Hopefully this can give you an idea of how easily your app's data can be tampered it. Be careful with what's inside your binary - no matter how hard you try to hide it, someone who tries hard enough will eventually find it - specially if they are being paid to screw you up. Do not try to reinvent the wheel, use actual, market proven security measures.</p>
</div></div>
<div class="blog-post footer-main">
<div class="footer-logos">
<a href="https://swiftrocks.com/rss.xml"><i class="fa fa-rss"></i></a>
<a href="https://twitter.com/rockbruno_"><i class="fa fa-twitter"></i></a>
<a href="https://github.com/rockbruno"><i class="fa fa-github"></i></a>
</div>
<div class="footer-text">
© 2025 Bruno Rocha
</div>
<div class="footer-text">
<p><a href="https://swiftrocks.com">Home</a> / <a href="blog">See all posts</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Blog Post (Right Sidebar) End -->
</div>
</div>
</div>
<!-- All Javascript Plugins -->
<script type="text/javascript" src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism4.js"></script>
<!-- Main Javascript File -->
<script type="text/javascript" src="js/scripts30.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H8KZTWSQ1R"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-H8KZTWSQ1R');
</script>
</body>
</html>