2014.06.08 15:06 [PROGRAMMING]

코드분석/악성코드분석/코드 분석/악성 코드 분석/리버싱/바이러스 분석/CODE 분석/익스플로잇 분석

코드를 분석할때 복잡한 코드를 현재 내가 진행중인 위치와 코드의 얼만큼을 분석했고 얼만큼의 함수를 더 분석해야 하는지 파악하며, 코드의 전체적인 흐름과 빠른 동적분석 진행을 위해 제작하게 되었습니다.

 

<동작 구성도 및 동작원리>

 

 

 

동작 원리에 대해 간단히 설명하겠습니다.

우선 디버깅 할 대상의 프로세스를 CreateProcess 함수에 DEBUG_PROCESS를 인자로 호출하게 되면 프로세스가 생성될때 디버깅 이벤트를 발생할 수 있는 프로세스로 생성이 됩니다.

이때 디버깅 루프를 돌면서 각 이벤트에 맞춰 프로그래밍 해주면 됩니다.

디버깅 루프코드만 다음을 참조했습니다.(http://blogs.msdn.com/b/reiley/archive/2011/07/21/data-breakpoint.aspx)

 

 

 

 

 

처음 발생되는 이벤트들은 DLL이벤트 입니다.

한개의 프로세스(프로그램)가 실행이 되면 해당 프로세스에서 필요한 DLL을 호출하게 됩니다.

이때 디버깅 이벤트는 DEBUG_EVENT 구조체를 사용하여 현재 발생된 디버깅 이벤트와 호출된 DLL 핸들 및 ImageBase 또는 발생된 DebugEventCode들의 정보를 담고있습니다.

 

 

 

DLL의 디버깅 이벤트가 발생하면 우선 제가 만든 함수인 PeParse함수에 디버깅 이벤트가 발생된 DLL의 핸들과 ImageBase 등의 기타 정보들을 인자로 호출합니다.

 

 

 

 

PeParse의 함수구조에대해 간단히 설명하겠습니다.

우선 함수에 "DLL", "EXE"문자열을 대상으로 DLL이벤트인지 EXE이벤트인지 IF문으로 분류하여 분석을 합니다.

그 이유는 EXE인경우 IAT테이블 대상으로 PE 구조를 파싱하고 DLL인경우 EAT테이블을 대상으로 파싱하기 위함입니다.

처음 함수에 진입하면 VirtualAlloc 함수로 65525의 크기 만큼의 동적메모리를 생성합니다.

여기서 동적메모리를 생성하는 이유는 현재 호출된 DLL또는 EXE파일의 바이너리 데이터를 그대로 메모리에 올리고 이 상태에서 PE구조 값과 .TEXT섹션의 OPCODE의 데이터들을 얻기 위해서입니다.

위에서 말씀드린대로 호출된 DLL또는 EXE핸들로 CreateFileMapping함수를 호출하고 MapViewOfFile을 호출하게되면 현재 호출된 DLL/EXE파일이 그대로 메모리에 올라가게 됩니다.

위의 그림에서 확인해보면 LPVOID Mapview 변수에 MapViewOfFile이 호출되면 메모리의 시작주소를 반환하게 되는데 직접 메모리를 확인해보면 호출된 DLL파일의 바이너리가 메모리에 올라가 있는것을 확인 할 수있습니다.

이 메모리의 시작주소를 대상으로 각각의 PE구조를 파싱하게 됩니다.

 

 

그리고 호출된 DLL의 이름을 찾는 방법에 대해 설명드리겠습니다.

실행파일의 이름을 찾는 방법은 간단하게 GetModuleFileName함수를 사용하는 방법 그리고 로드된 PE를 가상메모리에 올린후 직접 OFFSET을 계산하여 이름을 알아내는 방법이 있습니다.

GetModuleFileName의 함수는 프로세스의 핸들(IMAGE BASE)을 첫번째 인자로 전달해주면 호출된 파일의 경로와 이름을 쉽게 알아낼 수 있습니다.

저는 PE구조에 대해 공부하기 위해 직접 OFFSET을 계산하여 이름을 얻을수 있도록 구현했습니다. 

 

 

우선 IMAGE_IMPORT_DATA_DIRECTORY를 보면 EXPORT Table의 값이 있는지 없는지 체크합니다.

그리고 값이 있을경우 RVA이기 때문에 FILE OFFSET으로 변경해 줘야합니다.

(상세 계산법은 시중에 책이나 인터넷에 많이 알려져있으니 참고하시기 바랍니다.)

첫번째 작업으로 EXPORT Table의 주소값이 포함된 영역의 섹션을 찾습니다.

찾은경우 해당 섹션의 RVA값과 Pointer to Raw Data값을 뺍니다.

ntdll.dll에서 해당 값을 연산했을때 결과는 0x1000 - 0x400h = 0xC00 가 됩니다.

그럼 이제 여기서 EXPORT Table의 RVA값과 섹션에서의 연산값 0xC00를 다시 빼줍니다.

결과는 0x361B0 - 0xC00 = 0x355B0 값이 됩니다.

  

 

 

그림에서 확인해보면 IMAGE_EXPORT_DIRECTORY(IMAGE_EXPORT_DESCRIPTOR)의 시작주소 인것으로 확인할 수 있습니다.

이상태에서 Name RVA값을 얻고 해당 값 역시 RVA이기 때문에 OFFSET값을 구해 줘야합니다.

 

 

 

 

위에서 설명한 방식대로 구하게 되면 0x3AF6E - 0xC00 = 0X3A36E값이 되고 해당 파일 OFFSET으로 가보면 실제 ntdll.dll 문자열이 있는것을 확인 할 수있습니다.

처음에 MapViewOfFile함수로 로드되는 모듈의 핸들을 인자로 넘겨 호출된 모듈의 PE를 가상메모리에 로드 했었는데요 실제 이 주소로 가보면 PE첫 시작임을 확인 할 수 있었습니다.

해당 가상메모리의 주소는 0x6400000 였습니다. (할당되는 가상메모리의 주소는 유동적인 주소입니다.)

이 값에다 ntdll.dll의 OFFSET값 0x0X3A36E을 더하면 해당 가상메모리상태에서의 OFFSET값을 구할 수 있게됩니다.

 

다시한번 확인해보겠습니다.

이번 테스트의 경우  MapViewOfFile함수로 메모리에 로드했을때의 주소값은 0x2610000값 입니다. 

 


실행파일이 실제 로더에의해 실행되어질때 메모리에 매핑되는기준이 PE포맷의 IMAGE BASE주소값입니다.

MapViewOfFile 함수로 가상메모리에 매핑된 주소는 0x2610000 값입니다.

즉 MapViewOfFile로 리턴되는 가상메모리의 주소값이 IMAGE BASE의 값으로 보면됩니다.



이제 가상메모리주소값(IMAGE BASE값) 0x2610000 에다 RVA(Relative Virtual Address)을 더해주기만 하면됩니다.

RVA(Relative Virtual Address) 상대 가상 주소 로 해석할 수 있습니다.

즉 메모리에 매핑된 주소값으로부터 얼마만큼 떨어져 있느냐? 다시해석해서 IMAGE BASE에서 얼마만큼 떨어져 있는 주소값이냐?

이것을 공식으로 만든것이 IMAGE BASE + RVA = VA 가 되는것입니다.



이제 해당 0x2610000 + 0x3A36E 더한값을 포인터변수에 저장하고 출력해주기만하면 호출된 모듈의 이름을 구할 수 있습니다.

이런식으로 로드된 모듈의 핸들로 가상메모리에 매핑하고 코드를 작성해 나가면 됩니다.

물론 코드분석 대상의 프로세스 핸들(EX)notepad.exe)로 코드를 작성해야합니다.

 

<바운딩 처리된 PE에서의 함수호출 처리>

처음 코드를 작성하면서 E8과 FF15 부분에대한 OPCODE값을 계산해서 IAT테이블에서 해당되는 함수를 찾도록 구현했을때 두가지 처리방식에 대한 문제점이 발생했습니다.

첫번째로 바운딩 처리되었을때와 레지스터를 이용하여 함수호출을 하는경우입니다.

그 첫번째로 바운딩 처리된 PE구조를 확인해보겠습니다.

 

 c:\windows\system32\notepad.exe를 PEVIEW로 오픈해보면 다음과 같이 섹션헤더 아래에 BOUND IMPORT Directory Table, BOUND IMPORT DLL Names 섹션이 추가되어있는것을 확인할 수 있습니다.

 

 

IMPORT ADDRESS TABLE을 확인해보면 주소값이 RVA가 아닌 VA로 되어있는것을 확인 할 수 있습니다.

이것은 로딩시간을 단축하기위해 IAT에 메모리에 로드될 주소들을 고정시켜 버린것입니다.

 

 

실제 notepad.exe를 디버거에 올려서 확인해보겠습니다.

CALL GetStartupInfoA 부분의 OPCODE를 확인해보면 FF15 FC100C00이고 이것은 함수를 호출하는 코드입니다.

이것을 Big Endian으로 바꿔주면 FF15 000C10FC 가 됩니다. 즉 호출하는 주소는 0xC10FC가되는데 이 주소는 바운딩된 PE의 IAT테이블에서 찾을 수 없습니다.

 

 

그렇다면 일단 함수 내부로 진입해 보겠습니다.

GetStartInfoA함수의 첫 시작주소는 0x75BD1E10입니다. 참고로 GetStartInfoA함수는 KERNEL32.DLL에 있는 함수입니다.

 

 

현재 로드된 SYSTEM DLL들의 IMAGE BASE값을 확인해보겠습니다.

로드된 Kernel32.dll의 IMAGE BASE값은 0x75BD0000값입니다. 이 IMAGE BASE값은 운영체제가 재부팅될때마다 주소가 바뀝니다.

그 이유는 WINDOWS VISTA이후로 보안차원에서 WINDOWS시스템에서 ASLR(Address Space Layout Randomization)적용으로 메모리에 로딩되는 주소(IMAGE BASE)를 유동적으로 변경하여 사용하도록 했습니다.

그리고 실제 Visual Studio에서 컴파일옵션으로 설정해서 컴파일이 가능합니다.(ASLR이 적용된 PE는 재부팅이 될때마다 변경되는것이 아닌 프로세스가 재 실행될때마다 IMAGE BASE주소값이 변경되도록 컴파일이 가능합니다.)

이어서 함수의 진입 주소값과 로드된 IMAGE BASE값을 빼보겠습니다.

0x75BD1E10 -  0x75BD0000 = 0x1E10

 

 

PEVIEW로 Kernel32.dll을 로드시킨후 EXPORT ADDRESS TABLE에서 해당값을 찾아보면 GetStartupInfoA함수의 RVA주소 라는 것을 확인 할 수있습니다.

정리하면 호출된 함수 내부로 들어가 함수의 시작주소를 구하고 해당 시작주소의 IMAGE BASE를 빼주면 실제 로드된 모듈의 RVA값입니다.

이것을 코드로 옮기면 됩니다.


<레지스터로 호출하는 함수 처리>

 

 

테스트를 위해서 c:\windows\system32\calc.exe를 대상으로 디버거에 로드했습니다.

빨간밑줄의 OPCODE는 FFD7입니다. 이 OPCODE도 함수를 호출할때 사용하는 코드입니다.

근데 기존 호출과 다른점이 레지스터를 이용하여 함수를 호출합니다.

레지스터를 이용하여 호출하는경우 OPCODE는 FFD0 ~ FFD7까지 사용합니다. 각각 D0 = EAX 레지스터를 뜻하며 순차적으로 나열하면 다음과 같습니다.

D0 = EAX / D1 = ECX / D2 = EDX / D3 = EBX / D4 = ESP / D5 = EBP / D6 = ESI / D7 = EDI 

그럼 FFD7 이므로 호출되는 레지스터는 EDI가 될것이고 EDI가 가르키는 주소값은0x75C1CD5C입니다.

이또한 calc.exe가 갖고있는 IAT의 주소값이 아닙니다. 이값또한 Kernel32.dll에서 로드된 IMAGE BASE값과 EXPORT ADDRESS TABLE의 RVA값을 더한 값입니다.

 

 

실제 함수내부로 들어가면 0x75C1CD5C가 함수 시작주소인것을 확인 할 수 있습니다.

 

 

IMAGE BASE주소를 확인하고 연산하면0x75C1CD5C - 0x75BD0000 = 0x4CD5C가 됩니다.

 

 

확인결과 정확히 일치합니다.

정리하면 레지스터를 사용하여 호출하는 함수인경우 bounding 처리된 PE에서 처리된 방식과 같이 함수내부로 진입하여 처음 함수 시작주소를 구하고 로드된 모듈에서 IMAGE BASE를 빼고 연산결과로 나온 RVA값을 로드된 모듈의 EAT테이블에서 찾는다. 로 정리할 수 있겠네요

바운딩 처리되었을때 함수호출과 레지스터를 사용하여 호출하는 함수에 대한 처리는 이와 같은 방법으로 해결 했습니다.

 

<로드되는 모듈(DLL) 관리>

 

위의 방법으로 각각의 상황을 처리하려면 우선 로드되는 DLL마다 각각의 PE의 정보들을 파싱해서 동적메모리를 생성한 후 저장해야 합니다.

처음 Kernel32.dll, User32.dll, ntdll.dll등의 로드되는 모듈의 파싱된 정보가 들어있는 구조체의 주소를 담기위한 2차원배열의 동적메모리를 할당합니다.

그리고 이 동적메모리는 모듈이 로드될때마다 이 코드가 포함된 함수를 호출하여 메모리를 할당합니다.

함수가 호출될때마다 기존에 있던 주소들이 새로생성된 주소로 변경됩니다. 하지만 이렇게 주소가 변경되었다 하더라도 이전에 있던 주소들은 해제를 하지 않았기 때문에 유효한 상태가됩니다.

그래서 이전에 생성 됬던 주소값들을 관리하기위한 변수가 위에서 선언된 2차원 동적메모리 배열변수입니다.

 

 

첫번째 그림에서 ExportAddressFunctionManagerArr 2차원 동적메모리 변수에 주소값들을 관리하기 위한 변수를 생성했다면, 다음으로 모듈의 PE정보를 갖고있는 구조체의 주소를 담기위한 1차원 동적메모리 변수 ExportAddressFunction선언 합니다.

그리고 ExportAddressFunctionManagerArr[0] = ExportAddressFunction으로 연결시킵니다.

그리고 제일 밑에 ExportAddressFunctionManagerArr을 의주소를 관리하기 위한 목적으로 _PDLLPtrBridge구조체의 PTR_ExportAddressFunctionManagerArr변수에 연결시킵니다.

이렇게 연결시키는 이유는 함수의 인자값으로 넘겨줄때 ExportAddressFunctionManagerArr,ExportOrdinalTableManagerArr,ExportNameTableManagerARR을 각각 넘겨줄 필요없이 _PDllPtrBridge 구조체 하나만 넘겨주고 필요한 정보는 또 이구조체에 넣어서 사용하면 편리하기 때문에 이렇게 여러번 연결시켜서 사용했습니다.

 

이렇게 각 정보들을 연결시켜서 최종적으로 완성된 _PDllPtrBridge 구조체는 아래와 같습니다.

 

이 구조체가 담고있는 정보는 함수이름을 파싱하기위한 핵심정보들로 구성되어있습니다.

맨위의 DllHandle,LoadDllImageBase,DllName은 로드된 dll의 핸들정보 dll의 IMAGEBASE정보 그리고 dll의 이름정보를 저장하는 멤버변수입니다.

이변수도 동적메모리 주소공간에 순차적으로 각 값들이 저장되며 멤버변수가 가르키는 주소값은 각값들이 저장되어있는 메모리주소의 시작번지 주소를 담고있습니다.

그리고 아래로 내려오면 SaveCount라는 멤버변수가 있는데 이 함수의 역할은 현재 로드된 dll의 정보를 저장한 횟수를 카운트하는 멤버변수입니다.

예로 ntdll.dll,Kernel32.dll,User32.dll,KERNELBASE.dll이 로드되서 각값들이 이 구조체에 저장이 됬다고 하면 SaveCount의 값은 4가 됩니다.

이렇게 카운트를 하는이유는 추후에 이러한 데이터들을 대상으로 PDllPtrBridge구조체를 배열형식으로 사용해서 값을 찾기 위함입니다.

그리고 이외의 로드된 모듈의 EXPORT DESCRIPTION TABLE 정보, Ordinal Talbe정보, Name Table정보,Address Function Table정보등등의 많은 정보를 담고있습니다.

 

 

<PDllPtrBridge 구조체에서 함수이름 탐색>

 

 

 

내용을 정리하면 위의 그림과 같습니다.

우선 로드되는 모듈에 따라 각각의 PE정보를 파싱하는데 DLL의 EXPORT DESCRIPTION TABLE의 정보를 기반으로 각각의 값을 파싱하며, 테이블에서 파싱되는 값은 다음과 같습니다.

 

Name Rva                          000B8C54        Kernel32.dll

Ordinal Base                      00000001        

Number of Function             00000554         

Number of Name                 00000554

Address Table RVA             B579C

Address Name Table RVA    B6C5C

Ordinal Table RVA               B81AC

 

실제 함수의 진입점 포인터를 저장하기위해 위에서 1차원 동적메모리 배열 ExportAddressFunction을 선언했었고 선언할때 EXPORT DESCRIPTION TABLE의  Number of Function 값만큼 할당을 하고 저장을 합니다.

그리고 나머지 ExportOrdinalTable 함수와 ExportNameTalbe 함수는 EXPORT DESCRIPTION TABLE의 Number of Name 값으로 세팅합니다.

ordinal과 name table의 크기를 같게 정하는 이유는 두개 테이블안의 데이터 갯수는 서로 동일하기 때문입니다.

하지만 Number of Function값은 오직 Address table의 함수갯수와 같은점을 고려해서 해당테이블의 특성에 맞게 사이즈를 할당해서 메모리를 할당하면 됩니다.

 

 

 

그리고 로드된 DLL의 PE데이터들을 관리적 편의를 위해 PDllPtrBridge구조체를 선언해서 관리를 해줍니다.

PDllPtrBridge 구조체의 구성변수들은 위에서 설명한대로 동적할당한 메모리 주소포인터, IMAGE BASE주소,로드된 모듈의 이름, EXPORT Table 정보등을 저장하는 역할을 합니다.

이런식으로 디버깅 루프를 돌면서 DLL 모듈을 호출하는 이벤트가 발생될 경우 PEPARSE함수 내부로 진입하여 동적메모리를 할당하고 PDllPtrBridge 구조체에 관련정보들로 채우게 됩니다.

즉 디버깅 대상의 소프트웨어 브레이크포인트(0xCC) 이벤트가 발생되기 전까지 PDllPtrBridge 구조체에는 ntdll.dll, Kernel32.dll, KERNELBASE.dll, COMDLG32.dll,msvcrt.dll 등의 프로세스가 실행될때 필요한 dll들의 각 PE정보들로 채우게 되는것 입니다.

 

 

<PDllPtrBridge 구조체에서 함수이름을 찾는 과정>

 

처음단계에서는 디버깅당하는 대상의 프로세스에서 소프트웨어 브레이크포인트(0xCC)가 발생되기 전까지 로드되는 DLL 모듈의 PE정보들을 수집하게되고 이 수집된 데이터들이 있는 주소값 및 정보들을 PDllPtrBridge 구조체에 저장하여 관리를 한다고 했습니다.

그럼 이제 실제 함수가 호출되고 해당함수의 DLL에서 함수이름을 찾는 과정에대해 설명하겠습니다.

 

위에서 그림을 가져왔습니다 그림 넘버링 예정

 

 

다시 위의 몇번 그림을 가지고 설명하겠습니다.

우선 현재의 EIP가 0x000C36A6 이고 OPCODE FF15 FC100C00입니다. 하지만 이 주소값은 디버깅 되고있는 프로세스 IAT테이블에서 찾을수 없습니다.

그럼 일단 EIP를 한단게 더진행하여 GetStartupInfoA함수 내부로 진입합니다.

 

 

진입하면 EIP는 함수 시작주소인 0x75BD1E10으로 세팅이 됩니다.

여기서 처리되는게 가장중요한데. 함수 내부로 진입시 내부가 시스템 DLL영역인 경우가 있고 다음과 같이 시스템DLL영역이 아닌경우가 있습니다.

 

시스템 DLL이 아닌경우 함수가 호출되기 전과 호출되고 난 후의 주소값의 거리가 비슷합니다.

그러나 위의 그림에서와 같이 시스템 DLL인 경우 주소영역대가 0x7xxxxxxxx 번대로 할당되어 사용되어집니다

다시 설명드리지만 메모리에 로드되는 주소값(VA)은 IMAGEBASE + AddressFunctionRVA 값으로 구성되어있습니다.

 

그럼 다시 로드된 IMAGE BASE의 주소를 확인해 보겠습니다.

msvcrt.dll, Kernel32.dll, OLEAUT32.dll 모두 시스템 DLL들이며 주소영역대가 0x7xxxxxxxx대인것을 확인할 수 있습니다.

구글에서 이와관련된 자료를 겨우 찾았는데 다음과 같은 정보를 찾았습니다.

 

The system DLLs for Windows are currently based in memory from 0x70000000 to 0x78000000 on the Intel processors and from 0x68000000 to 0x78000000 on the MIPS processors.(http://www.codeproject.com/Articles/35829/Modify-the-Base-Addresses-for-a-DLL-Files-Series)

 

intel processor에서 사용되는 시스템 DLL의 영역은 0x70000000 to 0x78000000 이라고 하네요.

ASLR이 적용이 된다 하더라도 저 범위안에서 주소값이 결정되는것으로 생각이 됩니다.

하지만 이러한 정보들 대부분이 작성된시기가 2006년정도라서 신뢰되는 정보인지 의심이 되지만 그냥 믿고 저대로 코드를 작성했습니다.

현재의 EIP가 0x70000000 to 0x78000000 이면 Context 구조체에서 ESP레지스터 함수시작주소에 진입하는 순간 ESP는 리턴되는 주소로 세팅되어집니다.

그래서 ESP주소에 브레이크포인터(0xCC)를 걸고 탈출하게끔 작성했더니 윈도우7기준으로 아무이상 없이 함수를 잘 찾아줬습니다.

그러나 윈도우XP에서 문제가 발견되었는데 함수를 찾지 못하는 현상이 발견되었습니다.

그이유는 윈도우 XP에서 시스템 DLL이 로드되는 주소가 0x7Cxxxxxx 번대로 로드가 되는겁니다.

제가 예외처리한 영역의 주소는 0x70000000 to 0x78000000 이기때문에 시스템 DLL이라고 하더라도 저 주소 영역에 포함되지 않기때문에 그냥 진행해서 시스템DLL의 내부에서 호출되는 함수들까지 다 처리를 해버리는 경우가 발생되었습니다.

그래서 저부분을 그냥 0x70000000 to 0x80000000 번대로 바꿔주니 간단히 해결되긴했는데 조금더 테스트를 해봐야 정상적으로 작동하는지 정확히 알수 있을듯 싶습니다.

 

 

자 그럼 이제 0xC36A6 GetStartupInfoA 의 함수 내부로 들어와서 함수의 시작주소인 0x75BD1E10로 왔다고 가정합니다.

그럼 0x75BD1E10이 0x70000000 to 0x80000000 내에 포함되니까 if문으로 들어오게 되고 CONTEXT구조체의 ESP가 가르키는 주소에 리턴주소가 들어있기 때문에 해당 주소에 브레이크 포인터를 세팅합니다.

그리고나서 ParsingLoopDllExportName이라는 함수에 진입하게 되는데 이 함수의 역할은 루프를 돌면서 PDllPtrBridge 구조체에서 0x75BD1E10대상의 ExportAddressTable의 주소값과 비교하고 비교후 해당값에 맞는 데이터를 ExportOrdinalTable값에서 조사후 ExportNameTable에서 해당주소값의 함수이름을 찾아주는 역할을 합니다.

 

이 과정들을 좀 더 자세히 보겠습니다.

우선 GetStartupInfoA  함수의 진입 주소가 0x75BD1E10 이며 EIP의 주소값도 동일한 주소값으로 세팅된 상태입니다.

 

 

 

SaveCount의 값으로 로드된 dll의 갯수만큼 루프를 돌면서 LoadDllImageBase(0x75BD0000)라고 가정하고 0x75BD1E10 - LoadDllImageBase(0x75BD0000) = 0x1E10 값을 빼줍니다.

그리고 다시 루프내의 루프를 돌면서 PTR_ExportAddressFunctionManageArr[DllList][EATLocation] 저장된 모든 DLL들의 ExportAddressFunction RVA값과 비교를합니다.

 

 

 

루프를 돌면서 동일한 RVA값을 찾고 0x1E10까지 카운트한 값은 263입니다.

PEVIEW에는 264라고 표기되어있는데 PEVIEW값과 다른 이유는 PEVIEW에서는 COUNT를 1부터시작해서 0x1E10이 있는곳까지의 카운터가 264가된것이고 제가만든 프로그램에서는 0부터 카운터 했기때문에 264 - 1 = 263이 되는것입니다.

 

 

이제 263 값을 ExportOrdinalTable에서 0부터 1씩증가하면서 263이란 Ordinal 값을 갖고있는 녀석을 찾습니다.

Ordinal값 263이 있는곳까지 0부터 1씩증가해서 최종적으로 카운트된 값은 610 값입니다.

 

 

 

 

Export Ordinal Table에서 카운트된 610의 값을 그대로 ExportNameTable에 매치시키면 해당 함수명을 구할 수 있습니다.

이게 가능한 이유는 위에서 설명했었는데 ExportDescriptorTable에는 Address Table이 위치하는 주소값, Ordinal Table이 위치하는 주소값,NameTable이 위치하는 주소값들을 갖고있습니다.

그리고 각 값들이 몇개가 있는지 명시하는 부분이 있었는데요.

그부분이 NumberOfFunction과 NumberOName값이었습니다.

위에서도 언급한대로 NumberOfFunction의 갯수는 AddressTable의 RVA갯수와 같고 NumberOfName의값은 OrdinalTable과 NameTable의 RVA갯수로 사용합니다.

즉 NumberOfName = 8 이면 NameTable의 RVA갯수가 8개라는 얘기고 동시에 OrdinalTable의 RVA갯수도 8이라는 뜻이됩니다.

이러한 방식으로 DLL에서 함수네임을 파싱해서 출력해줍니다.

 

 

 

<프로그램 테스트 분석결과>

 

 

C:>dbg.exe -auto -debugreg c:\windows\system32\notepad.exe 커맨드로 노트패드 프로그램을 분석한 결과입니다.

노트패드 및 계산기,지뢰찾기 등등의 GUI 프로그램은 이벤트 드리븐 방식으로 동작하기 때문에 노트패드 프로그램 영역에 마우스를 움직이거나 클릭하거나 글을쓰거나 할때마다 이벤트가 발생되며 관련된 함수들을 호출하며 루프형식으로 이벤트를 처리합니다.

이렇게 실시간으로 발생되는 이벤트들도 AnalysisReport.txt에 함수가 호출된 순서대로 실시간 로그를 남깁니다.

 

 

 

C:>dbg.exe -auto -debugreg c:\windows\system32\winmine.exe를 커맨드로 지뢰찾기 프로그램을 분석한 결과입니다.

 

 

마찬가지로 C:>dbg.exe -auto -debugreg c:\windows\system32\mspaint.exe를 분석한 결과 입니다.

 

 

 

위와같이 분석이 진행되어 함수가 호출될때마다 실시간으로 txt파일에에 로그를 기록하도록 만들었습니다.

텍스트파일이 생성되는 파일명 규칙을 설명드리겠습니다.

우선 프로그램을 실행시켰을때 첫번째로 파일이 생성된 요일/월/시/분/초/년도/분석대상 실행파일이름/분석모드/부분분석 순으로 저장이 됩니다.

ex)notepad_auto.txt 인경우

dbg.exe -auto c:\windows\system32\notepade.exe로 명령어를 입력하여 분석시 로그파일이름은 날짜_notepad_auto.txt와 같이 생성이되며

부분분석인경우(특정 메모리주소의 시작과 끝을 정하여 분석하는 방법 자세한 내용은 뒤에 설명드리겠습니다.)

dbg.exe -auto 0x400345d 0x400348d c:\windows\system32\notepade.exe로 명령어를 입력하여 부분분석을 진행하여 분석시 로그파일 이름은 _notepad_auto_part.txt 로 분류되어 저장이됩니다.

 

 

AnalysisReport.txt의 내용을 확인해보면 호출된 함수의 EIP주소 그리고 함수명을 확인 할 수 있습니다.

결과 내용을 살펴보면 루프를 돌면서 반복적인 패턴으로 함수들을 중복호출하여 사용하는것을 확인할 수 있습니다.

로그 내용에서 the called funtion name is found in the EXE IAT table과 the called function name is found in the DLL EAT table 두가지 로그로 확인할 수 있는데요

이차이점을 간략히 말씀드리면 우선 함수 호출에 해당하는 OPCODE를 분석하고 LittleEndian을 BigEndian으로 변경해주면 VA주소값이 되는데 이값을 RVA값으로 변경하여 디버깅중인 프로세스의 IAT 함수를 뒤저서 해당되는 값의 함수 이름을 찾게 됩니다.

여기서 MapviewofFile 함수로 메모리에 올려진 PE의 바이너리 데이터들은 로더가 올린 프로세스가 아니기 때문에 IAT의 주소값을 계산해보면 INT를 가르키는 것을 확인 할 수있게 됩니다.

이런점을 이용하여 IAT테이블의 값을 가르켜 사용되는 함수 이름을 가저올수 있게 했습니다.

그리고 두번째 the called function name is found in the DLL EAT table 는 IAT테이블에서 함수이름을 찾지 못하는 경우가 간혹있습니다.

그런경우는 Bounding 처리된 PE구조(IAT주소를 지정된 주소로 고정시키는방식)을 사용할때 Step in 함수 내부로 들어갈경우 RAW파일에서의 IAT테이블에 고정된 주소값과 로더에의해 실행 되어질때의 주소값이 서로 다르게 처리되어 OPCODE를 분석후 IAT테이블에서 함수주소를 찾기에 어려움이 있었습니다.

그리고 또한가지의 어려움은 레지스터를 사용하여 CALL 요청을 하는 부분 EX)CALL EDI or CALL EAX 레지스터를 이용하여 함수를 호출하는 경우 OPCODE자체가 FFD1 ~ FFD7을 사용하게됩니다.

여기서 D1~D7은 각각 D1 = EAX, D2 = ECX, D3 = EDX.. 와 같이 레지스터와 1:1매칭되어 사용되어 지는데 OPCODE자체가 FF15 38130004 인경우 FF15 40001338로 VA이기 때문에 IAT테이블에서 값을 찾을수있지만 FFD1 같은 OPCODE인경우 RVA값이 아니기 때문에 IAT테이블에서 값을 찾을수 없습니다.

그래서 프로그램을 대폭 수정하였습니다.

먼저 디버깅 대상프로세스가 로드되면서 프로세스에서 사용될 DLL들이 호출되는데 호출되는 각 DLL들마다 PE구조를 분석하고 메모리를 할당하여 저장합니다.

예로 KERNEL32.DLL/NTDLL.DLL/USER32.DLL 등등 3개인경우 각각의 PE를 분석하는데 EXPORT ADDRESS TABLE/EXPORT NAME TABLE/ EXPORT ORDINAL 테이블을 모두 저장하도록 해놓고 만약 FFD1인 경우 Step in 함수내부로 진입하여 진입된 EIP주소값과 로드된 DLL들의 PE의 Image Base 값들을 순차적으로 빼고 RVA를 구하여 각각 순차적으로 EXPORT ADDRESS TABLE/EXPORT NAME TABLE/ EXPORT ORDINAL TABLE 들을 뒤저서 함수 이름을 찾도록 구현했습니다.

 

 

<기능 및 옵션 설명>

 

1.help 기능

 

 

 

프로그램을 사용하면서 분석대상 실행파일 위치를 잘못지정 했거나 옵션을 잘못지정 했을경우 메뉴를 확인할 수 있게 만들었습니다.

한글로 되어있기 때문에 한국인?이라면 쉽게 사용법을 확인하실수 있습니다.

 

2. auto mode

 

auto mode는 말그대로 프로그램의 시작부터 끝까지 자동으로 분석을 하고 그 결과를 리포트 하는 기능입니다.

사용 예)c:\> xxx.exe -auto c:\windows\system32\notepad.exe

 

<Auto mode 분석 예시>

 

3.Step mode

 

 

Step Mode는 제가 디버깅 하기위한 목적으로 만들었습니다.

Step Mode Original Entry Point에 브레이크 포인트를 걸고 순차적으로 디버깅을 진행합니다.(그냥 보통 디버거의 기능이라고 보시면됩니다.)

수동으로 직접 Enter를 입력하여 한라인씩 분석을 해야하므로 제가 개발한목적인 함수의 흐름을 파악하기에는 그닥 좋은 기능이 아닙니다.

즉 이 기능은  분석시간이 오래걸립니다.

사용 예)c:\> xxx.exe -step c:\windows\system32\notepad.exe


4.Manual Mode

 

 

Manual Mode모드는 함수가 호출되는 시점에 브레이크포인트를 걸어서 분석을 진행하게 됩니다.

OEP와 함수호출이 아닌 어셈코드를 모두 건너뛰고 함수를 호출하는 부분만 분석을 해서 결과를 리포트해 주는 기능입니다.

Console로 리포트해주는 결과는 EXE PE상의 IAT에 있는 함수이름을 호출해주는 결과와 함수내부로 진입하여 주소값을 계산하고 주고값이 시스템 DLL인경우 해당 DLL의 EXPORT TABLE값에서 함수를 찾기때문에 두가지의결과 FOUND IN DLL IMPORT/ FOUND IN DLL EXPORT 두가지로 나보여주게 됩니다.

 사용 예)c:\> xxx.exe -manual c:\windows\system32\notepad.exe


5.Partial Analysis Mode 




Partial Analysis Mode 는 분석대상의 메모리주소를 지정하면 지정된 영역만 분석을 진행하고 그 결과를 리포트 해주는 기능입니다.

예로 코드를 분석중 전체 분석이 필요없는 부분인 경우나 분석중에 특정영역에서 호출되는 함수를 파악하고 싶을때 사용할 수 있는 기능입니다.



부분분석을 진행한 결과입니다.

테스트시 주소를 지정한 영역은 0x34308d ~ 0x34309d 까지입니다. 첫번째 0x0으로 나오는 이유는 버그수정을 해야하는데..... 넘흐 귀찮네요 .. 뭐사용하는데 지장은 없으니 나중에 정 불편하면 그때 고처야겠습니다. ㅋ

사용 예)c:\> xxx.exe -auto 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -step 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -manual 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -auto -debugreg 0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -step -debugreg  0x34308d 0x34309d c:\windows\system32\notepad.exe

사용 예)c:\> xxx.exe -manual -debugreg 0x34308d 0x34309d c:\windows\system32\notepad.exe


6. DebugRegisterView Mode



DebugRegisterView Mode는 명령어 두번째 옵션중 -debugreg 옵션을 주게 되면 사용할 수 있습니다.

해당 옵션의 기능은 그림에서와같이 현재 EIP의 레지스터와 스택상태를 보여주는 옵션입니다.

이 기능 또한 제가 디버깅용으로 만든거기 때문에 수동으로 디버깅하지 않는이상 안쓰시는게 분석속도를 향상시키는데 도움이 됩니다.

 

 

<악성코드 샘플분석 결과>

 

요즘 제가 K사교육을 듣고있는데 거기서 수집한 악성코드 샘플입니다. 실제 악성코드라고 하네요..

아직 수업중에는 분석하지 않았는데 어떤 악성코드인지 궁금하네요 그래서 겸사겸사 프로그램도 테스트해볼겸 한번 돌려 보겠습니다.

 

우선 분석대상은 malware.exe이름의 악성코드입니다.

 

 

 

 

virustotal 확인결과입니다.

총 3개의 섹션으로 구성되어있구요. PE확인결과 패킹은 되어있지 않았습니다.

 

 

역시 VirusTotal 에서 확인한 결과입니다.

PE IMPORT 정보입니다.

해당 파일에서 사용되는 함수들 리스트 입니다.

PE IMPORT정보만 보면 대략 어떤행위를 하는지 예측할 수는있지만 저 리스트가 순차적으로 실행되는게 아니기 때문에 어떤함수를 호출하는지 알수는 없습니다.

그래서 저 함수들을 실행하는 순서대로 리스트화 해주는게 제가 제작한 프로그램의 목적입니다.

그럼 이제 돌려보도록 하겠습니다(두근두근ㅋㅋ)

 

 

 

옵션은 간단히 -auto 로해서 OEP에서부터 프로그램의 끝까지 자동으로 돌렸습니다.

한 1분정도 뒤에 종료가 되네요.. 결과를 확인해보겠습니다.

 

 

흠.....@_@ 결과를 뽑는건 만족하지만 리포트가 조금 그렇네요..(좀 많이 그런가..ㅋㅋ)

Cuckoo샌드박스 보고서처럼 만들어 주면 쓸만할것 같은데......

 

 

GetModuleFileName으로 호출된 모듈의 파일이름을 얻어오는 함수인데요.

이 함수 써서 만들려다가 어떤 헤더파일 선언해서 써야하는데 msdn찾아보니 뭐또 설치하라고해서 그냥 제가 만들어서 썼습니다.

대략 저 함수는 호출된 모듈의 핸들을 인자로 넘겨주면 호출된 모듈의 파일이름 ex)Kernel.dll, Ntdll.dll등 의 이름을 가저오는 역활을합니다.

이 함수 호출이후 중앙에 보면 0x402943 에서 402180함수를 반복적으로 호출해서 사용하는것을 확인할 수 있습니다.

이 부분은 루프문으로 보시면 됩니다.

그리고 하단에 보면 3개의 블록이 있는데 이 역시 4025CB, 4025D0, 402609, 40345C를 순차적으로 호출하고 3번 반복하는것으로 볼수 있습니다.

여기서 4개의 함수(4025CB, 4025D0, 402609, 40345C)가 한세트 인것으로 볼수 있습니다.

아마도 이 4개의 함수 호출이후 분기문에 의해 호출되는 함수가 틀려지는것으로 예상됩니다.

 

 

그리고 여기서부터 함수호출은 파일관련해서 조작하는것으로 볼 수 있습니다.

우선 GetWindowsDirecotryA함수로 윈도우 디렉토리경로를 얻고 GetModuleFileName으로 파일이름을 얻고난 후에 CreateFileA함수 파일을 읽던지 쓰기던지 하기 위해 파일스트림을 열고

SetFilePointer로 파일포인터를 세팅한 다음 그 부분부터 ReadFile함수로 데이터를 읽고있는것을 확인할 수 있습니다.

ReadFile함수부터는 루프문으로 반복적으로 특정데이터의 값을 읽고 있는것을 확인할 수 있습니다.

 

다음으로 다시 SetFilePointer함수로 파일포인터를 세팅하고 ReadFile 파일을 읽고 WriteFile 파일을쓰고를 반복하는것을 확인할 수 있습니다.

 

 

이 부분은 RegCreateKeyExA,RegSetvalueExA함수를 사용하는것으로보아 레스트리에 특정값을 쓰는것으로 추측할 수 있습니다.

뭐 그리고 GetSystemDirectoryA함수로 윈도우 시스템디렉토리 경로를 얻고  또 파일을  WriteFile로 데이터를 쓰고 있습니다.

 

 

여기서는 CreateToolhelp32Snapshot 함수로 프로세스의 상태정보를 얻는?(아마도 프로세스 관련 구조체를 여기에 넣고 이 함수 호출해서 Process32First로 처음 프로세스찾고 Process32Next로 다음 프로세스 찾는걸로 알고있는데 다시한번 봐야겠네요 오래되서 가물가물) 아무튼 Process32First,Process32Next함수로 특정 프로세스를 찾는듯 합니다.

 

여기는 OpenProcess가 호출되는거보니 위에서 Process32Next함수를 반복 호출해서 프로세스를 탐색해서 찾은것 같습니다.

여기는 일단 OpenProcess로 찾은 프로세스를 여는것같네요 그리고 VirtualAllocEx로 동적메모리를 생성하고 그 영역에 WriteProcessMemory로 값을 쓰는것 같습니다.

 

 

계속해서 프로세스를 찾아서 VirtualAllocEx함수와 WriteProcessMemory,GetmoduleHandleA,GetProceAddress,CreateRemoteThread,WaitForSingleObject를 사용하는것으로보아 코드를 인젝션하는것으로 예상되네요.

프로세스를 탐색하고 해당 프로세스영역에 가상메모리를 할당하고 그 영역에 특정 데이터를 쓰는것같은데요

상세한건 OLLYDBG나 IMMUNITY DBG와같은 전용 디버거로 분석하면 단번에 알수 있겠네요

이상 허접한 개발후기였습니다.


 

혹시 사용해 보고싶은 신분들은 비밀댓글로 이메일주소 남겨주시면 보내드립니다으리!!

 

'[PROGRAMMING]' 카테고리의 다른 글

코드분석 도구 개발하기(C언어)  (10) 2014.06.08
#1 DLL(묵시적 링킹)  (0) 2012.09.20
posted by 미스터리 DKL
2014.03.09 12:53 [PROGRAMMING]/▶Python

<블로그 포스팅용>

상세 분석

등록된 웹사이트 URL을 기반으로 악성코드를 수집하는 mwcrawler.py를 windows 버전으로 포팅한 것입니다.

실행방법은 윈도우에서 더블클릭으로 실행하여 메뉴를 선택하면 수집이 시작됩니다.

메뉴중에 1번은 프록시 IP/PORT를 설정해서 수집을 할 수 있고 2번은 기존과 동일하게 선택과 동시에 바로 수집을 합니다.

수집된 파일의 저장되는 경로는 C:\malware\unsorted\디렉토리 형태로 저장됩니다.

-

스샷과 코드 수정한부분 등록 할 예정

-

코드 수정

1. 프록시서버를 사용하는 환경을 위하여 프록시 IP/PORT를 세팅할 수 있도록 수정했습니다.

2. magic 모듈을 제거하고 읽어들인 파일의 바이너리 데이터를 기반으로 exe, dll, sys, pdf, doc 등의 확장자를 검증하도록 pefile모듈을 사용하여 작성했습니다

3.일반적인 PE외의 charicteristics값중에 reserve나 debug등이 있을 경우 special PE 디렉토리에 저장하도록 수정했습니다.

4. 파일이 저장되는 경로를 윈도우경로로 수정했습니다.

5. 저장되는 경로가 잘못되어 발생하는 오류를 수정했습니다.

 

코드 수정 계획

1. 현재 디버깅용으로 수정하지 않아서 디버깅용 로그는 따로 관리하도록 할 계획입니다.(현재는 육안으로 보기에는 좀 지저분합니다 오류는 아니에요 ㅎㅎ;;)

2. 모든 URL이 페이지 수와 상관없이 21회 반복되는 문제를 동적으로 탐색하도록 수정할 계획입니다.

3. Thug 모듈을 제거할 계획입니다.

 

소스코드


 

win_mwclawler.py

  

위의 코드는 기존에있던 mwcrawler.py에서 윈도우에서 실행되되록 수정한 win_mwcrawler.py 입니다.

빨간박스로 체크된 부분이 제가 수정한 부분입니다.

우선 첫번째로 mwcrawler.py소스를 실행시키면 모듈관련해서 에러가 발생이 되는데요.

모듈이 없는 경우가 대부분이고 모듈선언이 잘못된 경우입니다.

 

첫번째 오류로 BeautifulSoup 관련해서 오류가 발생합니다. 만약 BeautifullSoup 모듈이 없다면 설치를 해야합니다.

하지만 제 시스템에는 모듈을 설치한 상태였기 때문에 모듈 미설치로 인한 오류는 아니고 모듈이름이 잘못되어 발생한 오류 입니다.

즉 from BeautifullSoup import BeautifullSoup as bs -> from bs4 import BeautifulSoup as bs 와같이 변경하면 됩니다.

 

그리고 두번째 에러는 import magic 모듈이 없다는 에러였습니다.

해당 모듈을 사용하는 부분을 확인해보니 다음과 같은 코드로 함수로 구현하여 사용 하고있었습니다.

 

def gettype(file):

ms = magic.open(magic.MAGIC_NONE)

ms.load()

return ms.buffer(file)

 

함수 이름이 gettype으로 이름만 봐도 어떤의도로 사용되어지는지 짐작할 수 있었습니다.

파일을 인자로 gettype(file) 함수를 호출하여 파일을 함수에 넣어 버리면 파일타입이 리턴되겠구나 예상했는데 비슷한거 같습니다.

https://pypi.python.org/pypi/python-magic/ 에서 확인하면 파일타입을 확인 할 수 있는 모듈로 나와있습니다.

그래서 이 모듈을 windows버전에서 설치하고 사용한 사례가 있어 몇번의 시도끝에 설치는 성공적으로 했으나 윈도우에 설치된 magic모듈의 코드내용과

wmcrawler.py모듈에서 사용된 모듈의 코드내용이 서로 일치하지 않아 에러가 발생합니다.

그래서 magic모듈의 기능이 파일타입을 체크하는 모듈이므로 해당되는 코드부분과 모듈 선언부분을 주석처리 하고 파일타입을 체크할 수 있는 모듈을 직접 작성했습니다.

작성한 모듈이름은 filecheck.py이고 관련 내용은 뒤에서 설명하겠습니다.

 

그리고 프록시 서버를 경유하여 외부 인터넷망과 연결할 경우 프록시 세팅을 가능하도록 ProxySetting함수를 만들고 프록시를 세팅한 header를 리턴하도록 구현하여 parse함수에

인자로 전달합니다.

parse함수는 프록시가 세팅된 header로 Request하고 BeautifullSoup모듈을 이용하여 Response데이터를 파싱할 수 있도록 수정하였습니다.

 

그리고 decisor 함수에서 리눅스 경로로 지정된 부분을 윈도우 디렉토리 형태로 변환 하였으며, 에러를 리턴한 URL과 커넥션을 성공한 URL을 구분하고 time모듈을 이용하여 각각 에러와 커넥션한 시간을 로그형태로 저장하도록 변경하였습니다.

 

그리고 SaveLogData,TimeAndLogSave,Gettiime함수를 만들어 저장되는 형태를 각각HTML 데이터로 저장하도록 하고 각 값들을 MD5(Virustotal과 연결), 악성코드URL, 샘플이 저장된 하드디스크 경로를 하이퍼링크로 만들어 저장할 수 있도록 구현했습니다.

  

filecheck.py

 

[그림.2]

 

mwcraler.py 원본 파일에서 사용되던 magic모듈 사용이 어렵기 때문에 파일체크를 할 수 있도록 간단히 만들었습니다.

기능은 각각 파일타입의 특성에따라 간단히 체크할 수 있도록 구현했습니다.

바이너리에서 'PE'시그니처를 찾고 찾은 위치에서부터 IMAGE_FILE_HEADER의 Charicteristics 값을 체크하여 exe파일인지 dll파일인지 체크하도록 했습니다.

 

그림추가

 

EXE파일과 DLL의 경우 IMAGE_FILE_HEADER의 Charicteristics 값에 차이가있습니다.

DLL같은경우 0x200 IMAGE_FILE_DLL값이 있고 EXE같은 경우는 0x200 IMAGE_FILE_DLL 값이 들어있지 않습니다.

이러한 차이점을 이용하여 코드로 구현하였습니다.

 

그림추가

 

그리고 SYS파일은 EXE와 DLL과 동일하게 실행파일입니다. 정확히는 드라이버 파일인데요.

이 파일의 특성은 IMPORT Adress Table에 다음과 같은 파일을 필수적으로 갖고있습니다.

ntoskrnl.exe, hal.dll, ndis.sys, bootvid.dll, kdcom.dll을사용 합니다.

해당내용의 확인은 Python의 PEFILE이라는 모듈의 코드를 보면 def is_driver(self)함수의 내용으로 확인 할 수 있습니다.

 

그리고 PDF,DOC,HTML파일을 각각 파일의 특성에 맞게 체크할 수 있도록 구현하였습니다.

 

[그림.3]

 

win_wmclawler.py를 실행하여 한개의 사이트를 대상으로 악성코드 샘플을 모두 수집했다면 수집한 과정의 로그들을 각각 그림.3과 같이 HTML로 저장이 됩니다.

c:\malware\log\14_01_12(2014년1월12일)의 디렉토리이며 하위 디렉토리에 connection_success_log, error_log의 디렉토리에 저장이 됩니다.

그리고 저장된 파일을보면 D140112_T141430_malc0de.com.html(D날짜_T시간(14시14분30초)_로그가 발생된 사이트) 형식으로 저장합니다.

[그림.4]

 

그리고 그림.4와같이 샘플당 1개씩 관련된 데이터를 정리하여 하이퍼링크를 걸어줍니다.

 

 

[그림.5]

 

MD5항목은 MD5해쉬값으로 Virustotal에 검색할 수 있도록 URL을 연결하여 악성코드 정보를 검색할 수 있습니다.

 

 

 

[그림.6]

 

그리고 URL은 악성코드가 다운된 URL을 하이퍼링크로 걸어 다시 다운받을 수 있게 했습니다.

[그림.7]

 

그리고 System Direcroy Open:Click기능 입니다.

각각 악성코드의 샘플의 저장 디렉토리  C:\malware\unsorted\exe\해쉬값(디렉토리) 형식으로 저장이 되고 해쉬값 디렉토리 밑에 샘플이 저장이됩니다.

이때 시스템 디렉토리의 경로를 하이퍼링크로 걸어 확인할 수 있도록 구현했습니다.

[그림.8]

 

다음은 Connection Error Log 입니다.

이부분은 간단히 에러가 발생된 시간과 에러코드 그리고 에러가 발생한 URL을 보여줍니다.

win_wmcrawler.py(windows version)와 wmcrawler.py(linux version원본파일)에서 동일하게 Request할때 403 에러가 발생하는 경우가 생깁니다.

그러나 URL을 브라우저를 통해 직접 접근해보면 악성코드는 정상적으로 다운이 되어지는데요.

테스트결과 이부분의 문제점이 urllib2로 구현할때 헤더세팅이 잘못 된것으로 보여집니다.

urllib2.request할때 웹서버에서 정상적인 요청이 아닐경우 403에러로 다운을 하지 못하는 경우가 발생하는것 같습니다.

[그림.9]

마지막으로 악성코드를 수집한 화면입니다.

해쉬값으로 검증하여 동일한 파일이 있는지 체크하기 때문에 파일이 중복되는 경우는 없습니다.

 


기능 추가

 

악성코드를 분석하기 위해 샘플을 수집하고 수집된 수많은 샘플들을 일일이 오픈하여 동적분석을 하거나 PEVIEW로 확인하여 예측하고 정리하기는 많은 시간이 소요됩니다.

그래서 기존의 win_wmcrawler.py를 수정하여 기능을 추가하였습니다.

악성코드 분석을 공부하시는 분들에게는 샘플을 쉽게 확보하고 골라서 분석할 수 있는 즐거움이 있을것같습니다. ㅎㅎ


[그림.11]

우선 기존의 악성코드를 수집하는 win_mwcrawler에는 수집한 파일을 대상으로 파일사이즈 및 실행파일에 대한 정보가 없었는데 이번에 그 기능들을 추가하여 수집한 샘플들의 정보를 확인하는데 있어서 시간을 단축 할 수 있도록 수정 하였습니다. 

수정한 win_mwcrawler 실행하고 악성코드 샘플들을 수집하고 나면 그림.11과 같이 각 악성코드 샘플마다 분석을 진행하게 되는데 파이썬 모듈인 Pefile을 사용하여 개발 하였습니다.

우선 처음 악성코드를 수집하게 되면 각 파일의 정보들 Date, FileType,MD5,URL,Filesize를 수집한 후에 해당 악성코드를 별도로 작성한 peinfo.py로 넘어가서 이곳에서 pefile모듈을 불러와 악성코드의 PE구조를 분석하고 리포트 하도록 동작합니다.


[그림.12]

 

악성코드가 PE분석이 완료되면 리포트 되는 형식은 다음과 같습니다.

IMAGE_DOS_HEADER
IMAGE_NT_HEADER
IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER
IMAGE_DATA_DIRECTORIES
IMAGE_SECTION_HEADERS
IMPORT_INFORMATION

 

헤더정보들과 IMPORT 정보들만 확인 가능하게 구현했고 IMPORT_INFORMATION 항목에서 악성코드가 사용되는 함수들을 확인 할 수있기 때문에 저 항목을 통해 대략 어떤 성향의 악성코드인지 파악하고 샘플을 분류할 수 있습니다.

다음은 각각 항목들을 클릭 했을때의 리포트 결과입니다.


[그림.13 IMAGE_OPTIONAL_HEADER]


[그림.14 IMAGE_DATA_DIRECTORIES]

[그림.15 IMAGE_SECTION_HEADER]


[그림.16 IMPORT_INFORMATION]

 [그림.17 peinfo.py]





 

실행시 주의사항: 백신의 실시간탐지 기능을 정지하고 돌려야합니다. 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted by 미스터리 DKL
2014.01.08 14:58

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2014.01.06 17:22

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2013.11.11 21:13 [PROGRAMMING]/▶Python

※ 주의사항

아래 공격 코드는 연구 목적으로 작성된 것이며, 허가 받지 않은 공간에서는 테스트를 절대 금지합니다.

악의 적인 목적으로 이용할 시 발생할 수 있는 법적 책임은 자신한테 있습니다. 이는 해당 글을 열람할 때 동의하였다는 것을 의미합니다.

해당 문서의 저작권은 해당 프로젝트 참여 저자들에게 모두 있습니다. 외부에 공개시 법적 조치가 가해질 수 있습니다.

 

소스하이라이팅

http://cafe.naver.com/boanproject/book3608946/6573

개요

파이썬 기반으로 제작한 웹사이트 크롤링입니다.

개발한 목적은 웹사이트 점검시 수동점검과 자동점검을 진행하게되는데 스캐너같은경우는 스레드갯수만 조절이 가능하고 초당 리퀘스트 갯수를 조절 할 수가 없습니다.

또한 파로스나 burpsuite같은경우 크롤링기능을 사용할 수 있는데 크롤링할때 내가 원하는 페이지만 크롤링이 불가능합니다.

ex)www.test.com 이면 test.com만 크롤링하면되는데 http://www.test.com내의 www.pentest.com 의 링크가있을경우www.pentest.com url까지 모두 크롤링을 합니다.

그리고 마찬가지로 초당 리퀘스트설정이 없기때문에 과부하에 민감한 운영사이트에는 사용을 할 수가 없습니다.

그래서 python기반으로 초당 리퀘스트 세팅을 설정하고 점검사이트만 크롤링이 가능하도록 개발하였습니다.

그리고 원래 목적은 크롤링으로 개발하는 거였으나 크롤링을 완료하고 난 후에 주민등록을 검출할 수 있는 기능을 추가하였습니다.

이뿐만아니라 추후에 xss,sql injection 등등의 추가 모듈을 개발하여 적용하여 사용이 가능합니다.

 

 

 

 

 


테스트 환경 (해당 될시에만)

 운영 체제 (OS) : windows7, 가상 웹사이트


 

동영상 시연

본문 테스트 크기 10pt , 글꼴 (Verdana)

 

상세 분석

 

 

[그림.1]

 

알고리즘의 상단은 로그인하는 부분이고 하단은 크롤링기능을 하는 부분입니다.

 [그림.2]

 

그림2의 1,2,3의 URL을 수집합니다.

1번URL은 로그인 하기전의 Main page URL이고 2번은 로그인을 할때 Request보내는 URL입니다.

그리고 마지막으로 3번은 로그인을 완료하고 나온 다음페이지의 URL입니다.

총3가지의 URL을 수집합니다.

 

 [그림.3]

 

수집한 URL을 1.login 메뉴를 통해 입력합니다.

여기에서 가운데 URL을 보면 ID와 PASS 파라미터가 있습니다.

저렇게 입력을하게되면 저부분을 통해서 통으로 Request보내는 방식이 아닙니다.

저부분은 나중에 Mechanize 모듈을 이용하여 로그인 페이지의 Form을 찾고 Form 안의 태그에서 type='text'와 type='password'부분을 찾게됩니다.

자세한 과정은 이후에 설명하겠습니다.

 

 [그림.4]

 

그림.3번 과정을 거치고나면 2번메뉴를 세팅해야합니다.

2번메뉴는 프록시를 세팅하는 부분인데 이부분을 만들어 놓은 이유는 내부망에서 외부망으로 접속을할때 프록시서버를 경유하도록 하는 곳에서는 프록시를 설정 해야만 접속이 가능합니다. 프록시 세팅을 하지 않는다면 인터넷을 사용할 수 없습니다.

이러한 문제점을 해결 하기위해 프록시를 세팅할 수 있도록 기능을 만들었습니다.

프록시 세팅은 적용/미적용 으로 설정하여 사용이 가능합니다. 

 

 

 

 [그림.5]

 

프록시 세팅을한 후 3번메뉴 crawl start메뉴로 이동하면 ID와 PW를 입력할 수 있도록 나오게 되는데 이부분은 그림.5의 중간부분의 빨간 박스를 보면 각각 id, pass,, 0을 확인 할 수 있습니다. 

 

[그림.6]

 

우선 form 형식을 모두 리스트 형식으로 가저오고 가저온 폼안에 type='password'가 있는 리스트 데이터를 찾습니다.

그리고 해당폼에서 폼네임 Name='login_chk.asp' 값을 가저옵니다.

그리고 가저온 폼값에서 type='passwrod'를 찾고 이 위치를 중심으로 이값을 center라고 세팅한다고 가정합니다.

그리고 form 값 내의 center 에서 제일 가까운 type='text'를 찾습니다.

이렇게 하는 이유는 폼값내의 type='text'가 여러개 존재할 가능성도 있기 때문에 type='passwrod'에서 가장 가까운 type='text'는 99% 아이디를 입력받는 input 태그입니다. 

그렇기 때문에  center위치보다 작은 type='text'를 갖고있는 input 태그중에 type='password'와 가장 가까운 input 태그인것을 id를입력받는 폼으로써

해당태그의 name='' 값을 얻게됩니다.

그래서 얻게되는 값이 id, pass, login_chk.asp 가 되는것입니다.

이렇게 FORM/ID/PW 를 가저오는 이유는 Mechanize라는 python에서 사용되는 모듈을 사용하여 로그인 시스템을 구축하기 위해서입니다.

Mechanize를 사용해서 로그인을 할때는 반듯이 로그인 페이지의 Form값을 얻어와야합니다.

만약에 Mechanize에서 Form값을 인식을 하지 못할 경우에는 nr=0 또는 nr=1 와 같은식으로 0번째Form 첫번째Form을 지정하여 사용할 수 있습니다.

하지만 이렇게 간단히 되면 좋은데 그렇지 않습니다.

간혹 Form Name이 있는데도 불구하고 해당 html페이지의 폼값을 인식하지 못하는 경우가 있습니다.

이럴때를 대비해서 Urllib2 모듈을 사용하여 예외처리를 하는방식으로 결정했습니다.

하지만 Urllib2를 이용해서 로그인하는 방식은 로그인처리만 해놓고 크롤기능까지 가능하도록 구현은 하지않은 상태입니다.

언제든지 필요하면 구현가능 하지만 다른도구를 만들어 보고싶은게 있어서 아쉽지만 로그인 예외처리 방식은 잠시 보류합니다. 

 

 [그림.7]


이제 ID/PW를 입력하고 정상적으로 로그인이 되었는지 확인합니다.

그림.7번의 기능은 그냥 디버깅 확인용으로 구현한것입니다.

정상적으로 로그인 된 것을 확인 할 수 있습니다.


  [그림.8]

 

로그인을 성공하게 되면 크롤링이 몇초에 한번씩 request를 할것인지에 대한 설정을 할 수 있습니다. 

제가 점검용으로 사용할 크롤러를 만들기 위한 이유중에 한부분입니다.

보통 크롤러나 스캐너같은 경우 타켓사이트에대한 타임세팅을 할 수 없습니다.

스캐너같은경우는 스레드 설정은 가능하지만 역시 초당 리퀘스트 세팅을 할 수 없습니다.

그렇기 때문에 간혹 점검 하다보면 서버 과부하 문제로 자동툴사용을 금지요청을 하는경우도 있습니다.

하지만 리퀘스트당 초단위로 세팅하여 크롤링속도를 조절한다면 이러한 문제를 해결할 수 있을거라 생각했습니다.

시간이 조금 걸리겠지만 2초에 한번씩 리퀘스트를 해서 그 결과를 얻을 수 있다면 수동작업과 점검시간을 줄일 수 있을것입니다.

 

  [그림.9]


time setting을 마치고나면 본격적으로 크롤링을 시작하게됩니다.

그림.9을 보면 Request했을때의 에러가 발생하면 해당되는 URL에대한 HTTP ErrorCode를 추가했습니다.

그리고 정상적으로 존재하는 URL즉 Request에 성공한 URL은 Connect Success라는 명령어로 표시했습니다.

그리고 Request하고 Response하여 얻은 html페이지중에 하이퍼링크나 히든속성의 파라미터가 없을경우 None html page link이고

URL overrab은 html페이지에서 가저온 URL들중 중복된 URL입니다.

 

[그림.10]

크롤링의 간단한 원리는  그림.2번의 3번URL(Main) 부터 시작을 합니다.

메인 URL의 모든 하이퍼링크를 가저와 리스트형태로 저장합니다.

그리고 Hidden속성의 파라미터값과 value값을 결합합니다.

결합 후 현재 html을 불러온 url과 결합한 히든값을 재결합 합니다. 

즉 www.test.com + a=1&b=2 와 같이 결합니다.

하지만 이렇게 처리하려면 예외처리를 많이해야합니다. 물론 예외처리까지 어느정도 해놓은 상태이구요

예로들자면 www.test.com 의 url인경우와 www.test.com/ba/,www.test.com/ba 인경우 www.test.com/ba/login.asp? 등등의 경우라면 예외처리를 해야합니다.

www.test.com/ba + a=1&b=2 인경우 결합하게 되면 www.test.com/baa=1&b=2 와같은 url이완성이되며 이처럼 없는 url을 만들어냅니다.

그렇기 때문에 정상적인 url이 되도록 예외처리가 진행이 되어야합니다.

 


 

  [그림.11]

 

 크롤링의 동작순서는 첫번째 모든 하이퍼링크를 수집합니다. 그리고 수집하면서 히든속성의 값들을 URL로 구성하여 보관합니다.

이처럼 URL 을 수집합니다.

수집되는 URL중 중복되는 URL은 제외하고 하고 하이퍼링크URL을 Request 합니다.

그리고 종료되면 히든속성을 결합한 URL을 Request합니다.

이 두 과정이 진행될때 Request한적이 없는 URL만 추가로 수집하고 다시 Request하는 방식입니다.


  [그림.12]


히든속성으로 재결합된 URL이 크롤링을 마치면 각각의 결과들을 HTML페이지로 저장합니다.

그리고 제가 추가로 구현한 기능입니다.

보통 스캐너들에서 검출되는 주민등록번호라고 확인을해보면 죄다 가짜입니다.

그냥 숫자형태와 주민번호형태로 13자리이면 무조건 가저오는것 같습니다.

결론은 xss나 injection취약점에 대해는 잘 찾는 편이지만 그 외의 기능은 성실히 구현한것 같지않은 느낌이 듭니다.  

이것은 어디까지나 제가 사용해본 스캐너 경험담입니다. ㅋ 뭐 제각각입니다. xss/injection등도 오탐이 무지많은 멍청한 스캐너들도 많구요 ㅋ

각설하고 아무튼 수집된 URL을 대상으로 해당되는 페이지에 주민등록번호 유형의 숫자가 있을경우 수집을 합니다.

수집을 한 후 주민등록 검증방식으로 실제 사용되는 주민등록번호인지 가짜 주민번호인지 검증을 하게됩니다.

검증 결과는 실제 주민등록번호일경우 Original ok로 나오며, 가짜 주민번호일경우 Imitation false 로 표시했습니다.

 

  [그림.13]

로그형태로 한번보여주고...

 

  [그림.14]

 역시 스캔결과를 HTML페이지로 저장해줍니다.

scanresult.html을 오픈하면....

 

 [그림.15]

 

해당 결과에 대한 페이지를 그림과같이 보여줍니다.

실제 해당 페이지에 대한 링크를 클릭하면 어느위치에서 발생된 정보인지 쉽게 확인할 수 있구요
이와같이 추가모듈을 시험삼아 적용해 보았습니다.

물론 이외에 XSS/INJECTION 과같은 패턴을 모듈로 개발해서 붙이면 그럴듯한 스캐너가 될것같습니다.



posted by 미스터리 DKL
2013.11.06 10:33 [PROGRAMMING]/▶Python

[ 반복 메타 문자 ]

[메타 문자]

[의미]

*

0회 이상 반복

+

1회 이상 반복

?

0회 or 1회

{m}

m회 반복

{m, n}

m회부터 n회까지 반복

 

[ 매칭 메타 문자 ]

[메타 문자]

[의미]

.

줄바꿈 문자를 제외한 모든 문자와 매치됨

^

문자열의 시작과 매치됨

$

문자열의 마지막과 매치됨

[ ]

문자 집합 중 한 문자를 의미

|

또는(or)를 의미

{ }

정규식을 그룹으로 묶음

 

[ 이스케이프 기호 ]

[종류]

[설명]

\\

역슬래쉬 문자 자체

\d

모든 숫자와 매치됨 [0-9]

\D

숫자가 아닌 문자와 매치됨 [^0-9]

\s

화이트 스페이스 문자와 매치됨 [ \t\n\r\f\v]

\S

화이트 스페이스가 아닌 것과 매치됨 [^ \t\n\r\f\v]

\w

숫자 또는 문자와 매치됨 [a-zA-Z0-9_]

\W

숫자 또는 문자가 아닌 것과 매치됨 [^a-zA-Z0-9_]

\b

단어의 경계를 나타냄. 단어는 영문자 혹은 숫자의 연속 문자열

\B

단어의 경계가 아님을 나타냄

\A

문자열의 처음에만 일치

\Z

문자열의 끝에만 일치

 

[ 최소 매칭을 위한 정규식 ]

[기회]

[의미]

*?

*와 같으나 문자열을 취소로 매치함

+?

+와 같으나 문자열을 취소로 매치함

??

?와 같으나 문자열을 최소로 매치함

{m,n}?

{m,n}과 같으나 문자열을 최소로 매치함

 

[ 정규 표현식에서 사용 가능한 플래그 ]

[플래그]

[내용]

I, IGNORECATE

대, 소문자를 구별하지 않는다

L, LOCATE

\w, \W, \b, \B를 현재의 로케일에 영향을 받게 한다

M, MULTILINE

^가 문자열의 맨 처음, 각 라인의 맨 처음과 매치 된다

$는 문자열의 맨 끝, 각 라인의 맨 끝과 매치

S, DOTALL

.을 줄바꾸기 문자도 포함하여 매치하게 한다

U, UNICODE

\w, \W, \b, \B가 유니코드 문자 특성에 의존하게 한다

X, VERBOSE

정규식 안의 공백은 무시된다

 

[ re모듈의 주요 메소드 ]

[메소드]

[설명]

compile(pattern[, flags])

pattern을 컴파일하여 정규식 객체를 반환

match(pattern, string[,flags])

string의 시작부분부터 pattern이 존재하는지 검사하여

MatchObject 인스턴스를 반환

search(pattern, string[,flags])

string의 전체에 대해서 pattern이 존재하는지 검사하여

MatchObject 인스턴스를 반환

split(pattern, string[, maxplit=0])

pattern을 구분자로 string을 분리하여 리스트로 반환

findall(pattern, string[, flags])

string에서 pattern을 만족하는 문자열을 리스트로 반환

finditer(pattern, string[, flags])

string에서 pattern을 만족하는 문자열을 반복자로 반환

sub(pattern, repl, string[, count=0])

string에서 pattern과 일치하는 부분에 대하여

repl로 교체하여 결과 문자열을 반환

subn(pattern, repl, string[, count=0])

sub와 동일하나, 결과로(결과문자열, 매칭횟수)를

튜플로 반환

escape(string)

영문자 숫자가 아닌 문자들을 백슬래쉬 처리해서 리턴.

(임의의 문자열을 정규식 패턴으로 사용할 경우 유용)

 

[ Match 객체 ]

Match객체는 match(), search()의 수행 결과로 생성되며, 검색된 결과를 효율적으로 처리할 수 있는 기능 제공.

◎ Match객체가 지원하는 메소드와 속성

[메소드]

[속성]

group([group1, ...])

입력받은 인덱스에 해당하는 매칭된 문자열 결과의 부분 집합을 반환합니다.

인덱스가 '0'이거나 입력되지 않은 경우 전체 매칭 문자열을 반환합니다.

groups()

매칭된 결과를 튜플 형태로 반환

groupdict()

이름이 붙여진 매칭 결과를 사전 형태로 반환

start([group])

매칭된 결과 문자열의 시작 인덱스를 반환. (인자로 부분 집합의 번호나

명시된 이름이 전달된 경우, 그에 해당하는 시작 인덱스를 반환)

end([group])

매칭된 결과 문자열의 종료 인덱스를 반환. (인자로 부분 집합의 번호나

명시된 이름이 전달된 경우, 그에 해당하는 종료 인덱스를 반환)

pos

원본 문자열에서 검색을 시작하는 위치입니다.

endpos

원본 문자열에서 검색을 종료하는 위치입니다.

lastindex

매칭된 결과 집합에서 마지막 인덱스 번호를 반환. (일치된 결과가 없는 경우

에는 None을 반환)

lastgroup

매칭된 결과 집합에서 마지막으로 일치한 이름을 반환. (정규식의 매칭 조건에

이름이 지정되지 않았거나 일치된 결과가 없는 경우 None 반환)

string

매칭의 대상이 되는 원본 문자열입니다.

 

 

[ 예제 ] - re 모듈 함수

▶ re.match()와 re.search()의 차이

re.match()의 경우 대상 문자열의 시작부터 검색을 하지만,

re.search()함수는 대상 문자열 전체에 대해서 검색을 수행한다.

예) 아래와 같이 검색의 대상이 되는 문자열에 공백이 있는 경우나 또는

검색 키워드와 일치하는 문자열이 대상 문자열의 중간 이후에 존재하는 경우

re.match()함수는 검색을 못함.

 

▶ re.split() - 대상 문자열을 입력된 패턴을 구분자로 하여 분리

 

▶ re.findall() - 검색 문자열에서 패턴과 매칭되는 모든 경우를 찾아 리스트로 반환

 

▶ re.sub() - 패턴과 일치하는 문자열 변경

또한 변경할 문자열에 대해서 매칭된 문자열을 사용할 수도 있다.

 

 

[ 예제 ] - 정규 표현식 객체

▶ 동일한 패턴을 연속적으로 검색하는 경우, 정규식을 컴파일하여 정규표현식 객체를 생성.

 

▶ re.IGNORECASE 플래그로 대소문자 구분하지 않고 매칭 작업 수행

 

▶ re.MULTILINE 플래그를 설정하여 빈 라인을 제외하고 라인별로 분리

 

 

[ 예제 ] - Match 객체

▶ 일반적인 형식의 전화번호를 인삭하여 Match 객체가 지원하는 메소드 분석.

 

▶ 정규식 작성시 '(?<이름>..)' 형식으로 매칭 결과에 대해 이름을 부여하고,

groupdict() 메서드를 이용하여 사전 형태로 이름과 검색된 문자쌍을 얻음.

 

 

    

로우 문자열 표기법 (Raw string notation)

이스케이프 문자열을 표현하기 위하여 '\'(백슬래쉬)문자를 사용하기 때문에, 문자 '\'를 정규표현식

으로 표현하기 위해서는 '\\\\'로, 일반 문자열에선느 '\\'로 표현해야 합니다. 그래서 '\apple'이란

문자열을 검색하기 위해서는 아래와 같이 매우 복잡한 형식으로 표현해야 합니다.

 

 

로우 문자열 표기법은 문자열 앞에 'r'을 더한 것으로, \(백슬래쉬) 문자를 이스케이프 문자열로 처리

하지 않고 일반 문자와 동일하게 처리합니다. 이렇게 함으로써 정규표현식 및 문자열에서 '\'를 간단

하게 표현할 수 있습니다. 일반적으로 정규표현식에 사용되는 문자열에서는 이러한 편리함 때문에

많이 사용합니다.

 

 

출처:http://devanix.tistory.com/296

posted by 미스터리 DKL
2013.10.17 17:30 [PROGRAMMING]/▶Python

파이썬에서 제공해주는 뷰티풀 수프 웹페이지를 파서해주는 모듈이다

그러나 이 모듈에는 치명적인 버그가있다..

로그인 페이지에서 form태그를 파싱하는데 분명히 form태그가 있는대도 불구하고 긁어오지 못하는 현상이 벌어졌다.

그 원인은 바로

<html>

<head>

</head>

<script type = "text/javascript">

꽐라꽐라~

꽐라꽐라~

꽐라꽐라~

꽐라꽐라~

꽐라꽐라~

꽐라꽐라~

꽐라꽐라~

</script>

<body>

꽐라꽐라~

꽐라꽐라~

</body>

이건데.. 이것만봐서는 통..이해가 되질 않겠저?

결론은 헤드태그 다음에 바디태그가 없으면 헤드태그 이후의 태그들을 찾지못한다.

이런 바보같은 모듈같으니..

그냥 직접 만들어서 써야할듯하다 하,,,

인줄알았지만..... 몇번 삽질끝에 방법을 알아냈다 ;;;(이상하게 이럴리가 없는데 라고생각하고 삽질을 했다 될때까지 ㅠ)

버그가아니였다.. 역시 완벽한건가..ㄷㄷ

저런형식으로 form값을 가저오지 못할때는...

import bs4
import urllib2

url = 'http://www.****.co.kr/'

handle = urllib2.urlopen(url)
data = handle.read()
soup = bs4.BeautifulSoup(data)


html = str(soup)
so = bs4.BeautifulSoup(html)

위와같이 하면 해결된다.

원인은 soup자체가 UNICODE로 처리되는데 스트링으로 변경하지 않고 사용하게되면 폼을 찾지못하는 단순한 사용오류였던것이다...
 

posted by 미스터리 DKL
2013.10.02 17:44 [PROGRAMMING]/▶Python

어느날 리버싱을하면서 모르는 명령어가 발견이 됬다

분명이 몇번 봤던 명령어인데 머릿속에 들어오질 않았었던 것이다.

그래서 그때부터 인터넷과 어셈관련 문서들을 참조해 해결하곤 했었다.

이런일이 자주 일어나다보니(머리가 나빠서 OTL )아예 내가 한번 봤던 명령어는 내가만든 프로그램에 전부 저장해놓고 다음번엔 명령어 검색만으로 찾아내는식으로 만들어야겠다고 생각이 들었고 대충 어떤식으로 동작할지 그려보고 바로 코딩했다.

간략히 설명 하겠습니다.

 

개발언어 : python

개발도구 : pyscripter

 

 

어셈명령어는 정말 많다. 그걸 다외우려면 고생좀해야될것이다. 그리고 그걸 다외우고 다니는사람은 없을것 같은데.. 있을수도있다ㅎㅎ

무튼 저런식으로 명령어들이 있다. 저명령어가 뭐하는건지 찾아내려면 인터넷을 뒤지고 또뒤지고 뒤저서 자료를 모아야한다. 그런데 그걸 한번에 기억하는 천재가있으면 괜찮지만

사람은 잊어버릴수밖에없다 그럼 그때 똑같은 방법그대로 익혀야한다. 매번 인터넷과 문서뒤지기도 귀찮다.

 

 

예외처리좀 해야되는데 빨리써야함으로 그냥 기능만 정상작동되게 만들어놓고 사용중이다.

asmdic은 정말 간단하다.

 

<Command모드 기능설명>

FIND : ex:)FIND모드로 진입한다.

del : 데이터 사전 파일내용을 전부 삭제한다.

open : 데이터 사전 파일을 연다.(읽기전용이므로 수정이 불가능하다. 단! mod 명령어를 이용해 수정가능)

add : 데이터 사전파일에 어셈명령어를 추가한다.

mod : 하.. 스샷에 mod관련 설명이 없네요. 데이터사전파일은 읽기전용이라 수정이 불가하지만 mod명령어로 수정은 가능합니다.

 

<Find모드 기능설명>

exit: find모드에서 exit는 command모드로 돌아간다.

all : 데이터 사전에 있는 내용을 모두 출력한다.

find > [어셈명령어] : ex) find > jnz라고 입력하면 jnz관련 명령어를 출력해준다.

 

 

간략히 이런 기능의 프로그램이다.

겉으로 보이기엔 간단하지만 여기에 나름 속도 향상을 위해 깨알같이 MMF(Memory Map File) 기능으로 구현하였다.

그 이유는 출력을 예로 들겠다.

간단히 real10을 출력한다.

이때 일반적이면 컴퓨터 -> 레지스터 -> 메모리 -> 하드디스크 에서 파일에 엑세스하여 real10정보를 가저온다. 파일 크기가 작다면 체감상모르겠지만 데이터가 쌓이고 하다보면

굉장히 느려질것이다.

반면 mmf로 사용하게되면  컴퓨터 -> 레지스터 -> 메모리 -> 하드디스크에서 1번만 엑세스하여 메모리에 올려버린다.

그러고나면 컴퓨터 -> 레지스터 -> 메모리 이상태에서 데이터에 액세스 할 수 있기 때문에 빠른 데이터 처리가 가능하기 때문이다.

몇년전에 시스템프로그래밍 공부했던게 스치듯 기억나 적용해본 케이스이다.

 

 

그리고 데이터가 저장 될때는 위와같이 저장되며, 내용을 추가할때는 2라인과 같이 날짜와 적용한 시간을 넣어준다.

해당 파일은 읽기전용으로 저장된다.

마지막으로 데이터를 찾을때 대소문자 상관없이 검색할 수 있게 구현해놨다.

 

 

 

posted by 미스터리 DKL
2013.09.29 18:00 [PROGRAMMING]/▶Python

회사 pc는 설정을 이미 해놨고 집에서 다시 깔고 설정을 하려고보니 그 어디에도 자료가없다.

더구나 검색해도 나오지 않는다..OTL

리버싱하다 아이디어가 떠올라 어셈공부용 에디터프로그램을 하나 만들려고했는데 벌써 이설정 하려고 검색만 2시간한듯..하..

시간이..하..

다음에 이런 수고를 덜고 다른분들도 보고 도움이 되었으면 해서 올립니다.

 

 

개발할때 환경 중요하지요. 개발하는 기분이 틀리더군요 물론 개인적인 생각이지만요..ㅎㅎ

우선 세팅 화면은 이렇습니다.

 

Theme down > http://sutocom.net/2013/07/25/pyscripter-dark-theme/

테마를 다운로드 하는 주소구요

번거로우시면 제가 올린 파일 다운받으셔서 설정하셔도 됩니다.

 

 

DarkHighlight2.ini

 

Hacker BW.skn

 

 

 

테마 적용은 %APPDATA%\pyScripter\skins folder 에저장하라고 합니다.

그냥 skins 검색하셔서 pyscripter 하위 디렉토리에 있는곳에 저장하면됩니다.

참고로 제 경로입니다. C:\Users\DK\AppData\Roaming\PyScripter\Skins

 

 

이렇게 적용하면 끝입니다.

posted by 미스터리 DKL
2013.09.26 11:00 [PROGRAMMING]/▶Python

개요

웹사이트 취약점을 자동점검과 수동점검으로 진행하게 되는데 수동점검시 수많은 파라미터를 체크하기에는 시간이 부족할 수 있습니다.

그래서 중복되는 파라미터는 제외해서 url로 재구성하도록 만들어봤습니다.

하지만 이 도구를 사용해서 점검을 할 경우 중복되는 파라미터를 제외해주기 때문에 수동점검 속도는 올려주나 취약점을 놓칠수 있는 오류를 범할 가능성이 있습니다.

그렇기 때문에 한번 발견된 종류의 취약점중에 다수의 샘플을 얻을때 사용하는것이 효율적이라고 생각됩니다.

예로 xss 또는 sql injection 등의 취약점을 a라는 파라미터에서 발견되었고 해당사이트에 다수의 xss 취약점이 존재한다고 보고서를 작성할때 사용하면 좋을것이고

반면에 사이트에서 모든 xss 취약점 인자를 빠짐없이 다 찾는방식에서는 이 도구를 사용하면 안될것입니다.

python 문법을 인터넷으로 문법공부하고 바로 만들어본거여서 코드 가독성 및 안정성은 떨어지지만 여러 시행착오를 거처 완성했습니다.

이 도구를 만드는 목적은 실 생활에 필요한 도구?, 업무효율을 높일 수 있지않을까? 하고 설계 없이 생각한 대로 만들었는데 업무효율은 높일수 있지만 취약점 점검시 안정성은 떨어집니다.

 

 

사용된 언어 및 툴

Python3 / idle / pyscripter

 

동작 원리

간단한 동작원리 에대한 설명입니다.

url1.txt와 url2.txt 두개의 파일을 생성하고 url2.txt에서 사용자에게 url을 입력받는다.
url1에는 url2에서 입력받은 값을 복사하며 중복된 url은 복사하지 않는다. 즉 중복되지 않은 url만 url1.txt에 저장된다
ex) 처음 url2.txt에서 www.test.com?a=1&b=1&c=2 라는 url을 저장한다. 그럼 url1.txt에 해당 url이 없으면 저장하게된다.
그리고 두번째로 url2.txt에 www.test.com?ac=1&ba=1&c=2를 저장한다.
두번째로 저장하고 창을 닫으면 url1.txt와 url2.txt의 값이 같은지 securl.py에서 검사하게되고 값이 같을경우 추가하지않고 값이 없을경우 url1에 추가한다.


각 url의 파라미터 www.test.com?a=1&b=1&c=2 처음 1회는 ? ~ & 까지 그다음부터 끝까지는 위치를 기억한후 &전까지 탐색하여 딕셔너리로 저장한다.
그리고 dic1과 dic2를 하나씩 비교해서 같은값의 dic2의 키값으로 tmp변수에 저장한다. 즉 값이 같을때만 키값을 저장한다.
그리고 dic1의 키값을 뽑고 dic2의 비교한 키값을 저장하고 url2(dic2)에서 dic1과 dic2의 같은 키값을 제외한 같지 않은 키값의 파라미터만 재구성하여
url로 만들어 준다. 

 

시연

 

url2에 url주소를 입력하면 url1에 입력됬던 값드를 중복여부 체크후에 저장합니다.

 

[파라미터가 중복된 경우]

 

 그리고 위와 같이 url2의 파라미터가 url1에 있다면 all parameter match url pass 라고 알려줍니다.

하지만 파라미터는 중복이지만 full url은 url1에 없기 때문에 URL add를 합니다.

 

[파라미터가 중복이 되지 않은 경우]

 

test code aid : 1 2 3

test code tmp : 1

과 같이 나왔는데 이것은 url1의 3개의 파라미터  www.test.com?a=1&b=1&c=2 와 url2 의 파라미터  www.test.com?a=1&aid=74&bid=74를 비교하고 없는 파라미터만 보여주고

그 파라미터들만 재구성 합니다.

 

 

[파라미터가 중복이 된 경우]

 

그리고 동일한 url을 입력했을 경우는  URL overlab과 같이 중복검사 결과를 보여주고 url을 추가하지 않습니다.

 

[중복되지 않은 파라미터만 url로 재구성]

 

마지막으로 없는파라미터만 재구성하여 urllist.txt 파일에 저정합니다.

점검은 이 url로 점검을 진행하면 되는데 취약점 발견 누락이 발생할 수 있기 떄문에 처음에 설명한대로 사용하면 될것입니다.

개발 컨셉을 잘못잡아서 파이썬으로 처음만들어 본거지만 별 쓸모는 없는것 같네요...;

하지만 이것을 시작으로 더욱더 강력하고 사용자 편의 중심의 도구를 만들어 봐야겠습니다..

 

posted by 미스터리 DKL