Дизассемблирование программ, откомпилированных Visual C++. Часть 3.

Discussion in 'Реверсинг' started by _=(mac)=_, 28 May 2009.

  1. _=(mac)=_

    _=(mac)=_ Member

    Joined:
    28 Feb 2009
    Messages:
    9
    Likes Received:
    8
    Reputations:
    0
    Дизассемблирование программ, откомпилированных Visual C++

    Часть 3.


    Идентификация многомерных массивов


    В предъидущей статье я описывал идентификацию одномерных массивов целого типа, но на этот раз я решил немного поднапрячь мозг и описать разбор матрицы дробного типа, принимая к сведению некоторые замечания по поводу второй части)))

    Для начала мы, как обычно, напишем программу, принимающую от нас матрицу дробных чисел из 5 строк и 5 столбцов. Ну что ж… Приступим!​

    Code:
    #include <iostream>
    using namespace std;
    
    int main()
    {
    const int ROW = 5;
    const int COLUMN = 10;
    double matrix [ ROW ] [ COLUMN ];
    for (int i = 0; i < ROW; i++)
    	for (int j = 0; j < COLUMN; j++)
    		cin >> matrix [i] [j];
    return 0;
    }
    
    Матрица – это двумерный массив, поэтому для ввода каждого элемента необходимы два цикла, которые вкладываются друг в друга. В таких случаях всегда выполняется первым самый внутренний цикл. Он будет выполняться столько же, сколько и внешний.
    В примере нашем внешний цикл инициализирует переменную i, которая является индексом строки, а индексом столбца является переменная j во втором, вложенном, цикле. Во внутреннем цикле происходит ввод значения элемента матрицы с индексами i, j.
    Во время работы программы необходимо ввести 5 строк чисел по 10 элементов в каждой строке.
    Ну что ж… Компилим, загружаем в Иду, переходим на 00401000 и начинаем…


    .text:00401000 sub_401000 proc near
    .text:00401000
    .text:00401000 var_1A0 = dword ptr -1A0h
    .text:00401000 var_19C = dword ptr -19Ch
    .text:00401000 var_198 = dword ptr -198h
    .text:00401000 var_8 = dword ptr -8
    .text:00401000 var_4 = dword ptr -4
    ;Объявление локальных переменных…
    .text:00401000 push ebp
    .text:00401001 mov ebp, esp
    .text:00401003 sub esp, 1A0h
    .text:00401009 mov [ebp+var_8], 5
    .text:00401010 mov [ebp+var_4], 0Ah
    .text:00401017 mov [ebp+var_19C], 0
    .text:00401021 jmp short loc_401032
    ;И так… Здесь происходит присвоение ячейкам памяти [ebp+var_8] и [ebp+var_4] значения соответственно 5 и 10 (0Ah), это и есть объявление наших констант ROW и COLUMN, а вот строчка “mov [ebp+var_19C], 0” – это присвоение переменной [ebp+var_19C] (индекс строки - i) нулевого значения (int i = 0), после чего переход на 401032.
    .text:00401023 ; ---------------------------------------------------------------------------
    .text:00401023
    .text:00401023 loc_401023:
    .text:00401023 mov eax, [ebp+var_19C]
    .text:00401029 add eax, 1
    .text:0040102C mov [ebp+var_19C], eax
    ;Здесь инкременируется значение j (j++).
    .text:00401032
    .text:00401032 loc_401032:
    .text:00401032 cmp [ebp+var_19C], 5
    .text:00401039 jge short loc_401087
    .text:0040103B mov [ebp+var_1A0], 0
    .text:00401045 jmp short loc_401056
    ;Во!. Сначала сравниваем i с ROW, то бишь 5, и если i больше 5 или равен (jge), то переходим на адрес 401087, т.е. выход из программы, а если иначе, то переходим к выполнению следующих инструкций. А следующая инструкция у нас - mov [ebp+var_1A0], 0. Это присвоение некой ячейке памяти (переменной) нулевого значения. Хм… Опустим глаза ниже… Мы видим, что происходит увеличение нашей этой переменной на 1. Сомнений не остаётся: [ebp+var_1A0] – это j, индекс столбца. Значит теперь мы находимся уже в теле второго, вложенного цикла. А теперь джимп на 401056… Переходим туда…
    .text:00401047 ; ---------------------------------------------------------------------------
    .text:00401047
    .text:00401047 loc_401047:
    .text:00401047 mov ecx, [ebp+var_1A0]
    .text:0040104D add ecx, 1
    .text:00401050 mov [ebp+var_1A0], ecx
    ;В данном блоке инкременируется значение j и программа выполняется дальше, т.е. переходит управление на 401056))
    .text:00401056 loc_401056:
    .text:00401056 cmp [ebp+var_1A0], 0Ah
    .text:0040105D jge short loc_401085
    .text:0040105F mov edx, [ebp+var_19C]
    .text:00401065 imul edx, 50h
    .text:00401068 lea eax, [ebp+edx+var_198]
    .text:0040106F mov ecx, [ebp+var_1A0]
    .text:00401075 lea edx, [eax+ecx*8]
    ; Здесь идёт проверка условия внутреннего цикла, а именно проверка j на 10, т.е. сравнение с COLUMN. Если j больше или равно 10, то на 401085(где нас опять пошлют на 401023, где на единицу увеличивается i), в противном случае заносим в edx индекс i, после чего умножаем его на 50 с занесением результата в edx (imul edx, 50h). Так, а тут (lea eax, [ebp+edx+var_198]) в eax кладём смещение по указателю [ebp+edx – 198]. Теперь в ecx мы копируем j (mov ecx, [ebp+var_1A0]), после чего мы кладём в edx смещение [eax+ecx*8] – это как раз тот адрес, который ниже кладётся в стек как параметр к функции ввода. Это и есть буфер для ввода, в котором окажется число, которое мы введём. Этот буфер будет всегда уникален благодаря блокам 401023 и 401047, которые инкременируют значение индексов j и i, по содержанию которых как раз – таки и высчитывается ячейка памяти, где будет храниться введённое нами число.
    .text:00401078 push edx
    .text:00401079 mov ecx, offset dword_4207D4
    .text:0040107E call sub_401090
    .text:00401083 jmp short loc_401047
    ;Кладём в стек буфер для ввода и вызываем функцию ввода. Напомню, что массив является дробного типа, поэтому каждое введённое нами число будет ещё и в регистре сопроцессора ST7 и в памяти. Но при вводе нового числа в ST7 оно попадает на место старого, но старое так же и остаётся в памяти. Почему же, вы спросите, тогда нет команд fst, fstp и прочих команд сопроцессора, хоть и регистры ST задействованы? Потому, что мы вводим эти числа, но никаких операций над ними не выполняем. Так… Ну более менее разъяснил всё.. Теперь переходим по jmp short loc_401047…
    .text:00401085 ; ---------------------------------------------------------------------------
    .text:00401085
    .text:00401085 loc_401085:
    .text:00401085 jmp short loc_401023
    ; Сюда программа получает управление всегда, когда отработает вложенный цикл, а от сюда мы перейдём на инкремент j….
    .text:00401087 ; ---------------------------------------------------------------------------
    .text:00401087
    .text:00401087 loc_401087:
    .text:00401087 xor eax, eax
    .text:00401089 mov esp, ebp
    .text:0040108B pop ebp
    ;Так, вот и всё) Сюда программа переходит тогда, когда по 401039 мы не проходим проверку j < ROW, оно же j < 5, оно же cmp [ebp+var_19C], 5/ jge short loc_401087.


    Ещё раз немного повторюсь по логике работы этой программы. Главный цикл крутит свою работу 5 раз. В нём есть вложенный цикл, который выполняется 10 раз, запрашивая каждый раз новое число, а когда мы ввели 10 чисел, управление переходит на родительский цикл(как-никак условие вложенного цикла не истинно, потому что i будет равно 10), который увеличивает значение j на единицу, и выполнение вложенного цикла повторяется ещё раз, в памяти оказывается ещё 10 чисел, и управление переходит опять на родительский цикл, где j снова увеличивается. Получается, что когда условие главного цикла станет ложью (j = 5), в памяти будет 50 чисел.
    Ну, думаю немного разобрались… Правда единственный минус, что мы не сделали цикл вывода введённого массива. Про это я напишу следующую статью, в которой мы будем через OllyDbg лезть в откомпилированный файл этой нашей программы, и вписывать прямо в отладчике код нашего цикла вывода чисел.
     
    1 person likes this.
  2. Ra$cal

    Ra$cal Elder - Старейшина

    Joined:
    16 Aug 2006
    Messages:
    670
    Likes Received:
    185
    Reputations:
    78
    могу посоветовать важные куски расписывать отдельно. ибо комменты и код очень сложно отличить, от этого очень непросто новичкам будет понять этот текст. помимо напрягов на понимание материала, придется напрягаться, чтобы выделить собсно важные части. даже мне например это читать сложно, хотя я не мало времени просидел в дизассемблере =)
     
  3. 0x0c0de

    0x0c0de Elder - Старейшина

    Joined:
    25 May 2007
    Messages:
    441
    Likes Received:
    396
    Reputations:
    297
    Значит сначала по содержанию. Как мне кажется, следовало бы (я как понимаю статья для новичка) не копировать здесь листинг иды в первозданном виде, а имена переменных как в сорце отметить. Тогда будет понятней что было и что стало. И комментарии приобретут больше смысла.

    По поводу оформления теперь. Юзайте тег
    Code:
    , вот что я могу сказать. Комментарии к коду нафиг не надо выделять цветным. Как и сам код, толку 0 от этого, только вред читабельности.
    
    И еще, помню на васме Крис Касперски что-то подобное писал. Про идентификацию различных конструкций компилятора и там было довольно подробно.
     
    #3 0x0c0de, 31 May 2009
    Last edited: 31 May 2009
  4. 0verbreaK

    0verbreaK Elder - Старейшина

    Joined:
    30 Apr 2008
    Messages:
    318
    Likes Received:
    42
    Reputations:
    -3
    Похоже скорее на открытие новичка, у Криса все подробно и фундаментально исследовано, про все конструкции язков высокого уровня, в переводе на ассемблер