srg91 Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 (изменено) @Garik66 спросил - как использовать createthread, чтобы он не крашился. Начал отвечать и это слегка вышло за рамки простого ответа в тему, поэтому решил выделить это в отдельный топик. Я не очень хорошо знаю assembler, но попробую описать возможные причины. createthread создает поток и просит его выполнить call с адресом переданной функции. Например в данном случае createthread(my_function) в отдельном потоке выполнит call my_function: Spoiler alloc(my_function, 64) my_function: ret createthread(my_function) И тут вступает в дело стек, да. Вот эта штука (в Memory View вызывается через ПКМ на правом нижнем окне и выборе пункта Full stack): Spoiler После выполнения call my_function мы попадаем в нашу функцию, а в стек записывается адрес, куда нужно вернуться после выполнения этой функции (my_function). Собственно за возврат куда нужно и отвечает ret. Он берет первую запись из стека и делает jmp в правильное место. Собственно сам адрес возврата виден в стеке на скриншоте - это KERNEL32.BaseThreadInitThunk+24. Без ret ассемблер вывалится за пределы функции и попытается выполнить команду - add [eax],al , хотя для нас это просто участок пустой памяти - 00 00. Ну а так как в EAX ничего приличного нет, то программа выдает ошибку - сорян, не могу записать в данный участок памяти и падает. Поэтому ret обязателен. Из этого вытекает то, что при подходе к ret в последней записи в стеке (адрес в регистр ESP) должна быть с правильным адресом возврата, который был нам передан из call my_function. И основная причина крашей в том, что регистр ESP указывает не на тот адрес при выполнении команды ret. Программа выполняет jmp на неизвестный нам адрес и крешится из-за невозможности выполнить команды, на которые она попала (как с add [eax],al). А что собственно двигает стек и адрес регистра ESP? А двигают стек наши любимые команды push и pop. Собственно, когда мы пытаемся вызывать функцию и передаем параметры через push #100, на самом деле выполняются две команды - sub esp,4 и mov [esp],#100. Первая уменьшает адрес вершины стека на 4 байта, а вторая записывает на вершину стека значение 100. А pop eax сдвигает вершину в другую сторону - делает mov eax, [esp] и дальше сдвигает вершину стека назад add esp,4. Поэтому после того, как мы сдвинули стек с помощью push при передаче параметров в функцию - после её выполнения нужно сдвинуть стек обратно! Собственно следующий пример почти 100% скрешится: Spoiler [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) my_function: push #100 call second_function ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) Мы ввели новую функцию second_function, которая берет переданный аргумент и записывает его в EAX. Вроде бы всё просто, передали в функцию 100, получили в EAX ответ, а всё равно креш. Почему? А это видно на следующем скриншоте - на вершине стека теперь хранится значение 0x64 (=100): Spoiler Теперь когда выполнится ret он попытается прыгнуть на адрес 0x00000064, а не на 0x760E8744 как должен был. Как же этого избежать? Есть несколько путей: сохраняем значение регистра esp и возвращаем его перед ret внимательно следим за стеком по мере выполнения нашей функции и к концу программы он сам будет в правильном месте В целом первый способ очень неплох. Мы можем сохранить адрес регистра esp в памяти и вернуть его прямо перед вызовом ret. Например: Spoiler [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) alloc(esp_addr, 4) esp_addr: dd 0 my_function: // сохраняем текущий адрес вершины стека mov [esp_addr],esp push #100 call second_function // загружаем обратно адрес вершины стека mov esp,[esp_addr] ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) dealloc(esp_addr) Но это не совсем assembler-way, поэтому я предпочитаю следить за стеком самостоятельно. Для этого нужно обращать внимание на то, как со стеком работает вызываемая функция. Например, наша функция second_function не следит за стеком. Поэтому если мы делаем push для передачи параметров, то мы так же должны сдвинуть его назад. Это можно сделать через pop указав неиспользуемый регистр или просто добавить к адресу ESP 4 байта (размер адреса в 32 битной системе, в 64-битной - это 8 байт): Spoiler // через pop - eax используется, выполним pop в неиспользуемый ebx push #100 call second_function pop ebx // через сдвиг esp push #100 call second_function add esp,4 Собственно таким образом мы двигаем стек за функцией. И в зависимости от количества переданных push - на такое же количество нужно сдвинуть и регистр ESP. Например для 3-х аргументов нужно выполнить 3 pop или добавить к esp 3 * 4 (3 - количество сдвигов, 4 - размер инструкции): Spoiler // через pop push #100 push #200 push #300 call third_function pop ebx pop ebx pop ebx // через сдвиг esp push #100 push #200 push #300 call third_function add esp,c // 0xC = 4 * 3 = 12 В результате мы получим следующий код: Spoiler [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) alloc(second_function, 64) my_function: push #100 call second_function add esp,4 ret second_function: mov eax,[esp+4] ret createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) Единственное, есть небольшое исключение - функции, которые сами заботятся о стеке, после их вызова не нужно сдвигать регистр esp, просто делаем push и call. Их довольно просто отличить, посмотрим на примере следующего кода: Spoiler [ENABLE] //code from here to '[DISABLE]' will be used to enable the cheat globalalloc(my_function, 64) globalalloc(second_function, 64) globalalloc(third_function, 64) my_function: // функция, не сдвигающая стек за собой push #100 call second_function add esp,4 // функция, сдвигающая стек за собой push #100 push #200 call third_function ret second_function: mov eax,[esp+4] ret third_function: mov eax,[esp+4] add eax,[esp+8] ret 8 createthread(my_function) [DISABLE] //code from here till the end of the code will be used to disable the cheat dealloc(my_function) unregistersymbol(my_function) dealloc(second_function) unregistersymbol(second_function) dealloc(third_function) unregistersymbol(third_function) Основное отличие - после call функции которая сама двигает стек не идет ни каких add esp,8 или не выполняются pop: Spoiler Так же можно обратить внимание на код самой функции, а точнее на ret 8. Параметр 8 указывает программе, что нужно не просто взять адрес из вершины стека и прыгнуть на него, а так же после этого сдвинуть стек на 8 байт, т.е. выполнить ret и add esp,8. Собственно и получается, что add делаем не мы, а сама функция. Вот и получается, что следя за стеком и тем, как вызываемые функции заботятся о стеке - мы не получим крашей в вызове createthread. Изменено 30 апреля, 2017 пользователем srg91 12 Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 Респект за статью! Просьба хостить скриншоты на форум, потому что на чужих хостингах через некоторое время картинки как правило пропадают. Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 11 минуту назад, srg91 сказал: @Garik66 спросил @srg91 ответил Ещё раз ОГРОМНОЕ СПАСИБО!!! Жаль что не могу сразу на + 100 лайкнуть. @MasterGH может ты поставишь? 2 Ссылка на комментарий Поделиться на другие сайты Поделиться
ReWanet Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 1 минуту назад, Garik66 сказал: Жаль что не могу сразу на + 100 лайкнуть. Согласен очень полезная статья. Думаю стоит закрепить ее. Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 Только что, what228 сказал: Думаю стоит закрепить ее. Сделано Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 30 апреля, 2017 Автор Поделиться Опубликовано 30 апреля, 2017 9 minutes ago, MasterGH said: Просьба хостить скриншоты на форум Готово. Да, так даже удобнее, я что-то машинально. Спасибо. Спасибо за отзывы. Если есть ошибки - пишите, я исправлю. 2 Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 1 минуту назад, srg91 сказал: Да, так даже удобнее Нужно пользоваться плюшками статуса Разработчики Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 28 минуты назад, srg91 сказал: Параметр 8 указывает программе, что нужно не просто взять адрес из вершины стека и прыгнуть на него, а перед этим сдвинуть стека на 8 байт, т.е. выполнить add esp,8 и ret. Для ret 8. Сначала возврат по [ESP] (как обычно) и после этого (либо одновременно) esp сдвигается на 8. Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 30 апреля, 2017 Автор Поделиться Опубликовано 30 апреля, 2017 5 minutes ago, MasterGH said: Для ret 8 Спасибо, исправлено. Исправил картинки нескольких других статьях. Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 (изменено) 1 час назад, srg91 сказал: В целом первый способ очень неплох, мы сохранить адрес регистра esp и вернуть его перед ret. немного бросается в глаза и еще, мне кажется все это описание работы со стеком уже переводит статью в раздел для новичков, просто потому, что все подробно описано, с таким подробным описанием даже, тот кто только установил CE разберется что и как делать. Изменено 30 апреля, 2017 пользователем partoftheworlD 1 Ссылка на комментарий Поделиться на другие сайты Поделиться
Dison Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 Интересная и полезная статья, всегда хотел узнать как пользоваться createthread. Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 30 апреля, 2017 Автор Поделиться Опубликовано 30 апреля, 2017 (изменено) 17 minutes ago, partoftheworlD said: немного бросается в глаза Исправил, спасибо. 17 minutes ago, partoftheworlD said: и еще Я тоже так думаю, но предыдущие статьи показали, что это продвинутый уровень Изменено 30 апреля, 2017 пользователем srg91 Ссылка на комментарий Поделиться на другие сайты Поделиться
elvis66666 Опубликовано 30 апреля, 2017 Поделиться Опубликовано 30 апреля, 2017 srg91, спасибо за труд, было интересно почитать! Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 23 часа назад, srg91 сказал: @Garik66 спросил - как использовать createthread, чтобы он не крашился. Крах как оказалось у меня происходит не от ESP и правильности написания скрипта. а как я понял в непонимании какие параметры нужно передавать, вот это у меня пока не укладывается в голове. У меня крах всегда с ошибкой "access violation". Нужно будет всё-таки потратить время, чтобы понять.. Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 1 мая, 2017 Автор Поделиться Опубликовано 1 мая, 2017 15 minutes ago, Garik66 said: какие параметры нужно передавать ты про вызов любых функций из createthread? или про саму createthread? Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 4 минуты назад, srg91 сказал: ты про вызов любых функций из createthread? или про саму createthread? Это проще показать, чем описывать. А пока мне нужно самому покопаться. Просто пока не уложилось в голове. Ссылка на комментарий Поделиться на другие сайты Поделиться
uhx Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 (изменено) В 30.04.2017 в 14:43, partoftheworlD сказал: немного бросается в глаза и еще, мне кажется все это описание работы со стеком уже переводит статью в раздел для новичков, просто потому, что все подробно описано, с таким подробным описанием даже, тот кто только установил CE разберется что и как делать. Согласен)) А вообще гуглите calling convention, если до сих пор что-то не понятно. Это обычное согласование о вызове, я даже немного удивлен что люди, которые нехило так продвинуты в RE не знают об этом) Именно из за этого согласования у нас практически всегда адрес нашего класса лежит в ecx ( в начале функции он может перекладываться в какой-нибудь другой регистр, но не суть ). add esp (cdecl) обычно используется после вызова функций, у которых неопределенное количество аргументов. Ну, например тот же printf в Си. А в остальных случаях используется обычно stdcall и thiscall, при которых функция сама через ret чистит стек, так как знает сколько аргументов она принимает. Изменено 1 мая, 2017 пользователем uhx Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 19 минут назад, uhx сказал: что люди, которые нехило так продвинуты в RE не знают об этом) Если ты про меня, то всякое бывает, Я до 2014 г. с программированием и близко знаком не был. ни одного ЯП до сих пор не знаю, всё что знаю - узнал здесь и конечно теперь есть пробелы в знаниях и в понимании некоторых вещей. Ссылка на комментарий Поделиться на другие сайты Поделиться
uhx Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 1 час назад, Garik66 сказал: Если ты про меня, то всякое бывает, Я до 2014 г. с программированием и близко знаком не был. ни одного ЯП до сих пор не знаю, всё что знаю - узнал здесь и конечно теперь есть пробелы в знаниях и в понимании некоторых вещей. Да не парься)) Если раньше не встречалось - значит не особо и нужно было. Мне например просто в свое время стало интересно, как вызовы происходят и все такое. Тот же printf в OllyDbg разглядывал сидел, а потом только прочитал про CC. Разберешься, куда деваться) Ссылка на комментарий Поделиться на другие сайты Поделиться
MasterGH Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 3 часа назад, uhx сказал: Это обычное согласование о вызове, я даже немного удивлен что люди, которые нехило так продвинуты в RE не знают об этом) Пользователи, которые пишут статьи, ведут активность на форуме у меня на вес золота. @srg91 молодец, что написал статью. Огромный респект. Если кто-то будет их провоцировать, обижать, сразу забаню. Ссылка на комментарий Поделиться на другие сайты Поделиться
partoftheworlD Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 5 часов назад, uhx сказал: я даже немного удивлен что люди, которые нехило так продвинуты в RE не знают об этом) Видимо им это не нужно, раз не знают. Ссылка на комментарий Поделиться на другие сайты Поделиться
drs36 Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 В 30.04.2017 в 14:00, Garik66 сказал: Жаль что не могу сразу на + 100 лайкнуть. Да.статья супер. Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 1 мая, 2017 Автор Поделиться Опубликовано 1 мая, 2017 6 hours ago, uhx said: Согласен А вот за этим это всё и пишется, чтобы продвигаться дальше. Понятно, что это всё известно, но это же еще нужно знать куда копать, тут ваши комментарии и помогают Заодно тоже могли бы топик с основными ссылками создать, их бы и закрепили ) 1 hour ago, MasterGH said: Пользователи Спасибо, но в целом критика то к месту, да и выхлоп есть - правильные слова звучат, теперь еще и знаем куда смотреть ) Осталось только еще где-то время на это взять Но никто и не обещал, что будет быстро )) 1 hour ago, partoftheworlD said: Видимо Угум-с Ссылка на комментарий Поделиться на другие сайты Поделиться
uhx Опубликовано 1 мая, 2017 Поделиться Опубликовано 1 мая, 2017 2 часа назад, MasterGH сказал: Пользователи, которые пишут статьи, ведут активность на форуме у меня на вес золота. @srg91 молодец, что написал статью. Огромный респект. Если кто-то будет их провоцировать, обижать, сразу забаню. Никто никого и не обижает) Статья классная, безусловно, я просто немного дополнил ТСа, пояснив что эта вещь в общем и целом называется calling convention. Ну еще речь шла о том что возможно из-за столь подробного описания стоит перенести тему в раздел для новичков. Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения