Java沙箱实现是重写类加载器和安全管理器,通过设置的全局安全管理器来控制执行程序的权限 

说明: 安全策略只对安装安全管理器之后的类生效,之前的类不再此管理范围之内,利用这一点可以预先设置我们需要的操作,而对某个点之后的所有非法操作进行权限设置. 

类加载器重写

1. /**
2.  *  [重写的类加载器]
3.  *  沙箱程序类加载器,可根据指定路径加载制定类class文件.
4.  *  
5.  *  [说明]
6.  *  仅包内可见
7.  * 
8.  *  @author 刘金鑫
9.  *  @version 1.0
10.  * */  
11. package org.hljoj.core.judge.sandbox;  
12.   
13. import java.io.File;  
14. import java.io.FileInputStream;  
15.   
16. import org.hljoj.core.judge.util.ConstantParam;  
17.   
18. class SandboxClassLoader extends ClassLoader{  
19. /**默认classPath*/  
20. private String _classPath;  
21.       
22. /**
23.      *  构造函数
24.      * @param classPath 类加载器默认classPath
25.      * */  
26. public SandboxClassLoader(String classPath) {  
27. this._classPath = classPath;  
28.     }  
29.   
30. @Override  
31. protected Class<?> findClass(String className) throws ClassNotFoundException {  
32. return loadClass(_classPath, className);  
33.     }  
34.       
35. /**
36.      *  更改类加载器加载类的classpath,在制定路径下加载制定的类class文件
37.      * @param       classPath   要加载的类路径
38.      * @param       className   要加载的类名
39.      *              最为限定,只能加载不含包的类.
40.      * */  
41. public Class<?> loadClass(String classPath, String className) throws ClassNotFoundException{  
42. if(className.indexOf('.') >= 0) {  
43. throw new ClassNotFoundException(className);  
44.         }  
45.           
46. new File(classPath + ConstantParam.SEPARATOR + className + ".class");  
47. byte[] mainClass = new byte[(int) classFile.length()];  
48. try {  
49. new FileInputStream(classFile);  
50.             in.read(mainClass);  
51.             in.close();  
52. catch (Exception e) {  
53. //e.printStackTrace();  
54. throw new ClassNotFoundException(className);  
55.         }  
56.           
57. return super.defineClass(className, mainClass, 0, mainClass.length);  
58.     }  
59.       
60. /**
61.      *  获取classPath
62.      * @return String       classPath
63.      * */  
64. public String getClassPath(){  
65. return _classPath + ConstantParam.SEPARATOR;  
66.     }  
67. }

  重写安全管理器

1. /**
2.  *  [重写的安全管理器]
3.  *  安全管理器用来限制客户端提交的Java源程序运行的功能,
4.  * 对程序读/写本地文件系统,修改系统属性连接网络,
5.  * 数据库等一切可能对本地计算机系统造成危害的操作进行屏蔽,
6.  * 如有这些操作将抛出SecurityException异常,并终止程序执行.
7.  * 
8.  * [说明]:
9.  * 仅包内可见
10.  * 不允许提交的源程序执行exit(n)函数-即不允许源程序中途
11.  *  终止虚拟机的运行,但是调用源代码端可执行exit(n)函数.
12.  *  
13.  *  @author 刘金鑫
14.  *  @version 1.0
15.  * */  
16.   
17. package org.hljoj.core.judge.sandbox;  
18.   
19. import java.io.FilePermission;  
20. import java.lang.reflect.ReflectPermission;  
21. import java.security.Permission;  
22. import java.security.SecurityPermission;  
23. import java.util.PropertyPermission;  
24.   
25. import org.hljoj.core.judge.util.ConstantParam;  
26.   
27. class SandboxSecurityManager extends SecurityManager {  
28.       
29. public static final int EXIT = ConstantParam.RANDOM.nextInt();  
30.   
31. /**
32.      * 重写强行退出检测
33.      * 防止用户自行终止虚拟机的运行,但是调用程序端可以执行退出
34.      * */  
35. public void checkExit(int status) {  
36. if (status != EXIT)  
37. throw new SecurityException("Exit On Client Is Not Allowed!");  
38.     }  
39.   
40. /**
41.      *  策略权限查看
42.      * 当执行操作时调用,如果操作允许则返回,操作不允许抛出SecurityException
43.      * */  
44. private void sandboxCheck(Permission perm) throws SecurityException {  
45. // 设置只读属性  
46. if (perm instanceof SecurityPermission) {  
47. if (perm.getName().startsWith("getProperty")) {  
48. return;  
49.             }  
50. else if (perm instanceof PropertyPermission) {  
51. if (perm.getActions().equals("read")) {  
52. return;  
53.             }  
54. else if (perm instanceof FilePermission) {  
55. if (perm.getActions().equals("read")) {  
56. return;  
57.             }  
58. else if (perm instanceof RuntimePermission || perm instanceof ReflectPermission){  
59. return;  
60.         }  
61.   
62. throw new SecurityException(perm.toString());  
63.     }  
64.       
65. @Override  
66. public void checkPermission(Permission perm) {  
67. this.sandboxCheck(perm);  
68.     }  
69.   
70. @Override  
71. public void checkPermission(Permission perm, Object context) {  
72. this.sandboxCheck(perm);  
73.     }  
74.   
75. }

   沙箱操作 

1. /**
2.  *  [沙箱]
3.  *  模拟沙箱功能,限制执行的Java程序的所有可能对本地机器的危害.
4.  * 被执行的Java程序对于本机只有读属性,其他文件操作,更改系统属性,
5.  *  Socket网络连接,连接数据库等功能全部禁止.
6.  * 
7.  * 沙箱与主模块之间使用Socket进行通信,完全独立于系统接收执行信
8.  * 息类(包含要执行程序的相关信息)后执行,执行完毕后返回给主模块
9.  * 结果类(包含执行结果的相关信息)
10.  * 
11.  *  @author 刘金鑫
12.  *  @version 1.0
13.  * */  
14.   
15. package org.hljoj.core.judge.sandbox;  
16.   
17. import java.io.BufferedInputStream;  
18. import java.io.BufferedOutputStream;  
19. import java.io.ByteArrayInputStream;  
20. import java.io.ByteArrayOutputStream;  
21. import java.io.IOException;  
22. import java.io.ObjectInputStream;  
23. import java.io.ObjectOutputStream;  
24. import java.io.PrintStream;  
25. import java.lang.management.ManagementFactory;  
26. import java.lang.management.MemoryMXBean;  
27. import java.lang.reflect.InvocationTargetException;  
28. import java.lang.reflect.Method;  
29. import java.lang.reflect.Modifier;  
30. import java.net.ServerSocket;  
31. import java.net.Socket;  
32. import java.util.Timer;  
33. import java.util.TimerTask;  
34. import java.util.concurrent.Callable;  
35. import java.util.concurrent.FutureTask;  
36.   
37. import org.hljoj.core.judge.util.ConstantParam;  
38. import org.hljoj.core.judge.util.JudgeResult;  
39.   
40. public final class Sandbox {  
41.   
42. //用来收集线程的内存使用量  
43. private static MemoryMXBean _memoryBean = ManagementFactory.getMemoryMXBean();  
44.       
45. //定向输出  
46. private static ByteArrayOutputStream _baos = new ByteArrayOutputStream(1024);  
47.       
48. //Socket通信  
49. private static Socket _socket = null;  
50.       
51. private static ServerSocket _serverSocket = null;  
52.       
53. private static ObjectInputStream _inputStream = null;  
54.       
55. private static ObjectOutputStream _outputStream = null;  
56.       
57. //执行提交程序线程  
58. private static Thread _thread = null;  
59.       
60. //系统提供的默认的classpath  
61. private static String _classPath = null;  
62.       
63. private static long     _timeStart = 0;       
64. //程序运行时间  
65. private static long     _timeUsed = 0;  
66. private static int      _baseMemory = 0;  
67. //程序运行空间  
68. private static int      _memoryUsed = 0;  
69.       
70. //测评结果  
71. private static String _result = null;  
72.       
73. /**
74.      *  核心执行函数<br>
75.      * 用于执行指定Main.class的main函数.<br>
76.      * 根据线程ID可获取运行时间.
77.      * */  
78. private static String process(int runId, final int timeLimit) throws Exception {  
79. null;  
80. final Timer timer = new Timer(true);  
81.   
82. try {  
83. final Method mainMethod = getMainMethod(runId);  
84.   
85. new FutureTask<String>(new Callable<String>() {  
86. public String call() throws Exception {  
87. try {  
88.                         _timeStart = System.currentTimeMillis();  
89. //启动计时器  
90. 1);  
91. // 执行main方法  
92. null, new Object[] { new String[0] });  
93. catch (InvocationTargetException e) {  
94.                         Throwable targetException = e.getTargetException();  
95. // 超过内存限制  
96. if (targetException instanceof OutOfMemoryError)  
97.                             setResult(JudgeResult.MEMORY_LIMIT_EXCEED);  
98. // 非法操作处理  
99. else if (targetException instanceof SecurityException || targetException instanceof ExceptionInInitializerError) {  
100.                             setResult(JudgeResult.RESTRICT_FUNCTION);  
101. // 运行超时  
102. else if (targetException instanceof InterruptedException) {  
103.                             setResult(JudgeResult.TIME_LIMITE_EXCEED);  
104. else {  
105. // 运行时错误处理  
106. if (e.getCause().toString().contains("Output Limit Exceed"))  
107.                                 setResult(JudgeResult.OUTPUT_LIMIT_EXCEED);  
108. else  
109.                                 setResult(JudgeResult.RUNTIME_ERROR, e.getCause().toString());  
110.                         }  
111.                           
112. throw new RuntimeException("Runtime Exception");  
113. finally {  
114.                         timer.cancel();  
115.                     }  
116. return _baos.toString();  
117.                 }  
118.             });  
119. catch (Exception e) {  
120. throw new RuntimeException("Initalization Error");  
121.         }  
122.           
123. int) _memoryBean.getHeapMemoryUsage().getUsed();  
124. new Thread(task);  
125.         _thread.start();  
126. return task.get();  
127.     }  
128.       
129. /**
130.      *  启动计时器,对执行程序进行时间限制.<br>
131.      * 若超时,则中断执行线程<br>
132.      * @return TimerTask
133.      * */  
134. private static TimerTask getTimerTask(){  
135. return new TimerTask(){  
136. public void run() {  
137. if (_thread  != null)  
138.                     _thread.interrupt();  
139.             }  
140.         };  
141.     }  
142.       
143. private static int _outputSize = 0;  
144. /**
145.      *  初始化
146.      * @param classPath 系统默认classPath路径
147.      *  @param  port        socket服务器监听端口
148.      * */  
149. private static void inita(String classPath, int port) throws Exception{  
150.         _classPath = classPath;  
151.           
152. new ServerSocket(port);  
153.         _socket = _serverSocket.accept();  
154.           
155. new ObjectOutputStream(_socket.getOutputStream());  
156. new ObjectInputStream(_socket.getInputStream());  
157.           
158. //重新定向输出流  
159. new PrintStream(new BufferedOutputStream(_baos) {  
160. public void write(byte[] b, int off, int len) throws IOException {  
161.                 _outputSize += len - off;  
162. try {  
163. super.write(b, off, len);  
164. if (_outputSize > ConstantParam.OUTPUT_LIMIT)  
165. throw new RuntimeException("Output Limit Exceed" + _outputSize);  
166. catch (IOException e) {  
167. if(e.getMessage().equals("Output Limit Exceed")){  
168. throw e;  
169.                     }  
170.                 }  
171.             }  
172.         }));  
173.     }     
174.   
175. private static SandboxClassLoader _classLoader  = null;  
176. /**
177.      *  获取指定路径下Main.class类的main入口函数.
178.      * @param runId 指定类路径
179.      * @return Method 返回的main方法
180.      * */  
181. private static Method getMainMethod(int runId) throws Exception{  
182. new SandboxClassLoader(_classPath);  
183. "Main");  
184.           
185. null;  
186. "main", String[].class);  
187.           
188. if(!Modifier.isStatic(mainMethod.getModifiers()))   
189. throw new Exception("Method Of Main Is Not Static");  
190.           
191. true);  
192. return mainMethod ;  
193.     }  
194.       
195. /**
196.      *  测评接口.
197.      * 运行接收到的Java程序.
198.      * 
199.      * @param runId 执行id
200.      * @param problemId 提交问题id
201.      * @param submitId 提交用户id
202.      * @param timeLimit 时间限制
203.      * @param memoryLimit 空间限制
204.      * @param standardInput 程序标准输入字符串
205.      * @param standardOutput 程序标准输出字符串
206.      * */  
207. public static void run(int runId, int timeLimit, int memoryLimit,   
208.                                                                                                     String standardInput, String standardOutput) {  
209. 0;  
210. 0;  
211.         _baos.reset();  
212. 0;  
213.         setResult(JudgeResult.WRONG_ANSWER);  
214. //定向输入流  
215. new BufferedInputStream(new ByteArrayInputStream(standardInput.getBytes())));  
216.           
217. null;  
218.           
219. try {  
220. // 必须在执行前对垃圾回收,否则不准确.  
221.             System.gc();  
222.             output = process(runId, timeLimit);  
223. // 将程序输出与标准输出作比较  
224.             setResult(matchOutput(standardOutput.getBytes(), output.getBytes()));  
225. // 获取程序运行时间和空间  
226.             _timeUsed = System.currentTimeMillis() - _timeStart;  
227. int) ((_memoryBean.getHeapMemoryUsage().getUsed() - _baseMemory) / 1000);  
228. catch (Exception e) {  
229. if (e.getMessage().equals("Initalization Error")){  
230.                 setResult(JudgeResult.WRONG_ANSWER);  
231.             }  
232.         }  
233.           
234. if (_memoryUsed > memoryLimit)  
235.             setResult(JudgeResult.MEMORY_LIMIT_EXCEED);  
236.           
237. try {  
238. //向主模块返回执行结果  
239. int)_timeUsed, _memoryUsed, _result);  
240. catch (IOException e) {  
241.             e.printStackTrace();  
242.         }  
243.     }  
244.       
245. /**
246.      *  向主模块发送运行结果.
247.      * 
248.      * @param runId 运行runId
249.      *  @param timeUsed 代码运行时间(MS)
250.      * @param    memoryUsed 代码运行空间(B)
251.      *  @param result    代码执行结果
252.      * */  
253. private static void sendResult(int runId,int timeUsed, int memoryUsed, String result) throws IOException{  
254.         _outputStream.writeInt(runId);  
255.         _outputStream.writeInt(timeUsed);  
256.         _outputStream.writeInt(memoryUsed);  
257.         _outputStream.writeUTF(result);  
258.     }  
259.       
260. /**
261.      *  接收运行参数
262.      * 
263.      *  @param runId 运行runId
264.      *  @param timeLimit    限制代码运行时间(MS)
265.      * @param    memoryLimit    限制代码运行空间(B)
266.      * @param standardInput 标准输入
267.      * @param standardOutput 标准输出
268.      * */  
269. private static void receiveMsg() throws IOException{  
270. int runId = _inputStream.readInt();  
271. int timeLimit = _inputStream.readInt();  
272. int memoryLimit = _inputStream.readInt();   
273.         String standardInput = _inputStream.readUTF();  
274.         String standardOutput = _inputStream.readUTF();  
275.           
276.         run(runId, timeLimit, memoryLimit, standardInput, standardOutput);  
277.     }  
278.       
279. /**
280.      *  比较程序输出和标准输出,并返回比较结果.<br>
281.      *  标准返回1.Accepted 2.Wrong Answer 3.Presenting Error结果
282.      * 
283.      *  @param standOutput 标准输出结果
284.      *  @param output 程序输出结果
285.      *  @return int 比较结果
286.      * */  
287. private static int matchOutput(byte[] standardOutput, byte[] output){  
288. int i = 0;  
289. int j = 0;  
290.           
291. do{  
292. while (i < standardOutput.length && (standardOutput[i] == ConstantParam.SPACE || standardOutput[i] == '/t' ||   
293. '/r' || standardOutput[i] == '/n'))  
294.                     i++;  
295. while (j < output.length && (output[j] == ConstantParam.SPACE || output[j] == '/t' ||   
296. '/r' || output[j] == '/n'))  
297.                     j++;  
298. if (i < standardOutput.length && j < output.length && standardOutput[i] != output[j])  
299. return JudgeResult.WRONG_ANSWER;  
300.   
301.             i++;  
302.             j++;  
303. while(j <= i && i < standardOutput.length && j < output.length);  
304.           
305. if (i != j)  
306. return JudgeResult.PRESENTING_ERROR;  
307.           
308. return JudgeResult.ACCEPTED;  
309.     }  
310.       
311. /**
312.      *  设置测评结果.
313.      *  @param JudgeResult结果类型.
314.      * */  
315. private static void setResult(int resultType){  
316.         _result = JudgeResult.toString(resultType);  
317.     }  
318.       
319. /**
320.      *  设置测评结果.
321.      *  @param JudgeResult结果类型.
322.      * @param remark 测评结果的备注.
323.      * */  
324. private static void setResult(int resultType, String remark){  
325.         setResult(resultType);  
326.           
327. if (remark.endsWith("StackOverflowError"))  
328. "(" + JudgeResult.toString(JudgeResult.RUNTIME_STACK_OVERFLOW) + ")";  
329. else if (remark.endsWith("/ by zero"))  
330. "(" + JudgeResult.toString(JudgeResult.RUNTIME_DIVIDE_BY_ZERO) + ")";  
331. else if (remark.contains("ArrayIndexOutOfBoundsException"))  
332. "(" + JudgeResult.toString(JudgeResult.RUNTIME_ACCESS_VIOLATION) + ")";  
333. else  
334. "(" + JudgeResult.toString(JudgeResult.RUNTIME_ARRAY_BOUNDS_EXCEEDED) + ")";  
335.     }  
336.       
337. /**
338.      *  关闭网络连接
339.      * */  
340. private static void close(){  
341. try {  
342. if (_inputStream != null)  
343.                 _inputStream.close();  
344. if (_outputStream != null)  
345.                 _outputStream.close();  
346. if (_socket != null)  
347.                 _socket.close();  
348. catch (IOException e) {  
349.             e.printStackTrace();  
350.         }  
351.     }  
352.       
353. /**
354.      *  沙盒入口
355.      * 传入参数 : <br>
356.      *          classPath -- args[0] ------ 保存class的classpath<br>
357.      *          port          -- args[1] ------- 监听端口
358.      * */  
359. public static void main(String[] args) throws Exception{  
360. 0], Integer.parseInt(args[1]));  
361.           
362.         SecurityManager security = System.getSecurityManager();  
363. if (security == null)  
364. new SandboxSecurityManager());  
365.           
366. while (!_socket.isClosed()){  
367.             receiveMsg();  
368.         }  
369.           
370.         close();  
371.     }  
372. } 
373. 
374.