외규장각 도서 환수 모금 캠페인

Search Results for 'Study'

98 POSTS

  1. 2007.06.19 Thread Information Block
  2. 2007.06.19 Native API
  3. 2007.06.19 Function Interception
  4. 2007.06.19 Controlling Memory Space of Other Process
  5. 2007.06.19 Detecting Application Debugger

Thread Information Block

Posted 2007. 6. 19. 16:05, Filed under: Study/Computer Science

Win32 Thread Information Block
From Wikipedia, the free encyclopedia



In computing, the Win32 Thread Information Block (TIB) is a data structure in Win32 on x86 that stores info about the currently running thread.

The TIB is officially undocumented for Windows 9x. The Windows NT series DDK includes a struct NT_TIB in winnt.h that documents the subsystem independent part. Yet so many Win32 programs use undocumented fields so it is effectively a part of the API.

The TIB can be used to get a lot of information on the process without calling win32 API. Examples include emulating GetLastError(), GetVersion(). Through the pointer to the PEB one can obtain access to the import tables (IAT), process startup arguments, image name, etc.

[edit] Accessing the TIB

The TIB can be accessed as an offset of segment register FS.

It is common to not access TIB fields by offset from FS:[0], but rather first getting a the linear self-refernecing pointer to the stored at FS:[0x18]. That pointer is used in means of pointer arithmetics or cast to a struct pointer.

Example in C inlined-assembly for 32-bit x86:

// gcc (AT&T-style inline assembly).
void *getTIB()
{
    void *pTib;
    __asm__("movl %%fs:0x18, %%eax\n\t"
            "movl %%eax, %0"
            : "=rm" (pTib) : : "%eax");
    return pTib;
}
// Microsoft C
void *getTib()
{
    void *pTib;
    __asm {
        mov EAX, FS:[18h]
        mov [pTib], EAX
    }
    return pTib;
}

[edit] Contents of the TIB

Position Length Windows Versions Description
FS:[0x00] 4 Win9x and NT Current Structured Exception Handling (SEH) frame
FS:[0x04] 4 Win9x and NT Top of stack
FS:[0x08] 4 Win9x and NT Current bottom of stack
FS:[0x10] 4 NT Fiber data
FS:[0x14] 4 Win9x and NT Arbitrary data slot
FS:[0x18] 4 Win9x and NT Linear address of TIB
- - NT End of NT subsystem independent part
FS:[0x20] 4 NT Process ID
FS:[0x24] 4 NT Current thread ID
FS:[0x2C] 4 Win9x and NT Linear address of the thread-local storage array
FS:[0x30] 4 NT Linear address of Process Environment Block (PEB)
FS:[0x34] 4 NT Last error number
FS:[0x38] 4 NT Last status number
FS:[0x3C] 4 NT Count owned locks
FS:[0x40] 4 NT Hard errors mode
~ ~ ~ ~
FS:[0x60] 4 Win95/Win98 Last error number
~ ~ ~ ~
FS:[0x74] 4 WinME Last error number
Response : ,

Native API

Posted 2007. 6. 19. 15:49, Filed under: Study/Computer Science

The Native API (with capitalized N) is the publicly largely undocumented application programming interface used internally by the Windows NT family of operating systems produced by Microsoft. Most of the Native API calls are in ntdll.dll and ntoskrnl.exe (and its variants).

While most of Microsoft Windows is implemented using the documented and well-defined Windows API, a few components, such as the Client/Server Runtime Subsystem are implemented using the Native API, as they are started early enough in the Windows NT Startup Process that the Windows API is not available yet.

Some rootkits make use of the Native API to hide their presence from malware detection software.

Two specific areas where Microsoft has provided documentation for Native API calls include the Installable File System and Driver Development Kit SDKs.


reference - http://undocumented.ntinternals.net/

Response : ,

Function Interception

Posted 2007. 6. 19. 15:15, Filed under: Study/Computer Science

Function Interception

Notice the DLL project in the zip file. This function is in HookApi.h.

// Macro for adding pointers/DWORDs together without C arithmetic interfering 
// -- Taken from Matt Pietrek's book
// Thought it'd be great to use..
#define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))
//This code is very similar to Matt Pietrek's, except that it is written 
//according to my understanding...
//And Matt Pietrek's also handles Win32s 
//--(Because they it has some sort of a problem)
PROC WINAPI HookImportedFunction(HMODULE hModule,
			         //Module to intercept calls from
     PSTR FunctionModule, //The dll file that contains the function you want to 
			  //hook(ex: "USER32.dll")
     PSTR FunctionName,   //The function that you want to hook 
			  //(ex: "MessageBoxA")
     PROC pfnNewProc)     //New function, this gets called instead
{
    PROC pfnOriginalProc; //The intercepted function's original location
    IMAGE_DOS_HEADER *pDosHeader; 
    IMAGE_NT_HEADERS *pNTHeader;
    IMAGE_IMPORT_DESCRIPTOR *pImportDesc;
    IMAGE_THUNK_DATA *pThunk;
    // Verify that a valid pfn was passed

    if ( IsBadCodePtr(pfnNewProc) ) return 0; 

    pfnOriginalProc = GetProcAddress(GetModuleHandle(FunctionModule), 
                                                         FunctionName);
    if(!pfnOriginalProc) return 0;
    pDosHeader = (PIMAGE_DOS_HEADER)hModule; 
    //kindly read the ImgHelp function reference 
    //in the Image Help Library section in MSDN
    //hModule is the Process's Base address  (GetModuleHandle(0)) 
    //even if called in the dll, it still gets the hModule of the calling process
    //---That's you should save the hInstance of the DLL as a global variable, 
    //in DllMain(), because it's the only way to get it(I think)
    // Tests to make sure we're looking at a module image (the 'MZ' header)
    if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) )
        return 0;
    if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE ) 
	//Image_DOS_SIGNATURE is a WORD (2bytes, 'M', 'Z' 's values)
        return 0;
    // The MZ header has a pointer to the PE header
    pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew); 
    //it's like doing pDosHeader + pDosHeader->e_lfanew
    // e_lfanew contains a RVA to the 'PE\0\0' Header...An rva means, offset,
    // relative to the BaseAddress of module 
    // -pDosHeader is the base address..and e_lfanew is the RVA, 
    // so summing them, will give you the Virtual Address..
    // More tests to make sure we're looking at a "PE" image
    if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) )
        return 0;
    if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) 
	//IMAGE_NT_SIGNATURE is a DWORD (4bytes, 'P', 'E', '\0', '\0' 's values)
        return 0;
    // We now have a valid pointer to the module's PE header. 
    // Now get a pointer to its imports section
    pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, 
      pDosHeader, //IMAGE_IMPORT_DESCRIPTOR *pImportDesc;
      pNTHeader->OptionalHeader.DataDirectory
       [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    //What i just did was get the imports section by getting the RVA of it
    //(like i did above), then adding the base addr to it.
    //// pNTHeader->OptionalHeader.DataDirectory
    ///     [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
    //// IMAGE_DIRECTORY_ENTRY_IMPORT==1 -- Look at that PE documentation. 
    //// Pietrek's articles in MSJ and MSDN Magazine will be real helpful!
    //Go out if imports table doesn't exist
    if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader )
        return 0; //pImportDesc will ==pNTHeader. 
	//if the RVA==0, cause pNTHeader+0==pNTHeader -> stored in pImportDesc
	//Therefore, pImportDesc==pNTHeader
    // Iterate through the array of imported module descriptors, looking
    // for the module whose name matches the FunctionModule parameter
    while ( pImportDesc->Name ) //Name is a DWORD (RVA, to a DLL name)
    {
        PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name);

        if ( stricmp(pszModName, FunctionModule) == 0 ) 
	    //str"i"cmp,,, suggest you to ignore cases when comparing,
            break; //or strcmpi() in some compilers
        pImportDesc++;  // Advance to next imported module descriptor
    }
    // Get out if we didn't find the Dll name. 
    // pImportDesc->Name will be non-zero if we found it.
    if ( pImportDesc->Name == 0 )
        return 0;
 // Get a pointer to the found module's import address table (IAT)
 //           =====IMAGE_THUNK_DATA *pThunk;
    pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk);
 //This is what i was talkin about earlier...
 //In pThunk, if it was image loaded in memory, you'll get the address to 
 //entry point of functions
 //but in a disk file, It's a function name

 // Look through the table of import addresses, of the found 
 // DLL, looking for the function's entry point that matches 
 // the address we got back from GetProcAddress above.
    while ( pThunk->u1.Function )
    {
       if ( (DWORD)pThunk->u1.Function == (DWORD)pfnOriginalProc )
        {
       // We found it!  Overwrite the original address with the
       // address of the interception function.  Return the original
       // address to the caller so that they can chain on to it.
            pThunk->u1.Function = (PDWORD)pfnNewProc; 
	    // pfnNewProc is in the parameters of the function
	    //pfnOriginalProc = (PROC)(DWORD)pdw1;
            return pfnOriginalProc;
        }

        pThunk++;   // Advance to next imported function address
    }
    return 0; //function not found!!!!!
}
















PE 파일의 import address table을 직접 수정하는 방식으로 로드된 라이브러리가 호출하는 함수를 변경하고 있다.
자신이 작성한 코드를 수행한 후에 원래의 함수로 제어를 돌려주는 식으로 코드를 작성한 후에  원래 함수를 대체하는 방식으로 사용할 수 있겠다.
Response : ,

Controlling Memory Space of Other Process

Posted 2007. 6. 19. 14:55, Filed under: Study/Computer Science
================================================================================
Title : Controlling Memory Space of Other Process

Author : proXima
Contact : proxima ]at[ postech.ac.kr
Date : 2006/11/15 - 2006/11/15
================================================================================

요즘의 OS들은 대부분 보호모드(Protected Mode)를 지원한다.
보호모드라는 것은, 프로세스 내에서 메모리를 액세스 할 때에 자신의 가상 메모리 공간을 자신의 영역이라고 간주하도록 하는 것이다. 무슨 이야기냐 하면, OS는 프로세스 간에 메모리를 건드리지 못하게 보호해 준다는 것이다.
하지만 MS Windows에서는 디버깅을 위해 다른 프로세스의 메모리를 건드릴 수 있게 해 주는 API를 제공해 주는데, 그것들은 다음과 같다.



BOOL __stdcall ReadProcessMemory(
 HANDLE hProcess,
 LPCVOID lpBaseAddress,
 LPVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T* lpNumberOfBytesRead
);


BOOL __stdcall WriteProcessMemory(
 HANDLE hProcess,
 LPVOID lpBaseAddress,
 LPCVOID lpBuffer,
 SIZE_T nSize,
 SIZE_T* lpNumberOfBytesWritten
);


 
함수의 이름만 봐도, 뭐 하는 함수인지를 뻔히 알 수가 있다.
이 두 함수는 다른 프로세스의 메모리 공간에 있는 데이터를 읽어오거나(ReadProcessMemory) 혹은 다른 프로세스 메모리 공간에 데이터를 쓸 수 있게(WriteProcessMemory) 해 준다.

위에서 사용되는 인자 중 hProcess는 OpenProcess 혹은 CreateProcess를 통해 얻어온 타겟 프로세스의 핸들값이어야 한다. 그리고 lpBaseAddress는 타겟 프로세스의 가상메모리 주소여야 한다.
뭐 나머지는 별로 문제 될만한 건 없어 보인다.

그러면 간단하게 남의 프로세스 메모리를 읽어오는 프로그램을 만들어 보자.


HANDLE hTargetProcess = OpenProcess(PROCESS_VM_READ, FALSE, pid);
ReadProcessMemory(hTargetProcess, (LPCVOID)BaseAddr, (LPVOID)buf, \
                  sizeof(buf), (SIZE_T*)&nWritten);
CloseHandle(hTargetProcess);


 
간단하지만, 실수의 여지가 조금 있다. OpenProcess로부터 받아온 HANDLE값을 CloseHandle로 닫아주지 않을 경우가 생길 수 있다. 그러면 핸들이 계속 남아있게 된다.
항상 저렇게 3줄을 세트로 사용한다면 문제 없겠지만, 실수를 원천봉쇄하는 방법이 있다.
바로 Toolhelp 함수를 사용하는 것인데, 다음과 같다.


BOOL __stdcall Toolhelp32ReadProcessMemory(
  DWORD th32ProcessID,
  LPCVOID lpBaseAddress,
  LPVOID lpBuffer,
  SIZE_T cbRead,
  SIZE_T lpNumberOfBytesRead
);
 
아쉽게도, ReadProcessMemory만 된다. WriteProcessMemory에 대한 것은 없지만, 직접 만들어서 사용하여도 무방할 듯 하다.
아주 간단하게 Toolhelp32ReadProcessMemory처럼 동작하는 WriteProcessMemory에 대한 함수도 만들 수 있는데, 일단 Toolhelp32ReadProcessMemory 함수의 내부를 좀 보자.


MOV EDI,EDI
PUSH EBP
MOV EBP,ESP
PUSH ESI
PUSH DWORD PTR SS:[EBP+8]
PUSH 0
PUSH 10
CALL kernel32.OpenProcess
MOV ESI,EAX
TEST ESI,ESI
JE SHORT kernel32.7C863D5A
PUSH EDI
PUSH DWORD PTR SS:[EBP+18]
PUSH DWORD PTR SS:[EBP+14]
PUSH DWORD PTR SS:[EBP+10]
PUSH DWORD PTR SS:[EBP+C]
PUSH ESI
CALL kernel32.ReadProcessMemory
PUSH ESI
MOV EDI,EAX
CALL kernel32.CloseHandle
MOV EAX,EDI
POP EDI
POP ESI
POP EBP
RETN 14


 
디버거에서 코드를 가져다가 붙이는 바람에, JE SHORT kernel32.7C863D5A 라는 코드가 그대로 들어갔는데, 실제로 7C863D5A의 주소에 있는 명령은 밑에서 세 번째 줄에 있는 POP ESI이다.
이 코드가 조금 복잡해 보이더라도, 실제로 보면 굉장히 간단하다. 이 코드는 위에 C코드로 설명된 OpenProcess -> ReadProcessMemory -> CloseHandle 의 과정을 하나의 함수에 담아 실행되도록 만들어 둔 것을 뜻한다.
OpenProcess를 실행하기 전에 PUSH 10 을 하는 이유는, 그것이 PROCESS_VM_READ 플래그이기 때문이다.

아래에 MyWriteProcessMemory를 한 번 만들어 보도록 하겠다.
다른 부분은 다 같지만, PROCESS_VM_READ 대신 PROCESS_VM_WRITE를 통해 프로세스를 열어야 한다는 점이 다를 수 있다.


BOOL __stdcall MyWriteProcessMemory(
 DWORD pid,
 LPVOID lpBaseAddress,
 LPCVOID lpBuffer,
 SIZE_T cbWritten,
 SIZE_T* lpNumberOfBytesWritten)
{
    BOOL bRet = FALSE;

    HANDLE hTargetProcess = OpenProcess(PROCESS_VM_WRITE, FALSE, pid);
    if(hTargetProcess == NULL)
    {
        return bRet;
    }

    bRet = WriteProcessMemory(hTargetProcess, lpBaseAddress, lpBuffer, \
                              cbWritten, lpNumberOfBytesWritten);
    CloseHandle(hTargetProcess);

    return bRet;
}
 
이런 API들을 이용하여 게임핵처럼 다른 프로세스의 메모리를 읽고 쓰고 하는 프로그램들을 만들 수 있다. 물론 그런 프로그램들을 만드는 것이 다는 아니다.
총 여섯 편으로 계획한 연재 내용에는 없지만, 나중에 시간이 허락한다면 다른 프로세스의 메모리를 읽어서 그 프로세스의 메모리 공간에 떠 있는 모듈들이 어떤 함수들을 import하고 export하는 지를 알아내는 과정에 대해 조금 더 설명하도록 하겠다.
또한, 이와 같이 다른 프로세스의 메모리에 쓰기가 가능하기 때문에 'DLL Injection' 이라는 매우 강력한 무엇인가를 할 수 있게 되는데, DLL Injection에 대해서는 다음 글에서 자세히 다루도록 하겠다.

------------
dll 인젝션 찾다가 같이 찾은 자료들.
이런 내용들을 나도 모르는 것은 아니지만 이렇게 체계적으로 정리하고 깊이 파고드는 자세가 부족했던 것 같다.

상당히 자극 받았음 ㅋ
Response : ,

Detecting Application Debugger

Posted 2007. 6. 19. 14:51, Filed under: Study/Computer Science
================================================================================
Title : Detecting Application Debugger

Author : proXima
Contact : proxima ]at[ postech.ac.kr
Date : 2006/11/09 - 2006/11/15
================================================================================

보통 reverse engineering을 하기 위해서는 debugger와 disassembler를 사용한다.
하지만 어떤 프로그램들에는 anti-debug 루틴이 들어있어, debugger를 사용하기 어렵게 하기도 한다.
이 글에서는 간단하게 사용할 수 있는 몇 가지 방법을 소개하도록 하겠다.

PLUS 06학번들을 뽑을 때 냈던 문제에도 간단한 anti-debug 루틴이 들어있다.


1. IsDebuggerPresent
Win32 API 중에 IsDebuggerPresent 라는 함수가 있다. 만약 debug가 attach 된 프로세스에서 이 함수를 실행하게 되면 1이 리턴된다. WinXP에서 제공하는 IsDebuggerPresent 함수의 내부를 살펴보자.

앞으로 나오는 모든 코드에 포함된 숫자들은 16진수 값이다.


mov eax, dword ptr fs:[18]
mov eax, dword ptr ds:[eax+30]
mov eax, dword ptr ds:[eax+2]
retn
 
위에서 보는 것과 같이 매우 간단하다.
이렇게 간단한 코드가 어떻게 debugger를 디텍트할 수 있는지 살펴보자.

코드의 첫 줄을 보자. fs:[18] 을 볼 수 있다.
fs는 file segment로, fs:[0]에는 SEH(Structured Exception Handler)가 위치한다.
SEH 구조체의 내용 중에 TEB(Thread Environment Block)의 포인터가 존재하는데,
그 포인터의 위치가 바로 fs:[18] 이다.
첫 줄의 의미는, eax에 TEB의 스타트 포인트를 가져온다는 것이다.

두 번째 줄을 보자. ds:[eax+30] 에는 무엇이 있을까?
TEB 구조체에서 0x30 오프셋에 있는 내용은 PEB(Process Environment Block)의 포인터이다.
PEB에는 프로세스의 컨텍스트나 쓰레드 목록, 핸들 테이블 등, 프로세스에 specific한 정보들이 들어있다.

마지막줄을 보자. ds:[eax+2] 에는 무엇이 있을까?
결국 PEB의 0x02 오프셋에 있는 내용을 확인하겠다는 것인데,
PEB의 0x02 오프셋에는 BeingDebugged 라는 1바이트짜리 플래그가 들어있다.
디버거가 attach될 경우에는 이 BeingDebugged 플래그가 1이 되는데,
IsDebuggerPresent는 바로 이 플래그 값을 확인하여 자신이 디버그 당하고 있는지를 확인하는 것이다.

IsDebuggerPresent 함수를 직접 사용하는 경우는 함수에 BreakPoint를 걸어 건너뛰기 쉬우므로,
인라인 어셈을 이용하여 직접 어셈 코드를 적어주는 것도 하나의 방법이 될 수 있다.


2. Using Exception
디버거로 프로그램을 디버깅 하다가
데이터 혹은 코드를 잘못 건드리는 바람에 exception이 발생한 적 없는가?
보통은 디버거로 실행하는 중에 exception이 발생하게 되면,
프로그램 내부에 있는 exception handler보다 디버거가 먼저 exception을 캐치한다.
그것을 이용하여 디버깅을 조금 곤란하게 할 수 있다.

하지만 이 방법은 위험하고 또 오히려 프로그램을 짜는 입장에서 스스로 디버깅을 하다가
곤란해지는 경우가 생길 수 있으므로 별로 추천하고 싶지는 않다.

간단하게 소개만 하도록 하겠다.


int SEH1(unsigned int code, LPEXCEPTION_POINTERS ep)
{
  if(code == EXCEPTION_ILLEGAL_INSTRUCTION)
  {
    // (2)
    return EXCEPTION_EXECUTE_HANDLER;
  } else {
    // not expected!!!
    return EXCEPTION_CONTINUE_SEARCH;
  }
}

:
:
:

{
  :
  :
  :
  __try {
    // (1)
    __asm __emit 0x8f // intentional illegal instruction
  } __except {
    // (2)
  }
}

 
연속으로 실행되어야 하는 코드들이 있다고 하면,
그것을 위와 같은 코드에 두 개 혹은 세 개로 나누어 쓸 수 있게 된다.
(1) 이라고 된 부분에 첫 부분을 쓰고,
(2) 라고 된 부분에 두번째 부분을 쓰고,
(3) 라고 된 부분에 세번째 부분을 쓴다.

__asm __emit 0x8f 는 일부러 없는 인스트럭션을 실행하도록 하여 EXCEPTION_ILLEGAL_INSTRUCTION 예외를 발생시키는 역할을 한다.

그러면 실행하는 도중에 (1)의 코드가 실행되고 illegal instruction의 예외가 발생한다.
예외가 발생한 경우에 만약 디버거가 붙어있다면, 디버거가 예외를 잡아내고
더 이상 진행할 수 없는 상태가 된다. (물론 상태를 변경한다면 계속 진행할 수 있다.)

디버거가 붙어있지 않다면 SEH1의 함수 내부로 들어가 (2)가 실행된 뒤 리턴되어 (3)이 실행된다.
__try - __except 구문을 사용하는 함수에 넘어오는 인자는 (2)에서는 사용할 수가 없다.



3. NtGlobalFlag


mov eax, dword ptr fs:[30]
mov eax, dword ptr ds:[eax+68]
 

fs:[30]이 다시 등장했다.
위에서도 설명했듯이 fs:[30]에는 PEB가 존재하는데, 그 PEB의 0x68 오프셋에는 NtGlobalFlag라는 값이 들어있다.
1번에서 설명한 IsDebuggerPresent와 비슷하지만, 다른 점이라면 IsDebuggerPresent는 Windows에서 공식적으로 제공해 주는 함수인 반면, NtGlobalFlag는 그렇지 않다는 점이다.
만약 위의 코드가 실행된 뒤에 eax에 0x70의 값이 들어있다면, being debugged이다.


위에서 세 가지의 debugger detect 방법을 설명하였다.
실제로 위의 방법들이 전부는 아니다.
debugger를 찾아내는 방법들은 더 많이 있으며, 이런 방법으로도 찾아낼 수 있다는 것을 소개한 정도로 마치도록 하겠다.
Response : ,

« Previous : 1 : ··· : 6 : 7 : 8 : 9 : 10 : 11 : 12 : ··· : 20 : Next »

Recent Posts

Recent Comments

Recent Trackbacks

Total hit (Today , Yesterday )

Admin Write Post