Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to hook 'BSD socket' with fishhook tool? #1

Open
0x4d4746h opened this issue Jul 17, 2017 · 19 comments
Open

How to hook 'BSD socket' with fishhook tool? #1

0x4d4746h opened this issue Jul 17, 2017 · 19 comments
Assignees

Comments

@0x4d4746h
Copy link

Anybody succeed for connect() and send() methods?

@0x4d4746h
Copy link
Author

Test on the real phone. Not simulator

@aozhimin
Copy link
Owner

@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

@aozhimin
Copy link
Owner

And I will confirm in the future whether it is true, thanks @0x4d4746h

@aozhimin aozhimin self-assigned this Jul 19, 2017
@aozhimin
Copy link
Owner

aozhimin commented Sep 2, 2017

@0x4d4746h I have tested in the machine and simulator, we can hook the connect() and end() methods with fishhook. My test as follows:

image

image

@vzrao
Copy link

vzrao commented Sep 29, 2017

@aozhimin I am able to hook send() but not connect(). Can pls you confirm?

@aozhimin
Copy link
Owner

@vzrao I'm sure it's ok. I don't know where your problem is.

@vzrao
Copy link

vzrao commented Oct 2, 2017

@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!

@aozhimin
Copy link
Owner

@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]));
    }
}

@vzrao
Copy link

vzrao commented Oct 13, 2017

@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!

@aozhimin
Copy link
Owner

@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:

image

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

@vzrao
Copy link

vzrao commented Oct 16, 2017

@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

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration ];
    static NSString * const kWANIPQueryUrl = @"http://ifconfig.co/ip";
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:kWANIPQueryUrl]
    cachePolicy:NSURLRequestUseProtocolCachePolicy
    timeoutInterval:3];
    [[session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSLog(@"response:%@", response);
    }] resume];
    }

**main.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#include <arpa/inet.h>
#include "fishhook.h"

//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]));
}
}

@aozhimin
Copy link
Owner

@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:
image
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.

@aozhimin
Copy link
Owner

aozhimin commented Nov 2, 2017

@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.

@aozhimin
Copy link
Owner

aozhimin commented Nov 3, 2017

@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.

@aozhimin
Copy link
Owner

aozhimin commented Nov 3, 2017

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"

@vzrao
Copy link

vzrao commented Nov 3, 2017

@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

@aozhimin
Copy link
Owner

aozhimin commented Nov 4, 2017

@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.

@vzrao
Copy link

vzrao commented Nov 18, 2017

@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.

@aozhimin
Copy link
Owner

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants