2010-06-16

Шифрование строк

Иногда необходимо зашифровать строки в программе, это хоть немного, но затрудняет реверсинг. Недавно и мне потребовалось решить эту задачу. Решено было написать некоторый аналог препроцессора на python-е. Препроцессор просто заменяет макрос _C на вызов функции CSDecrypt. Например, этот код:

#include <stdio.h>
#include <windows.h>
#include "_CSD.h"

int f(char * t) {
  printf(_C("|t:%s| "),t);
  return 0;
}

int main(int argc, char ** argv) {
  f(_C("testing"));
  f(_C("test2"));
  return 1;
}


будет преобразован в следующий:

#include <stdio.h>
#include <windows.h>
#include "_CSD.h"

int f(char * t) {
  printf(CSDecrypt("\x00\xf2\xfb\xaa\xb4\xe1\xef\xb4",7,142),t);
  return 0;
}

int main(int argc, char ** argv) {
  f(CSDecrypt("\x00\xe7\xf1\xe6\xe2\xfe\xf6\xfe",7,147));
  f(CSDecrypt("\x00\x14\x04\x11\x17\x56",5,96));
  return 1;
}


_CSD.h содержит функцию расшифровки.

#pragma once

inline char* CSDecrypt(char *str, size_t len, BYTE key) {
  char *flag = str;
  str++;
  if (*flag=='\x01') {
    return str;
  }
  for (int i=0;i<len;i++) {
    str[i]^=(key+i);
  }
  *flag = '\x01';
  return str;
}



Желательно передать компилятору /Ob1, т.к. inline. И ещё, важный момент, память, в которай расположенна строка, должна быть доступна для записи, иначе будет исключение C0000005 (ACCESS VIOLATION).

вот сам код "препроцессора":

import sys, re
from random import randint

def XorStr(str,key):
  retval = ""
  for i in range(0,len(str)):
    t = ord(str[i])^int(key+i)
    retval += chr(t)
  return retval

def FmtXorStr(str):
  retval = r"\x00"
  for x in str:
    retval += r"\x%02x" % ord(x)
  return retval

def _C(obj):
  key = randint(1,254)
  str = XorStr(obj.group(1),key)
  retval = 'CSDecrypt("%s",%d,%d)' % (FmtXorStr(str),len(obj.group(1)),key)
  return retval

def main():
  try:
    f = open(sys.argv[1],"r")
    code = f.read()
    f.close()
    dest = open(sys.argv[2],"w")
  except:
    print "%s <in> <out>" % sys.argv[0]
    return 0
  
  r = re.sub(r'_C\("(.*?)"\)',_C,code)
  dest.write(r)
  dest.close()
  return 1

main()



Я сейчас почти полностью перешел на vim + scons. Вот пример как подключить "препроцессор" (pre.py) к scons:

env = Environment(
  CCFLAGS = "/Ob1 /c /D_CSD",
  LINKFLAGS = "/MERGE:.rdata=.text /MERGE:.data=.text /SECTION:.text,EWR"
)

PyPre = Builder(action = 'pre.py $SOURCE $TARGET',
    src_suffix = '.cpp', suffix = '.cxx')

env["BUILDERS"]["PyPre"] = PyPre

for x in Glob("*.cpp"):
  env.PyPre(x)

env.Program("test.exe",Glob("*.cxx"))


Конечно могут быть всякие баги-глюки, т.к. не особо тестилось всё это. Но осоновная идея я думаю понятна. Из известных недостатков:

1. Нельзя юзать так: char a[] = _C("test");
2. Не корректно обрабатываются спецсимволы ( например \n\r )
3. ...

UPD
проблема со спецсимволами решается через .decode("string_escape")

2010-01-11

Hook api

Небольшой код для перехвата функций методом модификации импорта.

bool HookImport(DWORD Base, DWORD Org, DWORD Hook)
{
    PIMAGE_FILE_HEADER pFileHdr;
    PIMAGE_OPTIONAL_HEADER pOptHdr;
    PIMAGE_IMPORT_DESCRIPTOR pImpDesc;
    bool rv = false;

    pFileHdr = (PIMAGE_FILE_HEADER)(*(PDWORD)(Base+0x3c)+Base+4);
    pOptHdr = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHdr+sizeof(IMAGE_FILE_HEADER));
    pImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)(Base +
                pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    for ( int i=0;pImpDesc[i].Characteristics; i++ )
    {
        char * CurrentModuleName = (char*)(pImpDesc[i].Name + Base);
        PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(pImpDesc[i].FirstThunk + Base);
        for ( int j=0; pThunk[j].u1.Ordinal; j++ )
        {
            if (pThunk[j].u1.Function == Org)
            {
                pThunk[j].u1.Function = Hook;
                rv = true;
                goto Exit;
            }
        }
    }
    Exit:
    return rv;
}

2010-01-05

ASCII [псевдо] арт ;)

В прошлом году было нечем заняться, нарисовал что-то типа подписи, теперь повсюду в свои исходники кидаю, мне нравится )
Выглядет оно вот так:

2010-01-04

Двусвязные списки

Когда-то написал этот простой набор макросов, и юзаю практически везде где требуются двусвязные списки:

#ifndef LIST_H_INCLUDED
#define LIST_H_INCLUDED

#define INIT_LIST_HEAD(head) \
  (head)->Flink = (head); \
  (head)->Blink = (head)

#define UNLINK_ENTRY(entry) \
  (entry)->Blink->Flink = (entry)->Flink; \
  (entry)->Flink->Blink = (entry)->Blink

#define ADD_ENTRY(head,entry) \
  (entry)->Flink=(head)->Flink; \
  (entry)->Blink=(head); \
  (head)->Flink->Blink = (entry); \
  (head)->Flink = (entry)

#define ADD_ENTRY_TAIL(head,entry) \
  (head)->Blink->Flink = (entry); \
  (entry)->Blink = (head)->Blink; \
  (entry)->Flink = (head); \
  (head)->Blink = (entry)

#define get_list_entry(type,entry)  (type)(entry)

#define list_for_each(pos, head) \
  for (pos = (head)->Flink; pos!=(head); pos = (pos)->Flink)

#define list_for_each_safe(pos, n, head) \
  for (pos = (head)->Flink, n = pos->Flink; pos != (head);\
    pos = n, n = (pos)->Flink)

#endif // LIST_H_INCLUDED

2009-08-26

bin to char

Небольшой простой скрипт

import sys

def main():
    try:
        f = open(sys.argv[1],'rb')
    except:
        print "Usage: %s filename > var.h" %(sys.argv[0])
        sys.exit(1)
    x = f.read()
    j = 0
    filename = sys.argv[1].replace('.','_')
    filename = filename.replace('\\','_')
    sys.stdout.write("unsigned char __file[] = { \n\t")
    for i in range(0,len(x)-1):
        j+=1
        t = "0x%02x," % ord(x[i])
        sys.stdout.write(t)
        if (j==16):
            j=0
            sys.stdout.write("\n\t")
        
    t = "0x%02x" % ord(x[i])
    sys.stdout.write(t)
    sys.stdout.write("\n};")
    f.close()

if __name__=='__main__':
    main()

2009-07-08

Плагин для Windbg

Плагин определяет сколько времени прошло между двумя точками в програме.
Используеться так:
!ct breakpoint1 breakpoint2

Пример на блокноте (notepad.exe):
Правда плагин считает только секунды, и для больших таймаутов не подходит
0:000> u winmain
notepad!WinMain:
005a138d 8bff mov edi,edi
005a138f 55 push ebp
005a1390 8bec mov ebp,esp
005a1392 83ec1c sub esp,1Ch
005a1395 56 push esi
005a1396 57 push edi
005a1397 6a06 push 6
005a1399 33f6 xor esi,esi
0:000> !ct 005a1392 005a1399
0:000> bl
0 e 005a1392 0001 (0001) 0:**** notepad!WinMain+0x5
1 d 005a1399 0001 (0001) 0:**** notepad!WinMain+0xc
0:000> g
...
Breakpoint 0 hit
Debug session time: Wed Jul 8 16:58:41.358 2009 (GMT+3)
System Uptime: 6 days 2:43:43.142
Process Uptime: 0 days 0:00:20.215
Kernel time: 0 days 0:00:00.015
User time: 0 days 0:00:00.000
------------------------------- 0 hit ------- 43.142000
Breakpoint 1 hit
Debug session time: Wed Jul 8 16:58:41.359 2009 (GMT+3)
System Uptime: 6 days 2:43:43.143
Process Uptime: 0 days 0:00:20.216
Kernel time: 0 days 0:00:00.015
User time: 0 days 0:00:00.000
------------------------------- 1 hit ------- 43.143000
Quick diff: 0.001000
---------------------------------------------
eax=0000000a ebx=00000000 ecx=00000065 edx=00000004 esi=00272d06 edi=005aa21c
eip=005a1399 esp=000afc50 ebp=000afc78 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
notepad!WinMain+0xc:
005a1399 33f6 xor esi,esi

0.001000 - это 1 милискунда
http://cid-eea2a7c767291ff9.skydrive.live.com/self.aspx/.Public/windbg|_catch|_time.rar

2009-06-25

Отладка драйвера в Windbg

Во время отладки драйвера с помощью vmware и Windbg часто возникает необходимость остановиться на точке входа драйвера. Если драйвер наш, и нам доступны исходники, можно просто добавить int 3 в код и перекомпилировать драйвер, но что делать если исходные коды не доступны?

Можно конечно вписать 0xCC в нужное место с помощью хексредактора, но тут есть ряд неудобств. Во-первых, нужно будет востанавливать оригинальный байт, а во-вторых у меня драйвер с изменённым байтом отказался загружаться, наверно нужно исправить контрольную сумму в заголовке? Дополнительная трата времени... Но можно поступить иначе.

Дизассемблируем ntkrnlpa.exe (или ntoskrnl.exe), находим там неэкспортируемую функцию IopLoadDriver, запоминаем/записываем адресс. В моём случае - 0049DEE4.

Прокручиваем листинг вниз, ищем примерно такой код:
PAGE:0049E546 0C4                 push    [ebp+Object]
PAGE:0049E54C 0C8 push edi
PAGE:0049E54D 0CC call dword ptr [edi+2Ch]
PAGE:0049E550 0C4 cmp eax, ebx
PAGE:0049E552 0C4 mov ecx, [ebp+var_98]
PAGE:0049E558 0C4 mov [ebp+var_54], eax
PAGE:0049E55B 0C4 mov [ecx], eax
PAGE:0049E55D 0C4 jge short loc_49E566
PAGE:0049E55F 0C4 mov [ebp+var_54], 0C0000365h

call dword ptr [edi+2Ch] - это переход на точку входа в загружаемого драйвера, значит находим смещение этого кода относительно начала функции IopCallDriver. В моём сулучае: 0x0049E54D - 0x0049DEE4 = 0x669. И ставим в windbg точку останова:
bp IopLoadDriver+0x669

Когда windbg останавливаеться на этой точке - нажимаем F11, и попадаем на EP драйвера =)
Конечно подразумеваеться что установленны корректные отладочне символы.

Вместо выполнения "сложных" арифметических операций (0x0049E54D - 0x0049DEE4 = 0x669) можно пойти в Options -> General -> Disassembly и поставить галочку "Function Offsets", тогда IDA выдаст что-то такое:
IopLoadDriver(x,x,x,x)+662  0C4                 push    [ebp+Object]
IopLoadDriver(x,x,x,x)+668 0C8 push edi
IopLoadDriver(x,x,x,x)+669 0CC call dword ptr [edi+2Ch]
IopLoadDriver(x,x,x,x)+66C 0C4 cmp eax, ebx
IopLoadDriver(x,x,x,x)+66E 0C4 mov ecx, [ebp+var_98]
IopLoadDriver(x,x,x,x)+674 0C4 mov [ebp+var_54], eax
IopLoadDriver(x,x,x,x)+677 0C4 mov [ecx], eax
IopLoadDriver(x,x,x,x)+679 0C4 jge short loc_49E566
IopLoadDriver(x,x,x,x)+67B 0C4 mov [ebp+var_54], 0C0000365h

Архив