Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.94/79: Рейтинг темы: голосов - 79, средняя оценка - 4.94
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
1

Очень нужен инлайн ассемблер в шарпе

31.12.2009, 12:39. Показов 16228. Ответов 29
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Дали мне лабу, в которой нужно сделать инлайн вставку в С++. Ну это элементарно, Ватсон.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const int count = 10;
int[] mass=new int[count];
...
__asm
{
lea eax, mass ;загружаем эффективный адрес
mov ebx, 4 ; размер переменной типа int в байтах
xor ecx, ecx
xor edx, edx
notend: 
mov [eax+ecx], edx
add ecx, 4 ; переходим к следующему элементу массива
cmp ecx, [count]
jl notend
}
Но у меня хватило ума поспорить с преподом, что я смогу заюзать асм в шарпе да еще без длл (ну не знал я, что в шарпе нет инлайн ассемблера. Был под давлением сессии). Помогите кто может это осуществить. Задача крайне проста: нужно просто очистить массив из 10 элементов типа int с помощью ассемблерных команд, но использовать из надо без подключаемой длл. Указатель на массив трогать не надо!
Вот нашел я такое: http://www.xakep.ru/magazine/xa/108/118/1.asp
Как известно, ассемблеру предрекали полное и безоговорочное забвение еще в стародавние времена, когда появился С++, поразивший программистов своей ООП-архитектурой. Всего несколько лет назад вышли платформа .Net Framework и язык программирования C#. Казалось бы, низкоуровневому программированию в таких условиях существовать просто не суждено. Однако и сегодня встречаются задачи, для решения которых нужно приносить жертвы древнему богу программирования, имя которому - Assembler. Остатки старой элиты cpp'шников приводят массу аргументов против C#, и первым в списке называемых ими недостатков обычно выступает тот факт, что C# не поддерживает ассемблер. Но есть одно но. Дело в том, что мы с тобой совершенно с этим не согласны! Есть у русских тайные погреба, и вам их не засыпать! Способ юзать ассемблер в сишарповых прогах у нас тоже есть, и рассказывать мы о нем будем, комментируя код во врезке, поэтому срочно посмотри на нее, испей бутылочку темного пива и приготовь свой разум к восприятию нижеследующего текста.

Вначале обрати внимание, что наши классы содержат флаг unsafe. Этот флаг необходим при использовании указателей C#. Для того чтобы они работали, нужно изменить свойства проекта, установив опцию «Properties -> Build -> Allow unsafe code».

Наша программа-консоль начинается со строки 3, где мы вызываем универсальную функцию CallAssembler, в которую передается ссылка на массив байт, содержащий машинные команды процессора. Универсальность этой функции заключается в том, что она одинаково подходит для любого вызываемого кода. В начале этой функции (строка 22) идет, казалось бы, совершенно бесполезная (а на самом деле имеющая ключевое значение) операция - объявление переменной типа int. Когда мы объявляем эту переменную, она ложится в стек на самый низ выделенного для функции фрейма. Исходя из этого, мы можем узнать адрес верхушки стека, подсмотрев в отладчике, по какому смещению находится эта переменная относительно верхушки фрейма.

Для того что бы посмотреть на выполнение программы в ассемблере, нужно зайти в меню «Debug -> Windows -> Disassembly». Здесь мы можем наблюдать инициализацию этой переменной: «dword ptr [ebp-40h],4». Из кода видно, что переменная инициализируется по смещению 0x40, представляя собой важную константу, которую мы используем в строке 24. Получив указатель на переменную t (строка 23), мы меняем его так, чтобы он указывал на обратный адрес (как известно, для команды ret он нужен, поскольку автоматически записывается при вызове функций командой call).

Так вот этот самый адрес находится выше адреса фрейма стека вызванной функции на четыре байта. И вычисляем мы его в строке 24, используя данную константу. В строке 26 мы рассчитываем адрес первого байта массива asm (машинных команд), которые мы должны запустить, а в строке 28 - перезаписываем обратный адрес функции CallAssembler на адрес первого байта массива asm. Таким образом, когда работа функции CallAssembler закончится и будет вызвана команда ret, управление передастся нашему машинному коду. А как же вернуть управление вызывающему коду? Об этом должен позаботиться код, на который мы передали управление. Я сделал просто «mov dword ptr[esp], eax», перезаписав обратный адрес на значение из eax, поскольку CallAssembler возвращает обратный адрес, а перед выходом из CallAssembler он записывается в eax.

В принципе мы могли бы на этом остановиться, но лично мне не нравятся все эти вызовы CallAssembler каждый раз, когда необходимо выполнить asm-код. Поэтому далее мы поступим хитро, изменив команду call, которая вызвала CallAssembler, в результате чего вызываться теперь будет только код из массива asm. Для этого мы модифицируем адресный операнд команды call. Вот пример бинарного представления команды call: E8 3F 10 44 79. Она состоит из пяти байтов. Первый байт (E8) указывает процессору тип команды, а другие четыре байта – это и есть адрес, который необходимо поменять. Адрес этого операнда мы узнаем в строке 29 - он равен адресу ret минус четыре байта.

Да, кстати. Перед модификацией мы должны сделать несколько шаманских действий. Дело в том, что мы не можем просто так менять код в памяти, ведь страницы памяти, содержащей код, имеют атрибут PAGE_EXECUTE_READ. Этот атрибут формально запрещает изменения кода, но обычно от этой великой майкрософтовской защиты бывает мало пользы. Кроме того, в .Net-массивы располагаются на страницах памяти, которые содержат атрибут PAGE_READWRITE. Другими словами, выполнение кода в массиве запрещено, поэтому мы, используя API VirtualProtect, выставим необходимые атрибуты PAGE_EXECUTE_READWRITE. Теперь можно спокойно изменять код в памяти (строка 33) и делать выход из CallAssembler.
Вот текст программы
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
 
namespace ConsoleApplication4
{
    unsafe class test
    {
        [DllImport("kernel32.dll")]
        static extern bool VirtualProtect(int* lpAddress, uint dwSize, uint flNewProtect, uint* lpflOldProtect);
            unsafe public static int CallAssembler(byte[] asm)
            {
                int t = 4;
                int* p = &t;
                p += 0x40 / 4 + 1;
                t = *p;
                fixed (byte* b = &asm[0])
                {
 
                    *p = (int)b;
                   int* p2 = (int*)new IntPtr(*(p - 1)).ToPointer();
                    uint last = 0;
                    bool flug = VirtualProtect(p2 - 1, 32, 0x40, &last);
                    flug = VirtualProtect((int*)new IntPtr(b).ToPointer(), 32, 0x40, &last);
                    *p2 = (int)(b + 3);
                }
                return *p;
            }
 
        static void Main(string[] args)
        {
            byte[] asm = new byte[] 
            { 
             0x89, 0x04, 0x24, // mov dword ptr[esp], eax
             0xB8, 0x00, 0x00, 0, 0, // mov eax, 777h 
             0xC3 // ret 
            };
            int[] mass = new int[10];
            int b = CallAssembler(asm);
            Console.WriteLine(b);
            Console.ReadLine();
        }
    }
 }
Что тут переделать, чтобы выполнить поставленную задачу? Очистить надо mass. Массив можно сделать динамический.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
31.12.2009, 12:39
Ответы с готовыми решениями:

перевод паскаля в ассемблер. очень нужен
Пожалуйста, помогите перевести программу из паскаля в ассемблер! uses crt; var...

Нужен очень простой и очень содержательный мануал по взаимодействию приложений, написанных на VB 6.0
Добрый день всем, очень нужен очень простой в понимании и очень содержательный мануал по...

Нужен очень очень хитрый запрос Select
Здравствуйте гуру программирования я наткнулся на гигантскую для меня проблему!!! Запрос нужно...

Очень нужен совет!
Уже несколько дней сижу со списком произвольного типа, не получается реализовать метод динамической...

29
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 12:53  [ТС] 2
Вот проект ConsoleApplication4.rar
Приветствуются любые идеи. В этом журнале почти все написано, просто я не могу толком реализовать.
0
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 14:12  [ТС] 3
Неужели никто ничего не знает?
0
1144 / 853 / 262
Регистрация: 30.04.2009
Сообщений: 3,581
31.12.2009, 14:30 4
Мне самому эта тема очень интересна. Попробую сделать, когда будет время.
Но в описанном способе используется DLL библиотека.
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 15:15  [ТС] 5
nicolas2008, нет.
C#
1
2
[DllImport("kernel32.dll")]
        static extern bool VirtualProtect(int* lpAddress, uint dwSize, uint flNewProtect, uint* lpflOldProtect);
Это они юзают для своих нужд системную библу для импорта винапи ф-и. Это не страшно. Главное, чтобы не импортировать асмовские библы, да еще и самим написанные))
В этом коде они выполняют последовательность команд записанную тут
byte[] asm = new byte[]
{
0x89, 0x04, 0x24, // mov dword ptr[esp], eax
0xB8, 0x00, 0x00, 0, 0, // mov eax, 777h
0xC3 // ret
};
первая команда возвращает управление от вызвавшей её процедуры CallAssembler к основной программе. Вторая - видимо пример использования аналога асмовской команды. Вот мне нужно написать код это с помощью таких команд
Тема еще обсуждается здесь http://www.rsdn.ru/forum/dotnet/943060.all.aspx
Как бы написать команды асма на шарпе и ему скормить не выйдет, из-за .net. Но у них есть зато MSIL. И еще вот это извращение с использованием не самих асмовских команд, а их машинного представления.
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
31.12.2009, 16:03 6
Поиграться с шарпом я не могу, а сам-то асм код выполняется ?
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 16:12  [ТС] 7
Да. Но коряво. Щас я скину длл и шарповский проект. Там нужен деф файл. Оно код выполняет, но после отого не модет найти точку возврата
0
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 16:36  [ТС] 8
Вообщем вот что вышло WindowsFormsApplication14.rar
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
31.12.2009, 16:56 9
А почему в def файле ф-ция завется TestFunction, хотя в асм коде есть только TestProc ?

Если честно, то я все-таки не понял причем тут длл, если нужно обходится без нее ?

А насчет кода, который из журнала, может быть нужно самому глянуть
Для того что бы посмотреть на выполнение программы в ассемблере, нужно зайти в меню «Debug -> Windows -> Disassembly». Здесь мы можем наблюдать инициализацию этой переменной: «dword ptr [ebp-40h],4». Из кода видно, что переменная инициализируется по смещению 0x40, представляя собой важную константу, которую мы используем в строке 24.
Может быть там и не 40 будет
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 17:18  [ТС] 10
09
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
31.12.2009, 17:29 11
довольно сильно смущает строчка
Код
00000006 sub esp,7Ch
Значит дно где-то в районе ebp-7Ch. Почему, правдо, t находится не там я не знаю.

sceleton.asm
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.386
 
.MODEL flat,stdcall
 
OPTION CASEMAP:NONE
 
Include windows.inc
Include user32.inc
Include kernel32.inc
 
IncludeLib user32.lib
IncludeLib kernel32.lib
 
.DATA
 
.code
DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
; этот код выполняется при загрузке библиотеки в память.
    mov  eax,TRUE
    ret
DllEntry Endp
; See skeleton.def: This is an exported function
TestProc proc
    
    ret 
TestProc endp
 
End DllEntry
sceleton.def
Assembler
1
2
LIBRARY skeleton
EXPORTS TestProc
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 17:40  [ТС] 12
Не удается найти точку входа "TestProc" в DLL "77.dll".
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.386
.MODEL flat,stdcall
OPTION CASEMAP:NONE
Include windows.inc
Include user32.inc
Include kernel32.inc
IncludeLib user32.lib
IncludeLib kernel32.lib
.code
DllEntry proc hInstance:HINSTANCE, reason:DWORD, reserved1:DWORD
        mov  eax,TRUE
        ret
DllEntry Endp
; See skeleton.def: This is an exported function
TestProc proc
jmp m1
a db "Happy",0
b db "New Year",0  
m1:
    invoke MessageBox,NULL,addr a,addr b,MB_OK
    ret
TestProc endp
End DllEntry
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
31.12.2009, 17:44 13
деректива
Assembler
1
end metka
указывает асму, что это конец файла и дальше он его просто не читает.
Перенесите в конец
Assembler
1
End DllEntry
Кстати линковщик асма вполне ругается, если не находит какой-то экспортируемой ф-ции.
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 17:47  [ТС] 14
Цитата Сообщение от Goodwin98 Посмотреть сообщение
End DllEntry
Я понял этот косяк) Спасибо. Все равно не находит точку входа. В том примере, что был до этого, он работал. Но с глюками. Но точку входа находил

Добавлено через 1 минуту
Может тут поменять EnteryPoint?
C#
1
2
3
4
5
6
 [DllImport("77.dll", EntryPoint = "TestProc", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = true)]
         private static extern void TestProc();
         private void button1_Click(object sender, EventArgs e)
        {
            TestProc();
        }
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
31.12.2009, 18:01 15
В том примере кроме этой точки ничего и небыло....
Попробуйте "DllEntryPoint"
А нельзя просто написать ?
C#
1
[DllImport("77.dll")]
Вложения
Тип файла: rar 77.rar (2.4 Кб, 25 просмотров)
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 18:30  [ТС] 16
C#
1
2
3
4
5
6
 [DllImport("77.dll", EntryPoint = "DllEntryPoint", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = true)]
        private static extern void DllEntryPoint();
         private void button1_Click(object sender, EventArgs e)
        {
            DllEntryPoint();
        }
Так?

Не удается найти точку входа "DllEntryPoint" в DLL "77.dll".
Хотя вроде она там одна и есть.

Вот такое
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    .386
    .model flat, stdcall
    option casemap :none  
 
    include \masm32\include\windows.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\user32.lib
 
    szText MACRO Name, Text:VARARG
      LOCAL lbl
        jmp lbl
          Name db Text,0
        lbl:
      ENDM
 
    return MACRO arg
      mov eax, arg
      ret
    ENDM
 
.code
 
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
         szText LmTitle,"Happy"
        .if reason == DLL_PROCESS_ATTACH
            szText ATTACHPROCESS,"PROCESS_ATTACH"
            invoke MessageBox,NULL,ADDR ATTACHPROCESS,addr LmTitle,MB_OK
            return TRUE       
        .endif
        ret
LibMain Endp
End LibMain
Почему-то выполняется и у него нет никаких проблем в точкой входа. Хотя я вообще убрат эту процедуру. Говорит только
Попытка выполнения управляемого кода под блокировкой OS Loader. Запуск управляемого кода в пределах функции DllMain или функции инициализации образа может вызвать зависание приложения.
Добавлено через 13 минут
Вот я упростил сколько мог. Оно работает, но с ошибкой "Попытка выполнения управляемого кода ..."
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    .386
    .model flat, stdcall
    option casemap :none  
 
    include \masm32\include\windows.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\user32.lib  
.code
LibMain proc hInstDLL:DWORD, reason:DWORD, unused:DWORD
jmp m1
MbTitle db "Happy",0
MbMsg db "New Year",0
 m1:
 .if reason == DLL_PROCESS_ATTACH
invoke MessageBox,NULL,addr MbMsg,addr MbTitle,MB_OK
        mov eax, TRUE    
  .endif
     ret
LibMain Endp
End LibMain
Добавлено через 5 минут
Цитата Сообщение от galileopro Посмотреть сообщение
функции инициализации образа
Непойму, что здесь имеется в виду.
0
101 / 101 / 19
Регистрация: 29.12.2009
Сообщений: 204
31.12.2009, 18:37 17
Цитата Сообщение от galileopro Посмотреть сообщение
<функции инициализации образа>
Непойму, что здесь имеется в виду.
м.б то, что кодом в dll создаётся видимое окно?
1
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
31.12.2009, 20:19  [ТС] 18
Возможно. Только вот такой вопрос: кто знает как можно передать данные из библиотеки в программу и из программы в библиотеку?

Добавлено через 2 минуты
Та ошибка возникает, когда пытаешься возпользоваться функцией Windows прямо из библиотеки. Если там вместо
Assembler
1
2
 .if reason == DLL_PROCESS_ATTACH
invoke MessageBox,NULL,addr MbMsg,addr MbTitle,MB_OK
написать
Assembler
1
2
mov eax, 7
add eax, 5
то никаких ошибок нет.

Добавлено через 30 минут
Вот скажем такая dll
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    .386
    .model flat, stdcall
    option casemap :none  
 
    include \masm32\include\windows.inc
    include \masm32\include\user32.inc
    includelib \masm32\lib\user32.lib  
.code
LibMain proc
        mov eax, TRUE    
     ret
LibMain Endp
 
P1 proc
mov eax, 7
    Ret
P1 EndP
 
P2 proc
mov eax, 12
    Ret
P2 EndP
End LibMain
.def
LIBRARY 77
EXPORTS P1
EXPORTS P2
И нужно чтобы P1 - суммировала 2 числа, а Р2 - принимала на входе массив и на выходе давала сумму его елементов.
0
Пробующий
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
01.01.2010, 20:53  [ТС] 19
Вобщем все у меня с библиотеками заработало. Скоро выложу пример. Ничего не вылетает, все супер. Вот вопрос: как мне передать в библиотеку не переменную а строку? Ну или указатель на неё? (Имя файла для обработки)
0
2537 / 833 / 10
Регистрация: 31.05.2009
Сообщений: 1,668
01.01.2010, 21:45 20
Где-то при объявлении ф-ции в шарпе указывается, что она stdcall, т.е. параметры будут передаваться через стек. А в асме что-то вроде
Assembler
1
2
3
4
5
testproc proc  stroka:dword
  mov esi,[stroka]
.....
  ret
testproc endp
Остальное масм сделает сам.
1
01.01.2010, 21:45
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
01.01.2010, 21:45
Помогаю со студенческими работами здесь

Очень нужен парсинг!
Здравствуйте, ВСЕ! Я новичок на Форуме,поэтому,возможно,пишу не в том разделе... У меня большая...

Очень нужен совет!!!
Очень нужен совет! Кто знает разъясните пожалуйста, что делать? Яша ориентируется на контент при...

1C 7.7. очень нужен совет
Добрый день уважаемые форумчане. Нужна помощь начинающему программисту. Сейчас пишу программу 7.7...

Очень нужен файл
Добрый день. Можно к Вам еще раз обратиться за помощью? Не могли бы Вы скачать файл примера с post...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru