Flash SWF 파일 추출하기
오후 10:08 99-12-02 조경민
-------------------------------------------------------------

상황 : SWF 플래쉬 파일을 Flash ActiveX컨트롤로 VC 프로그램에
        붙여서 컴파일 하면 SWF파일은 VC프로그램안에 포함된다.
        이때 Test.exe안에 포함된 어떤 SWF파일을 추출하여야 한다.

과정 :
먼저 나는 Test.exe파일을 바이너리로 열어 기존의 포함되었던 swf파일
도 바이너리로 열어서 비교해 보았다. 일딴 swf파일은 FWS라는 세바이트
로 시작하는것 같았다. 역시 Test.exe파일의 중하단 부분에 FWS로 시작
하는 부분이 있었다. 그 뒤는 swf파일과 동일하여 여기서 부터 추출을
하면 될거라 생각했다.

swf파일의 크기를 짐작하기
먼저 test.swf파일의 크기를 보았다. 0x082e로 2094 바이트였다.
이를 바이트로 나누니 2e 08로 들어갈것이므로 FWS부터 이 2e 08을 찾아
보았다.
F W S 2e 08 00 00 .... 이런식의 나열이었다. 플래쉬파일이 몇메가도
되므로~~~~~~~~~~~ 이부분이 파일의 크기라고 짐작을 하여 코딩을 하였다.
나중에 알게 되었다. 다음은 플래쉬 파일의 포맷 백서(white paper)이다.

http://www.macromedia.com/software/flash/open/spec/SWFfileformat.html

SWF파일 포맷
----------------
F UI8  기호  1byte
W UI8  기호  1byte
S UI8  기호  1byte
   UI8   single byte file version 1byte
   UI32  파일 크기 4byte (Unsigned Int in 32bit Compiler)
   :
이런 식이었다.  

그런데 문제는 Exe파일 도중에 어느 부분에서 절대적으로 나온다는 것은
정해지지 않았다. 나는 간단한 최소의 소스코드에 swf파일을 붙인 간단한
플래쉬 exe프로그램을 만들었다. 이때 FWS가 시작한 위치는0x2cf20 였다.
따라서 적어도 이 위치에서 부터 시작해서 보는 것이 좋을거라는 생각을했다.
이 위치서 부터 하나씩 읽어오면서 FWS가 나오면 SWF파일을 추출하는것이다.

다음은 FlashExtractor의 처음 버전인 Console 버전이다.

void Save_To_SWF( char* pStore, unsigned long size )
{
        FILE* fp;
        fp = fopen("sample.swf","wb");
        if( fp == NULL)
        {
                printf(" Can't Write to SWF!!! \n");
                return;
        }
        
        fwrite( (const void*)buf, sizeof(char), (long)size, fp);        
        fclose(fp);
}

unsigned long Extract_SWF_From_EXE( const char *pfname, char* pStore )
{
        FILE* fp;        
        char size[4];
        unsigned long wSize = 0;
        memset( size, 0, sizeof(size) );
        
        fp = fopen(pfname,"rb");
        if( fp == NULL)
        {
                printf("[Error] : Can't Open %s !!!\n",pfname );
        }

        fseek( fp, (long)SKIP_EXE, 0 );
        while( !feof( fp ))
        {
                if( fgetc(fp) == 'F' )
                if( fgetc(fp) == 'W' )
                if( fgetc(fp) == 'S' )
                {        // then I found it!
                        
                        printf("I Found it  SWF .....\n");
                        fgetc(fp);        // ignore one byte
                        memcpy( (void*)&wSize, 4, sizeof(char));
                        printf("Size : %ld\n",wSize);
                        fseek( fp, -8, SEEK_CUR);
                        pStore = new char[wSize];                                 
                        fread( (void*)pStore, sizeof(char), (long)wSize, fp);
                        break;
                        }
                }
        }
        fclose(fp);
        return wSize;
}

============================================================================

다음은 MFC 버전이다.

// Extract to get SWF in EXE Resource
long CFlashExtractorDlg::ExtractSWF( char* sExeName, char*& pSwfBuf )
{
        // Well I don't know where start resource address in Exe File
        // But There exist After below address cos it was counted by small
        // Dialog based application binary EXE.
        // any app has a SWF after that address...
        #define SKIP_EXE                        0x2cf20L

        CFile file;
        long wSize = 0;
        pSwfBuf = NULL;
        
        file.Open( sExeName, CFile::modeRead | CFile::typeBinary );
        if( file.m_hFile == NULL )         
        {
                MessageBox("Can't Open Exe File","Error");                
                return 0;
        }
        
        // Set pos to find in exe fastly.
        file.Seek( (LONG) SKIP_EXE, CFile::begin );
        
        // blind finding will be start.
        while( file.GetPosition() < file.GetLength() )
        {                
                if( FGetC(file) == 'F' )        
                if( FGetC(file) == 'W' )
                if( FGetC(file) == 'S' )
                {        // then I found SWF in EXE                        
                        TRACE(" I Found SWF Resource \n");                        
                        
                        FGetC(file); // ignore one byte header                                                
                        // Read SWF Size in SWF Header
                        file.Read( (void*)&wSize, 4);        // Maybe 4 byte . I guess --;                        
                        TRACE(" SWF Size : %ld\n",wSize );
                        
                        // Return the pos to get entire SWF to SWFBUF
                        file.Seek( -8, CFile::current);        
                        // and prepare BUF
                        // **CAUTION** I'll allocate pSwfBuf, Don't Alloc or Realloc
                        pSwfBuf = new char[wSize];                
                        
                        // And Read it!
                        file.Read( pSwfBuf, wSize);                                        
                        TRACE(" SWF Readed\n");
                        break; // blah, now I have to relax , break break..
                }                
        }
        
        // File Close
        file.Close();        
        return wSize;        // Readed SWF File Size
        
}

void CFlashExtractorDlg::SaveToSWF(  char*& pSwfBuf, long wSize, char* sSwfName )
{        
        CFile file(CFile::modeCreate | CFile::shareDenyNone );
        file.Open( sSwfName, CFile::modeCreate |CFile::modeWrite| CFile::typeBinary );
        
        file.Write( (void*)pSwfBuf, wSize );
        file.Close();
        delete [] pSwfBuf;        // I'll delete pSwfBuf!
        TRACE(" Saved SWF File To ; %s",sSwfName);
}

// Cute routine :) for emulate dos::fgetc, I love it.
char CFlashExtractorDlg::FGetC(CFile& file)
{
        char buf;
        file.Read(&buf, 1);
        return buf;
}

===========================================================================

그리고 마지막으로 ATL COM 부분으로 만들었으며

STDMETHODIMP CSWFExtractor::get_ExeFileName(BSTR *pVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        *pVal = (BSTR)(LPCTSTR)m_sExeFileName;
        return S_OK;
}

STDMETHODIMP CSWFExtractor::put_ExeFileName(BSTR newVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        m_sExeFileName = newVal;
        return S_OK;
}

STDMETHODIMP CSWFExtractor::get_SwfFileName(BSTR *pVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        *pVal = (BSTR)(LPCTSTR)m_sSwfFileName;
        return S_OK;
}

STDMETHODIMP CSWFExtractor::put_SwfFileName(BSTR newVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        m_sSwfFileName = newVal;
        return S_OK;
}

// Extract to get SWF in EXE Resource
STDMETHODIMP CSWFExtractor::ExtractSWF()
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        // Well I don't know where start resource address in Exe File
        // But There exist After below address cos it was counted by small
        // Dialog based application binary EXE.
        // any app has a SWF after that address...
        #define SKIP_EXE                        0x2cf20L
        CFile file;
        UINT wSize = 0;
        
        if( m_pSwfBuf != NULL)
                delete [] m_pSwfBuf;
        
        m_pSwfBuf = NULL;
                
        if( file.Open( m_sExeFileName, CFile::modeRead | CFile::typeBinary ) == FALSE )         
        {                
                MessageBox(NULL,"Can't Open Exe File","Error",MB_OK);
                return S_OK;
        }
        
        // Set pos to find in exe fastly.
        file.Seek( (LONG) SKIP_EXE, CFile::begin );
        
        // blind finding will be start.
        while( file.GetPosition() < file.GetLength() )
        {                
                if( FGetC(file) == 'F' )        
                if( FGetC(file) == 'W' )
                if( FGetC(file) == 'S' )
                {        // then I found SWF in EXE                        
                        TRACE(" I Found SWF Resource \n");                        
                        
                        FGetC(file); // ignore one byte header ( File Version )
                        // Read SWF Size in SWF Header
                        file.Read( (void*)&wSize, 4);        // Maybe 4 byte . I guess --; <- great! UI32 Type!
                        TRACE(" SWF Size : %ld\n",wSize );
                        
                        // Return the pos to get entire SWF to SWFBUF
                        file.Seek( -8, CFile::current);                // Back to the home!
                        // and prepare BUF
                        // **CAUTION** I'll allocate pSwfBuf, Don't Alloc or Realloc
                        m_pSwfBuf = new char[wSize];
                        
                        // And Read it!
                        file.Read( m_pSwfBuf, wSize);
                        TRACE(" SWF Readed\n");
                        m_nSwfSize = wSize;
                        MessageBox(NULL,"Extract Finished ","Message",MB_OK);
                        CString buf;
                        buf.Format("%s %ld worked",(LPCTSTR)m_sExeFileName, m_nSwfSize);
                        MessageBox(NULL,(LPCTSTR)buf,"Message",MB_OK);
                        break; // blah, now I have to relax , break break..
                }                
        }
        
        // File Close
        file.Close();

        return S_OK;
}


STDMETHODIMP CSWFExtractor::SaveSWF()
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        if( m_pSwfBuf == NULL)
        {
                MessageBox(NULL,"Sorry but u have to ExtractSWF to do this before ","Message",MB_OK);
                return S_OK;
        }

        CString sSwfName;
        
        if( m_sSwfFileName.IsEmpty() == TRUE )
                sSwfName = _T("sample.swf");
        else
                sSwfName = m_sSwfFileName;

        CFile file(CFile::modeCreate | CFile::shareDenyNone );
        file.Open( sSwfName, CFile::modeCreate |CFile::modeWrite| CFile::typeBinary );
        
        file.Write( (void*)m_pSwfBuf, m_nSwfSize );
        file.Close();
        
        TRACE(" Saved SWF File To ; %s",sSwfName);
        MessageBox(NULL,"Saved SWF ","Message",MB_OK);
        return S_OK;
}

// Cute routine :) for emulate dos::fgetc, I love it.
char CSWFExtractor::FGetC(CFile& file)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        char buf;
        file.Read(&buf, 1);
        return buf;
}

STDMETHODIMP CSWFExtractor::GetSWFSize(long *RetVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        *RetVal = 0;
        if( m_pSwfBuf != NULL )
        {
                *RetVal = m_nSwfSize;
        }
        else
                MessageBox(NULL,"Sorry but u have to ExtractSWF to do this before ","Message",MB_OK);

        return S_OK;
}

STDMETHODIMP CSWFExtractor::GetSWFData(BSTR *RetVal)
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState())

        // TODO: Add your implementation code here
        *RetVal = NULL;
        if( m_pSwfBuf != NULL )
        {
                *RetVal = (BSTR)m_pSwfBuf;
        }
        else
                MessageBox(NULL,"Sorry but u have to ExtractSWF to do this before ","Message",MB_OK);
        
        return S_OK;
}
==============================================================================

아래는 그것을 쓰는 예제이다.

    Dim obj As New FLASHEXTRACTORLib.SWFExtractor
    obj.ExeFileName = "c:\\Test.exe"
    obj.SwfFileName = "c:\\my.swf"
    obj.ExtractSWF
    obj.SaveSWF

+ Recent posts