메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

공유 메모리 덤프를 구문 분석하기 위해 Python 앱 빌드하기

Struct 유틸리티를 사용하여 분석을 위해 시스템 데이터 추출

Asha Shivalingaiah, Software Engineer, IBM
Asha Shivalingaiah is a Software Engineer working as a tester at the Australia Development Lab for IBM Security Solution, Tivoli. Since joining the IBM Rational team in 2008, she has worked with Rational Rhapsody and other Rational portfolio tools for managing the software development life cycle. She is well versed in modeling languages such as UML 2 and SysML, and in using Rational Rhapsody for model-driven development.

요약:  Linux 플랫폼에서 시스템 인식 가능한 공유 메모리 덤프를 구문 분석하고 Python과 struct 유틸리티를 사용하여 예상된 데이터 형식을 추출하는 방법에 대해 배워봅시다. 이 기사에서 덤프 파일의 2진 파일 형식을 읽어 데이터의 형식을 판별하는 방법을 먼저 확인할 것입니다. 즉, 이는 데이터를 구문 분석하고 추출하며 분석하기 위해 필요합니다. 그 다음으로, 해당 형식을 기반으로 파일을 구문 분석하는 방법을 살펴 본 다음에, 유효성 검증 결과를 출력하기 위해 예상된 형식과 해당 결과를 일치시킬 것입니다.

원문 게재일:  2011 년 7 월 06 일
난이도: 중급 원문:  보기 PDF:  A4 and Letter (33KB | 10 pages)Get Adobe® Reader®
페이지뷰:  1793 회
의견:  


메모리 덤프는 운영 중인 구체적인 지점에서 작업하는 메모리의 레코드된 상태를 드러낸다. 이는 시스템 조건의 "법의학적" 증거를 제공하기 때문에 시스템 관리에서 중요한 도구이다.

시작하기 전에

필자는 이 기사에서 지시사항과 코드 샘플의 경우, Python 사이트에서 다운로드할 수 있는 Python 버전 2.4를 사용했다(아래 참고자료 참조). 독자는 다른 버전으로 다른 결과를 도출할 수도 있다.

시작하기 전에 다음 사항에 익숙해져야 한다.

  • 기존 공유 메모리의 /dev/shm 구현
  • Linux 시스템에서 공유 메모리 데이터 덤프 수동으로 보기
  • 특정 종속 항목(Linux 파일 열기, 읽기, 쓰기, 닫기 개념, 파일 디스크립터 및 파일이 열릴 수 있는 모드 사용, 기본 Python 구조 개념)
  • 일반적인 GNU/Linux

Linux에서 공유 메모리 덤프 이해하기

/dev/shm은 기존 공유 메모리 개념의 구현이다. 이는 프로그램 사이에 데이터를 전달하는 수단으로 널리 사용되고 허용된다. /dev/shm에서 하나의 프로그램 또는 디먼은 다른 프로세스(관련된 권한 레벨에서)가 액세스할 수 있는 메모리 부분을 작성한다. 이는 프로세스 사이에 데이터를 공유하는 빠르고 간편한 메소드이다.

각 프로그램은 자체적인 파일을 작성한다. 필자의 예제에서는 /dev/shm/dumpmem에 위치한 파일 이름 dumpmem을 사용한다.

Linux에서 공유 메모리 덤프 수동으로 보기

이러한 공유 메모리 파일이 2진 형식이므로 Linux에서 파일 표시장치에 일반적으로 사용되는 cat 유틸리티를 사용하여 공유 메모리 파일(일반적으로 shm 파일로 참조됨)을 확인할 수 없다. 독자가 이를 제네릭(generic) 파일 보기 메소드로 보려고 시도하는 경우 알아볼 수 없는 문자의 덩어리로 보일 것이다. 필자는 hexdump) 유틸리티를 사용하여 mem 파일을 읽고 이를 인식 가능한 형식으로 본다. 한편 다른 유틸리티들은 이 용도로 사용 가능하다.

이 기사를 위해 hexdump에 대한 사용법 패턴은 다음과 같다.

hexdump <optional switches> /dev/shm/dumpmem for <switches> supported

hexdump에 대한 자세한 정보로의 링크는 참고자료를 참조한다.


시나리오 정의하기

우리가 작업할 시나리오는 호스트가 수신한 패킷을 분석하고 공유 mem 파일 /dev/shm/dumpmem로 해당 데이터를 저장하는 네트워크 탐지기(network sniffer)이다. 이 데이터는 수신된 패킷에 대한 정보가 들어있다.

파일은 일반적으로 다음과 같다.

  • 메모리 파일 스토리지는 /dev/shm/dumpmem이다.
  • dumpmem 파일 형식은 다음이 포함된다.
    • 누가 전송했는지 알리는 소스 주소의 4바이트
    • 누구에게 전송될 지 알리는 대상 주소의 4바이트
    • 소스 포트의 2바이트(다시 말해서, 패킷이 사용한 소스에서 포트)
    • 대상 포트의 2바이트(마찬가지로, 패킷이 사용할 대상에서 포트)
    • 프로토콜의 2바이트(패킷이 일부가 되는 프로토콜)
    • 패킷이 네트워크 스니펫으로 확인한 시간소인을 표시하는 시간의 4바이트
  • 1 레코드 길이 = dumpmem 스펙의 합계(다시 말해서, 18바이트)
  • 최대 메모리 파일 크기는 1킬로바이트이므로 이는 1024바이트를 포함할 수 있다(1024 / 18 = 56 레코드)

독자가 Linux 터미널에서 파일을 수동으로 hexdump 및 표시하는 경우, 이는 다음과 같다.


목록 1. 덤프 파일 표시하기

# hexdump /dev/shm/fdshm
0000000 0004 0000 0400 0000 fc64 0a00 00fb e000
0000010 14e9 14e9 0011 0000 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0800
0000030 1668 0000 0000 0000 0032 0000 0000 0000
0000040 0000 0000 0001 e000 0000 0000 0002 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
0000060 0000 0000 0000 0800 0100 0000 0000 0000
0000070 0008 0000 0000 0000 fc64 0a00 fd64 0a00
0000080 2328 03ea 0006 0000 0000 0000 0000 0000
0000090 0000 0000 0000 0000 0000 0000 0000 0800
00000a0 7700 0001 0000 0000 0040 0000 0000 0000
00000b0 fd64 0a00 fc64 0a00 03ea 2328 0006 0000
00000c0 0000 0000 0000 0000 0000 0000 0000 0000
00000d0 0000 0000 0000 0800 0a00 0000 0000 0000
00000e0 0040 0000 0000 0000 fc64 0a00 fd64 0a00
00000f0 2328 03ec 0006 0000 0000 0000 0000 0000
0000100 0000 0000 0000 0000 0000 0000 0000 0800
0000110 7700 0001 0000 0000 0040 0000 0000 0000

파일 구문 분석하기를 수반하는 단계를 살펴보자.


덤프 파일 구문 분석하기

메모리 덤프 파일에서 데이터를 이해하는 단계는(형식 식별, 구문 분석 및 파일 읽기) 다음과 같이 상대적으로 간단하다.

  1. 파일을 연다.
  2. 파일 디스크립터로 바이트를 읽는다.
  3. 필요할 때에 데이터를 인식 가능한 문자열 형식으로 변환한다.
  4. 인식한 버퍼가 온전한지 여부 및 잘린 부분이나 오류가 있는지 여부를 확인한다.
  5. 버퍼에서부터 데이터를 언팩한다.
  6. 정보를 추출한다.
  7. 데이터를 인쇄한다.
  8. 루프를 빌드하여 공유 데이터 덤프의 각 레코드에서 1-7단계를 수행한다. (독자는 이를 수동으로 수행하고 싶지는 않다. 그렇지 않은가?)

프로세스 플로우를 자세히 살펴보자.

파일 열기

공유 메모리 파일을 열려면 일반 양식 fd = open(fileName, mode)을 사용한다. fd는 파일 디스크립터이자 파일로 향한 포인터이다. 이 예제의 경우 다음을 사용한다.

  • fileName: /dev/shm/dumpmem
  • mode: rb(2진 모드에서 읽기 전용)

목록 2. 공유 메모리 파일 열기

fd = open('/dev/shm/dumpmem ','rb')

바이트 읽기

이전 함수 호출에서 취득한 파일 디스크립터를 사용하여 바이트를 읽기 위해 필자는 다음 코드를 사용한다. 이는 다음과 같이 전달된 파일 매개변수에서부터 바이트의 표시된 수를 읽는다.


목록 3. 공유 메모리 파일 열기

def ReadBytes(self, fd, noBytes):
 '''
 Read file and return the number of bytes as specified
 '''
 data = fd.read(noBytes)
 return data

buffer = ReadBytes('/dev/shm/dumpmem ', 18)
# Pass the file name and pass the number of bytes
# Number of bytes is 18 since in the example scenario each record
#  is of length 18

여기에서 바이트를 읽는 것은 필수 정보를 추출하는 데 충분하지 않다. 이는 문자열이 인식되는 경우 버퍼를 리턴한다. 이는 이해 가능한 문자열 형식으로 구문 분석되고 변환되어야 한다.

데이터 변환

Python struct는 파일 또는 다른 소스 중에서 네트워크 연결에서부터 저장된 2진 데이터를 처리하기 위해 사용될 수 있다. Python struct는 두 가지 광범위한 기능이 있다. 즉, 이는 packunpack이다.

pack의 역할은 주어진 형식에 따라 팩한 값 v1, v2, ...이 들어있는 문자열을 리턴하는 것이다. 인수는 형식이 필요한 값과 정확하게 일치해야 한다.

unpack의 역할은 주어진 형식에 따라 해당 문자열을 언팩하는 것이다. (가정하건대 pack(fmt, ...)으로 팩됨). 정확히 하나의 항목을 포함할지라도 결과는 튜플(tuple)이다. 문자열은 형식이 필요한 데이터의 양을 정확하게 포함해야 한다. 즉, len(string)calcsize(fmt)와 동일해야 한다.

허용 가능한 형식은 다음과 같다.

  • 1바이트 형식:
    • 부호 있는 문자에 대해 b
    • 부호 없는 문자에 대해 B
  • 2바이트 형식:
    • 짧은 정수에 대해 h
    • 부호 없는 짧은 경우에 대해 H
  • 4바이트 형식:
    • 긴 경우에 대해 l
    • 부호 없는 긴 경우에 대해 L
  • 8바이트 형식:
    • 매우 긴 경우에 대해 q
    • 부호 없는 매우 긴 경우에 대해 Q

버퍼 바이트를 팩하고 언팩하는 경우에 대해 지원하는 다른 형식의 경우 참고자료에 나열된 Python 서적을 참조하자.

버퍼 확인

인식된 18바이트의 버퍼가 온전하고 잘린 부분이나 오류가 없는지 확인하려면 바이트 크기가 인식할 때 예상한 대로 여전히 18인지 확인하기 위해 calcsize 함수를 사용할 수 있다. 이 목적을 위해 Python assert 함수를 사용할 수 있다.


목록 4. 버퍼가 정확한지 확인하기

self.assertEqual(len(buffer), struct.calcsize('llllh'))

# 4 l's is 4*4 bytes = 16 bytes and h is 2 bytes so that is 18 bytes
# we could use QQh which is 2*8 + 2 = 18 bytes as well

데이터 언팩

이제 버퍼가 실제로 18바이트인지 확인했으니 버퍼에서부터 데이터를 언팩할 수 있다. struct는 바이트의 수, 버퍼 이름 및 인식되어야 하는 오프셋을 제공하는 유용한 unpack_from 함수를 제공한다.

struct.unpack_from(fmt, buffer[, offset=0])

세부사항 추출

우리의 시나리오에서 이는 추출하려는 내용의 세부사항이다.


목록 5. 추출되는 세부사항

sourceAddress = (struct.unpack_from('B', buffer,0),
                     struct.unpack_from('B', buffer,1),
                     struct.unpack_from('B', buffer,2),
                     struct.unpack_from('B', buffer,3))
destinationAddress = (struct.unpack_from('B', buffer,4),
                     struct.unpack_from('B', buffer,5),
                     struct.unpack_from('B', buffer,6),
                     struct.unpack_from('B', buffer,7))
sourcePort = (struct.unpack_from('B', buffer,8),
                     struct.unpack_from('B', buffer,9))
destinationPort = (struct.unpack_from('B', buffer,10),
                     struct.unpack_from('B', buffer,11))
protocolUsed = (struct.unpack_from('B', buffer,12),
                     struct.unpack_from('B', buffer,13))
timeStamp = (struct.unpack_from('B', buffer,14),
                     struct.unpack_from('B', buffer,15),
                     struct.unpack_from('B', buffer,16),
                     struct.unpack_from('B', buffer,17))

참고: 플랫폼 및 mem 구조가 빅 엔디안(big-endian)인지, 리틀 엔디안(little-endian)인지 여부에 따라, 바이트가 인식되는 순서로 스왑해야 할 수 있다.

출력 인쇄

이제 인식한 2진 버퍼에서부터 언팩된 값이 있으니, 필수 출력을 얻기 위해 표준 print 명령을 사용할 수 있다.


목록 6. 세부사항 인쇄하기

print "sourceAddress =" ,  
      (struct.unpack_from('B', buffer,0),struct.unpack_from('B', buffer,1),
      struct.unpack_from('B', buffer,2),struct.unpack_from('B', buffer,3))
print "destinationAddress = " ,
      (struct.unpack_from('B', buffer,4),struct.unpack_from('B', buffer,5),
      struct.unpack_from('B', buffer,6),struct.unpack_from('B', buffer,7))
print "sourcePort = " , (struct.unpack_from('H',buffer,8))
print "destinationPort = " , (struct.unpack_from('H',buffer,10))
print "protocolUsed = " , (struct.unpack_from('H',buffer,12))
print "timeStamp = " ,  
      (struct.unpack_from('B', buffer,14),struct.unpack_from('B', buffer,15),
      struct.unpack_from('B', buffer,16),struct.unpack_from('B', buffer,17))

목록 6으로부터 예상된 출력은 다음 형식이어야 한다.


목록 7. 인쇄로부터 출력

sourceAddress =  ((192,), (168,), (10,), (102,))
destinationAddress =  ((207,), (168,), (1,), (103,))
sourcePort =  (11299,)
destinationPort =  (11555,)
protocolUsed =  (256,)
timeStamp =  ((1,), (12,), (0,), (1,))

모든 레코드에 대해 프로세스 자동화

이제 전체 공유 메모리 파일에서부터 레코드를 모두 읽고 인쇄하려면 다음과 같이 루프를 작성한다.


목록 8. 모든 레코드를 읽고 인쇄하기 위한 루프 작성하기

for element in range (0,56):
#loop 18 since we know the file size and
#the record length: 1024/18 = 56 records
		
      buffer = ReadBytes('/dev/shm/dumpmem ', 18)
      self.assertEqual(len(buffer), struct.calcsize('llllh'))
        
      sourceAddress = struct.unpack_from('B', buffer,0),
                  struct.unpack_from('B', buffer,1),
                  struct.unpack_from('B', buffer,2),
                  struct.unpack_from('B', buffer,3))
      destinationAddress = struct.unpack_from('B', buffer,4),
                       struct.unpack_from('B', buffer,5),
                       struct.unpack_from('B', buffer,6),
                       struct.unpack_from('B', buffer,7))
      sourcePort = struct.unpack_from('B', buffer,8),
                 struct.unpack_from('B', buffer,9)
      destinationPort = struct.unpack_from('B', buffer,10),
                    struct.unpack_from('B', buffer,11))
      protocolUsed = ,struct.unpack_from('B', buffer,12),
                  struct.unpack_from('B', buffer,13))
      timeStamp = struct.unpack_from('B', buffer,14),
                struct.unpack_from('B', buffer,15),
                struct.unpack_from('B', buffer,16),
                struct.unpack_from('B', buffer,17))
        
      print "sourceAddress = " ,  
            struct.unpack_from('B', buffer,0),
            struct.unpack_from('B', buffer,1),
            struct.unpack_from('B', buffer,2),
            struct.unpack_from('B', buffer,3))
      print "destinationAddress =  " ,
            struct.unpack_from('B', buffer,4),
            struct.unpack_from('B', buffer,5),
            struct.unpack_from('B', buffer,6),
            struct.unpack_from('B', buffer,7))
      print "sourcePort =  " ,
            struct.unpack_from('H',buffer,8))
      print "destinationPort =  " ,
            struct.unpack_from('H',buffer,10))
      print "protocolUsed =  " ,
            struct.unpack_from('H',buffer,12))
      print "timeStamp = " ,  
            struct.unpack_from('B', buffer,14),
            struct.unpack_from('B', buffer,15),
            struct.unpack_from('B', buffer,16),
            struct.unpack_from('B', buffer,17))

여기까지가 내용의 전부이다! 우리는 Linux에서 2진 mem 덤프의 알려진 형식을 구문 분석하였고, Python에서부터 struct를 사용하여 2진 데이터 덤프를 읽고 이를 인식 가능한 형식으로 표시했다.


참고자료

교육

제품 및 기술 얻기

  • Python 웹 사이트에서 Python을 다운로드하자.

  • 자신에게 가장한 적합한 방법으로 IBM 제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나, SOA Sandbox에서 SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.

토론

  • developerWorks community에 참여한다. 개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

필자소개

Asha Shivalingaiah is a Software Engineer working as a tester at the Australia Development Lab for IBM Security Solution, Tivoli. Since joining the IBM Rational team in 2008, she has worked with Rational Rhapsody and other Rational portfolio tools for managing the software development life cycle. She is well versed in modeling languages such as UML 2 and SysML, and in using Rational Rhapsody for model-driven development.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=리눅스
ArticleID=699937
ArticleTitle=공유 메모리 덤프를 구문 분석하기 위해 Python 앱 빌드하기
publish-date=07062011
author1-email=ashashiv@au1.ibm.com
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.