├── LICENSE ├── test ├── aa.class ├── test.class └── test.java ├── README ├── type.h ├── vm_error.h ├── Makefile ├── libelf.h ├── trace.h ├── vm_error.c ├── safe_printf.h ├── list.h ├── log.h ├── safe_printf.c ├── jvm.c ├── log.c ├── jvm.h ├── libelf.c ├── trace.c ├── bytecode.h └── classloader.c /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/aa.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rednaxelafx/ajvm/HEAD/test/aa.class -------------------------------------------------------------------------------- /test/test.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rednaxelafx/ajvm/HEAD/test/test.class -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A hobby jvm, just want to know how a java virtual machine works. 2 | 3 | Author wzt http://www.cloud-sec.org 4 | -------------------------------------------------------------------------------- /type.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPE_H 2 | #define TYPE_H 3 | 4 | typedef unsigned int u4; 5 | typedef unsigned short u2; 6 | typedef unsigned char u1; 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /vm_error.h: -------------------------------------------------------------------------------- 1 | #ifndef VM_ERROR_H 2 | #define VM_ERROR_H 3 | 4 | #define VM_ERROR_CLASS_FILE 0 5 | #define VM_ERROR_MEMORY 1 6 | #define VM_ERROR_INTERP 2 7 | 8 | void jvm_warning(int flag, char *fmt, ...); 9 | void jvm_error(int flag, char *fmt, ...); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: wvm 2 | 3 | CC = gcc 4 | CFLAGS = -fno-stack-protector -g 5 | 6 | .c.o: $(CC) $(CFLAGS) \ 7 | -c -o $*.o $< 8 | 9 | OBJS = jvm.o classloader.o interp_engine.o vm_error.o trace.o libelf.o safe_printf.o log.o 10 | 11 | wvm: $(OBJS) 12 | $(CC) -o wvm $(OBJS) -lpthread 13 | 14 | clean: 15 | rm -f wvm *.o 16 | 17 | -------------------------------------------------------------------------------- /test/test.java: -------------------------------------------------------------------------------- 1 | class aa { 2 | int a = 6; 3 | 4 | int debug(int a, int b) 5 | { 6 | int sum; 7 | 8 | sum = a + b; 9 | 10 | return sum; 11 | } 12 | } 13 | 14 | public class test { 15 | public static void main(String args[]) { 16 | int a; 17 | 18 | aa bb = new aa(); 19 | a = bb.debug(1, 2); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libelf.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBELF_H 2 | #define LIBELF_H 3 | 4 | #include 5 | #include "trace.h" 6 | 7 | int elf_init(const char *binary_path); 8 | void elf_exit(void); 9 | int check_elf_header(Elf64_Ehdr *elf_hdr); 10 | int parse_elf_section_name(Elf64_Ehdr *elf_hdr); 11 | int parse_elf_symbol_table(Elf64_Ehdr *elf_hdr); 12 | int __parse_elf_section(Elf64_Ehdr *elf_hdr); 13 | int parse_elf_section(const char *binary_path); 14 | int parse_elf_symbol(const char *binary_path); 15 | int search_symbol_by_addr(uint64_t rip, CALL_TRACE *trace); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /trace.h: -------------------------------------------------------------------------------- 1 | #ifndef TRACE_H 2 | #define TRACE_H 3 | 4 | #define X86_64 5 | 6 | #define MAX_TRACE_NUM 128 7 | 8 | #define GET_BP(x) asm("movq %%rbp, %0":"=r"(x)); 9 | 10 | typedef struct trace_st { 11 | unsigned long rip; 12 | char *symbol_name; 13 | unsigned long symbol_addr; 14 | unsigned int offset; 15 | unsigned int size; 16 | }CALL_TRACE; 17 | 18 | unsigned long *top_bp; 19 | void calltrace(void); 20 | void calltrace_gnu(void); 21 | void show_calltrace(CALL_TRACE *trace); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /vm_error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vm_error.c (c) wzt 2012, 2013 http://www.cloud-sec.org 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2, 7 | * or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "jvm.h" 25 | #include "log.h" 26 | #include "trace.h" 27 | #include "vm_error.h" 28 | 29 | void jvm_warning(int flag, char *fmt, ...) 30 | { 31 | va_list arg; 32 | char buf[1024]; 33 | 34 | va_start(arg, fmt); 35 | vsprintf(buf, fmt, arg); 36 | va_end(arg); 37 | 38 | switch (flag) { 39 | case VM_ERROR_CLASS_FILE: 40 | printf("%s", buf); 41 | break; 42 | case VM_ERROR_MEMORY: 43 | printf("%s", buf); 44 | break; 45 | case VM_ERROR_INTERP: 46 | printf("%s", buf); 47 | break; 48 | default: 49 | printf("VM Error: Unkown flag.\n"); 50 | break; 51 | } 52 | 53 | calltrace(); 54 | } 55 | 56 | void jvm_error(int flag, char *fmt, ...) 57 | { 58 | va_list arg; 59 | char buf[1024]; 60 | 61 | va_start(arg, fmt); 62 | vsprintf(buf, fmt, arg); 63 | va_end(arg); 64 | 65 | switch (flag) { 66 | case VM_ERROR_CLASS_FILE: 67 | printf("%s", buf); 68 | break; 69 | case VM_ERROR_MEMORY: 70 | printf("%s", buf); 71 | break; 72 | case VM_ERROR_INTERP: 73 | printf("%s", buf); 74 | break; 75 | default: 76 | printf("VM Error: Unkown flag.\n"); 77 | break; 78 | } 79 | 80 | calltrace(); 81 | mmap_exit(); 82 | calltrace_exit(); 83 | exit(0); 84 | } 85 | -------------------------------------------------------------------------------- /safe_printf.h: -------------------------------------------------------------------------------- 1 | #ifndef SAFE_PRINTF_H 2 | #define SAFE_PRINTF_H 3 | 4 | typedef struct func_arg { 5 | long arg[6]; 6 | long va_arg; 7 | int arg_idx; 8 | }FUNC_ARG; 9 | 10 | #define INIT_ARG(func_arg) \ 11 | do { \ 12 | func_arg.arg_idx = 1; \ 13 | } while (0); 14 | 15 | #define GET_ARG(func_arg) \ 16 | do { \ 17 | asm("movq %%rdi, %0":"=r"(func_arg.arg[0])); \ 18 | asm("movq %%rsi, %0":"=r"(func_arg.arg[1])); \ 19 | asm("movq %%rdx, %0":"=r"(func_arg.arg[2])); \ 20 | asm("movq %%rcx, %0":"=r"(func_arg.arg[3])); \ 21 | asm("movq %%r8, %0":"=r"(func_arg.arg[4])); \ 22 | asm("movq %%r9, %0":"=r"(func_arg.arg[5])); \ 23 | } while (0); 24 | 25 | #define VA_START_ARG(func_arg) \ 26 | do { \ 27 | asm("movq %%rbp, %%rax\n\t" \ 28 | "addq $0x10, %%rax\n\t" \ 29 | "movq %%rax, %0":"=r"(func_arg.va_arg)); \ 30 | } while (0); 31 | 32 | #define VA_NEXT_ARG(value, func_arg, type) \ 33 | do { \ 34 | if (func_arg.arg_idx <= 5) { \ 35 | value = (type)func_arg.arg[func_arg.arg_idx++]; \ 36 | } \ 37 | else { \ 38 | value = *(type *)(func_arg.va_arg); \ 39 | func_arg.va_arg += sizeof(long); \ 40 | } \ 41 | } while (0); 42 | 43 | void safe_printf(const char *fmt, ...); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | #include 5 | 6 | struct list_head { 7 | struct list_head *prev, *next; 8 | }; 9 | 10 | 11 | #define INIT_LIST_HEAD(name_ptr) do { (name_ptr)->next = (name_ptr); \ 12 | (name_ptr)->prev = (name_ptr); \ 13 | }while (0) 14 | 15 | 16 | #define OFFSET(type, member) (char *)&(((type *)0x0)->member) 17 | #define container_of(ptr, type, member) ({(type *)((char * )ptr - OFFSET(type, member)); }); 18 | 19 | #define list_for_each(pos, head) for (pos = head->next; pos != head; pos = pos->next) 20 | #define list_for_each_prev(pos, head) for (pos = head->prev; pos != head; pos = pos->prev) 21 | #define list_for_each_safe(pos, n, head) for (pos = head->next, n = pos->next; pos != head; pos = n, n = pos->next) 22 | #define list_entry(ptr, type, member) container_of(ptr, type, member) 23 | 24 | static inline void list_add_tail(struct list_head *new, struct list_head *head) 25 | { 26 | head->prev->next = new; 27 | new->prev = head->prev; 28 | new->next = head; 29 | head->prev = new; 30 | } 31 | 32 | static inline void list_add_tail1(struct list_head *new, struct list_head *head) 33 | { 34 | new->next = head; 35 | new->prev = head->prev; 36 | head->prev->next = new; 37 | head->prev = new; 38 | } 39 | 40 | static inline void list_add(struct list_head *new, struct list_head *head) 41 | { 42 | new->next = head->next; 43 | new->prev = head; 44 | head->next->prev = new; 45 | head->next = new; 46 | } 47 | 48 | static inline void list_del(struct list_head *p) 49 | { 50 | p->prev->next = p->next; 51 | p->next->prev = p->prev; 52 | } 53 | 54 | static inline int list_empty(struct list_head *head) 55 | { 56 | return head->next == head; 57 | } 58 | 59 | #define FREE_LIST(type, link_head) { \ 60 | type *p = NULL; \ 61 | struct list_head *s = NULL; \ 62 | struct list_head *q = NULL; \ 63 | for (s = (&link_head)->next; s != &link_head; s = q) { \ 64 | if (!s) \ 65 | return ; \ 66 | q = s->next; \ 67 | p = list_entry(s, type, list); \ 68 | if (p) { \ 69 | list_del(s); \ 70 | free(p); \ 71 | p = NULL; \ 72 | } \ 73 | }} 74 | 75 | #define FREE_LIST_SAFE(type, link_head) { \ 76 | { \ 77 | type *p = NULL; \ 78 | struct list_head *s = NULL; \ 79 | struct list_head *q = NULL; \ 80 | list_for_each_safe(s, q, (&link_head)) { \ 81 | p = list_entry(s, type, list); \ 82 | if (p) { \ 83 | list_del(s); \ 84 | free(p); \ 85 | } \ 86 | }} \ 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * log.h - Zhitong Wang 2012, 2013 3 | */ 4 | 5 | #ifndef LOG_H 6 | #define LOG_H 7 | 8 | #include 9 | #include 10 | 11 | #define LOG_NUM 10 12 | #define LOG_SIZE (20 * 1024 * 1024) // 20M 13 | 14 | typedef enum { 15 | LOG_FATAL = 0, 16 | LOG_ERROR, 17 | LOG_INFO, 18 | LOG_DEBUG, 19 | LOG_DEBUG1, 20 | LOG_DEBUG2, 21 | LOG_NOLEVEL 22 | }LOG_LEVEL; 23 | 24 | typedef struct log_arg { 25 | int log_level; 26 | int log_file_num; 27 | int log_size; 28 | int curr_log_num; 29 | char log_path[1024]; 30 | char curr_log[1024]; 31 | FILE *log_fp; 32 | pthread_mutex_t log_lock; 33 | }LOG_ARG; 34 | 35 | int log_init(char *log_path, int log_level); 36 | int debug_init(int debug_level); 37 | void log_destroy(void); 38 | 39 | void log_lock(void); 40 | void log_unlock(void); 41 | #define debug(fmt, ...) do_log(LOG_DEBUG, __FILE__, \ 42 | __FUNCTION__, __LINE__, \ 43 | fmt, ##__VA_ARGS__); 44 | 45 | #define __debug(fmt, ...) do_debug(LOG_DEBUG, __FILE__, \ 46 | __FUNCTION__, __LINE__, \ 47 | fmt, ##__VA_ARGS__); 48 | 49 | #define debug1(fmt, ...) do_log(LOG_DEBUG1, __FILE__, \ 50 | __FUNCTION__, __LINE__, \ 51 | fmt, ##__VA_ARGS__); 52 | 53 | #define __debug1(fmt, ...) do_debug(LOG_DEBUG1, __FILE__, \ 54 | __FUNCTION__, __LINE__, \ 55 | fmt, ##__VA_ARGS__); 56 | 57 | #define debug2(fmt, ...) do_log(LOG_DEBUG2, __FILE__, \ 58 | __FUNCTION__, __LINE__, \ 59 | fmt, ##__VA_ARGS__); 60 | 61 | #define __debug2(fmt, ...) do_debug(LOG_DEBUG2, __FILE__, \ 62 | __FUNCTION__, __LINE__, \ 63 | fmt, ##__VA_ARGS__); 64 | 65 | #define fatal(fmt, ...) do_log(LOG_FATAL, __FILE__, \ 66 | __FUNCTION__, __LINE__, \ 67 | fmt, ##__VA_ARGS__); 68 | 69 | #define __fatal(fmt, ...) do_debug(LOG_FATAL, __FILE__, \ 70 | __FUNCTION__, __LINE__, \ 71 | fmt, ##__VA_ARGS__); 72 | 73 | #define error(fmt, ...) do_log(LOG_ERROR, __FILE__, \ 74 | __FUNCTION__, __LINE__, \ 75 | fmt, ##__VA_ARGS__); 76 | 77 | #define __error(fmt, ...) do_debug(LOG_ERROR, __FILE__, \ 78 | __FUNCTION__, __LINE__, \ 79 | fmt, ##__VA_ARGS__); 80 | 81 | #define info(fmt, ...) do_log(LOG_INFO, __FILE__, \ 82 | __FUNCTION__, __LINE__, \ 83 | fmt, ##__VA_ARGS__); 84 | 85 | #define __info(fmt, ...) do_debug(LOG_INFO, __FILE__, \ 86 | __FUNCTION__, __LINE__, \ 87 | fmt, ##__VA_ARGS__); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /safe_printf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * safe_printf.c 3 | 4 | * by Zhitong Wang zhitong.wangzt@aliyun-inc.com 5 | * 6 | * XXX work on x86_64 system. 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include "safe_printf.h" 14 | 15 | char *itoa(long num, char *str, int radix) 16 | { 17 | char string[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 18 | char *s = str, *p; 19 | char tmp; 20 | 21 | if (num < 0) 22 | return NULL; 23 | 24 | if (!num) { 25 | *s++ = '0'; 26 | *s = 0; 27 | return s; 28 | } 29 | 30 | while (num) { 31 | *s++ = string[num % radix]; 32 | num /= radix; 33 | } 34 | *s = 0; 35 | 36 | --s; 37 | for (p = str; p < s; s--, p++) { 38 | tmp = *p; 39 | *p = *s; 40 | *s = tmp; 41 | } 42 | 43 | return s; 44 | } 45 | 46 | int safe_write_char(char value) 47 | { 48 | write(1, &value, sizeof(char)); 49 | } 50 | 51 | int safe_write_string(char *value) 52 | { 53 | write(1, value, strlen(value) + 1); 54 | } 55 | 56 | int handle_format_string(char *format, char *buf) 57 | { 58 | int i, num; 59 | char c; 60 | 61 | if (format[0] == '\0') { 62 | safe_write_string(buf); 63 | return 0; 64 | } 65 | 66 | if (format[0] == '0') { 67 | num = atoi(format + 1); 68 | c = '0'; 69 | } 70 | else { 71 | num = atoi(format); 72 | c = ' '; 73 | } 74 | 75 | num -= strlen(buf); 76 | for (i = 0; i < num; i++) 77 | safe_write_char(c); 78 | safe_write_string(buf); 79 | } 80 | 81 | void safe_printf(const char *fmt, ...) 82 | { 83 | FUNC_ARG func_arg; 84 | const char *s = fmt; 85 | int type_flag = 0; 86 | int long_flag = 0; 87 | char tmp[32] = {0}, *q = tmp; 88 | 89 | INIT_ARG(func_arg) 90 | GET_ARG(func_arg) 91 | VA_START_ARG(func_arg) 92 | 93 | while (*s) { 94 | switch (*s) { 95 | case '%': 96 | if (type_flag == 1) { 97 | safe_write_char(*s); 98 | type_flag = 0; 99 | } 100 | else 101 | type_flag = 1; 102 | s++; 103 | break; 104 | case 'd': 105 | if (type_flag == 1) { 106 | long value; 107 | char buf[32] = {0}; 108 | 109 | if (long_flag == 1) 110 | VA_NEXT_ARG(value, func_arg, long) 111 | else 112 | VA_NEXT_ARG(value, func_arg, int) 113 | itoa(value, buf, 10); 114 | safe_write_string(buf); 115 | type_flag = 0; 116 | long_flag = 0; 117 | } 118 | else 119 | safe_write_char(*s); 120 | s++; 121 | break; 122 | case 'x': 123 | if (type_flag == 1) { 124 | long value; 125 | char buf[64] = {0}; 126 | 127 | *q = 0; 128 | if (long_flag == 1) 129 | VA_NEXT_ARG(value, func_arg, long) 130 | else 131 | VA_NEXT_ARG(value, func_arg, int) 132 | itoa(value, buf, 16); 133 | handle_format_string(tmp, buf); 134 | 135 | memset(tmp, '\0', 32); 136 | q = tmp; 137 | type_flag = 0; 138 | long_flag = 0; 139 | } 140 | else 141 | safe_write_char(*s); 142 | s++; 143 | break; 144 | case 'l': 145 | if (type_flag == 1) 146 | long_flag = 1; 147 | else 148 | safe_write_char(*s); 149 | s++; 150 | break; 151 | case 'c': 152 | if (type_flag == 1) { 153 | char value; 154 | 155 | VA_NEXT_ARG(value, func_arg, char) 156 | safe_write_char(value); 157 | type_flag = 0; 158 | } 159 | else 160 | safe_write_char(*s); 161 | s++; 162 | break; 163 | case 's': 164 | if (type_flag == 1) { 165 | char *value; 166 | 167 | VA_NEXT_ARG(value, func_arg, char *) 168 | safe_write_string(value); 169 | type_flag = 0; 170 | } 171 | else 172 | safe_write_char(*s); 173 | s++; 174 | break; 175 | case '0' ... '9': 176 | if (type_flag == 1) 177 | *q++ = *s; 178 | else 179 | safe_write_char(*s); 180 | s++; 181 | break; 182 | default: 183 | safe_write_char(*s++); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /jvm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012, 2013 wzt http://www.cloud-sec.org 3 | * 4 | * jvm.c 5 | * 6 | * This program is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU General Public License 8 | * as published by the Free Software Foundation; either version 2, 9 | * or (at your option) any later version. 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "jvm.h" 32 | #include "trace.h" 33 | #include "type.h" 34 | #include "list.h" 35 | #include "log.h" 36 | 37 | extern int disass_bytecode(struct list_head *list_head); 38 | 39 | void jvm_usage(const char *proc) 40 | { 41 | fprintf(stdout, "usage: %s