Сейчас на форуме: (+5 невидимых)

 eXeL@B —› Дневники и блоги —› Блог DenCoder'а
<< . 1 . 2 . 3 . >>
Посл.ответ Сообщение


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 05 июля 2015 15:23 · Поправил: DenCoder
· Личное сообщение · #1

Всем привет! Всем здоровья, достатка, удачи, успехов... всех благ! )

Вот и нашлось немножко времени, чтобы завести блог. Давно хотел, но время для меня на вес золота...

С вашего позволения здесь буду описывать процессы обхода/взлома/разбора защит, с которыми приходилось сталкиваться. Кому-то может пригодиться, кому-то - нет... Но за время работы немного узнал, что есть такая защита, перед которой некоторые коллеги пасуют - VMProtect. Может, в общем и есть такие страшные версии вмпрота, на которые терять кучу времени бессмысленно - не знаю, я с такими не сталкивался. Но скажу, что очень часто снимать вмпрот или разбирать то, что им накрыто, просто нет необходимости!

Вмпроты, которые мне попались в мае (сразу 2 подряд) - не такие страшные. Первым из них была накрыта lockpdfu.dll - компонент DRM защиты pdf-ок. Подробнее --> здесь <--.
Человек 2 или 3 года ждал, когда же, наконец, найдётся кто-нибудь, кто даст возможность свободно пользоваться pdf-документами на любом компе и редактировать. И находился некто с других сайтов, кто чуть не кинул... Попробую объяснить морально-идеологический аспект в двух словах прежде всего:

Человек - учитель, ему необходим учебный материал без ограничений использования с возможностью редактировать. Одна из купленных тетрадей, на файле которой я помогал решить задачу, стоила что-то вроде 70 гривен, использовать можно только на 2х компах и никак нельзя что-то пометить, что-то исправить. Чтобы, например, поделиться с учениками, сколько-то нужно дополнительно проплачивать... мда... куда катится мир, одни то ли бизнесмены, то ли барыги...

Ставим задачу: перехватить расшифровку запротекченной тетради, записать в отдельную папку оригинальный pdf.

Из совета Vovan666 запротектил пдф-ку персональной версией, после чего без проблем из памяти AcroRd32 достал чистую pdf-ку. честно говоря, не понял про протект персональной версией, но проверил можно ли что-то из памяти выцепить: даже и обычные pdf-ки в первозданном виде в памяти не хранятся, сигнатур %pdf-1.4 .. %pdf-1.7 нет. То, что находится с такой сигной - какая-то полоска для самого ридера )

Я пошёл своим путём. Раз нельзя выцепить из памяти - незаметно (для защиты) встанем на место после декрипта и незаметно сольём поток в файл. Звучит просто, но пришлось немного попотеть...

CreateFile, SetFilePointer, CloseHandle удалось хукнуть джампом без проблем. А вот ветка кода из acrord32.dll, вызывающая чтение функой ReadFile была хукнута в NtReadFile защитой + каким-то магическим образом проверялся crc этой ветки, а также защита не давала поставить ни софт бряк, ни джамп на ReadFile ни до, ни внутри, ни после... в дрова лезть не будем! Софт бряк нельзя поставить, а против харда защита ничего не имеет против! Вспомнил, что имеется такая функция AddVectoredExceptionHandler и быстро нарисовалась идея (что не я - первооткрыватель, знаю, потому и пишу, что вспомнил) поставить 2 харда на ReadFile и после и обрабтать по своему. Потратил немножко времени на кодинг, но удалось, с первой подзадачей справился, осталось только записать файл.

Записать файл - тут кажется, проще не бывает. Но во-первых под 7кой и выше нужно перевести для этого акроридер в незащищённый режим, чтобы были привилегии, во-вторых - NtWriteFile также хукнута защитой и возвращала жестокий AD. Снимать хук без изучения защиты бесполезно - работать как надо не будет. ReadFile, WriteFile - так защита обменивалась какими-то важными данными. Не стал тратить время на изучение механизмов, сделал проще: против вызова NtWriteFile в месте после хука защита тоже ничего не имела против. Тут, я думаю, подробности излишни, в ntdll индексы всех фунок(nt-сервисов) идут по возрастающей по порядку во всех версиях винды, не встречалось другого(берём адрес хукнутого сервиса, находим следующий/предыдущий, берём оттуда индекс сервиса и минус/плюс 1)

Новичкам с опытом кодинга может быть полезно

В следующий раз (может быть сегодня вечером, может и через года 2) опишу отлом какой-то из версий вмпрота под x64, где тоже есть морально-идеологический аспект

-----
IZ.RU


| Сообщение посчитали полезным: TLN, Zoeken


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 24 декабря 2015 10:54
· Личное сообщение · #2

Тут интересную штуку нашёл. Поделюсь позже и может быть!... Но по всему одно из трёх -
1) либо это баг вмпрота с 3мя различными вм(2 с декрементом vmp, 1 с инкрементом)
2) либо часть ключа проверяется раньше и при верности по вычисленному происходит корректная вторая распаковка(немыслимая гипотеза, но всё же)
3) либо в 3ей вм заложен механизм перезаписи указателей на хендлеры примитивов

Пока продолжаю анализ...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 26 декабря 2015 16:13
· Личное сообщение · #3

Стыдно признаться, попробую оправдаться тем, что меня много вещей отвлекли, но это не оправдание... 100% верный вариант - 4) банально глупая ошибка оказалась у меня...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 30 декабря 2015 22:25 · Поправил: DenCoder
· Личное сообщение · #4

MasterSoft пишет:
Code:
type=activation&code=
type=deactivation&hash=
activation.php?code=
deactivation.php?hash=

Строки шифрованы? Если нет - то в моих экземплярах не нашёл. Перебирать индексы строк и подавать на вход функции дешифратора ленновато под Новый Год

-----
IZ.RU




Ранг: 281.8 (наставник), 272thx
Активность: 0.250.01
Статус: Участник
Destroyer of protectors

Создано: 30 декабря 2015 22:28 · Поправил: MasterSoft
· Личное сообщение · #5

DenCoder пишет:
Строки шифрованы? Если нет - то в моих экземплярах не нашёл. Перебирать индексы строк и подавать на вход функции дешифратора ленновато под Новый Год

да ладно? дайте файл я найду)))

Added:
DenCoder пишет:
да нету там вашего!!!

ну нету так нету, как скажете




Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 19 февраля 2016 06:21 · Поправил: DenCoder
· Личное сообщение · #6

MasterSoft пишет:
дайте файл я найду))

да нету там вашего!!!

Итог за уходящий год и вновь пришедший:
VMProtect - ерунда... есть планы и разнятся с поставленными задачами...
ARM - ерунда... привыкнуть надо...
Hex-Rays - не понимаю тех людей, кто им пользуется... для меня изначальный вид в асме проще для понимания, чем си-код, полученный им...
mips - ерунда... с delay slot'ами...

Отдельно о mips'е:
Была поставлена задача. Срок выполнения был оценен расплывчато - от 3х недель до 2 месяцев. И должен сообщить, что достиг-таки того уровня, который позволил мне, не взирая на все проблемы, уложиться в минимальный из поставленных - 3 недели и 2 лня.

Были проделаны работы:
1) выяснение формата редчайшей файловой системы - заказчик выяснил бОльшую часть, я устранил некоторые ошибки
2) формат файлов конфигов - полностью на мне необходимый минимум для запуска девайса
3) написание утилиты для вставки и редактирования файлов в эту фс
4) выяснение конкретных данных, алго преобразования их из употребительного вида в исходный вид в файле...
5) прочий реверс в сочетании с ручным декомпилем особо важных частей кода
...

Сейчас я осознаю под впечатлением кажущейся мелочности всей проделанной работы. Но на истоке её............. не было не то что вёсел, да и лодки не было... Не осознаю полностью, да и незачем это сейчас, какова работа была проделана. Но если б знать бы заранее, каким курсом надо было плыть к цели - за 2-3 дня при должном усердии можно б было достичь причала! Это невозможно было лишь потому, что и некоторые знания заказчика зависели от качества моего реверса... А это всегда неотъемлемое время!

Какое отличие от всех ранее проделанных работ по другим проектам - впервые за всю свою жизнь не увидел индусского говнокода, и заказчик хорошо железно подкован... Хоть местами своими догадками и мешали друг другу наполовину, сработались отлично в общем!

Вот и всё! Никакой конкретики несколько лет к ряду, всё только для истории...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 26 февраля 2016 17:47 · Поправил: DenCoder
· Личное сообщение · #7

DenCoder пишет:
VMProtect - ерунда... есть планы и разнятся с поставленными задачами...

Я часто писал, что где работаю, туда ветер и дует. И всегда есть желанный причал, куда не доплыть галсом на порывах... где можно не только переоборудоваться и запастись провизией...

Нашёл для себя, что есть парусное дело, законы аэродинамики - ветер нельзя изменить, но можно повернуть парус! Веков ещё 20 назад сила ветра использовалась в своих целях независимо от его направления. Можно даже плыть и против ветра - бейдевиндом. Маневрируя в нужные моменты плюсом и минусом угла между ветром и курсом...

И вот вопрос - можно ли по принципу аналогии подчинить себе в реверс-инжиниринге "ветер"? Можно ли придумать такую систему "парусов" и манёвров, чтобы независимо от специфики проекта, мы продолжали двигаться тем же курсом? Если ветер - абстрактный вектор специфики проекта, сила ветра - ход работы над ним, в роли паруса - знания и опыт, то какие манёвры тут есть?...

Самый главный вопрос - применима ли здесь аналогия? Очень бы хотелось, но, похоже, не в 3х-мерной геометрии это возможно... На аналогии если ехать, можно мозг сломать, но под пиратскую тему она очень подходит.

В общем, в планах "побывать-таки на 9 крупных причалах". И что-нибудь придумаю!...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 12 марта 2016 15:46 · Поправил: DenCoder
· Личное сообщение · #8

Столкнулся с FlexLM. Забавно, но как будто копия VMProtect. Логика такая же, только код невиртуализирован:
Code:
  1.                 cmp     edx, eax
  2.                 push    rbp
  3.                 mov     rbp, offset causerOfBug_Chunk_bp1R_m9_bp1L
  4.                 xchg    rbp, [rsp]
  5.                 push    rcx
  6.                 push    rdx
  7.                 mov     rcx, [rsp+10h]
  8.                 mov     rdx, offset causerOfBug_Chunk_bp1R_m6
  9.                 cmovb   rcx, rdx
  10.                 mov     [rsp+10h], rcx
  11.                 pop     rdx
  12.                 pop     rcx
  13.                 retn


Примерно такой же вид принял бы девиртуализированный с вмпрота байт-код конца каждой ленты с ветвлением.

Продолжение следует....

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 15 марта 2016 21:04
· Личное сообщение · #9

Ида лажает на spd (Stack Pointer Delta) на функциях с мелкими фрагментами, размазанными по образу. Написал скрипт для фикса
Code:
  1. #include <idc.idc>
  2.  
  3. static recursFunc(curChunk)
  4. {
  5.   auto x, y, arid = GetArrayId("chunks"), idx, addLong, forgot, fch;
  6.   do
  7.   {
  8.     forgot = 0;
  9.     Message("curChunk %x\n", curChunk);
  10.  
  11.     y = BADADDR;
  12.     for(= Rfirst(curChunk); x != BADADDR; x = Rnext(curChunk, x))
  13.     {
  14.       if((XrefType() & 0x1f) == fl_JN)
  15.       {
  16.         fch = FirstFuncFchunk(curChunk);
  17.         while(fch != BADADDR)
  18.         {
  19.           if(>= GetFchunkAttr(fch, FUNCATTR_START) && x <= GetFchunkAttr(fch, FUNCATTR_END))
  20.             break;
  21.           fch = NextFuncFchunk(x, fch);
  22.         }
  23.  
  24.         if(fch != BADADDR)
  25.         {
  26.           idx = GetFirstIndex(AR_LONG, arid);
  27.           while(idx != -1)
  28.           {
  29.             if(== GetArrayElement(AR_LONG, arid, idx))
  30.             {
  31.               forgot = 1;
  32.               break;
  33.             }
  34.             idx = GetNextIndex(AR_LONG, arid, idx);
  35.           }
  36.  
  37.           if(forgot == 0)
  38.           {
  39.             if(Byte(curChunk) != 0xc3) addLong = 0;
  40.             else addLong = -8;
  41.  
  42.             auto xSpd = GetSpd(x);
  43.             if(__int64(xSpd) < 0) Message("next %x spd before -%x\n", x, -GetSpd(x));
  44.             else Message("next %x spd before %x\n", x, GetSpd(x));
  45.             if(GetSpd(x) + addLong != GetSpd(curChunk))
  46.             {
  47.               AddUserStkPnt(x,  GetSpDiff(x) - GetSpd(x) + GetSpd(curChunk) - addLong);
  48.               xSpd = GetSpd(x);
  49.               if(__int64(xSpd) < 0) Message("next %x spd after -%x\n", x, -xSpd);
  50.               else Message("next %x spd after %x\n", x, xSpd);
  51.  
  52.               return -1;
  53.             }
  54.  
  55.             if(== BADADDR) y = x;
  56.             else
  57.             {
  58.               idx = GetLastIndex(AR_LONG, arid) + 1;
  59.               SetArrayLong(arid, idx, x);
  60.             }
  61.           }
  62.         }
  63.       }
  64.       else if((XrefType() & 0x1f) == fl_F) y = x;
  65.     }
  66.     
  67.     if(== BADADDR)
  68.     {
  69.       idx = GetFirstIndex(AR_LONG, arid);
  70.       if(idx != -1)
  71.       {
  72.         y = GetArrayElement(AR_LONG, arid, idx);
  73.         DelArrayElement(AR_LONG, arid, idx);
  74.       }
  75.     }
  76.     
  77.     curChunk = y;
  78.   }while(!= BADADDR);
  79.  
  80.   return 0;
  81. }
  82.  
  83. static main()
  84. {
  85.   auto curChunk, arid, result;
  86.  
  87.   curChunk = FirstFuncFchunk(ScreenEA());
  88.  
  89.   do
  90.   {
  91.     arid = CreateArray("chunks");
  92.     result = recursFunc(curChunk);
  93.     DeleteArray(arid);
  94.   }while(result == -1);
  95.   
  96.   Message("Done!");
  97. }


Скрипт специфический под x86/x64 из-за инструкции ret с кодом 0xc3. Но и под другие платформы можно заюзать, если заменить этот опкод на нужный.
Не хватает правда пока фиксов для некоторых иснтрукций, типа lea rsp, [rsp - 8]. Но это нетрудно, если надоест вручную править spd после таких, то вставлю пару строчек.

Добавлено спустя 17 часов 36 минут
Поправил скрипт, теперь в нём учитываются циклы, и это не зацикливает его в 50% случаев.
Code:
  1. #include <idc.idc>
  2.  
  3. static recursFunc(curChunk)
  4. {
  5.   auto x, y, arid = GetArrayId("chunks"), arIdComp = GetArrayId("CompChunks"), idx, addLong, forgot, fch;
  6.   do
  7.   {
  8.     forgot = 0;
  9.     Message("curChunk %x\n", curChunk);
  10.  
  11.     y = BADADDR;
  12.     for(= Rfirst(curChunk); x != BADADDR; x = Rnext(curChunk, x))
  13.     {
  14.       if((XrefType() & 0x1f) == fl_JN)
  15.       {
  16.         fch = FirstFuncFchunk(curChunk);
  17.         while(fch != BADADDR)
  18.         {
  19.           if(>= GetFchunkAttr(fch, FUNCATTR_START) && x <= GetFchunkAttr(fch, FUNCATTR_END))
  20.             break;
  21.           fch = NextFuncFchunk(x, fch);
  22.         }
  23.  
  24.         if(fch != BADADDR)
  25.         {
  26.           idx = GetFirstIndex(AR_LONG, arid);
  27.           while(idx != -1)
  28.           {
  29.             if(== GetArrayElement(AR_LONG, arid, idx))
  30.             {
  31.               forgot = 1;
  32.               break;
  33.             }
  34.             idx = GetNextIndex(AR_LONG, arid, idx);
  35.           }
  36.  
  37.           idx = GetFirstIndex(AR_LONG, arIdComp);
  38.           while(idx != -1)
  39.           {
  40.             if(== GetArrayElement(AR_LONG, arIdComp, idx))
  41.             {
  42.               forgot = 1;
  43.               break;
  44.             }
  45.             idx = GetNextIndex(AR_LONG, arIdComp, idx);
  46.           }
  47.           
  48.           if(forgot == 0)
  49.           {
  50.             if(Byte(curChunk) != 0xc3) addLong = 0;
  51.             else addLong = -8;
  52.  
  53.             auto xSpd = GetSpd(x);
  54.             if(__int64(xSpd) < 0) Message("next %x spd before -%x\n", x, -GetSpd(x));
  55.             else Message("next %x spd before %x\n", x, GetSpd(x));
  56.             if(GetSpd(x) + addLong != GetSpd(curChunk))
  57.             {
  58.               AddUserStkPnt(x,  GetSpDiff(x) - GetSpd(x) + GetSpd(curChunk) - addLong);
  59.               xSpd = GetSpd(x);
  60.               if(__int64(xSpd) < 0) Message("next %x spd after -%x\n", x, -xSpd);
  61.               else Message("next %x spd after %x\n", x, xSpd);
  62.  
  63.               return -1;
  64.             }
  65.  
  66.             if(== BADADDR)
  67.             {
  68.               y = x;
  69.               idx = GetLastIndex(AR_LONG, arIdComp) + 1;
  70.               SetArrayLong(arIdComp, idx, x);
  71.             }
  72.             else
  73.             {
  74.               idx = GetLastIndex(AR_LONG, arid) + 1;
  75.               SetArrayLong(arid, idx, x);
  76.             }
  77.           }
  78.         }
  79.       }
  80.       else if((XrefType() & 0x1f) == fl_F) y = x;
  81.     }
  82.     
  83.     if(== BADADDR)
  84.     {
  85.       idx = GetFirstIndex(AR_LONG, arid);
  86.       if(idx != -1)
  87.       {
  88.         y = GetArrayElement(AR_LONG, arid, idx);
  89.         DelArrayElement(AR_LONG, arid, idx);
  90.         idx = GetLastIndex(AR_LONG, arIdComp) + 1;
  91.         SetArrayLong(arIdComp, idx, y);
  92.       }
  93.     }
  94.     
  95.     curChunk = y;
  96.   }while(!= BADADDR);
  97.  
  98.   return 0;
  99. }
  100.  
  101. static main()
  102. {
  103.   auto curChunk, arid, arIdComp, result;
  104.  
  105.   curChunk = FirstFuncFchunk(ScreenEA());
  106.  
  107.   do
  108.   {
  109.     arid = CreateArray("chunks");
  110.     arIdComp = CreateArray("CompChunks");
  111.     result = recursFunc(curChunk);
  112.     DeleteArray(arid);
  113.     DeleteArray(arIdComp);
  114.   }while(result == -1);
  115.   
  116.   Message("Done!");
  117. }


Кото-то может заинтересуется, подумав над скриптом, откуда там может быть 2 джампа (fl_JN от одного и того же адреса). Всё просто:
ветвление осуществляется, если проще -
Code:
  1. mov [rsp], loc_addr1
  2. [любая проверка условия]
  3. cmovz [rsp], loc_addr2
  4. retn

Чтобы граф красиво выгдядел, и чтоб не путаться при 200+ фрагментах, очень полезно воспользоваться на адресе с retn такими idc-командами
Code:
  1. AddCodeXref(ScreenEA(), loc_addr1, XREF_USER | fl_JN)
  2. AddCodeXref(ScreenEA(), loc_addr2, XREF_USER | fl_JN)


......

Может чего в скрипте лишнего написал? Я впервые на них пишу... Перечисление фрагментов неудобно - от меньшего к большему, от того он раздулся. Функция названа RecursFunc() потому, что изначально задумывалось её сделать рекурсивной, но мне показалось, что это будет чревато вылетами бедной иды.

Отдельный момент вызывает интерес:
Code:
  1.   do
  2.   {
  3.     arid = CreateArray("chunks");
  4.     arIdComp = CreateArray("CompChunks");
  5.     result = recursFunc(curChunk);
  6.     DeleteArray(arid);
  7.     DeleteArray(arIdComp);
  8.   }while(result == -1);


Почему так? Зачем после первой правки начинать всё сначала? Всё дело в том, что поправив в одном месте spd, необходимо проверить в остальных фрагментах ранее - могут "непредсказуемо" сбиться. Непредсказуемо в кавычках потому, что какая-то логика в "ошибках" иды на самом деле есть. Может, есть какая-то скрытая настройка по части разных сегментов и spd... но это будет отдельная тема...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 16 марта 2016 18:49 · Поправил: DenCoder
· Личное сообщение · #10

Надоело фиксить spd после команд lea rsp, [rsp -/+ 8]. Обновил скрипт. Теперь это делается автоматически.
Code:
  1. #include <idc.idc>
  2.  
  3. static recursFunc(curChunk)
  4. {
  5.   auto x, y, arid = GetArrayId("chunks"), arIdComp = GetArrayId("CompChunks"), idx, addLong, forgot, fch, xSpd;
  6.   do
  7.   {
  8.     forgot = 0;
  9.     Message("curChunk %x\n", curChunk);
  10.  
  11.     y = BADADDR;
  12.     for(= Rfirst(curChunk); x != BADADDR; x = Rnext(curChunk, x))
  13.     {
  14.       if((XrefType() & 0x1f) == fl_JN)
  15.       {
  16.         fch = FirstFuncFchunk(curChunk);
  17.         while(fch != BADADDR)
  18.         {
  19.           if(>= GetFchunkAttr(fch, FUNCATTR_START) && x <= GetFchunkAttr(fch, FUNCATTR_END))
  20.             break;
  21.           fch = NextFuncFchunk(x, fch);
  22.         }
  23.  
  24.         if(fch != BADADDR)
  25.         {
  26.           idx = GetFirstIndex(AR_LONG, arid);
  27.           while(idx != -1)
  28.           {
  29.             if(== GetArrayElement(AR_LONG, arid, idx))
  30.             {
  31.               forgot = 1;
  32.               break;
  33.             }
  34.             idx = GetNextIndex(AR_LONG, arid, idx);
  35.           }
  36.  
  37.           idx = GetFirstIndex(AR_LONG, arIdComp);
  38.           while(idx != -1)
  39.           {
  40.             if(== GetArrayElement(AR_LONG, arIdComp, idx))
  41.             {
  42.               forgot = 1;
  43.               break;
  44.             }
  45.             idx = GetNextIndex(AR_LONG, arIdComp, idx);
  46.           }
  47.           
  48.           if(forgot == 0)
  49.           {
  50.             if(Byte(curChunk) != 0xc3) addLong = 0;
  51.             else addLong = -8;
  52.  
  53.             xSpd = GetSpd(x);
  54.             if(__int64(xSpd) < 0) Message("next %x spd before -%x\n", x, -GetSpd(x));
  55.             else Message("next %x spd before %x\n", x, GetSpd(x));
  56.             if(GetSpd(x) + addLong != GetSpd(curChunk))
  57.             {
  58.               AddUserStkPnt(x,  GetSpDiff(x) - GetSpd(x) + GetSpd(curChunk) - addLong);
  59.               xSpd = GetSpd(x);
  60.               if(__int64(xSpd) < 0) Message("next %x spd after -%x\n", x, -xSpd);
  61.               else Message("next %x spd after %x\n", x, xSpd);
  62.  
  63.               return -1;
  64.             }
  65.  
  66.             if(== BADADDR)
  67.             {
  68.               y = x;
  69.               idx = GetLastIndex(AR_LONG, arIdComp) + 1;
  70.               SetArrayLong(arIdComp, idx, x);
  71.             }
  72.             else
  73.             {
  74.               idx = GetLastIndex(AR_LONG, arid) + 1;
  75.               SetArrayLong(arid, idx, x);
  76.             }
  77.           }
  78.         }
  79.       }
  80.       else if((XrefType() & 0x1f) == fl_F)
  81.       {
  82.         y = x;
  83.         //48 8D 64 24
  84.         if(Dword(curChunk) == 0x24648d48)
  85.         {
  86.           xSpd = Byte(curChunk + 4);
  87.           if(xSpd >= 0x80) xSpd = xSpd - 0x100;
  88.           AddUserStkPnt(x,  GetSpDiff(x) - GetSpd(x) + GetSpd(curChunk) + xSpd);
  89.         }
  90.       }
  91.     }
  92.     
  93.     if(== BADADDR)
  94.     {
  95.       idx = GetFirstIndex(AR_LONG, arid);
  96.       if(idx != -1)
  97.       {
  98.         y = GetArrayElement(AR_LONG, arid, idx);
  99.         DelArrayElement(AR_LONG, arid, idx);
  100.         idx = GetLastIndex(AR_LONG, arIdComp) + 1;
  101.         SetArrayLong(arIdComp, idx, y);
  102.       }
  103.     }
  104.     
  105.     curChunk = y;
  106.   }while(!= BADADDR);
  107.  
  108.   return 0;
  109. }
  110.  
  111. static main()
  112. {
  113.   auto curChunk, arid, arIdComp, result;
  114.  
  115.   curChunk = FirstFuncFchunk(ScreenEA());
  116.  
  117.   do
  118.   {
  119.     arid = CreateArray("chunks");
  120.     arIdComp = CreateArray("CompChunks");
  121.     result = recursFunc(curChunk);
  122.     DeleteArray(arid);
  123.     DeleteArray(arIdComp);
  124.   }while(result == -1);
  125.   
  126.   Message("Done!");
  127. }


Но сейчас функция достигла 174 фрагментов и это ещё, кажется, только половина, и времени на скрипт тратится около 1 минуты. Надо либо фиксить нэйтив иды, либо фикс-плаг писать, но времени на это сейчас нет.

Добавлено спустя 40 минут
Убрал лишний вывод сообщений и пошустрел скрипт - 5 сек

Добавлено спустя 15 часов 24 минуты
Странная вещь со скриптом. При количестве фрагментов функции, меньшем 100 он ещё нормально работал. Но вот, наконец, функция раскручена полностью в 197 фрагментов, и граф всё-таки построен так, ветвление через retn и jmp [rsp] не мешает - навигация по коду функции как будто ничего такого и не было. Но стали твориться спонтанные вещи... Теперь не всё правит скрипт...

Пришлось помучаться! Да да, именно помучаться! Не поизучать нормально характер глюков иды, а помучаться!!! Там нечего изучать - выполняем SetSpDiff() в одном месте - в другом слетает. Что со скриптом? Возможные причины(версии):
1) Случайным образом прерывается, не достигая конца
2) DeleteArray() не всегда полностью удаляет созданный массив
3) DelArrayElement() не всегда удаляет элемент массива, может быть даже не всегда тот
4) Что-то ещё вряд ли кому ведомое

Начиная понимать, что эта мистика чисто из-за багов idc-движка, задался экспериментом:
несколько раз запустил скрипт, каждый раз перед этим обнуляя везде spDiff вот так

Code:
  1. auto fch = FirstFuncFchunk(ScreenEA());
  2. while(fch != BADADDR)
  3. {
  4.   SetSpDiff(fch, 0);
  5.   fch = NextFuncFchunk(fch, fch);
  6. }


И что же получилось... Я не знаю, каким чудом у вас получается что-то писать на idc, как вы вообще ему верите, но у меня вот такой маленький списочек адресов фрагментов, на которых каждый раз idc не справился со своей задачей:

Code:
  1. 41DE1947
  2. 41D18625
  3. 41EAAA6A
  4. 41CCA2AA


Была бы проблема со скриптом - адрес каждый раз был бы один и тот же, так как генератора случайных чисел в скрипте нет... Касательно idc - иду в нейтиве править! Надо бы проверить питон...

Добавлено спустя 15 часов 28 минут
Но вряд ли на это время есть...

Добавлено спустя 16 часов 41 минуту
Самое интересное - 173 фрагмента в самом наихудшем их расположение потребовали бы таким idc-скриптом, даже если об не прерывался, 2 ^ 173 вызовов recursFunc()

И пришла в голову умная мысль на основе наблюдений, что надо фиксить spd у фрагментов в восходящем их порядке! Т.е. так, как они перечисляются функцией NextFuncFchunk(). Но заранее неизвестно, у какого фрагмента какой должен быть spd. Поэтому их придётся запоминать по мере выполнения в третьем массиве... Пока что из этой функи вытащил столько, с чем можно пробовать и созрели пара вариантов для достижения цели. Если не выгорит, придётся копать выше и тогда уже реализую новый скрипт.

Цель - пока секрет, позже напишу

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 17 марта 2016 21:27
· Личное сообщение · #11

Выгорело! Иде повезло

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 21 марта 2016 14:09
· Личное сообщение · #12

DenCoder пишет:
Цель - пока секрет, позже напишу

Не суеверен особо, но стараюсь держаться поговорки "не говори оп, пока не перепрыгнул".

Цель была необычная - исправить "баг" плагина под известное приложение.
Повозился немного... Достоинства защиты:
1) Неизученные пока методы антидебага
2) Противодействие аттачу дебаггером
3) Противодействие установке в контексте регистра Dr7 как изнутри, так и извне
4) Антипатчинг
...

но поборол! Как? Инжект + AddVectoredExceptionHandler() + PAGE_EXECUTE_READ + маленький встроенный дизасм + взвод TF.... )))

Добавлено спустя 5 минут
Суть "бага" - то ли защита каким-то образом обнаруживала, что её обошли, то ли недообошли - не знаю, оригинальный кряк делал не я. Но на определённых действиях плагин портил свой код, для чего и понадобилось поудобней представить ту самую функу в 173(или 174) куска. Дальше была просто найдено несколько возможных целей - попытки хукнуть не увенчались успехом... нашёлся способ проще - таблица! с rva правок - ну не палево? )

....

-----
IZ.RU





Ранг: 990.2 (! ! !), 380thx
Активность: 0.680
Статус: Модератор
Author of DiE

Создано: 28 марта 2016 22:13
· Личное сообщение · #13

DenCoder пишет:
1) Неизученные пока методы антидебага
2) Противодействие аттачу дебаггером
3) Противодействие установке в контексте регистра Dr7 как изнутри, так и извне
4) Антипатчинг


что там неизученного такого?))) интересно же а вех да, рулит! )

-----
[nice coder and reverser]


| Сообщение посчитали полезным: HandMill


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 29 марта 2016 14:35 · Поправил: DenCoder
· Личное сообщение · #14

Да мной не изучено. Не тратил время на изучение, так обошёл, без дебага.

Изучение, возможно, ещё предстоит )

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

Антидебаг в основном построен на 2х известных функциях - IsDebuggerPresent() и CheckRemoteDebuggerPresent(). Но не только, были ещё непонятные эксепшены, которые как ни передавай приложению - дебаг вылетал... Хотя, может, чего-то упустил...

Добавлено спустя 5 минут
Hellspawn пишет:
а вех да, рулит!

Кстати, из возможных противодействий веху - периодическая проверка последнего установленного хендлера и установка своего на переднем плане. В том числе можно и напрямую список проверять, не забывая ксорить с процессКуки... Ни разу такого пока не встречалось...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 06 апреля 2016 14:00 · Поправил: DenCoder
· Личное сообщение · #15

Наконец, выдалась пара свободных дней. Эти дни, конечно, можно отдохнуть, но я б хотел продолжить один из своих забытых проектов. В "борьбе с багами" флекса написался маленький x64-дизасм, помогший обойти дополнительные проверки лицензии. По всем правилам лучше б было разобрать все эти проверки полностью и сделать что-то универсальное типа кейгена, но глюки иды, без которых это и так затратно, вынудили сократить исследования... Из всего можно извлечь пользу. И польза в том, что этот проект не просто напомнил мне о забытом дизасме, который имел только лишь поддержку x86, без fpu и mmx, sse, sse2. А и побудил нацелиться расширить область применения и исправить пробелы. Надеюсь, 2-3 дней хватит хотя бы на основу (в основе структуры и константы, ими управляется логика дизасма, тут уж я люблю так ), а если нет - попутного мне ветра!

Преимущества своего дизасма в том, что не надо никого просить исправлять баги. В любое свободное время сел и поправил. В том числе, появляется возможность прикрутить его к вмтрейсеру и сделать из него эволюционное продолжение в направлении "инструмента статической распаковки"(позаимствовал термин у Faust'а), не основанного ни на какой платформе, будь то ида, олли или ещё что. Возможность расширенного сигнатурного анализа с какими угодно масками с проверками по смыслу и т.д...

Замахи большие, но ветер слабый...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 17 апреля 2016 17:06 · Поправил: DenCoder
· Личное сообщение · #16

Добавил rex-префиксы, удалил несколько инструкций, которых нет под x64, добавил movsxd, cqo и cdqe. Подправил большую декодирования полей modR/M и SIB, из-за rex-префиксов над декодингом ещё надо будет поработать...

Необходимость делать что-то другое, периодически что-то даже новое, не даёт вплотную заняться своим дизасмом. Так вот получил первый опыт по "форсированной" регистрации андроид-приложения, у которого в качестве основной защиты использовался класс LicenseChecker... Запатчил в 2х местах. Патчить код лучше в smali-виде, так надёжнее. Хорошо, что есть справочник по его опкодам, для справки в паре мест пригодилось.

Благо, какие-то инструменты уже есть. В принципе, всё что надо из них - apktool и SignApk, что-нить удобное для поиска... ну и мозги, конечно. Можно и иду использовать, но почему-то на некоторые методы она не переходит. Пробовал для подписи заюзать keytool и JarSigner - не хочет на планшете ставиться apk. Также для более удобного чтения кода неплохо бы в джаву преобразовать. dex2jar - первый этап, он нужен для преобразования apk в jar-архив. Дальше нужен декомпилятор, вот тут не повезло немного: jd-gui разбирает коряво некоторые методы, а бывает и целые классы. Посоветовали fernflower, но пока не юзал, мне хватило и этого всего.

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 23 мая 2016 20:24 · Поправил: DenCoder
· Личное сообщение · #17

Тем временем, после выбора очередного проекта и небольшого отдыха
--> Link <--

Я там не поставил лайк. Ну и? И не поставлю
И началось выклянчивание через личку. Не удовлетворил ответом - пошли в ход грубости... Я думаю, не стоит выкладывать ради чистоты истории...

Оставлю только в общих чертах.

Что ж, клянчили-клянчили, и выклянчили. Выклянчили пост в теме про денуво (пост там попросил сам удалить):
Молодец, Bronco!!!

Только это не повод закидывать меня спамом в личке! У меня своих задач хватает, чем-то помочь могу только по свободе от них.

ELF_7719116 пишет в личке:
Я еще поспамлю немного )

Вопрос: так понимаю есть наработки по вм вмпрота. таки было бы неплохо пустить их для денувки. по крайней мере я могу приспособить будущий софт на денувку под Ваши наработки


Вот блин, не ожидал от тебя ещё.
У вас, чё, мания всё выклянчивать? Я не только вмпротом занимаюсь, это не единственное дело-то!
Раз Вам не плохо было бы - пускаете свои наработки! А я не вижу в этом никакой выгоды ни для себя, ни для семьи, ни для человечества в целом!

Чем я хочу заниматься, у меня времени нет, а ещё Вам тут вынь, да положь. Всё, что надыбал по вмпроту, читайте в блоге здесь же. Не дебилы же? Будет у меня время, хорошо, раскрою там непонятные вопросы.
=============================================================================

Добавлено спустя 7 минут
Про что забыл - про желание упомянуть. Теперь очень сложно его вызвать...

Моё мнение по всему этому - то ли ребята коллективно забухали по скайпу (возможно, это известная конфа), то ли от счастья крыша поехала. У вас много свободного от работы времени - пожалуйста, при чём здесь я?
Я у Вамита ничего не клянчил, у него почерпнул только некоторые термины... зареганный вмпрот не имею права дать, и даже забудьте про это! Наработки - Вы заметили, что не так часто бывает свободное время, и кое-чем даже не имею права поделиться. В vmprotect 2.xx нет ничего такого, чем смог, тем поделился. Чем делюсь - оно в блоге, в основном для истории, мне же самому полезно бывает, потому как здесь я описываю так, как порой не вспомню.

Добавлено спустя 13 часов 4 минуты
Как я понял позже, исходя из переписки с ELF_7719116, "проект" встал, потому как запустить распакованное дело от OEP проблемно. Это уместилось в одно небольшое предложение! А не в 5 абзацев, как первоначально у Bronco:


Привет!
Вопрос как к опытному. Функи_процедуры в Лариске, опечатывали в три слоя или захода.
Вторая ВМ имеет таблицу указателей (поксорены), и если я правильно понял, это главное отличие от новой вм.
Вопрос 1.
Не считая макросов по бекапу регистров на входе в вм, переходы между "блоками" исполняют командой jmp reg. В таблице 256 указателей, без дубликатов порядка 80, не имеющие дубликатов 7, из них 2 возможно уводят в другой цикл, но на них прерываний нет.
Как двигло выходит из цикла?
Вопрос 2.
Учитывая что и опыт и практика у тебя больше моего, может чем то подсобишь в плане вм?
Вопрос3.
Пикод используется, как символика для скриптового языка ВМДвигла?


Мне, честно говоря, пришлось потрудиться, чтоб что-то понять, чтоб на это что-то ответить... Да, донести смысл того, что хочешь, оказывается умение...

2 человека. Один знает, другой хочет спросить. Неужели трудно сосредоточиться и понятным языком задать вопрос? От этого же зависит ответ! И не только. Ценить время другого, которое тратится на понимание вопроса, не все умеют? Некоторые пишут какую-то абракадабру, несвязную чушь, открывая новый топик, и что самое интересное - находятся люди, которые их непонятным чудом понимают... 1 из 5-10...

Вот что отвечу на понятый мной вопрос по проблеме(и исключительно потому, что в теме есть 3 адекватных знакомых по их постам здесь, и это не Bronco):
В настоящее время вмпротом нет времени продолжать заниматься. Но над проблемой думалось на досуге. В теории запустить дамп можно. Это ведь имелось в виду? Или я опять не понял?

Добавлено спустя 13 часов 26 минут
Только у вас там, кажется, 3.x - может не совсем подойти.

В старте дампа из OEP накрытой ранее вмпротом 2.x dll основных 3 проблемы: импорт, таблица релоков и дельта.

Таблицу релоков можно вставить как секцию. Таблица там - не помню, то ли спрятана, то ли так лежит, но проходя стадии распаковки её можно найти.

Дельта - необходимый аргумент для вм-диспетчера. Это я упускал всегда из виду - пока серьёзно и не пробовал запускать дампы. Но помню о ней.

Импорт - на мой взгляд есть самая быстрая реализация, есть правильная. Правильная - это убрать замусоренные стабы на функи и заменить кривые "адреса" на правильные, для чего нужно по всем пройтись... возможно поиском на этой стадии распаковки. Быстрая - оставляем всё, как есть, но перед OEP вставляем свой импортёр - вырванный кусок вм-ленты процесса импорта.

Добавлено спустя 13 часов 30 минут
Прог для этого готовых нет, а кодом трассировки делиться не стану - он ужасный Обычно я лучше пишу...

Добавлено спустя 13 часов 32 минуты
Ну и да - если импортёр вставить правильно, то можно избежать поисков в около 100-200 лент проверок crc.

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 25 мая 2016 14:44 · Поправил: DenCoder
· Личное сообщение · #18

Далее волею зануд и школьников обстоятельств проект приобретает закрытый характер!

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 11 июня 2016 16:19
· Личное сообщение · #19

Интересная задача встала - реализовать эксплойт под две версии прошивки для девайса на процессоре MIPS. Прошивка размером 128 кб. С нуля искать уязвимости было бы затяжно. Но код уже был к этому времени изучен в процессе предыдущего реверса для восстановления файлов конфигурации...

Девайс предоставляет usb-интерфейс для работы с ним, одна из функций которого - запись определённых файлов. Файлы строго определённого формата, суть которого - набор некоторых блоков. Один из блоков - сама начинка, чаще всего это elf, и обязательно должен присутствовать блок с подписью неизвестным ключом из сертификата. Сертификат известен, на факторизация RSA2048 пока на грани фантастики...

Набор блоков... Первое, что бросилось в глаза - собирается инфа обо всех блоках в файле, и для каждой такой инфы структура размером 0x18 байт на стеке! Второе - контроля число блоков нет, контроль повтора блоков - слабый, проверяются только соседние блоки, они не должны повторяться. Также есть некий блок, размер которого может быть до 128 байт, и после сбора инфы обо всех блоках он также пишется ниже по стеку. И самое важное - хендлер находится под стеком, что значит - опустив стек до хендлера, можно перезаписать его 128 байт!

На x86 в 128 байт влезло бы достаточно... Но мипс имеет ущербный ассемблер. С одной стороны есть преимущества. Например, инструкция
Code:
  1. mov eax, [ebx + ecx + 0x80]
размером 7 байт на мипсе выглядела бы так
Code:
  1. addiu $s0, $a0, $a1
  2. lw $s0, 0x80($s0)
, в mips16 это бы заняло 4 байта. Но есть недостатки:
1) хендлер работает в режиме mips32, что значит - все инструкции либо 4 байта, либо 8, если используется расширение. Необходимо сохранить некоторые регистры перед переключением режима с 32 на 16. И x86-инструкция push reg в один байт на mips32 занимает 4.
2) Переключение режимов с целью выиграть на размере кода возможно 2мя способами, самый малоёмкий - jalx, 4 байта. Но тогда надо сохранять $ra - адрес возврата, по которому должна перейти какая-то функция, выполнявшаяся на момент вызова хендлера. А это 4 байта. Сам этот адрес вовзрата не сохраняется в мипсе, как на x86 при выполнение call.
3) Есть специфические регистры, обмен между которыми что на мипс16, что на мипс32 занимал бы 4 байта. На x86 это всегда 2 байта. Когда надо уложиться в 128 байт, думаешь о каждой мелочи
4) Нельзя в одну команду уложиться, чтоб в регистр записать произвольный адрес, если
Code:
  1. addr & 0xF0000000 != pc & 0xF0000000
. Либо, если можно - то это всегда на мипс16 будет 6 байт - 4 байта на адрес за функцией + 2 байта инструкция. Выигрыш такой схемы только если адрес используется больше 2х раз, и функция не больше определённого размера (используется pc-привязка для адресации)
...

Всё это накладывает ограничения такие, что в один эксплойт не уложиться. И схема такая - первая часть эксплойта восстанавливает хендлер и записывает часть кода по свободному адресу. Вторая часть эксплойта делает то же самое, но уже для восстановления хендлера использует часть сохранённого кода и добавляет немного больше кода по свободному адресу. Второй, третьей, ..., (n-1)-ной частями собираем нужный код и последней частью активируем!

Сама по себе уязвимость странная в том, что на блок отводится 128 байт, когда там хватило бы 8! Не буду писать, что это за блок, но стоит призадуматься - большая вероятность, что это закладка, специально кем-то созданная и оставленная во всех версиях, чтоб когда надо ей кому-то воспользоваться... Я уже не говорю про ноутбуки, планшеты и мобильники... Может быть в мультиварках, телевизорах, пылесосах уже пора искать закладки?

-----
IZ.RU


| Сообщение посчитали полезным: daFix


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 16 июня 2016 03:28
· Личное сообщение · #20

Решено было перезаписать хвост эксепшен-хендлера, так как это даёт больше места для кода за счёт ненадобности восстанавливать исходный хендлер. Но не всё так просто, пришлось побороться с кешем инструкций, чтоб код в хвосте выполнялся. Чтобы сбросить кеш инструкций, мипсу нужно выполнить побольше условных переходов... Одно мешает с некоторой вероятностью - прерывание от таймера в неподходящий момент...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 18 июня 2016 03:18
· Личное сообщение · #21

Победа!

-----
IZ.RU


| Сообщение посчитали полезным: HandMill


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 30 июня 2016 13:43
· Личное сообщение · #22

Глюки иды:

Code:
  1. ROM:80007808 028 02 18 F4 0E                   jal     0x81003BD0
  2. ROM:8000780C 028 3E 0D                         la      $a1, byte_80007900+4
  3. ROM:8000780E 028 3D 0C                         la      $a0, byte_80007900


На второй инструкции должно быть la $a1, byte_80007900, потому как инструкция в бранч-слоте.

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 18 сентября 2016 16:54 · Поправил: DenCoder
· Личное сообщение · #23

DenCoder пишет:
Одно мешает с некоторой вероятностью - прерывание от таймера в неподходящий момент...

И было выпущено ещё 2 эксплойта под версии прошивки новее. То, что хендлер прерывания от таймера способен попортить код в неподходящем месте, решается определённым количеством невыровненных по границе 4 байт блоков в файле. Правильный подбор невыровненных блоков позволяет повысить вероятность задержки момента конца их разбора в функции настолько, чтобы попасть в нужный интервал времени, когда хвост хендлера перезаписывается сразу после его отработки. К сожалению, из-за таймера и неопределённости момента заливки файла в девайс 100% успеха недостижимо - 64 байта всегда за разные интервалы времени заливаются по usb, и отчасти виновата винда. Достигнуто было в районе 50-80 в зависимости от версии прошивки.

-----
IZ.RU


| Сообщение посчитали полезным: daFix


Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 16 декабря 2016 00:09
· Личное сообщение · #24

Исследовал один сервис. Антидебага нет, есть самозащита... Такое бывает у антивирусов. Интересна одна вещь - где под x86 приёмы не прокатывают, под x64 - как раз самое то

-----
IZ.RU





Ранг: 529.0 (!), 110thx
Активность: 0.290.04
Статус: Участник
5KRT

Создано: 23 января 2017 20:03
· Личное сообщение · #25

Ден, продолжай писать, интересно было

-----
Research For Food





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 26 января 2017 13:59
· Личное сообщение · #26

Да как-нибудь когда-нибудь... Не обо всём могу писать, но думаю, что где-нибудь уже в марте добавлю что-нибудь о методах борьбы с обфускацией при статическом анализе, либо другое что-то.

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 01 марта 2017 18:42
· Личное сообщение · #27

Пишу скрипт на idc... Пожалел 100 раз, что в своё время не решил реализовывать обработку кода с разделением функций между idc и нативным приложением. Эту возможность можно реализовать, например, через пайпу - открыть канал и писать туда команды для нативного приложения, а по другому каналу можно принимать ответ... Почему именно так лучше? Потому что:
1) idc медленный, обработка строк, массивов занимает много больше времени, чем если бы нативно
2) idc ограничен в возможностях кодинга, приходится изобретать много чего, чтоб работало как надо

-----
IZ.RU





Ранг: 338.5 (мудрец), 349thx
Активность: 2.112.42
Статус: Участник

Создано: 03 марта 2017 03:59 · Поправил: difexacaw
· Личное сообщение · #28

3. Пайпы(каналы) тоже крайне тормозные. Порты(LPC) на порядки шустрее.

-----
vx





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 03 марта 2017 17:55 · Поправил: DenCoder
· Личное сообщение · #29

Вообще лучше, конечно, полностью независимая нативная реализация обработки кода с возможностью обмена информацией с идой. Но это займёт большее время, поскольку обрабатываемый код под процессор ARM.

Подцель - детектов циклов и занесение их в список для целей дальнейшего поиска в них какого-то адреса. Для реализации этого нужно следить за пройденным путём, и если следующий адрес блока замечен где-то в пути, то выделять подпуть от того места до конца. На idc алго для этого построен на строках и массивах. Пройденный путь кода, в котором начальные адреса блоков, реализован на idc в виде строки типа
Code:
  1. 0000023458-0000023484-00000234ac-...
.

Списков в idc нет, поэтому вместо списка массив, для образования которого используются функции GetLastIndex()/SetArrayLong().

Пройденный путь и подпути можно реализовать и в виде массива idc-функциями GetLastIndex()/SetArrayLong(), каждый массив с элементами цикла содержал бы тогда в имени уникальное строковое представление из одного или нескольких элементов цикла.

Очень интересно в idaapi реализованы и строки, и массивы - нет ограничения по размеру. И это одна из возможных причин тормозов функций для работы с ними. 1600 блоков и выделение 300 циклов обрабатывается idc-скриптом около 8 сек. Если заменить эти функции на запись в пайпу, адреса блоков будут передаваться внешнему приложению. Весь вопрос - что быстрее? Преобразование адреса в строку и добавление её в другую или отправка/приём? Добавление элемента в idc-массив или отправка/приём?

difexacaw пишет:
Порты(LPC) на порядки шустрее.

Использование LPC требует либо написания плагина для иды, либо перехвата каких-то функций иды(много вариантов, каких именно функций) и это время.

Другой вариант - использовать питон вместо idc. И некогда все эти варианты тестировать, нужно просто знать.

Глобальная цель - деобфускация. В замусоривании используются такие приёмы, которые раньше мне не встречались, потому назвал их "лжециклы" и "скрутка". Суть их объясню ниже. Поправьте, если подобное видели и этому есть общепринятое название.

лжециклы
всевозможные варианты реализации простой конструкции
Code:
  1. do
  2. {
  3. ...
  4. }while(false);


Например,

Code:
  1. int a = 4;
  2. while(+ 1 < 6)
  3. {
  4. ...
  5. = 5;
  6. }


скрутка
В разных ветках кода имеется общий код проверки какого-то условия. В одной ветке условие истинно, в другой - ложно.

Например
Code:
  1. for(int i = 0; i < 10; i++)
  2. {
  3.   //1я часть кода цикла 1
  4.   //...
  5.   
  6.   a = 0;
  7.   goto ext_code;
  8.  
  9. loop_1:
  10.   //2я часть кода цикла 1
  11.   //...
  12. }
  13.  
  14. for(int i = 0; i < 10; i++)
  15. {
  16.   //1я часть кода цикла 2
  17.   //...
  18.   
  19.   a = 1;
  20.   goto ext_code;
  21.  
  22. loop_2:
  23.   //2я часть кода цикла 2
  24.   //...
  25. }
  26.  
  27. ext_code:
  28.   if(== 0) goto loop_1;
  29.   else goto loop_2;


В представлении графом асм-кода ветви кода выглядят переплетёнными, скрученными, откуда и название.

Для деобфускации подобного пытался найти что-то готовое, не нашёл.

Добавлено спустя -48 минут
На текущий момент выделены из 344 циклов
24 настоящих, несущих полезную нагрузку
24 "лжецикла" с более сложной реализацией, ждут улучшения скрипта/улучшения общего подхода

Остальные все обработаны - развёрнуты, но на это потребовалось при текущей реализации около 24 часов (с перерывами на сон, перекуры, ...). Работа проделывалась в полуавтоматическом режиме - указывался адрес, по которому скрипт делал своё дело. "Скрутки" были обнаружены в ходе работы. Сколько их - надо модифицировать алго.

Добавлено спустя 2 часа 47 минут
Определил, чем была произведена обфускация. Был использован Android Clang, основанный на известной компиляторной инфраструктуре LLVM. То есть обфускация производится на этапе компиляции. Попробую от этого оттолнкуться...

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 04 марта 2017 03:06 · Поправил: DenCoder
· Личное сообщение · #30

Бывает, видим использование какого-то регистра, сравнение или использование в вычислениях, а в ближайшем окружении выше не видно его установки. Функция огромная! Нужно найти адрес установки регистра. На помощь приходит скрипт(для ARM-инструкций):

Code:
  1. #include <idc.idc>
  2.  
  3. static FindLongInArray(arId, x)
  4. {
  5.   auto addr, idx;
  6.   idx = GetFirstIndex(AR_LONG, arId);
  7.   while(idx != -1)
  8.   {
  9.     addr = GetArrayElement(AR_LONG, arId, idx);
  10.     if(addr == x) break;
  11.     idx = GetNextIndex(AR_LONG, arId, idx);
  12.   }
  13.   
  14.   return idx;
  15. }
  16.  
  17. static findAddr(path, addr)
  18. {
  19.   return strstr(path, sprintf("%08x", addr));
  20. }
  21.  
  22. static FindBlock(x)
  23. {
  24.   auto addr, block = GetArrayId("block"), idx;
  25.   idx = GetFirstIndex(AR_LONG, block);
  26.   while(idx != -1)
  27.   {
  28.     addr = GetArrayElement(AR_LONG, block, idx);
  29.     if(addr == x) break;
  30.     idx = GetNextIndex(AR_LONG, block, idx);
  31.   }
  32.   
  33.   return idx;
  34. }
  35.  
  36. static FindBlockEnd(x)
  37. {
  38.   auto addr, block = GetArrayId("blockEnd"), idx;
  39.   idx = GetFirstIndex(AR_LONG, block);
  40.   while(idx != -1)
  41.   {
  42.     addr = GetArrayElement(AR_LONG, block, idx);
  43.     if(addr == x) break;
  44.     idx = GetNextIndex(AR_LONG, block, idx);
  45.   }
  46.   
  47.   return idx;
  48. }
  49.  
  50. static findBlockByAddr(addr, addrDn, addrUp)
  51. {
  52.   auto block = GetArrayId("block"), blockEnd = GetArrayId("blockEnd"), idx;
  53.   idx = GetFirstIndex(AR_LONG, block);
  54.   while(idx != -1)
  55.   {
  56.     addrDn = GetArrayElement(AR_LONG, block, idx);
  57.     addrUp = GetArrayElement(AR_LONG, blockEnd, idx);
  58.     if(addrDn <= addr && addrUp >= addr) break;
  59.     idx = GetNextIndex(AR_LONG, block, idx);
  60.   }
  61.   
  62.   return idx;
  63. }
  64.  
  65. static CollectLoops(curAddr, path)
  66. {
  67.   auto x, y, loops = GetArrayId("loops"), block = GetArrayId("block"), idx, fidx, l, verBack = 0,
  68.   setverBack = 1, blockEnd = GetArrayId("blockEnd"), prevAddr, xref;
  69.   
  70.   idx = GetLastIndex(AR_LONG, block) + 1;
  71.   SetArrayLong(block, idx, curAddr);
  72.   if(path != "")
  73.     path = sprintf("%s-%08x", path, curAddr);
  74.   else path = sprintf("%08x", curAddr);
  75.   
  76.   do
  77.   {
  78.     //Message("%x\n", curAddr);
  79.     x = Rfirst(curAddr);
  80.     if(== BADADDR || (xref = (XrefType() & 0x1f)) != fl_F && xref != fl_JN)
  81.     {
  82.       //Message("End: %x\nPath:%s\n", curAddr, path);
  83.       return 1;
  84.     }
  85.   
  86.     if(verBack == 1)
  87.     {
  88.       y = RfirstB(curAddr);
  89.       if((XrefType() & 0x1f) == fl_JN || RnextB(curAddr, y) != BADADDR)
  90.       {
  91.         SetArrayLong(blockEnd, idx, prevAddr);
  92.       
  93.         l = findAddr(path, curAddr);
  94.         if(!= -1)
  95.         {
  96.           //Message("Loop: %s\n", substr(path, l, -1));
  97.           fidx = GetLastIndex(AR_STR, loops) + 1;
  98.           SetArrayString(loops, fidx, substr(path, l, -1));
  99.           return 6;
  100.         }
  101.       
  102.         fidx = FindBlock(curAddr);
  103.         if(fidx == -1)
  104.         {
  105.           idx = GetLastIndex(AR_LONG, block) + 1;
  106.           SetArrayLong(block, idx, curAddr);
  107.           path = sprintf("%s-%08x", path, curAddr);
  108.         }
  109.         else return 7;
  110.       }
  111.     }
  112.         
  113.     if(xref == fl_JN)
  114.     {
  115.       SetArrayLong(blockEnd, idx, curAddr);
  116.       
  117.       l = findAddr(path, x);
  118.       if(!= -1)
  119.       {
  120.         //Message("Loop: %s\n", substr(path, l, -1));
  121.         fidx = GetLastIndex(AR_STR, loops) + 1;
  122.         SetArrayString(loops, fidx, substr(path, l, -1));
  123.         return 2;
  124.       }
  125.  
  126.       fidx = FindBlock(x);
  127.       if(fidx == -1)
  128.       {
  129.         idx = GetLastIndex(AR_LONG, block) + 1;
  130.         SetArrayLong(block, idx, x);
  131.         path = sprintf("%s-%08x", path, x);
  132.         curAddr = x;
  133.         setverBack = verBack = 0;
  134.       }
  135.       else return 3;
  136.     }
  137.     else if((= Rnext(curAddr, x)) != BADADDR && (XrefType() & 0x1f) == fl_JN)
  138.     {
  139.       SetArrayLong(blockEnd, idx, curAddr);
  140.       
  141.       l = findAddr(path, y);
  142.       if(!= -1)
  143.       {
  144.         //Message("Loop: %s\n", substr(path, l, -1));
  145.         fidx = GetLastIndex(AR_STR, loops) + 1;
  146.         SetArrayString(loops, fidx, substr(path, l, -1));
  147.         return 4;
  148.       }
  149.  
  150.       fidx = FindBlock(y);
  151.       if(fidx == -1) CollectLoops(y, path);
  152.       
  153.       fidx = FindBlock(x);
  154.       if(fidx == -1)
  155.       {
  156.         idx = GetLastIndex(AR_LONG, block) + 1;
  157.         SetArrayLong(block, idx, x);
  158.         path = sprintf("%s-%08x", path, x);
  159.         curAddr = x;
  160.         setverBack = verBack = 0;
  161.       }
  162.       else return 5;
  163.     }
  164.     else
  165.     {
  166.       prevAddr = curAddr;
  167.       curAddr = x;
  168.     }
  169.     
  170.     if(setverBack == 1) verBack = 1;
  171.     else setverBack = 1;
  172.   }while(1);
  173. }
  174.  
  175. static findSetReg(addr, regnum, addrEnd, backPath, foundAddrsId, passedBlocksId)
  176. {
  177.   auto x, y, cmd, addrDn = -1, addrUp, result, idx, fidx;
  178.   
  179.   while(1)
  180.   {
  181.                  //Если адрес в пределах границ блока
  182.                  if(addrDn == -1 || addr < addrDn || addr > addrUp)
  183.                  {
  184.                         fidx = findBlockByAddr(addr, &addrDn, &addrUp);
  185.                         if(fidx == -1) return -1;
  186.                         
  187.                         if(FindLongInArray(passedBlocksId, addrDn) == -1)
  188.                         {
  189.                               idx = GetLastIndex(AR_LONG, passedBlocksId) + 1;
  190.                               SetArrayLong(passedBlocksId, idx, addrDn);
  191.                         }
  192.                         //если адрес блока уже попадался
  193.                         else
  194.                         {
  195.                               //Message("BREAK\n\n");
  196.                               return -1;
  197.                         }
  198.  
  199.                         /*
  200.                         if(backPath != "")
  201.                         {
  202.                               if(findAddr(backPath, addrDn) != -1) return -1;
  203.                               backPath = sprintf("%s-%08x", backPath, addrDn);
  204.                         }
  205.                         else backPath = sprintf("%08x", addrDn);
  206.                         */
  207.  
  208.                         //Message("current block: %d %x-%x\n", fidx, addrDn, addrUp);
  209.                  }
  210.  
  211.                  //Message("%x\n", addr);
  212.     cmd = DecodeInstruction(addr);
  213.     if(cmd.itype < 0x12 || cmd.itype > 0x15 && cmd.itype != 0x20)
  214.     {
  215.       //пока так
  216.       if(GetOpType(addr, 0) == o_reg && GetOperandValue(addr, 0) == regnum)
  217.       {
  218.                               //Message("found R%d: %x\n", regnum, addr);
  219.                               return addr;
  220.                         }
  221.     }
  222.  
  223.     if(addrEnd != -1 && addrEnd == addr) return 0;
  224.  
  225.     x = RfirstB(addr);
  226.     while(!= BADADDR && (XrefType() & 0x1f) != fl_JN && (XrefType() & 0x1f) != fl_F) x = RnextB(addr, x);
  227.     if(== BADADDR) return -1;
  228.  
  229.                  y = x;
  230.                  do
  231.                  {           
  232.                         y = RnextB(addr, y);
  233.                         while(!= BADADDR && (XrefType() & 0x1f) != fl_JN && (XrefType() & 0x1f) != fl_F) y = RnextB(addr, y);
  234.                         if(== BADADDR) break;
  235.                         
  236.                         result = findSetReg(y, regnum, addrEnd, backPath, foundAddrsId, passedBlocksId);
  237.                         if(result != -1)
  238.                         {
  239.                               //добавить в массив
  240.                               idx = GetLastIndex(AR_LONG, foundAddrsId) + 1;
  241.                               result = SetArrayLong(foundAddrsId, idx, result);
  242.                               //Message("index: %d\nresult: %d\n", idx, result);
  243.                         }
  244.     }while(1);
  245.  
  246.     addr = x;
  247.   }
  248. }
  249.  
  250. static main()
  251. {
  252.   auto curAddr, result, block, blockEnd, loops, idx, path = "", backPath = "", foundAddrsId, passedBlocksId, regnum;
  253.   
  254.   block = GetArrayId("block");
  255.   if(block != -1) DeleteArray(block);
  256.  
  257.   blockEnd = GetArrayId("blockEnd");
  258.   if(blockEnd != -1) DeleteArray(blockEnd);
  259.   
  260.   loops = GetArrayId("loops");
  261.   if(loops != -1) DeleteArray(loops);
  262.   
  263.   //do
  264.   {
  265.     loops = CreateArray("loops");
  266.     block = CreateArray("block");
  267.     blockEnd = CreateArray("blockEnd");
  268.     curAddr = FirstFuncFchunk(ScreenEA());
  269.     result = CollectLoops(curAddr, path);
  270.     Message("Циклы и блоки собраны\n%d блоков\n%d циклов\n", GetLastIndex(AR_LONG, block) + 1, GetLastIndex(AR_STR, loops) + 1);
  271.  
  272.                  //Найти приравнивание регистра вверх
  273.                  regnum = AskLong(0, "Enter register number(0-15)");
  274.                  if(regnum != -1)
  275.                  {
  276.                         passedBlocksId = GetArrayId("passedBlocks");
  277.                         if(passedBlocksId != -1) DeleteArray(passedBlocksId);
  278.                         passedBlocksId = CreateArray("passedBlocks");
  279.                  
  280.                         foundAddrsId = GetArrayId("foundAddresses");
  281.                         if(foundAddrsId != -1) DeleteArray(foundAddrsId);
  282.                         foundAddrsId = CreateArray("foundAddresses");
  283.                         
  284.                         curAddr = findSetReg(ScreenEA(), regnum, -1, backPath, foundAddrsId, passedBlocksId);
  285.                         //if(curAddr != -1)
  286.                         {
  287.                               if(curAddr != -1) Message("R%d: %x\n", regnum, curAddr);
  288.                               idx = GetFirstIndex(AR_LONG, foundAddrsId);
  289.                               while(idx != -1)
  290.                               {
  291.                                    curAddr = GetArrayElement(AR_LONG, foundAddrsId, idx);
  292.                                    Message("R%d: %x\n", regnum, curAddr);
  293.                                    idx = GetNextIndex(AR_LONG, foundAddrsId, idx);
  294.                               }
  295.                         }
  296.                         DeleteArray(foundAddrsId);
  297.                         DeleteArray(passedBlocksId);
  298.                  }
  299.                  
  300.     DeleteArray(loops);
  301.     DeleteArray(block);
  302.     DeleteArray(blockEnd);
  303.   }//while(result == 0);
  304.   
  305.   Message("%d Done!\n", result);
  306. }


В скрипте не учтены инструкции LDM. функция CollectLoops() затормаживает процесс на 5-15 секунд при больших функциях. Интересно было бы увидеть, как это будет выглядеть на питоне, и насколько быстрей будет работать. По питону у меня пока уровень знаний чуть больше 0... Кстати, сбор всех циклов и блоков для целей поиска установки регистра не нужен. Не нужен, как оказалось и в моём скрипте, и в следующей модификации интегрирую функцию в findSetReg, чтобы сбор производился по необходимости.

Скрипт обновлён 5 марта 2017.
Исправлены ошибки в CollectLoops

-----
IZ.RU





Ранг: 324.3 (мудрец), 221thx
Активность: 0.480.37
Статус: Участник

Создано: 04 марта 2017 21:07 · Поправил: DenCoder
· Личное сообщение · #31

Скрипту, конечно, нужна ещё оптимизация, что есть одно из последних дел...

Чем полезен скрипт?

Во-первых, экономит время и мозги в нахождении простых вещей.

Во-вторых, это не просто скрипт, а алгоритм. И его можно переложить на любой другой язык, скриптовый или компилируемый. Может быть на питоне, может быть на си под capstone... Что сделать - это стратегия! Язык - это тактика, дело наживное! Не важно как, если не знать, что!

В-третьих, с небольшими изменениями функции findSetReg() в него можно добавить findSetRegForward(поиск установки регистра вперёд) и findUseReg(поиск использования регистра вперёд), что вместе при должной доработке будет базой для деобфускации в части удаления мусорных инструкций, не управляющих ветвлением...

Знаю - ничего нового я не открыл, примерно так же и работают деобфускаторы. Но в деобфускации я прохожу какой-то свой собственный путь, жалея время на чтиво(была б возможность параллельно читать теорию - непременно воспользовался бы). На пути иногда посматриваю налево и направо и вижу какие-то элементы теории. Например, прозрачные и непрозрачные предикаты. В VMProtect(также на пример) в невиртуализированном коде как раз используются прозрачные предикаты, в виртуализированном - непрозрачные, но только при несоблюдении условий проверки целостности кода и если стоит точка останова не первом байте вызываемой из вм апи-функции...

Добавлено спустя -24 минут
Пытаюсь поймать попутку, останавливают - не туда... Иногда подбрасывают поближе )

-----
IZ.RU



<< . 1 . 2 . 3 . >>
 eXeL@B —› Дневники и блоги —› Блог DenCoder'а
:: Ваш ответ
Жирный  Курсив  Подчеркнутый  Перечеркнутый  {mpf5}  Код  Вставить ссылку 
:s1: :s2: :s3: :s4: :s5: :s6: :s7: :s8: :s9: :s10: :s11: :s12: :s13: :s14: :s15: :s16:


Максимальный размер аттача: 500KB.
Ваш логин: german1505 » Выход » ЛС
   Для печати Для печати