2020import android .text .TextUtils ;
2121import android .util .Log ;
2222import android .util .Pair ;
23+ import android .util .SparseArray ;
2324
2425import org .apache .commons .io .IOUtils ;
2526import org .apache .commons .lang3 .ArrayUtils ;
4142import fi .iki .elonen .NanoHTTPD ;
4243
4344
44- public class HenkakuServer extends NanoHTTPD {
45+ class HenkakuServer extends NanoHTTPD {
4546
4647 private Context context ;
4748
@@ -50,12 +51,12 @@ public class HenkakuServer extends NanoHTTPD {
5051 private byte [] stage1 ;
5152 private byte [] stage2 ;
5253
53- public HenkakuServer (Context ctx , int port ) {
54+ HenkakuServer (Context ctx , int port ) {
5455 super (port );
5556 context = ctx ;
5657 }
5758
58- public synchronized void setIpAddress (String ipAddress ) {
59+ synchronized void setIpAddress (String ipAddress ) {
5960 currentIpAddress = ipAddress ;
6061 }
6162
@@ -85,7 +86,7 @@ private Pair<ArrayList<Integer>, List<Byte>> preprocessRop(byte[] urop) throws E
8586 int symtab = reloc_offset + reloc_size ;
8687 int symtab_n = symtab_size / 8 ;
8788
88- Map < Integer , String > reloc_map = new HashMap <>();
89+ SparseArray < String > reloc_map = new SparseArray <>();
8990
9091 for (int x = 0 ; x < symtab_n ; ++x ) {
9192 int sym_id = buf .getInt (symtab + 8 * x );
@@ -168,33 +169,6 @@ private String preprocessToJs(byte[] loader) throws Exception {
168169 return String .format ("\n payload = [%1$s];\n relocs = [%2$s];\n " , payload , relocations );
169170 }
170171
171- /**
172- * Convert the exploit to a shellcode in binary format
173- *
174- * @param exploit payload compiled code
175- * @return the shellcode
176- * @throws Exception
177- */
178- private byte [] preprocessToBin (byte [] exploit ) throws Exception {
179- Pair <ArrayList <Integer >, List <Byte >> data = preprocessRop (exploit );
180-
181- int size = 4 + data .first .size () * 4 + data .second .size ();
182- byte [] out = new byte [size + ((-size ) & 3 )];
183- ByteBuffer buf = ByteBuffer .wrap (out ).order (ByteOrder .LITTLE_ENDIAN );
184-
185- buf .putInt (data .second .size ());
186-
187- for (Integer val : data .first ) {
188- buf .putInt (val );
189- }
190-
191- for (Byte val : data .second ) {
192- buf .put (val );
193- }
194-
195- return out ;
196- }
197-
198172 /**
199173 * Finalize the exploit with the addesses from the device
200174 *
@@ -258,10 +232,9 @@ private byte[] patchExploit(byte[] exploit, Map<String, String> params) throws E
258232 *
259233 * @param stage code of the current stage
260234 * @param url address to fetch the next stage
261- * @return modified shellcode
262235 * @throws UnsupportedEncodingException
263236 */
264- private byte [] writePkgUrl (byte [] stage , String url ) throws UnsupportedEncodingException {
237+ private void writePkgUrl (byte [] stage , String url ) throws UnsupportedEncodingException {
265238
266239 // prepare search pattern
267240 byte [] pattern = new byte [256 ];
@@ -273,14 +246,16 @@ private byte[] writePkgUrl(byte[] stage, String url) throws UnsupportedEncodingE
273246 // find url placeholder in loader
274247 int idx = Collections .indexOfSubList (a , b );
275248
276- // convert the url to a byte array
277- byte [] urlArray = url .getBytes ("UTF-8" );
278-
279- // write the url in the loader
280- System .arraycopy (urlArray , 0 , stage , idx , urlArray .length );
281- Arrays .fill (stage , idx + urlArray .length , idx + 256 , (byte ) 0x0 );
249+ if (idx >= 0 ) {
250+ // convert the url to a byte array
251+ byte [] urlArray = url .getBytes ("UTF-8" );
282252
283- return stage ;
253+ // write the url in the loader
254+ System .arraycopy (urlArray , 0 , stage , idx , urlArray .length );
255+ Arrays .fill (stage , idx + urlArray .length , idx + 256 , (byte ) 0x0 );
256+ } else {
257+ Log .e ("henkaku" , "URL filler not found in payload" );
258+ }
284259 }
285260
286261 /**
@@ -293,11 +268,11 @@ private String getLoaderJs() throws Exception {
293268
294269 // reuse the modified loader if the ip address hasn't changed
295270 if (stage1 == null || lastIpAddress == null || !lastIpAddress .equals (getIpAddress ())) {
296- InputStream is = context .getAssets ().open ("loader.rop.bin" );
297- byte [] loader = IOUtils .toByteArray (is );
298- String url = "http://" + getIpAddress () + ":" + getListeningPort () + "/stage2" ;
299- stage1 = writePkgUrl (loader , url );
300271 lastIpAddress = getIpAddress ();
272+ InputStream is = context .getAssets ().open ("loader.rop.bin" );
273+ stage1 = IOUtils .toByteArray (is );
274+ String url = "http://" + lastIpAddress + ":" + getListeningPort () + "/stage2" ;
275+ writePkgUrl (stage1 , url );
301276 }
302277
303278 return preprocessToJs (stage1 );
@@ -314,16 +289,14 @@ private InputStream getExploitBin(Map<String, String> params) throws Exception {
314289
315290 // reuse the preprocessed exploit if the ip address hasn't changed
316291 if (stage2 == null || lastIpAddress == null || !lastIpAddress .equals (getIpAddress ())) {
317- InputStream is = context .getAssets ().open ("exploit.rop.bin" );
318- byte [] exploit = IOUtils .toByteArray (is );
319- stage2 = preprocessToBin (exploit );
320- String url = "http://" + getIpAddress () + ":" + getListeningPort () + "/pkg" ;
321- stage2 = writePkgUrl (stage2 , url );
322292 lastIpAddress = getIpAddress ();
293+ InputStream is = context .getAssets ().open ("stage2.bin" );
294+ stage2 = IOUtils .toByteArray (is );
295+ String url = "http://" + lastIpAddress + ":" + getListeningPort () + "/pkg" ;
296+ writePkgUrl (stage2 , url );
323297 }
324298
325- byte [] patched = patchExploit (stage2 , params );
326- return new ByteArrayInputStream (patched );
299+ return new ByteArrayInputStream (patchExploit (stage2 , params ));
327300 }
328301
329302 private InputStream getPackageFile (String uri ) throws IOException {
@@ -339,8 +312,13 @@ public Response serve(IHTTPSession session) {
339312 Response response ;
340313
341314 String uri = session .getUri ();
315+ String query = session .getQueryParameterString ();
342316 Log .d ("henkaku" , String .format ("Request URI: %s" , uri ));
343317
318+ if (query != null ) {
319+ Log .d ("henkaku" , String .format ("Request params: %s" , query ));
320+ }
321+
344322 String agent = session .getHeaders ().get ("user-agent" );
345323 if (agent != null && !agent .contains ("PlayStation Vita 3.60" ) && (uri .equals ("/" ) || uri .equals ("stage1" ))) {
346324 Log .d ("henkaku" , "Request from non PS Vita" );
0 commit comments