• XSS.stack #1 – первый литературный журнал от юзеров форума

Десимволизация при реверсе Go

samarie

HDD-drive
Пользователь
Регистрация
06.04.2023
Сообщения
34
Реакции
83
Депозит
0.00
Оригинальная статья: https://www.freebuf.com/articles/others-articles/176803{.}html
Переведенно специально для xss.pro

Многие люди в Китае считают реверс-инжиниринг golang сложным, но это не так. Да, по сравнению с другими языками программирования, в Golang слишком много функций. Однако, если в Golang не использовать символы, то его сложность будет примерно такой же, как у .NET (без обфускации). Цель этой статьи - решить проблему символизации, с которой сталкиваются при реверсе Golang.

Первое изображение - это дизассемблированный код без удаленной информации о символах, на котором можно увидеть имена всех функций. Второе изображение - это тот же дизассемблированный код , но с удаленной информацией о символах, в котором имена функций времени выполнения GOLANG отсутствуют (для подготовки образца лучше всего скомпилировать и сравнить "hello world" на Linux).

228bbe55b9ea25c9974c9.png

Рисунок 1 Разбор языка go без удаления символьной информации
692bda902a0ded06a18f5.png

Рисунок 2 разборка языка go с удалением символьной информации
II. Анализ
Сперва хочу выразить благодарность https://rednaga.io/2016/09/21/reversing_go_binaries_like_a_pro/链接的作者所提供的的脚本和资料 (Reversing GO binaries like a pro). Он выложил скрипт golang_loader_assist.py, который может быть использован для восстановления символьной информации. Однако этот скрипт нельзя использовать с программами go, скомпилированными в среде windows. Скрипт golang_loader_assist.py использует .gopclntab, который является очень важным сегментом. Этот сегмент не включается в программу go, скомпилированной посимвольно в среде windows, как показано на рисунке 3:

8f98e398936d6de9ccb5d.png

Рисунок 3.
10b003f5bfc7acddca82f.png

Рис. 4 Работа без удаления символьной информации под linux.

Поэтому, опираясь на работу других, для обратной десимволизации Golang в среде Windows, основным является определение расположения файла .gopclntab:
  1. Сначала рассмотрим процесс выполнения golang_loader_assist.py, а затем перейдем к десимволизации Golang в среде Windows.
Ниже приведен основной код golang_loader_assist.py

Python:
Line 1: def renamer_init():

Line 2:     renamed = 0

Line 3:     gopclntab =idaapi.get_segm_by_name('.gopclntab')

Line 4:     ifgopclntab is not None:

Line 5:         # Skip unimportant header and gotosection size

Line 6:         addr = gopclntab.startEA + 16

Line 7:         size, addr_size = create_pointer(addr)

Line 8:         addr += addr_size

Line 9:         # Unsure if this end is correct

Line 10:         early_end = addr + (size * addr_size *2)

Line 11:         while addr < early_end:

Line 12:             func_offset, addr_size =create_pointer(addr)

Line 13:             name_offset,addr_size = create_pointer(addr + addr_size)

Line 14:             addr += addr_size * 2

Line 15:             func_name_addr = Dword(name_offset +gopclntab.startEA + addr_size)                                            +gopclntab.startEA

Line 16:             func_name =GetString(func_name_addr)

Line 17:             MakeStr(func_name_addr,func_name_addr + len(func_name))

Line 18:             appended = clean_func_name =clean_function_name(func_name)

Line 19:             debug('Going to remap function at0x%x with %s - cleaned up as %s' % (func_offset, func_name, clean_func_name))

Line 20:             ifidaapi.get_func_name(func_offset) is not None:

Line 21:                 if MakeName(func_offset,clean_func_name):

Line 22:                     renamed += 1

Line 23:                 else:

Line 24:                     error('clean_func_nameerror %s' % clean_func_name)

Line 25:     return renamed

Код 1 Основной код в файле golang_loader_assist.py



Как показано на рисунке 4: начальный адрес .gopclntab равен 0813a140, и далее следует пошаговый расчет программы.

Из строки 6 видно, что значение позиции addr = gopclntab.startEA + 16 равно 3fcc.

Из строки 13 видно, что name_offset = 3fcc

из строки 15 name_offset + gopclntab.startEA+ addr_size = 0813a140+3fcc+4=0813e110

Dword(0813e110)=3ffc

func_name_addr = 0813e13c

func_name == (main.main)

998d1a8a9d50fe2785626.png

Рисунок 5 Отображение адреса результата выполнения первого шага кода 1
Так что если позиция строки известна, разумно ли инвертировать .gopclntab? Ответ определенно да.

2. Расшифровка символов в Windows:

IDA использует сочетание клавиш shift+f12 для вызова окна строк для поиска таких ключевых слов, как runtime или main, как показано на рисунке 6:

621ff7bf8c203d50b874d.png

Рисунок 6 Результаты поиска строки ida
Нажмите на случайную строку, например runtime.memhash_varlen, как показано на рисунке 7:

d30726f166edb802a4051.png

Рисунок 7 Адрес строки runtime.memhash_varlen
Адрес runtime.memhash_varlen равен 004c1d8c, выделенная мышью часть рисунка 7 - это адрес функции, а под ним 20f6c - адрес смещения имени функции (строки runtime.memhash_varlen) относительно .gopclntab.

Тогда адрес .gopclntab путем инверсии будет: 004c1d8c-20f6c = 004a0e20, как показано на рисунке 8:

467e3b4fd84371cea0514.png

Внимательные читатели, возможно, уже заметили, что позиция .gopclntab содержит код функции-индикатора!!!!!!! Он на самом деле настолько прост, что представлен четырьмя байтами: FB FF FF FF.

3. Советы

a) Измените сегменты IDA - Новый добавленный сегмент должен называться .gopclntab, тогда вы сможете использовать idapython (плагин ida)

имя сегмента .goclntab

начальный адрес (поиск FB FF FF FF FF FF FF FF)

конечный адрес (конец сегмента .text)

b) После этого IDA загружает golang_loader_assist.py

i. gopclntab = ida_segment.get_segm_by_name('.gopclntab')

if gopclntab is notNone.

ii. # Пропускаем несущественный заголовок и переходим к размеру секции.

iii. addr = gopclntab.startEA + 8

iv. size, addr_size = create_pointer(addr)

v. addr += addr_size здесь addr = gopclntab.startEA + 8 (или, возможно, +16)

c) Обратная разработка Go в IDA приводит к тому, что f5 не будет работать, причина в том, что за каждой функцией следует либо функция runtime_morestack, либо runtime_morestack_noctxt, поэтому прямая структуризация этих двух функций позволит использовать f5.

d) runtime_newproc, запускает параллельный процесс, не теряйте ее из виду.

e) runtime_makechan, более специфичный канал языка go.

f) golang_loader_assist.py включает версию IDApython, если вы не можете использовать ее напрямую, вы можете заменить оригинальный python файл ida_segment .get_segm_by_name на idaapi.get_segm_by_name. (В других местах аналогично будет idat_segment, ida_search, ida_funcs, ida_xref для idaapi).

Резюме​

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

__________________________________________________________

От себя хотелось бы добавить
https://github.com/sibears/IDAGolangHelper -- Set of IDA Pro scripts for parsing GoLang types information stored in compiled binary
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх