|
5 | 5 | import org.junit.After;
|
6 | 6 | import org.junit.Test;
|
7 | 7 |
|
| 8 | +import java.io.OutputStream; |
| 9 | +import java.io.ByteArrayOutputStream; |
8 | 10 | import java.io.IOException;
|
9 | 11 | import java.lang.reflect.InvocationTargetException;
|
10 | 12 | import java.lang.reflect.Method;
|
11 | 13 | import java.security.cert.CertificateException;
|
12 | 14 | import java.security.cert.X509Certificate;
|
| 15 | +import java.util.Arrays; |
13 | 16 | import javax.net.ssl.X509TrustManager;
|
14 | 17 |
|
15 | 18 | public class ClassCreationTest {
|
16 | 19 | private static Object badgerObject = null;
|
17 | 20 | private static Class bananaClass = null;
|
| 21 | + private static Class hasFieldClass = null; |
| 22 | + private static Class primitiveArrayClass = null; |
| 23 | + private static Class myOutputClass = null; |
18 | 24 | private static Class trustManagerClass = null;
|
19 | 25 | private static Class formatterClass = null;
|
20 | 26 | private static Class weirdTrustManagerClass = null;
|
| 27 | + private HasField hasField; |
21 | 28 |
|
22 | 29 | @Test
|
23 | 30 | public void simpleClassCanBeImplemented() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
@@ -161,6 +168,147 @@ public void interfaceMethodCanHaveUnrelatedOverload() throws ClassNotFoundExcept
|
161 | 168 | assertEquals("checkServerTrusted B", script.getNextMessage());
|
162 | 169 | }
|
163 | 170 |
|
| 171 | + // Issue #119 |
| 172 | + @Test |
| 173 | + public void interfaceHasFieldCanBeImplemented() throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| 174 | + loadScript("var HasField = Java.use('re.frida.HasField');" + |
| 175 | + "var SimpleHasField = Java.registerClass({" + |
| 176 | + " name: 're.frida.SimpleHasField'," + |
| 177 | + " implements: [HasField]," + |
| 178 | + " methods: {" + |
| 179 | + " getName: function () {" + |
| 180 | + " return 'hasField';" + |
| 181 | + " }," + |
| 182 | + " getCalories: function (grams) {" + |
| 183 | + " return grams * 2;" + |
| 184 | + " }," + |
| 185 | + " }" + |
| 186 | + "});" + |
| 187 | + "Java.use('re.frida.ClassCreationTest').hasFieldClass.value = SimpleHasField.class;"); |
| 188 | + HasField hasField = (HasField) hasFieldClass.newInstance(); |
| 189 | + assertEquals("hasField", hasField.getName()); |
| 190 | + assertEquals(100, hasField.getCalories(50)); |
| 191 | + assertEquals("Field", hasField.field); |
| 192 | + } |
| 193 | + |
| 194 | + // Issue #121 |
| 195 | + @Test |
| 196 | + public void primitiveArrayInterfaceMethodsCanBeImplemented() throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| 197 | + loadScript("var PrimitiveArray = Java.use('re.frida.PrimitiveArray');" + |
| 198 | + "var SimplePrimitiveArray = Java.registerClass({" + |
| 199 | + " name: 're.frida.SimplePrimitiveArray'," + |
| 200 | + " implements: [PrimitiveArray]," + |
| 201 | + " methods: {" + |
| 202 | + " getByteArray: [{" + |
| 203 | + " returnType: '[B'," + |
| 204 | + " argumentTypes: []," + |
| 205 | + " implementation: function () {" + |
| 206 | + " return [1, 2, 3, 4, 5];" + |
| 207 | + " }" + |
| 208 | + " }]," + |
| 209 | + " setIntArray: function (array, off) {" + |
| 210 | + " var s = '';" + |
| 211 | + " for (var i = off; i < array.length; i++) s += array[i];" + |
| 212 | + " return s;" + |
| 213 | + " }," + |
| 214 | + " }" + |
| 215 | + "});" + |
| 216 | + "Java.use('re.frida.ClassCreationTest').primitiveArrayClass.value = SimplePrimitiveArray.class;"); |
| 217 | + PrimitiveArray primitiveArray = (PrimitiveArray) primitiveArrayClass.newInstance(); |
| 218 | + assertEquals(Arrays.equals(new byte[] { 1, 2, 3, 4, 5 }, primitiveArray.getByteArray()), true); |
| 219 | + assertEquals("345", primitiveArray.setIntArray(new int[] { 1, 2, 3, 4, 5 }, 2)); |
| 220 | + } |
| 221 | + |
| 222 | + // Issue #122 |
| 223 | + @Test |
| 224 | + public void extendingClassCanBeImplemented() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { |
| 225 | + loadScript("const bytes = [];" + |
| 226 | + "var MyOutputStream = Java.registerClass({" + |
| 227 | + " name: 're.frida.MyOutputSteam'," + |
| 228 | + " superClass: Java.use('java.io.OutputStream')," + |
| 229 | + " methods: {" + |
| 230 | + " write: [{" + |
| 231 | + " returnType: 'void'," + |
| 232 | + " argumentTypes: ['int']," + |
| 233 | + " implementation: function (b) {" + |
| 234 | + " bytes.push(b);" + |
| 235 | + " }" + |
| 236 | + " }]," + |
| 237 | + " toString: {" + |
| 238 | + " returnType: 'java.lang.String'," + |
| 239 | + " argumentTypes: []," + |
| 240 | + " implementation: function () {" + |
| 241 | + " return bytes.join(',');" + |
| 242 | + " }" + |
| 243 | + " }," + |
| 244 | + " }" + |
| 245 | + "});" + |
| 246 | + "Java.use('re.frida.ClassCreationTest').myOutputClass.value = MyOutputStream.class;"); |
| 247 | + OutputStream myOutput = (OutputStream) myOutputClass.newInstance(); |
| 248 | + myOutput.write(new byte[] { 1, 2, 3, 4, 5 }); |
| 249 | + assertEquals("1,2,3,4,5", myOutput.toString()); |
| 250 | + myOutput.write(new byte[] { 1, 2, 3, 4, 5 }); |
| 251 | + assertEquals("1,2,3,4,5,1,2,3,4,5", myOutput.toString()); |
| 252 | + } |
| 253 | + |
| 254 | + // Issue #122 |
| 255 | + @Test |
| 256 | + public void extendingClassCanInvoekSuperMethod() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException { |
| 257 | + loadScript("var SuperClass = Java.use('java.io.ByteArrayOutputStream');" + |
| 258 | + "var MyOutputStream = Java.registerClass({" + |
| 259 | + " name: 're.frida.MyOutputSteamEx'," + |
| 260 | + " superClass: SuperClass," + |
| 261 | + " methods: {" + |
| 262 | + " write: [{" + |
| 263 | + " returnType: 'void'," + |
| 264 | + " argumentTypes: ['[B', 'int', 'int']," + |
| 265 | + " implementation: function (b, off, len) {" + |
| 266 | + " this.$super.write(b, off, len);" + |
| 267 | + " }" + |
| 268 | + " }, {" + |
| 269 | + " returnType: 'void'," + |
| 270 | + " argumentTypes: ['int']," + |
| 271 | + " implementation: function (b) {" + |
| 272 | + " this.$super.write(b);" + |
| 273 | + " }" + |
| 274 | + " }]," + |
| 275 | + " }" + |
| 276 | + "});" + |
| 277 | + "Java.use('re.frida.ClassCreationTest').myOutputClass.value = MyOutputStream.class;"); |
| 278 | + OutputStream myOutput = (OutputStream) myOutputClass.newInstance(); |
| 279 | + myOutput.write(new byte[] { '1', '2', '3', '4', '5' }); |
| 280 | + assertEquals("12345", myOutput.toString()); |
| 281 | + myOutput.write(new byte[] { '1', '2', '3', '4', '5' }); |
| 282 | + assertEquals("1234512345", myOutput.toString()); |
| 283 | + } |
| 284 | + |
| 285 | + // Issue #124 |
| 286 | + @Test |
| 287 | + public void extendInterfaceCanBeImplemented() throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| 288 | + loadScript("var Eatable = Java.use('re.frida.Eatable');" + |
| 289 | + "var EatableEx = Java.use('re.frida.EatableEx');" + |
| 290 | + "var BananaEx = Java.registerClass({" + |
| 291 | + " name: 're.frida.BananaEx'," + |
| 292 | + " implements: [EatableEx, Eatable]," + |
| 293 | + " methods: {" + |
| 294 | + " getName: function () {" + |
| 295 | + " return 'Banana';" + |
| 296 | + " }," + |
| 297 | + " getNameEx: function () {" + |
| 298 | + " return 'BananaEx';" + |
| 299 | + " }," + |
| 300 | + " getCalories: function (grams) {" + |
| 301 | + " return grams * 2;" + |
| 302 | + " }," + |
| 303 | + " }" + |
| 304 | + "});" + |
| 305 | + "Java.use('re.frida.ClassCreationTest').bananaClass.value = BananaEx.class;"); |
| 306 | + EatableEx eatable = (EatableEx) bananaClass.newInstance(); |
| 307 | + assertEquals("Banana", eatable.getName()); |
| 308 | + assertEquals("BananaEx", eatable.getNameEx()); |
| 309 | + assertEquals(100, eatable.getCalories(50)); |
| 310 | + } |
| 311 | + |
164 | 312 | private Script script = null;
|
165 | 313 |
|
166 | 314 | private void loadScript(String code) {
|
|
0 commit comments