Open 0x4d4746h opened 7 years ago
Test on the real phone. Not simulator
@0x4d4746h I'm not sure, according to some person's tests, the library fishhook of Facebook,Can't hook function of BSD socket like connect()
, so probably Apple modified the underlying implementation, without a standard way of BSD socket
And I will confirm in the future whether it is true, thanks @0x4d4746h
@0x4d4746h I have tested in the machine and simulator, we can hook the connect()
and end()
methods with fishhook. My test as follows:
@aozhimin I am able to hook send() but not connect(). Can pls you confirm?
@vzrao I'm sure it's ok. I don't know where your problem is.
@aozhimin Is it possible to share the related .h and .m files? If not, where are these hook_xxx functions defined, in AppDelegate.m or some other individual file? Thanks!
@vzrao I'm so sorry see your reply now. In fact, you can declare these hook_xxx
methods in any files, Here is the sample code:
static int (*orig_connect)(int, const struct sockaddr *, socklen_t);
static ssize_t (*orig_send)(int, const void *, size_t, int);
int hook_connect(int socket, const struct sockaddr *addr, socklen_t address_len) {
char ipString[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), ipString, INET_ADDRSTRLEN);
NSLog(@"ip:%s", ipString);
return orig_connect(socket, addr, address_len);
}
ssize_t hook_send(int socket, const void * buffer, size_t length, int flags) {
NSLog(@"send:%s", buffer);
return orig_send(socket, buffer, length, flags);
}
int main(int argc, char * argv[]) {
@autoreleasepool {
struct rebinding socket_rebinding = {"connect", hook_connect, (void *)&orig_connect };
struct rebinding send_rebinding = {"send", hook_send, (void *)&orig_send };
rebind_symbols((struct rebinding[2]){socket_rebinding, send_rebinding}, 2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
@aozhimin thank you for replying. I am still not able to see connect() got hooked.
my code is to download some pictures like below:
NSURLSessionDataTask imageTask = [session dataTaskWithRequest:request completionHandler:
^(NSData data, NSURLResponse response, NSError error) {
if( nil != response ){
if (data) {
NSLog(@"Fetching image succeed");
// process the fetched image
}else{
NSLog(@"Fetching image failed");
}
}else{
NSLog(@"Fetching image failed");
}
}];
[imageTask resume];
send() and recvmsg() were hooked/observed. Since these functions use the same header <sys/socket.h> as connect(), they should be in the same library.
will connect() happen every time there is network/http request? I doubt connect() is not called at all in my case. But NSURLSessionDataTask eventually will use BSD functions, right?
Thanks!
@vzrao According to my test, when use the NSURLSession API to make HTTP requests, the BSD socket functions will eventually be called, such as connect()
function.
Here is my test code:
static NSString * const kWANIPQueryUrl = @"http://ifconfig.co/ip";
NSURLRequest *request = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:3];
[[session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"response:%@", response);
}] resume];
and here is the code for hook method of BSD socket:
static int (*orig_connect)(int, const struct sockaddr *, socklen_t);
static ssize_t (*orig_send)(int, const void *, size_t, int);
int hook_connect(int socket, const struct sockaddr *addr, socklen_t address_len) {
char ipString[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(((struct sockaddr_in*)addr)->sin_addr), ipString, INET_ADDRSTRLEN);
NSLog(@"ip:%s", ipString);
return orig_connect(socket, addr, address_len);
}
ssize_t hook_send(int socket, const void * buffer, size_t length, int flags) {
NSLog(@"send:%s", buffer);
return orig_send(socket, buffer, length, flags);
}
int main(int argc, char * argv[]) {
@autoreleasepool {
struct rebinding socket_rebinding = {"connect", hook_connect, (void *)&orig_connect };
struct rebinding send_rebinding = {"send", hook_send, (void *)&orig_send };
rebind_symbols((struct rebinding[2]){socket_rebinding, send_rebinding}, 2);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
The following is the output of the Xcode console:
2017-10-16 20:48:25.457 Debug[1964:43193] ip:188.113.88.193
The following screenshot is from Charles:
The IP address 188.113.88.193
is the same as Charles' remote address, so it is proved that the NSURLSession API will eventually call the BSD Socket function.
The following is a backtrace info:
* thread #11, queue = 'com.apple.networking.connection.0x7fa47bc834c0', stop reason = breakpoint 20.1
* frame #0: 0x0000000105a54aa9 Debug`hook_connect(socket=18, addr=0x00007fa47be21988, address_len=16) at main.m:32
frame #1: 0x00000001084bbd32 libsystem_network.dylib`tcp_connection_destination_perform_socket_connect + 787
frame #2: 0x000000010848d25b libsystem_network.dylib`tcp_connection_handle_destination_prepare_complete + 62
frame #3: 0x00000001084bb5b0 libsystem_network.dylib`tcp_connection_destination_start + 421
frame #4: 0x000000010848c712 libsystem_network.dylib`tcp_connection_start_destination + 84
frame #5: 0x000000010848c523 libsystem_network.dylib`tcp_connection_handle_start_next_destination_helper + 198
frame #6: 0x000000010848c649 libsystem_network.dylib`tcp_connection_handle_start_next_destination + 97
frame #7: 0x000000010848dc1c libsystem_network.dylib`__tcp_connection_start_host_block_invoke_3 + 104
frame #8: 0x00000001084b183b libsystem_network.dylib`tcp_connection_host_resolve_result + 1849
frame #9: 0x00000001083e524e libsystem_dnssd.dylib`handle_addrinfo_response + 509
frame #10: 0x00000001083e33f5 libsystem_dnssd.dylib`DNSServiceProcessResult + 665
frame #11: 0x000000010827d49b libdispatch.dylib`_dispatch_client_callout + 8
frame #12: 0x00000001082708a5 libdispatch.dylib`_dispatch_source_latch_and_call + 1750
frame #13: 0x000000010826b830 libdispatch.dylib`_dispatch_source_invoke + 1057
frame #14: 0x00000001082637fb libdispatch.dylib`_dispatch_queue_drain + 1818
frame #15: 0x0000000108262ea9 libdispatch.dylib`_dispatch_queue_invoke + 601
frame #16: 0x0000000108265af2 libdispatch.dylib`_dispatch_root_queue_drain + 1420
frame #17: 0x0000000108265561 libdispatch.dylib`_dispatch_worker_thread3 + 111
frame #18: 0x00000001085ae5a2 libsystem_pthread.dylib`_pthread_wqthread + 1299
frame #19: 0x00000001085ae07d libsystem_pthread.dylib`start_wqthread + 13
@aozhimin Thank you Zhimin. Are you testing connect() on real device? I was able to see connect() get hooked on emulator but not on real iPhone.
what kind of device you test on? Mine is an iPhone 6s with iOS 10.3.3(14G60).
I just copied and pasted your code into main.m and ViewController.m (viewDidLoad) with minimum modification:
ViewController.m
**main.m
//int main(int argc, char * argv[]) { // @autoreleasepool { // return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); // } //}
static int (orig_connect)(int, const struct sockaddr , socklen_t);
static ssize_t (orig_send)(int, const void , size_t, int);
int hook_connect(int socket, const struct sockaddr addr, socklen_t address_len) { char ipString[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(((struct sockaddr_in)addr)->sin_addr), ipString, INET_ADDRSTRLEN); NSLog(@"ip:%s", ipString); return orig_connect(socket, addr, address_len); }
ssize_t hook_send(int socket, const void * buffer, size_t length, int flags) { NSLog(@"send:%s", buffer); return orig_send(socket, buffer, length, flags); }
int main(int argc, char argv[]) { @autoreleasepool { struct rebinding socket_rebinding = {"connect", hook_connect, (void )&orig_connect }; struct rebinding send_rebinding = {"send", hook_send, (void *)&orig_send }; rebind_symbols((struct rebinding[2]){socket_rebinding, send_rebinding}, 2); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
@vzrao Yep, the above test was tested on the simulator, it is only part of the test. I also tested on the real machine. iPhone 5s with iOS 8.1.3.
The following figure shows the output of the console:
We still see the output like ip:
, but did not see 188.113.88.193
. so we hooked BSD
connect()
function successfully, but the NSURLSession API may not call BSD Socket functions.
I will confirm this conclusion later, and then reply to you.
@vzrao
People have had issues with socket and/or connect in the past. One problem is that fishhook can only hook external calls, which means function calls within the same library generally cannot be hooked. In this case, calls to socket and connect from within the same library (libSystem) cannot be hooked. With the simulator, the system libraries are broken out into many sub-libraries, including libsystem_network.dylib. With many sub-libraries, this means function calls from one sub-library to another can be hooked on the simulator, but on device where there's just a single libSystem, those same calls are within the same library and cannot be hooked.
@vzrao There are some additional explanations. The following is the assembly code of connect()
function in iOS simulator:
0x106cc1558 <+255>: movl %eax, -0xac(%rbp)
0x106cc155e <+261>: movl $0x0, -0xa8(%rbp)
0x106cc1568 <+271>: leaq -0xb0(%rbp), %rsi
0x106cc156f <+278>: movl $0x20, %edx
0x106cc1574 <+283>: movl %ebx, %edi
0x106cc1576 <+285>: callq 0x106ceed7a ; symbol stub for: connect
However, The following is in SE(10.3.2):
0x18aa165c8 <+312>: str w8, [sp, #0x4c]
0x18aa165cc <+316>: stp xzr, xzr, [sp, #0x58]
0x18aa165d0 <+320>: str xzr, [sp, #0x50]
0x18aa165d4 <+324>: orr w2, wzr, #0x20
0x18aa165d8 <+328>: add x1, sp, #0x48 ; =0x48
0x18aa165dc <+332>: mov x0, x19
0x18aa165e0 <+336>: bl 0x18a9bc03c ; __connect
0x18aa165e4 <+340>: cbnz w0, 0x18aa16504 ; <+116>
As you can see, unlike the iOS simulator, there isn't symbol stub for: connect
, it call connect()
use bl and direct address, this is why can't hook connect()
in the iDevice.
As for send()
function:
int ret ;
Dl_info dylib_info;
ssize_t (*fun_send)(int, const void *, size_t, int) = send;
if ((ret = dladdr(fun_send, &dylib_info))) {
NSLog(@"sendlib :%s", dylib_info.dli_fname);
}
and the output of NSLog statement is as follows:
Printing description of dylib_info.dli_fname:
(const char *) dli_fname = 0x000000018a8d06f0 "/usr/lib/system/libsystem_c.dylib"
@aozhimin Thank you Zhimin! The high level purpose is to let all the network traffic of specific apps to go through a proxy server (where bytes can be counted). We've looked at Network Extension but it requires managed devices (MDM).
Any advice?
Thanks! Rao
@vzrao NEAppProxyTCPFlow
?
APPL Sample Code
Maybe you can try Network Extension.
I am trying to solve it by CFNetwork
. If I have any progress will inform you immediately.
@aozhimin
Thank you Zhimin. We tried Personal VPN and all the traffic of our app did went through our proxy. But the problem is that if there are other apps running in background at the same time, those traffic will go through the proxy as well, which is not what we want.
@vzrao
I'm not sure if the Network Extension can filter other app traffic.
I tried to hook CFStreamCreatePairWithSocket()
, this works when invoke this method directly like CocoaAsyncSocket,but not work in NSURLSession API.
Finally I confirmed that socket is related to the stream(CFStream or NSStream), but I have not found a breakthrough.
Anybody succeed for connect() and send() methods?