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

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

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

Студворк — интернет-сервис помощи студентам
Дали мне лабу, в которой нужно сделать инлайн вставку в С++. Ну это элементарно, Ватсон.
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
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
31.12.2009, 12:39
Ответы с готовыми решениями:

перевод паскаля в ассемблер. очень нужен
Пожалуйста, помогите перевести программу из паскаля в ассемблер! uses crt; var i,n0,n1,ic,a,b,c,k1: integer; m:array of string; ...

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

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

29
Пробующий
 Аватар для galileopro
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
01.01.2010, 22:13  [ТС]
Студворк — интернет-сервис помощи студентам
Goodwin98, а через стек они будут передаваться наиболее быстро, да? Просто я умудрился выкрутиться так:
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
;Name of procedure Progonka. Input parameters: InpFile- string local variable, 
;OutFile- string local variable.
.486
.model flat, stdcall
option casemap:none
include \MASM32\INCLUDE\windows.inc
include \MASM32\INCLUDE\kernel32.inc
include \MASM32\INCLUDE\user32.inc
includelib \MASM32\LIB\kernel32.lib
includelib \MASM32\LIB\user32.lib
 
;public declaration of metod Progonca
 public  Progonca
 
.data
fName db "t"
hFile DWORD ?
stdout DWORD ?
buf WORD 1024 dup(?)
HeadHeap DWORD ?
pointer DWORD ?
Sleep PROTO :DWORD
FileSize DWORD ?
 
 .code
 DllMain    proc hInst:dword,   event:dword,    no_usw:dword
 @@m:   mov eax, 1
    Ret
DllMain EndP
Progonca    proc    InpFile: DWORD
lea eax, fName
mov edx, InpFile
mov bl, [edx]
m7:
mov [eax], bl
inc eax
add edx, 2
mov bl, [edx]
cmp bl, 0
jne m7
 
invoke MessageBox,NULL,ADDR fName,ADDR fName,MB_OK
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov stdout, eax
invoke CreateFile, offset fName, GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
mov hFile, eax
invoke GetFileSize, hFile, 0
mov FileSize , eax
call GetProcessHeap
mov HeadHeap, eax
invoke HeapAlloc, HeadHeap, 0, FileSize 
mov pointer, eax
invoke ReadFile, hFile, pointer, FileSize ,offset FileSize , NULL
mov eax, pointer
 
add eax, FileSize
mov ecx, FileSize
m1:
sub eax, 2
sub ecx, 2
mov bx, [eax]
mov buf[ecx], bx
cmp ecx, 0
jg m1
 
invoke WriteConsoleA, stdout, ADDR buf,FileSize, offset FileSize, NULL
invoke CloseHandle, hFile
invoke Sleep, 1000d
invoke ExitProcess, 0
Progonca EndP
End DllMain
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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
 
namespace WindowsFormsApplication14
{
    unsafe public partial class Form1 : Form
    {
        unsafe public Form1()
        {
            InitializeComponent();
        }
        [DllImport("79.dll")]
        unsafe private static extern void Progonca(char* x);
        unsafe private void button1_Click(object sender, EventArgs e)
        {
            string s = "1.txt";
            fixed (char* p = s.ToCharArray())
            {
                //чтобы сборщик мусора не трогал наш массив символов
                Progonca(p);
            }   
        }
    }
}
Оно более-менее работает, но... Оно правильно передает имя файла, но сволочь не открывает его Хотя если вручную указать имя файла, то все окрывается за милую душу. Не знаю менять или нет.

Assembler
1
2
cmp bl, 0
jne m7
Тут я пользуюсь, что компилятор шарповский(как и Visual C++) в конце любой строки добавляет '\0' - символ, код которого в ANSI - "0". Насчет Юникода не знаю, но тут похоже все равно. И как только он находит этот символ - я выхожу из парсинга.
0
Пробующий
 Аватар для galileopro
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
01.01.2010, 22:19  [ТС]
Выложу проект. Хоть корявенько, но работает. WindowsFormsApplication14.rar
0
Пробующий
 Аватар для galileopro
185 / 98 / 10
Регистрация: 28.04.2009
Сообщений: 1,101
01.01.2010, 22:20  [ТС]
Рекомендую для этих целей WinAsmStudio, она под библиотеку dll сама заготовку делает.
0
8 / 8 / 4
Регистрация: 11.03.2015
Сообщений: 212
27.12.2016, 13:15
Теперь работает всё проще. Есть встроенный ассемблер. MSDN
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18362 / 14292 / 5370
Регистрация: 17.03.2014
Сообщений: 28,970
Записей в блоге: 1
27.12.2016, 13:29
Naomis, ссылка которую ты привел на самом деле относится к C, а не C#. Это кривой автоматический перевод так постарался.
1
8 / 8 / 4
Регистрация: 11.03.2015
Сообщений: 212
27.12.2016, 13:48
OwenGlendower, тогда это очень грустно, виноват. Тогда у меня вопрос. Работать с c# и ассембрем можно только тем как было показано выше? А если есть ассемблеровская библиотека, то к ней нужно подключаться через DLLImport?
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,576
27.12.2016, 15:24
Цитата Сообщение от Naomis Посмотреть сообщение
то к ней нужно подключаться через DLLImport
Можно.

Есть еще Fasm.Net (github).
2
8 / 8 / 4
Регистрация: 11.03.2015
Сообщений: 212
27.12.2016, 15:47
Someone007, Есть какой подробный гад про DLLImport и ассемблер?

Цитата Сообщение от Someone007 Посмотреть сообщение
Есть еще Fasm.Net (github).
это работает только на 32б, я так понимаю?
0
Эксперт .NET
6691 / 4102 / 1607
Регистрация: 09.05.2015
Сообщений: 9,576
27.12.2016, 17:22
Цитата Сообщение от Naomis Посмотреть сообщение
Есть какой подробный гад про DLLImport и ассемблер?
Незнаю. Может где нибудь и есть.

Цитата Сообщение от Naomis Посмотреть сообщение
это работает только на 32б, я так понимаю?
Никто вам не запрещает скачать fasm и скомпилить x64 версию fasm.obj, а затем перекомпилить и саму библиотеку fasm_managed под x64 используя полученный fasm.obj.
1
8 / 8 / 4
Регистрация: 11.03.2015
Сообщений: 212
27.12.2016, 17:28
Someone007, Спасибо большое за небольшой ликбез.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
27.12.2016, 17:28

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

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

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

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

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


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

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru