Reversing


IDA Hexray


간혹 헥스레이에서 문자열이 "test1234" 대신 atest 요런 식으로 변수명으로 나와서 불편할 때가 있다.


이런 경우 설정방법.



요렇게 문자열 대신 변수명으로 나올때



헥스레이 옵션에서 "Print only constant string literals" 옵션 체크 해제




이제 잘 보인다. 


'Reversing' 카테고리의 다른 글

암호학 정리(for CTF)  (0) 2018.04.05
WinDBG 고급 명령어들  (0) 2017.06.07
DBI - pin & pintool  (4) 2017.05.22
DBI - Frida 를 이용한 DBI  (3) 2016.09.07
Embedded 기기 리버싱 - 펌웨어 리버싱  (1) 2016.07.14

암호학 정리(for CTF)

2018. 4. 5. 11:36



CTF 를 위한 기초 암호학 개념들을 간단히 정리했다. (아주 간단히...)




1.기본 개념


RSA 용어 

- 인증서 종류 : .pem(base64 로 저장), .der(바이너리로 저장)

o() = euler's totient function, 오일러 파이 함수
o(6) = 3
=> 1,2,3,4,5,6 중 나눠지는 2,3 은 제외하고 서로소인 1,4,5 인 3개

p = 315274063651866931016337573625089033553
q = 311155972145869391293781528370734636009
e = 12405943493775545863
n = p * q

공개키 :  n, e 
개인키 : d
암호문 : c (실제론 큰 정수)
복호화 : m (실제론 큰 정수)


- 암호화 과정
1) 소수 p 와 q 를 정한다.    
        p = 123123123
        q = 590129113

2) n = p*q 를 구한다.
        n = p * q

3) o(n) = (p-1)*(q-1) 을 구한다.
        phi = (p-1)*(q-1)

4) 1 < e < o(n) 이고 o(n) 과 서로소인 e 를 구한다.

5) (d * e) mod o(n)=1 이고 o <= d <= N 인 d 를 구한다.
   d = gmpy.invert(e, phi)

- 암호연산 : c = m^e mod n  ( m ** e % n )

- 복호연산 : m = c^d mod n  ( c ** d % n )
key = RSA.construct(n,e,d)










2. 점검툴


- yafu (인수분해 등) 
factor(0x192873aba29290cbade10) => p, q 로 분해

- openssl 
> openssl rsa -in pubkey.pem -pubin -text -modulus
Modulus (256 bit):
    00:d8:e2:4c:12:b7:b9:9e:fe:0a:9b:c0:4a:6a:3d:
    f5:8a:2a:94:42:69:b4:92:b7:37:6d:f1:29:02:3f:
    20:61:b9
Exponent: 12405943493775545863 (0xac2ac3e0ca0f5607)
Modulus=D8E24C12B7B99EFE0A9BC04A6A3DF58A2A944269B492B7376DF129023F2061B9
writing RSA key
-----BEGIN PUBLIC KEY-----
MEIwDQYJKoZIhvcNAQEBBQADMQAwLgIhANjiTBK3uZ7+CpvASmo99YoqlEJptJK3
N23xKQI/IGG5AgkArCrD4MoPVgc=
-----END PUBLIC KEY-----

- rsatool


- rsactftool

$ RsaCtfTool.py --dumpkey --key ./pubkey.pem
[*] n: 98099407767975360290660227117126057014537157468191654426411230468489043009977
[*] e: 12405943493775545863

- sympy  :  수학기호 사용 모듈 ( x = symbol(x); x+y 등)
http://docs.sympy.org/latest/tutorial/solvers.html#a-note-about-equations 








3. 문제유형


1. RSA 
- 키 인수분해 크랙(bit 수가 너무 적은 경우)
- 이미 분해된 인수(codegate 2013? 유형, ex: SSL pcap decrypt)
- lsb oracle attack


2. DES/AES
- padding oracle attack




## ETC ##

RSA 곱셈의 성질

- 암호문 cipher2, cipher5 가 있다.
- (c2 * c5) %n) 을 복호화 한 것은 m2 * m5 와 같다.
=> ((c2 * c5) % n)^d mod n == m2 * m5 
 
(2 암호문 * flag %n) ^d mod n == 2 * flag 평문


WinDBG 고급 명령어들

2017. 6. 7. 10:16


WinDBG 고급 분석을 위한 명령어들 간단 정리




# 설정 미리 해놓기(배치파일)

- windbg.bat

"C:\Program Files\Windows Kits\10\Debuggers\x86\windbg.exe" -c".symfix+;.load c:\\blwdbgue.dll;g" "c:\program files\internet explorer\iexplore.exe"




# syntax color highlight

- blwdbgue 다운로드 및 .load 로 로드




# 심볼 설정 쉽게 하기

0:009> .symfix+



# 심볼 확인


0:009> x jscript9!JS::JavascriptArray::*

6548df1c          jscript9!Js::JavascriptArray::IsEnumerable (<no parameter info>)

6534cdfb          jscript9!Js::JavascriptArray::GetFromIndex (<no parameter info>)

6548fa51          jscript9!Js::JavascriptArray::BigIndex::BigIndex (<no parameter info>)

6548deb2          jscript9!Js::JavascriptArray::SetEnumerable (<no parameter info>)

653ba673          jscript9!Js::JavascriptArray::EntrySome (<no parameter info>)

65327b86          jscript9!Js::JavascriptArray::HasItem (<no parameter info>)

652a2f78          jscript9!Js::JavascriptArray::`vftable' = <no type information>

6548f898          jscript9!Js::JavascriptArray::BigIndex::SetItem (<no parameter info>)

6548dff8          jscript9!Js::JavascriptArray::IsDirectAccessArray (<no parameter info>)

6534be75          jscript9!Js::JavascriptArray::Sort (<no parameter info>)

65531508          jscript9!Js::JavascriptArray::EntryInfo::Map = <no type information>

653a6b3e          jscript9!Js::JavascriptArray::EntryIsArray (<no parameter info>)


0:009> x jscript9!JS::JavascriptArray::Get*

0:009> x jscript9!JS::JavascriptArray::Set*





# 하드웨어 bp (access)

- ba w4 0x1f1f0000




# 주소 정보 출력

- 0:009> x jscript9!JS::JavascriptArray::*




# 힙 정보 확인(page heap, stack trace 설정해야 자세히 보임)

- !heap -p -a 0x254a0a12




# 로드된 모듈 정보 출력


0:009> lm

start    end        module name

013a0000 0145c000   iexplore   (deferred)             

04780000 048ac000   Cooxie     (deferred)             

05a80000 05a9e000   iToolsBHO   (deferred)             

63b90000 63d53000   d3d9       (deferred)             

63ec0000 65297000   Flash32_25_0_0_171   (deferred)             

652a0000 65562000   jscript9   (pdb symbols)          C:\Program Files\Windows Kits\10\Debuggers\x86\sym\jscript9.pdb\6E55E6B5AC4B4699BFCF4B58510435202\jscript9.pdb

65570000 656a6000   DWrite     (deferred)             

656b0000 659f7000   d2d1       (deferred)             

65f00000 66cbd000   MSHTML     (pdb symbols)          C:\Program Files\Windows Kits\10\Debuggers\x86\sym\mshtml.pdb\98191859560C471FB6BA0B1D33DAACCB2\mshtml.pdb

66cc0000 679e9000   IEFRAME    (pdb symbols)          C:\Program Files\Window






DBI - pin & pintool

2017. 5. 22. 23:54



 PIN 에 대해서 공부한지는 조금 되었으나 적당히 기존 코드로만 붙여넣기 해서 사용했던 것이 사실이다. 최근 개인 프로젝트로 PIN 을 사용할 일이 생겨서 하는 김에 정리해 둔다.





PIN


 PIN 이란, Intel 에서 제작 및 관리중인 Dynamic Binary Instrumentation Framework 이다. BI 분야에서 실행을 하지 않고 조사하는 SBI 와 동적으로 실행중 코드를 조사하는 DBI 가 있는데, 그 중 PIN 은  DBI 를 위한 SDK(?) 정도로 볼 수 있다. 


Intel 이란 기업에서 관리하기 때문에 상당히 업데이트가 잘 되고 있으며 매뉴얼도 매우 충실하게 잘 되어 있다. 또한 수많은 예제코드가 들어 있어서 몇개의 코드만 분석해 보면 바로 사용방법을 알 수 있을 정도로 완성도가 높다. 


한가지 단점은 C++ 로 작성해서 DLL 로 컴파일 해야 하므로 스크립트 언어에 비해 수정&적용이 조금 오래 걸린다는 점이 있다. 하지만 이러한 단점은 장점에 비해 크지 않으므로 수년전부터 현재까지도 PIN 은 매우 인기 있는 DBI 프레임워크로 자리잡았다. 


PIN 은 그 자체로 pin.exe 로 독립된 바이너리이며, 실행 파일을 에뮬레이션 해준다. 에뮬레이션 하는 동안 PINTOOL 로 작성한 DLL 코드를 통해 다양하게 동적으로 코드를 수행할 수 있다.


 


PINTOOL


PINTOOL 은 PIN SDK 를 이용하여 개발한 라이브러리이다. pin.exe 실행 시 옵션으로(-t) 지정해주면 해당 PINTOOL 을 이용하여 타겟 바이너리를 실행시켜 준다.



Build PINTOOL


 다운로드 및 설치는 검색하면 많으니 타 자료를 참고바란다. 여러가지 방법이 있지만 개인적으로는 cygwin + make 가 편한 듯 하다. visual studio 로도 가능하지만 컴파일 프로그램 특성상 소스코드 수정, 컴파일 및 실행, 빌드된 파일 이동, 실행 등을 자주 해야 해서 차라리 make 로 배치 파일을 만드는 것이 편하다.


# make.bat

cd C:\Users\hyunmini\Desktop\pin-3.2-81205-msvc-windows\source\tools\MyPinTool

make 

copy /y obj-ia32\MyPinTool.dll c:\Users\FSI\Desktop\dbi\pin

cd c:\Users\hyunmini\Desktop\dbi\pin


 





Simple Example 1 - call tracer


#include "pin.H"
#include <asm/unistd.h>
#include <iostream>
#include <fstream>
#include <list>

UINT32 insCount = 0;    
UINT32 bblCount = 0;    
UINT32 threadCount = 0; 
UINT32 traceCount = 0;

std::ostream * out = &cerr;

KNOB<string> KnobTraceString(KNOB_MODE_WRITEONCE,  "pintool",
    "s", "", "trace string");

INT32 Usage()
{
    cerr << "This tool prints out the traced call with arguments." << endl;
    cerr << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

string invalid = "invalid_rtn";

const string *Target2String(ADDRINT target) {
    string name = RTN_FindNameByAddress(target);
    if (name == "") {
        return &invalid;
    } else if (name == ".text" || name == "unnamedImageEntryPoint") {
        return new string(StringFromAddrint(target));
    }
    else
        return new string(name);
}

string ReadCString(ADDRINT target){
    ADDRINT Buffer;
    PIN_SafeCopy(&Buffer, (ADDRINT *)(target), sizeof(ADDRINT));
    string cstring;
    while (1) {
        char c = 0;
        if (PIN_SafeCopy(&c, (ADDRINT *) Buffer, 1) != 1)
            break;
        if (c == 0)
            break;
        cstring += c;
        Buffer += 1;
    }
    return cstring;
}

VOID  do_call_args(ADDRINT ins,const string *s, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    PIN_LockClient();
    string img_name = IMG_Name(IMG_FindByAddress(ins));
    PIN_UnlockClient();
    string base_img_name = img_name.substr(img_name.find_last_of("/\\") + 1);
    string argstr1 = ReadCString(esp);
    string argstr2 = ReadCString(esp+4);
    string argstr3 = ReadCString(esp+8);
    if( 
        argstr1.find(KnobTraceString.Value()) != string::npos || 
        argstr2.find(KnobTraceString.Value()) != string::npos || 
        argstr3.find(KnobTraceString.Value()) != string::npos
      ){
        *out << StringFromAddrint(ins) << " : " << base_img_name << "!" << *s << "(0x" << std::hex << arg0 << ", 0x" << arg1 << ", 0x" << arg2 << "...)" << endl;
        if(argstr1.length()>10){
            argstr1 = argstr1.substr(0,10);
            argstr1 += "...";
        }
        if(argstr2.length()>10){
            argstr2 = argstr2.substr(0,10);
            argstr2 += "...";
        }
        if(argstr3.length()>10){
            argstr3 = argstr3.substr(0,10);
            argstr3 += "...";
        }
        *out << "    arg1 : " << argstr1 << endl;
        *out << "    arg2 : " << argstr2 << endl;
        *out << "    arg3 : " << argstr3 << endl;
    }
}

VOID  do_call_args_indirect(ADDRINT ins, ADDRINT target, BOOL taken, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    if( !taken ) return;
    const string *s = Target2String(target);
    do_call_args(ins, s, arg0, arg1, arg2, esp);
    if (s != &invalid)
        delete s;
}

/* ===================================================================== */

VOID Trace(TRACE trace, VOID *v)
{
    PIN_LockClient();
    IMG img = IMG_FindByAddress(TRACE_Address(trace));
    PIN_UnlockClient();

    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){
        for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
        {
            INS tail = BBL_InsTail(bbl);
            if( INS_IsCall(tail) ) {
                if( INS_IsDirectBranchOrCall(tail) ) {
                    // direct call
                    const ADDRINT target = INS_DirectBranchOrCallTargetAddress(tail);
                    INS_InsertPredicatedCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args),
                                                IARG_INST_PTR,
                                                IARG_PTR, Target2String(target), 
                                                IARG_FUNCARG_CALLSITE_VALUE, 0,
                                                IARG_FUNCARG_CALLSITE_VALUE, 1,
                                                IARG_FUNCARG_CALLSITE_VALUE, 2,
                                                IARG_REG_VALUE, REG_ESP,
                                                IARG_END);
                }
                else {
                    // indirect call
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);
                }
            }
            else {
                RTN rtn = TRACE_Rtn(trace);
                if( RTN_Valid(rtn) && !INS_IsDirectBranchOrCall(tail)) {
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);       
                }
            }
        }
    }
}

int main(int argc, char *argv[])
{
    PIN_InitSymbols();
    if( PIN_Init(argc,argv) ){  return Usage(); }
    string fileName = "trace_result.txt";
    if (!fileName.empty()) { out = new std::ofstream(fileName.c_str());}

    TRACE_AddInstrumentFunction(Trace, 0);

    PIN_StartProgram();
    return 0;
}



실행결과


x00489d99 : VUPlayer.exe!_mbscmp(0x3c34f3c, 0x3be35f8, 0x74aeac8...)
    arg1 : AAAAAAAAAA...
    arg2 : #VUPlayer ...
    arg3 : 
0x004552ac : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f140, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : mod
    arg3 : 
0x004552d2 : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f13c, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : s3m
    arg3 : 
0x004552f8 : VUPlayer.exe!_mbsicmp(0x3c3408c, 0x51f138, 0x0...)
    arg1 : AAAAAAAAAA...
    arg2 : xm
    arg3 : 

0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x004562a3 : VUPlayer.exe!0x004b9f68(0x0, 0x3c3408c, 0x0...)
    arg1 : 
    arg2 : AAAAAAAAAA...
    arg3 : 
0x004b9f68 : VUPlayer.exe!BASS_MusicLoad(0x4562a8, 0x0, 0x3c3408c...)
    arg1 : ‰Eüƒ}ü
    arg2 : 
    arg3 : AAAAAAAAAA...
0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x00456191 : VUPlayer.exe!fopen(0x3c3408c, 0x51e098, 0x12d198...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : dœP
0x00453313 : VUPlayer.exe!lstrcpyA(0x12cc44, 0x3c3408c, 0x12d3a8...)
    arg1 : 
    arg2 : AAAAAAAAAA...
    arg3 : 0ü¿4P
0x00453325 : VUPlayer.exe!fopen(0x12cc44, 0x51e098, 0x12d3a8...)
    arg1 : AAAAAAAAAA...
    arg2 : rb
    arg3 : AAAAAAAAAA...






Taint Analysis with PIN


taint analysis 는 이론부터 설명하면 매우 길기 때문에 더이상의 설명은 생략한다!(...)

결론은 쓸만하지만 오류 없이 쓰려면 훨~씬 세세한 컨트롤을 해줘야 함. 그러므로 여기까지만..


 * 코드 일부 생략.

 ** (http://shell-storm.org/blog/Taint-analysis-and-pattern-matching-with-Pin 코드를 아주 많이 참고 하였음을 밝힙니다)


#include "pin.H"

#include <asm/unistd.h>
#include <iostream>
#include <fstream>
#include <list>

UINT32 insCount = 0;    
UINT32 bblCount = 0;    
UINT32 threadCount = 0;
UINT32 traceCount = 0;
BOOL start_taint = false;

std::ostream * out = &cerr;
std::list<ADDRINT> addressTainted;
std::list<REG> regsTainted;

string invalid = "invalid_rtn";

////////////////////////
//  trace user input  //
////////////////////////

VOID fgetsBefore(ADDRINT arg0, ADDRINT arg1, ADDRINT arg2) {
    *out << "[Taint] fgets() : " << endl;
    *out << " buf : 0x" << std::hex << arg1 << endl;
    *out << " size : 0x" << arg2 << endl;
    int size = static_cast<ADDRINT>(arg2);
    start_taint = true;
    for (int i=0;i<size;i++){
        addressTainted.push_back(arg1+i);
    }
}

VOID recvBefore(ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT arg3) {
    *out << "[Taint] recv() : " << endl;
    *out << " buf : 0x" << std::hex << arg2 << endl;
    *out << " size : 0x" << arg3 << endl;
    int size = static_cast<ADDRINT>(arg3);
    start_taint = true;
    for (int i=0;i<size;i++){
        addressTainted.push_back(arg2+i);
    }
}

///////////////////////
/// taint analysis  ///
///////////////////////
bool IsRegTainted(REG reg){
    list<REG>::iterator i;

    for(i = regsTainted.begin(); i != regsTainted.end(); i++){
        if (*i == reg){
          return true;
        }
    }
    return false;
}

bool IsMemTainted(ADDRINT mem){
    list<ADDRINT>::iterator i;
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (mem == *i){
            return true;
        }
    }
    return false;
}

bool removeRegTainted(REG reg){
  switch(reg){

    case REG_EAX:  regsTainted.remove(REG_EAX);
    case REG_AX:   regsTainted.remove(REG_AX);
    case REG_AH:   regsTainted.remove(REG_AH);
    case REG_AL:   regsTainted.remove(REG_AL);
         break;

    case REG_EBX:  regsTainted.remove(REG_EBX);
    case REG_BX:   regsTainted.remove(REG_BX);
    case REG_BH:   regsTainted.remove(REG_BH);
    case REG_BL:   regsTainted.remove(REG_BL);
         break;

    case REG_ECX:  regsTainted.remove(REG_ECX);
    case REG_CX:   regsTainted.remove(REG_CX);
    case REG_CH:   regsTainted.remove(REG_CH);
    case REG_CL:   regsTainted.remove(REG_CL);
         break;

    case REG_EDX:  regsTainted.remove(REG_EDX); 
    case REG_DX:   regsTainted.remove(REG_DX); 
    case REG_DH:   regsTainted.remove(REG_DH); 
    case REG_DL:   regsTainted.remove(REG_DL); 
         break;

    case REG_EDI:  regsTainted.remove(REG_EDI); 
    case REG_DI:   regsTainted.remove(REG_DI); 
         break;

    case REG_ESI:  regsTainted.remove(REG_ESI); 
    case REG_SI:   regsTainted.remove(REG_SI); 
         break;

    default:
      return false;
  }
  *out  << "\t\t\t" << REG_StringShort(reg) << " is now freed" << std::endl;
  return true;
}

bool taintReg(REG reg){
    if (IsRegTainted(reg)){
        * out << "\t\t\t" << REG_StringShort(reg) << " is already tainted" << std::endl;
        return false;
    }

    switch(reg){

        case REG_EAX:  regsTainted.push_front(REG_EAX); 
        case REG_AX:   regsTainted.push_front(REG_AX); 
        case REG_AH:   regsTainted.push_front(REG_AH); 
        case REG_AL:   regsTainted.push_front(REG_AL); 
            break;

        case REG_EBX:  regsTainted.push_front(REG_EBX);
        case REG_BX:   regsTainted.push_front(REG_BX);
        case REG_BH:   regsTainted.push_front(REG_BH);
        case REG_BL:   regsTainted.push_front(REG_BL);
            break;

        case REG_ECX:  regsTainted.push_front(REG_ECX);
        case REG_CX:   regsTainted.push_front(REG_CX);
        case REG_CH:   regsTainted.push_front(REG_CH);
        case REG_CL:   regsTainted.push_front(REG_CL);
            break;

        case REG_EDX:  regsTainted.push_front(REG_EDX); 
        case REG_DX:   regsTainted.push_front(REG_DX); 
        case REG_DH:   regsTainted.push_front(REG_DH); 
        case REG_DL:   regsTainted.push_front(REG_DL); 
            break;

        case REG_EDI:  regsTainted.push_front(REG_EDI); 
        case REG_DI:   regsTainted.push_front(REG_DI); 
            break;

        case REG_ESI:  regsTainted.push_front(REG_ESI); 
        case REG_SI:   regsTainted.push_front(REG_SI); 
            break;

        default:
            *out  << "\t\t\t" << REG_StringShort(reg) << " can't be tainted" << std::endl;
            return false;
  }
  *out << "\t\t\t" << REG_StringShort(reg) << " is now tainted" << std::endl;
  return true;
}

VOID ReadMem(INS ins, ADDRINT memOp){
    // memory -> reg
    list<ADDRINT>::iterator i;
    ADDRINT addr = memOp;
    REG read_reg;
  
    if (INS_OperandCount(ins) != 2)
        return;

    read_reg = INS_OperandReg(ins, 0);
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (addr == *i){
            // tainted memory > register
            *out << "[Read][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
            taintReg(read_reg);
            return ;
        }
    }
    if (IsRegTainted(read_reg)){
        // untainted memory > tainted register => clear reg
        *out << "[Read][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
        removeRegTainted(read_reg);
    }
}

VOID WriteMem(INS ins, ADDRINT memOp){
    // reg -> memory
    list<ADDRINT>::iterator i;
    ADDRINT addr = memOp;
    REG read_reg;
  
    if (INS_OperandCount(ins) != 2)
        return;

    read_reg = INS_OperandReg(ins, 1);
    for(i = addressTainted.begin(); i != addressTainted.end(); i++){
        if (addr == *i){
            // tainted memory
            *out << "[Write][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
            if (!REG_valid(read_reg) || !IsRegTainted(read_reg)){
                // untainted register > tainted memory => clear memory
                addressTainted.remove(addr);
            }
            *out << std::hex << "\t\t\t0x" << addr << " is now freed" << std::endl;
            return;
        }
    }

    if (IsRegTainted(read_reg)) {
    // tainted reg > untainted memory => tainted
        addressTainted.push_back(addr);
        *out << std::hex << "\t\t\t0x" << addr << " is now tainted" << std::endl;
        return;
    }
}

VOID spreadReg(INS ins)
{
    REG reg_r, reg_w;

    if (INS_OperandCount(ins) != 2) return;

    reg_r = INS_RegR(ins, 0);  // src
    reg_w = INS_RegW(ins, 0);  // dest 

    if (REG_valid(reg_w)){
        // reg_w not const(address)
        if (IsRegTainted(reg_w) && (!REG_valid(reg_r) || !IsRegTainted(reg_r))){
            // untainted reg > tainted reg => clear reg
            *out  << "[SPREAD]\t\t" << INS_Address(ins) << ": " << INS_Disassemble(ins) << std::endl;
            *out  << "\t\t\toutput: "<< REG_StringShort(reg_w) << " | input: " << (REG_valid(reg_r) ? REG_StringShort(reg_r) : "constant") << std::endl;
            removeRegTainted(reg_w);
    }
    else if (!IsRegTainted(reg_w) && IsRegTainted(reg_r)){
        // tainted reg > untainted reg => tainted
        *out  << "[SPREAD]\t\t" << INS_Address(ins) << ": " << INS_Disassemble(ins) << std::endl;
        *out  << "\t\t\toutput: " << REG_StringShort(reg_w) << " | input: "<< REG_StringShort(reg_r) << std::endl;
        taintReg(reg_w);
        }
    }
}

VOID Instruction(INS ins, VOID *v){
    if (IMG_IsMainExecutable(IMG_FindByAddress(INS_Address(ins)))){
        // callback for read instruction
        // memory -> reg
        if (INS_OperandCount(ins) > 1 && INS_MemoryOperandIsRead(ins, 0) && INS_OperandIsReg(ins, 0)){
            if (start_taint){
                  INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)ReadMem,
                    IARG_PTR, ins,
                    IARG_MEMORYOP_EA, 0,
                    IARG_END);
            }
        }
        // callback for write instruction
        // reg -> memory
        else if (INS_OperandCount(ins) > 1 && INS_MemoryOperandIsWritten(ins, 0)){
            if (start_taint){
                INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)WriteMem,
                    IARG_PTR, ins,
                    IARG_MEMORYOP_EA, 0,
                    IARG_END);
            }
        }
        // callback for spread instruction
        // reg -> reg
        else if (INS_OperandCount(ins) > 1 && INS_OperandIsReg(ins, 0)){
            if (start_taint){
                //*out << "[Spread][0x" << std::hex << INS_Address(ins) <<"] : " << INS_Disassemble(ins) << endl;
                 INS_InsertCall(
                    ins, IPOINT_BEFORE, (AFUNPTR)spreadReg,
                    IARG_PTR, ins,
                    IARG_END);
            }
        }
    }
}

VOID Image(IMG img, VOID *v){
    // fgets(&Buf, 2000, File);
    RTN fgetsRtn = RTN_FindByName(img, "fgets");
    if (RTN_Valid(fgetsRtn))
    {
        RTN_Open(fgetsRtn);
        RTN_InsertCall(fgetsRtn, IPOINT_BEFORE, (AFUNPTR)fgetsBefore,
                        IARG_FUNCARG_CALLSITE_VALUE, 0,
                        IARG_FUNCARG_CALLSITE_VALUE, 1,
                        IARG_FUNCARG_CALLSITE_VALUE, 2,
                        IARG_END);
        RTN_Close(fgetsRtn);
    }
    // recv(sock, &buf, 2000, 0);
    RTN recvRtn = RTN_FindByName(img, "recv");
    if (RTN_Valid(recvRtn))
    {
        RTN_Open(recvRtn);
        RTN_InsertCall(recvRtn, IPOINT_BEFORE, (AFUNPTR)recvBefore,
                        IARG_FUNCARG_CALLSITE_VALUE, 0,
                        IARG_FUNCARG_CALLSITE_VALUE, 1,
                        IARG_FUNCARG_CALLSITE_VALUE, 2,
                        IARG_FUNCARG_CALLSITE_VALUE, 3,
                        IARG_END);
        RTN_Close(recvRtn);
    }
}

VOID Fini(INT32 code, VOID *v){
    /*
    list<ADDRINT>::iterator li;
    for(li=addressTainted.begin(); li!=addressTainted.end(); li++){
        * out << std::hex << *li << endl;
    }
    */
    *out << "[+] Finish!!" << endl;
}

/////////////////////////////
//////   call trace /////////
/////////////////////////////
const string *Target2String(ADDRINT target) {
    string name = RTN_FindNameByAddress(target);
    if (name == "") {
        return &invalid;
    } else if (name == ".text" || name == "unnamedImageEntryPoint") {
        return new string(StringFromAddrint(target));
    }
    else
        return new string(name);
}

string ReadCString(ADDRINT target){
    ADDRINT Buffer;
    PIN_SafeCopy(&Buffer, (ADDRINT *)(target), sizeof(ADDRINT));
    string cstring;
    while (1) {
        char c = 0;
        if (PIN_SafeCopy(&c, (ADDRINT *) Buffer, 1) != 1)
            break;
        if (c == 0)
            break;
        cstring += c;
        Buffer += 1;
    }
    return cstring;
}

VOID  do_call_args(ADDRINT ins,const string *s, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    PIN_LockClient();
    string img_name = IMG_Name(IMG_FindByAddress(ins));
    PIN_UnlockClient();
    string base_img_name = img_name.substr(img_name.find_last_of("/\\") + 1);
    string argstr1 = ReadCString(esp);
    string argstr2 = ReadCString(esp+4);
    string argstr3 = ReadCString(esp+8);
    bool tainted_func = false;

    if ( IsMemTainted(arg0) ) {
        tainted_func = true;
    }
    if ( IsMemTainted(arg1) ) {
        tainted_func = true;
    }
    if ( IsMemTainted(arg2) ) {
        tainted_func = true;
    }
    
    if ( tainted_func ) {
        *out << "[tainted arg]" << StringFromAddrint(ins) << " : " << base_img_name << "!" << *s << "(0x" << std::hex << arg0 << ", 0x" << arg1 << ", 0x" << arg2 << "...)" << endl;

        if (argstr1.length()>10){
            argstr1 = argstr1.substr(0,10);
            argstr1 += "...";
        }
        if (argstr2.length()>10){
            argstr2 = argstr2.substr(0,10);
            argstr2 += "...";
        }
        if (argstr3.length()>10){
            argstr3 = argstr3.substr(0,10);
            argstr3 += "...";
        }
        *out << "    arg1 : " << argstr1 << endl;
        *out << "    arg2 : " << argstr2 << endl;
        *out << "    arg3 : " << argstr3 << endl;
    }
}

VOID  do_call_args_indirect(ADDRINT ins, ADDRINT target, BOOL taken, ADDRINT arg0, ADDRINT arg1, ADDRINT arg2, ADDRINT esp) {
    if ( !taken ) return;
    const string *s = Target2String(target);
    do_call_args(ins, s, arg0, arg1, arg2, esp);
    if (s != &invalid)
        delete s;
}

/* ===================================================================== */

VOID Trace(TRACE trace, VOID *v)
{
    PIN_LockClient();
    IMG img = IMG_FindByAddress(TRACE_Address(trace));
    PIN_UnlockClient();

//    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){
    if (IMG_Valid(img) && IMG_IsMainExecutable(img)){   
        for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
        {
            INS tail = BBL_InsTail(bbl);
            if ( INS_IsCall(tail) ) {
                if ( INS_IsDirectBranchOrCall(tail) ) {
                    // direct call
                    const ADDRINT target = INS_DirectBranchOrCallTargetAddress(tail);
                    INS_InsertPredicatedCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args),
                                                IARG_INST_PTR,
                                                IARG_PTR, Target2String(target), 
                                                IARG_FUNCARG_CALLSITE_VALUE, 0,
                                                IARG_FUNCARG_CALLSITE_VALUE, 1,
                                                IARG_FUNCARG_CALLSITE_VALUE, 2,
                                                IARG_REG_VALUE, REG_ESP,
                                                IARG_END);
                }
                else {
                    // indirect call
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);
                }
            }
            else {
                RTN rtn = TRACE_Rtn(trace);
                if ( RTN_Valid(rtn) && !INS_IsDirectBranchOrCall(tail)) {
                    INS_InsertCall(tail, IPOINT_BEFORE, AFUNPTR(do_call_args_indirect),
                                    IARG_INST_PTR,
                                    IARG_BRANCH_TARGET_ADDR, IARG_BRANCH_TAKEN,  
                                    IARG_FUNCARG_CALLSITE_VALUE, 0,
                                    IARG_FUNCARG_CALLSITE_VALUE, 1,
                                    IARG_FUNCARG_CALLSITE_VALUE, 2,
                                    IARG_REG_VALUE, REG_ESP,
                                    IARG_END);       
                }
            }
        }
    }
}
/////////////////////////////////////////////////
////////////// call trace end ///////////////////
/////////////////////////////////////////////////

int main(int argc, char *argv[]){
    PIN_InitSymbols();
    if ( PIN_Init(argc,argv) ){ return -1; }
    string fileName = "taint_analysis_result.txt";
    if (!fileName.empty()) { out = new std::ofstream(fileName.c_str());}

    IMG_AddInstrumentFunction(Image, 0);
    INS_AddInstrumentFunction(Instruction, 0);
    TRACE_AddInstrumentFunction(Trace, 0);
    PIN_AddFiniFunction(Fini, 0);

    PIN_StartProgram();
    return 0;
}




실행결과


[Taint] fgets() : 
 buf : 0x12f76c
 size : 0x7d0
[tainted arg]0x0040113e : reader_taint.exe!myreadfile(0x12f76c, 0x61616161, 0x61616161...)
    arg1 : aaaaaaaaaa...
    arg2 : 
    arg3 : 
[Read][0x401029] : mov dl, byte ptr [ecx+0x5]
            dl is now tainted
            0x12f55f is now tainted
[Read][0x401035] : mov cl, byte ptr [eax+0x6]
            cl is now tainted
            0x12f55d is now tainted
[Read][0x401041] : mov al, byte ptr [edx+0x8]
            al is now tainted
            0x12f55e is now tainted
[Write][0x40104d] : mov byte ptr [ecx+0x2], 0x66
            0x12f76e is now freed
[Write][0x401054] : mov byte ptr [edx+0x4], 0x67
            0x12f770 is now freed
[Write][0x40105b] : mov byte ptr [eax+0xb], 0x68
            0x12f777 is now freed
[Write][0x401062] : mov byte ptr [ecx+0x15], 0x6d
            0x12f781 is now freed
[tainted arg]0x00401071 : reader_taint.exe!strcpy(0x12f560, 0x12f76c, 0x61616101...)
    arg1 : 
    arg2 : aafagaaaaa...
    arg3 : 
[tainted arg]0x004010a0 : reader_taint.exe!main(0x401143, 0x12f76c, 0x61666161...)
    arg1 : ƒÄëë3À‹...
    arg2 : aafagaaaaa...
    arg3 : 
[+] Finish!!


한줄 요약 : PIN 은 위대하다. 그러나 정말 제대로 활용하려면 어렵다..


'Reversing' 카테고리의 다른 글

암호학 정리(for CTF)  (0) 2018.04.05
WinDBG 고급 명령어들  (0) 2017.06.07
DBI - Frida 를 이용한 DBI  (3) 2016.09.07
Embedded 기기 리버싱 - 펌웨어 리버싱  (1) 2016.07.14
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01

DBI - Frida 를 이용한 DBI

2016. 9. 7. 13:05


DBI


Dynamic Binary Instrumentation - 동적 바이너리 조사



 printf("test %s", buf); 등과 같이 특정 시점에서의 변수값 등을 확인하거나 프로그램의 행위를 조사하는 일을 Instrumentation 라고 부른다. 소스 코드 수준에서 하는 행위를 SI (Source Instrumentation), 바이너리 수준에서 하는 행위를 BI 라 한다. BI 중에서도 바이너리 실행중에 조사하는 것을 DBI 라 부른다. 조금 더 구체적으로 설명하면, 바이너리가 어떤 함수들을 호출하는지, 또 인자값은 무엇인지 등을 조사하는 것이라 보면 될 것 같다. 예를 들어, 악성코드 분석을 할때 각종 모니터링 툴들을 켜고 악성코드를 실행하여 행위를 분석하는 것 또한 동적 바이너리 조사의 일종이라고 볼 수 있다.



DBI 구현



 DBI 를 구현하는 방법에는 디버거 스크립트를 이용하는 방법, 후킹을 이용하는 방법, 코드 캐쉬와 CPU 가상화를 이용한 에뮬레이팅 등이 있다. 먼저 디버거를 이용하는 경우에는 디버거 스크립트와 조건부 브레이크포인트, 로그기능 등을 이용할 수 있다. 후킹을 이용하는 경우 주요 함수들( 네트워크 관련 함수들, File I/O 함수들, 문자열 처리 함수 등)에 후킹을 설정한 뒤 함수 후킹 시 인자값 등을 기록하거나 변조하는 행위들을 할 수 있다. 마지막으로 에뮬레이팅을 이용한 방법은 코드를 읽어서 가상 CPU 로 실행시키는 방법이다. 이전의 두가지 방법과는 다르게 투명성이 보장된다. 디버거, 후킹의 경우 이미 많이 알려져 있고 프로세스의 코드를 직접 변경하기 때문에 보호기법에 의해 쉽게 발견될 수 있다. 하지만 에뮬레이팅의 경우 코드를 복사해서 해석한뒤 실행하므로 일반적인 탐지기법들을 우회할 수 있다. 

   


DBI 로 할 수 있는 것들



    • Flow Analysis
    • Reversing Automation
    • Malware Analysis
    • Taint Analysis
    • Binary Visualization (ex: VERA)
    • Vulnerability(Exploit) Analysis


이외에도 기타 상상할 수 있는 다양한 자동 분석들이 가능하다. DBI 를 이용해서 안티디버깅 코드를 탐지하는 논문도 보았고, unpacking automation 논문도 본 적이 있다. 최근에는 DBI 를 Tracer 로 사용해서 로그를 남기고 그 정보들을 활용하여 Symbolic Execution, SMT Solver 등과 연계하여 취약점을 자동으로 찾거나(AEG, Automatic Exploit Generation) CTF 문제 등을 자동으로 풀어주는 등 Advanced 한 주제들도 지속적으로 연구되고 있다. 관심이 있다면 Triton, Angr 등의 Framework 를 살펴보면 된다. 








DBI Framework


 PIN & PINTOOL


intel 에서 관리하는 DBI 프레임워크로 기업이 관리하는 특성상 유지보수가 매우 잘 되고 있으며 다양한 플랫폼을 지원한다. 단점은 Intel 아키텍쳐(intel cpu)만 지원한다는 점과 C++ 로 작성해야 해서 매번 컴파일 하는 것이 조금 번거롭다는 것이다. 코드를 해석해서 가상 CPU 에서 실행시킨다. 실제로 원본 파일은 실행조차 되지 않는다. 때문에 투명성이 보장되지만 속도 이슈가 있다. 


(참고) http://hyunmini.tistory.com/87  



 FRIDA


FRIDA 는 scriptable 한 DBI 프레임워크로, http://www.frida.re 에서 무료로 다운로드 및 설치가 가능하다.(심지어 오픈소스이다. 근데 vala 라는 특이한 프로그래밍 언어로 작성되어 있다. 신기함...) FRIDA 는 기타 DBI 프레임워크와 약간 다른 구조를 가지고 있는데, DBI 를 위한 주 조작은 자바스크립트를 통해서 하며, C/S 구조로 동작하게 된다. 즉 처음에 바이너리에 프레임워크 라이브러리를 인젝션하여 파이프를 만들어 놓고, 그 파이프를 통해서 명령을 주고 받으면서 바이너리 조사를 할 수 있도록 되어 있다. 물론 C/S 를 사용하지 않고 그냥 PIN 처럼 내부적으로 동작하도록 할 수도 있다. 여러가지 언어를 이용하여 스크립트를 작성할 수 있으나(바이너리 조작은 자바스크립트로 해야 한다) Python 이 가장 단순하고 좋은 듯 하다. 하지만 제작자는 발표 등을 할 때 node.js 를 많이 사용하는 모습을 보여주고 있다. FRIDA 의 가장 큰 장점은 다양한 플랫폼을 지원한다는 것이다. PIN 과는 달리 ARM 아키텍쳐를 지원하므로 Android, IOS 앱에도 적용이 가능하다. 또한 64 bit 에서도 정상적으로 동작한다.(awesome!!)

또한, 컴파일이 필요없는 스크립트 언어로 작성이 가능하다는 것도 매력적이다. 코드 수정이 빈번한 경우 매번 컴파일을 하는 것만큼 귀찮은 것은 없기 때문이다.    




 ETC(DinamoRIO, Valgrind, DynInst 등)


아직 해보지 않아서 잘 모름. Valgrind 의 경우 실제로 취약점을 꽤 많이 찾아준 것으로 알고 있다.






DBI 예제 (with FRIDA)

 


FRIDA 기본 원리


FRIDA 는 C/S 의 구조를 지원하기 위해 윈도우의 경우 DLL 인젝션을, *닉스 계열의 경우 라이브러리 인젝션을 수행한다. 그 후 API 후킹(주소값만 지정하면 유저 custom 함수도 가능), stalking(이벤트 기반) 기능 등을 이용하여 DBI 가 가능하다. 이때, 실제 DBI 를 수행하는 코드는 자바스크립트를 이용하여 작성해 주어야 한다.




FRIDA 설치


c:\> pip install frida      // pip 는 python 모듈 관리를 위한 프로그램이며 이를 이용하여 쉽게 설치가 가능함




FRIDA 참고 자료 및 API


http://www.frida.re/docs/javascript-api        // 아직 관련 자료가 많지 않아 공식 문서를 보는 것이 가장 좋다.

https://github.com/OALabs/frida-extract      // frida 를 이용한 PE Extract 코드 (악성코드 등에서 PE 파일 추출) 





FRIDA 예제 코드


 아래 코드는 DBI 에 대한 개념이해를 위해 직접 발로 짠 코드이다. 간단하게 socket 관련 함수인 send() 함수를 후킹하여 버퍼를 기록하고, send() 함수를 호출한 코드 주소값들(BackTrace) 을 기록해주는 코드이다.


import sys
import pefile
import frida

def on_message(message, data):                 
    # c/s 구조인 경우 메시지를 보낼 수 있다. 메시지를 처리할 핸들러를 여기에 작성
    print "[%s] -> %s" % (message, data)

def main(target_process):
    session = frida.attach(target_process)       # attach => target 에 DLL 인젝션 수행
    script = session.create_script("""           # 자바스크립트 로드(실질적인 DBI 코드)

    var sendPtr = Module.findExportByName("ws2_32.dll", "send");        # send 함수 주소
    console.log('send address: ' + sendPtr.toString());
    console.log('>> Hooking ws2_32!send <<');
    Interceptor.attach(sendPtr, {               # send 함수에 후킹 설정(inline hooking 수행)
        onEnter: function (args) {              # 함수 실행 전 수행될 코드(인자값 기록, 변조 등 가능)
            console.log("buf : " + Memory.readCString(args[1]));    # 메모리에서 직접 read
            console.log("send called from:" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\\n") + " ");
        },
        onLeave: function (retval) {         # 함수 실행 후 수행될 코드(리턴값 기록, 조작 등 가능)
        }
    });
""") 
 
    script.on('message', on_message)       # 메시지 핸들러 설정
    script.load()                       
    raw_input('\n\n')
    session.detach()
 
if __name__ == '__main__':
    target_process = 'test.exe'
    main(target_process) 



 실행


   






FRIDA 예제 코드2 


이번에는 인자값 조작을 해 보자. send 로 전송되는 패킷 내용을 변조하는 코드이다. 위 코드와 거의 동일하며 함수 실행 전 인자값으로 넘어온 buffer 문자열을 조작한다.


import sys
import pefile
import frida

def on_message(message, data):
    print "[%s] -> %s" % (message, data)

def main(target_process):
    session = frida.attach(target_process)
    script = session.create_script("""

    var sendPtr = Module.findExportByName("ws2_32.dll", "send");
    console.log('send address: ' + sendPtr.toString());
    console.log('>> Hooking ws2_32!send <<');

    Interceptor.attach(sendPtr, {
        onEnter: function (args) {
            console.log("buf : " + Memory.readCString(args[1]));
            Memory.writeAnsiString(args[1],"HTTP/1.1 200 OK\\nServer: simple web server\\nContent-Type: text/html\\n\\nWelcome To My testServer !!                                         ");
            console.log("replaced buf : " + Memory.readCString(args[1]));            
        },
        onLeave: function (retval) {
        }
    });

""") 
 
    script.on('message', on_message)
    script.load()
    raw_input('\n\n')
    session.detach()
 
if __name__ == '__main__':
    target_process = 'vul_http_server.exe'
    main(target_process)



실행



'Reversing' 카테고리의 다른 글

WinDBG 고급 명령어들  (0) 2017.06.07
DBI - pin & pintool  (4) 2017.05.22
Embedded 기기 리버싱 - 펌웨어 리버싱  (1) 2016.07.14
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01
WinDBG 심볼 간편 설정  (0) 2016.03.01


# 펌웨어 획득


- 벤더 홈페이지 다운

- 플래시메모리 덤프



# 펌웨어 분석 툴


- binwalk 


root@kali:~/Desktop# binwalk -h


 Binwalk v1.2.2-1

 Craig Heffner, http://www.devttys0.com


 Usage: binwalk [OPTIONS] [FILE1] [FILE2] [FILE3] ...


 Signature Analysis:

-B, --binwalk                 Perform a file signature scan (default)

-R, --raw-bytes=<string>      Search for a custom signature

-A, --opcodes                 Scan for executable code signatures

-C, --cast                    Cast file contents as various data types

-m, --magic=<file>            Specify an alternate magic file to use


                              ...









1) 펌웨어 시그니처 분석



root@kali:~/Desktop# binwalk n604s_kr_9_942.bin


DECIMAL   HEX       DESCRIPTION

-------------------------------------------------------------------------------------------------------------------

46692     0xB664     LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 209044 bytes

131072     0x20000   TRX firmware header, little endian, header size: 28 bytes, image size: 3788800 bytes, CRC32: 0x75B1E9D4 flags: 0x0, version: 1

131100     0x2001C   LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4378624 bytes

1565732   0x17E424   CramFS filesystem, little endian size 2351104 version #2 sorted_dirs CRC 0x9ee064f0, edition 0, 1954 blocks, 376 files










2) 펌웨어 아키텍쳐 분석



root@kali:~/Desktop# binwalk -A n604s_kr_9_942.bin  | head -10


DECIMAL   HEX       DESCRIPTION

-------------------------------------------------------------------------------------------------------------------

9280       0x2440     MIPSEL instructions, function epilogue

9376       0x24A0     MIPSEL instructions, function epilogue

9992       0x2708     MIPSEL instructions, function prologue

10028     0x272C     MIPSEL instructions, function epilogue

10276     0x2824     MIPSEL instructions, function prologue

10280     0x2828     MIPSEL instructions, function epilogue

10288     0x2830     MIPSEL instructions, function prologue











3) 펌웨어 엔트로피 분석


root@kali:~/Desktop# binwalk -E n604s_kr_9_942.bin 


    










4) 펌웨어 파일시스템 추출 



root@kali:~/Desktop# dd if=./n604s_kr_8_96.bin of=n604s_fs.bin bs=1 skip=1565732

2124820+0 records in

2124820+0 records out

2124820 bytes (2.1 MB) copied, 2.60455 s, 816 kB/s



root@kali:~/Desktop# file n604s_fs.bin

n604s_fs.bin: Linux Compressed ROM File System data, little endian size 2121728 version #2 sorted_dirs CRC 0x9094e641, edition 0, 1646 blocks, 353 files




5) 펌웨어 파일시스템 마운트


root@kali:~/Desktop# mkdir /n604fs


root@kali:~/Desktop# mount -t cramfs ./n604s_fs.bin /n604fs/

mount: warning: /n604fs/ seems to be mounted read-only.


root@kali:~/Desktop# cd /n604fs/


root@kali:/n604fs# ls

bin  cramfs  default  dev  etc  home  lib  linuxrc  ndbin  plugin  proc  save  sbin  tmp  usr  var




6) 파일시스템 분석 후 fmk 를 이용하여 바이너리 수정/파일시스템 추가 등 조작 후 build 





7) 만들어진 new-firmware.bin 이미지를 업데이트(관리자 페이지 수동 업데이트)





8) 간혹 업데이트 시 에러가 발생하는 경우 부트코드에서 검증을 수행하는 것

 - 간단한 체크섬 (우회 쉬움)

 - 암호화 (우회 어려움)




** 맥에서 바로 UART 시리얼 통신 확인하기


     screen  /dev/tty.usbmodemfa2321 115200








# 부트코드 리버싱

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


- 펌웨어에서 부트코드 분리 

  # dd if=~~  of=~~  skip=~~  count=~~ bs=1


- 부트코드 아키텍쳐, load address, size 지정

  => file 명령, binwalk 결과값 이용

 

- load address 찾는 법

  => 부트코드이므로 bss 섹션 초기화 과정이 있을 것 

       => li(or la), $0x80001000  등의 코드가 2번 연속으로 나오고 move $a1, $zero 루프 등으로 초기화 하는 부분을 찾음


  => 부트코드 |  bss 섹션       과 같은 구조이므로 bss 섹션 시작주소에서 부트코드 사이즈를 빼 주면 시작주소가 나옴

      => bss 섹션 시작주소 - 부트코드 사이즈 = 부트코드 시작주소


- 부트중 펌웨어 해쉬값 검증 코드 등을 찾아서 패치 









'Reversing' 카테고리의 다른 글

DBI - pin & pintool  (4) 2017.05.22
DBI - Frida 를 이용한 DBI  (3) 2016.09.07
Windbg 기본 - (3) 분석시 유용한 명령어들  (2) 2016.03.01
WinDBG 심볼 간편 설정  (0) 2016.03.01
Windows Kernel Reversing - Object 분석  (0) 2015.01.28


분석시 유용한 명령어 추가 정리.




# uf    //  함수 디스어셈블, 함수 한번에 전체 디스어셈블, 분기문 보기좋게 보여줌


0:014> uf mshtml!CUndoPropChange::~CUndoPropChange

mshtml!CUndoUnit::~CUndoUnit:

678dc2fd 8b4008          mov     eax,dword ptr [eax+8]

678dc300 85c0            test    eax,eax

678dc302 0f8580a92300    jne     mshtml!CUndoUnit::~CUndoUnit+0x7 (67b16c88)


mshtml!CUndoUnit::~CUndoUnit+0x16:

678dc308 c3              ret


mshtml!CUndoUnit::~CUndoUnit+0x7:

67b16c88 50              push    eax

67b16c89 6a00            push    0

67b16c8b ff351884dc67    push    dword ptr [mshtml!g_hProcessHeap (67dc8418)]

67b16c91 ff15fc128967    call    dword ptr [mshtml!_imp__HeapFree (678912fc)]

67b16c97 e96c56dcff      jmp     mshtml!CUndoUnit::~CUndoUnit+0x16 (678dc308)


mshtml!CUndoPropChange::~CUndoPropChange:

67c03c81 8bff            mov     edi,edi

67c03c83 56              push    esi

67c03c84 8bf0            mov     esi,eax

67c03c86 57              push    edi





# uf /c     //  함수 호출 관계만 보여줌

0:014> uf /c mshtml!CUndoPropChange::~CUndoPropChange

mshtml!CUndoPropChange::~CUndoPropChange (67c03c81)

  mshtml!CUndoUnit::~CUndoUnit+0x10 (67b16c91):

    call to mshtml!_imp__HeapFree (678912fc)

  mshtml!CUndoPropChange::~CUndoPropChange+0x14 (67c03c95):

    unresolvable call: call    dword ptr [ecx+8]

  mshtml!CUndoPropChange::~CUndoPropChange+0x1a (67c03c9b):

    call to mshtml!VariantClear (67a4b90d)



# x      //  * 등을 사용하여 심볼 정보 확인, 클래스 정보 확인 등에 유용함


0:014> x mshtml!CUndoPropChange::~CUndoProp*    //  뒷부분을 정확히 모를때

67c03c81          mshtml!CUndoPropChange::~CUndoPropChange (<no parameter info>)


0:014> x mshtml!CUndoPropChange::*          // 해당 클래스 메소드 확인

67a5156c          mshtml!CUndoPropChange::`vftable' = <no type information>

67c03c81          mshtml!CUndoPropChange::~CUndoPropChange (<no parameter info>)

67c03c22          mshtml!CUndoPropChange::CUndoPropChange (<no parameter info>)

67c03c51          mshtml!CUndoPropChange::`vector deleting destructor' (<no parameter info>)

67c03cdf          mshtml!CUndoPropChange::PrivateDo (<no parameter info>)

67c03cae          mshtml!CUndoPropChange::Init (<no parameter info>)

67c03c51          mshtml!CUndoPropChange::`scalar deleting destructor' (<no parameter info>)



# dds      //  4바이트씩 정리해서 심볼정보를 보여줌(문자열 포함) // 스택정보 파악 시 유용함


0:014> dds esp

05afd530  05774e50

05afd534  00477e58

05afd538  0048cb80

05afd53c  67c03c60 mshtml!CUndoAttrValueSimpleChange::`scalar deleting destructor'+0xf

05afd540  05afd57c

05afd544  05afd550

05afd548  678dc23d mshtml!CUndoUnitBase::Release+0x1e

05afd54c  00000001

05afd550  05afd58c

05afd554  67a6cfab mshtml!CImplPtrAry::ReleaseAll+0x21

05afd558  0048cb80

05afd55c  0048cb80

05afd560  06f3d030

05afd564  00000000

05afd568  678dc296 mshtml!CParentUnitBase::GetUnitType+0x82

05afd56c  06f3d030

05afd570  06f3d02c

05afd574  00000000

05afd578  05afd594

05afd57c  6789a594 mshtml!CStyleSheet::CAryFontFaces::`vftable'

05afd580  00000004




 



WinDBG 심볼 간편 설정

2016. 3. 1. 21:46


WinDBG 심볼 간편 설정




WinDBG 를 사용하는 경우 심볼 설정을 해 주어야 윈도우 관련 DLL 심볼을 보며 편하게 리버싱을 할 수 있다. 여러가지 방법이 있으나 매일


까먹어서 정리해 둔다.





  • WinDBG 심볼 패스 설정하기
  - [File] - [Symbol File Path...] - 아래 주소 입력

  SRV*c:\symbols*http://msdl.microsoft.com/download/symbols


   - 0:000> .reload



  • 심볼 패스 쉽게 설정하기
  - symfix 명령어 이용하기 (폴더만 지정해주면 알아서 다운로드함)
 0:000>  .symfix  c:\symbols
 
0:000>  .reload  





  • 심볼 패스 추가하기 
  - sympath 명령 뒤에 + 를 붙여서 자신의 프로그램 심볼이 있는 폴더를 추가 해준다.

 0:000>  .sympath+  c:\mysymbols
 
0:000>  .reload  



이상 끝!


제목은 커널 리버싱이지만, 단순히 툴 소개이다.(-.-;;) 애플리케이션 분석을 하다가도 커널에서 관리하는 구조체 등을


분석해야 하는 일이 자주 생긴다. 해서 이런 저런 자료들을 찾다보니 편리한 툴을 하나 찾았다. 


Symbol Type Viewer 인데, 분석할 파일만 넣어주면 자동으로 Microsoft 심볼 서버와 연동해서 구조체들을 보여준다.


ntdll.dll. kernel32.dll 등의 파일의 구조체 정보가 유용하다. 


아래 그림은 ntdll.dll 을 열어 본 화면이다.





.idc 로 export 해서 IDA Pro 에서 분석 정보로 활용할 수도 있다.



설명 끝~


http://decompiler.fit.vutbr.cz


온라인으로 제공되는 디컴파일러 입니다. 상당히 유용하네요.


Arm , MIPS, 심지어 PowerPC 도 지원됩니다. 



해보니 헥스레이처럼 간지(?)가 나진 않지만...무료이고 다양한 플랫폼을 지원하므로 급할때 유용합니다.




심심풀이 워게임풀이 하던중 간만에 vb 문제가 나와서 vb로 디컴파일 후 분석을 하는데 정적분석만으론 풀 수 없는


문제라 ida 분석및 디버깅이 필요했다. 


vb decompiler 를 이용해 얻은 정보들을 ida 로 그대로 옮길 수 있다. 






map 파일을 생성해서 olly 등에서 참조할 수도 있으니 알아두자!   :)




리버싱을 하다보면 가끔 닷넷 프로그램을 리버싱 해야 할 경우가 있다. 사실 나도 닷넷 프로그래밍을 공부해본 적은 


없기 때문에 자세한 내부구조 까지는 알 수 없으나, 리버싱이 언제나 그렇듯이 중요한 건 프로그램에서 내가 고칠 


부분을 찾고, 코드 패칭 등 원하는 작업을 할 수 있으면 되기 때문에 큰 어려움은 없었다. 


가장 중요한 사실은 닷넷 프로그램은 관리코드 기반 언어이기 때문에 디컴파일이 매우 잘 된다는 것이다. :)


이번 코드 게이트 대회에서도 닷넷 프로그램이 문제에 나와서 나온 김에 정리를 해둔다.



# 닷넷 프로그램


 - 관리 기반 코드 ( = java JVM처럼 중간언어로 컴파일됨, MSIL 이란 언어로 컴파일 됨 )

 - 그러므로 디컴파일이 매우 쉽게 됨

 - 난독화 솔루션 존재하나, 어느정도 원복해주는 툴 또한 존재함



# 관련 리버싱 툴


 1) .net reflector ( 디컴파일러, http://www.red-gate.com/products/dotnet-development/reflector/ )

    - 가장 많이 사용되는 닷넷용 디컴파일러 / .net 다양한 언어 지원 / 다양한 플러그인 지원 / 유료(ㅠㅠ)

  

                                          [ 그림1 ] .net reflector 


 2) dotpeek ( 디컴파일러, http://www.jetbrains.com/decompiler/ )

    - 최근에 알게된 닷넷용 무료(!) 디컴파일러 / 깔끔한 인터페이스 / 아직은 플러그인이 별로 없는 것이 단점

                                                  [ 그림 2 ] dotpeek

 


# 닷넷 reflector 유용한 필수 플러그인


 1) Reflexil

    - MSIL 언어 수정을 가능하게 해주는 플러그인 

    - MSIL 언어를 알아야함 =_=/ 아니면 소스 받아서 컴파이를 아예 다시 하거나~! 


 2) deblector 

    - 닷넷 디버깅 도우미(?)

    - 리플렉터 상에서 디버깅을 하며 값 변경, 셋팅 등 일반적인 디버거의 기능을 추가해줌





# 코드게이트 문제 풀이


                                                   [ 그림 3 ] 코드게이트 binary 100 문제


문제를 열어보면 닷넷파일이고, 숫자키를 입력하게 되어 있다. 전형적인 crackme or keygen 문제이다. 


닷넷이니 일단 디컴파일을 한 뒤 소스를 확인 해보면, 아래와 같은 비교 루틴을 찾을 수 있다.


TransFormable() 함수 내부를 살펴보면, 입력받은 키값이 16글자인지 비교한 뒤 , 입력받은 키값을 AES 함호화 하여


저장되어 있는 키값과 비교한다. 이 2 조건을 통과하면 정답을 출력하도록 되어 있다. 즉, 소스만으로 평문 키값을 알


아내긴 어렵다. 방법은 여러가지가 있겠지만, 여기선 플러그인을 활용하여 코드패칭을 하고 실행하여 바로 키값이 출


력되도록 할 것이다. 


[ 그림 4 ] 키값 비교 및 정답 출력 함수


[ 그림 5 ] 무조건 정답출력 루틴으로 가도록 코드 패칭 (  ceq -> cgt 로 조작 )


[ 그림6 ] 파일명을 우클릭하여 Save as 로 패칭된 파일 저장



[ 그림 7] 정답화면

 















이전글에 이어서 Windbg 기본 명렁어 정리 중입니다.


역시 뭐든 안하면 까먹어요...열심히 합시다!!




- dt : dump variable info

 

  dt [mod!]Name Field [Field]

 ex) dt ntdll!_PEB


 > dt !_PEB // PEB 구조체 확인


 > dt !_PEB @$peb  // 현재 프로세스 PEB 확인

  +0x000 InheritedAddressSpace : 0 ''

   +0x001 ReadImageFileExecOptions : 0 ''

   +0x002 BeingDebugged    : 0x1 ''

   +0x003 SpareBool        : 0 ''

   +0x004 Mutant           : 0xffffffff Void

   +0x008 ImageBaseAddress : 0x00400000 Void

   +0x00c Ldr              : 0x00241ea0 _PEB_LDR_DATA

   +0x010 ProcessParameters : 0x00020000 _RTL_USER_PROCESS_PARAMETERS

   +0x014 SubSystemData    : (null) 

   +0x018 ProcessHeap      : 0x00140000 Void

   +0x01c FastPebLock      : 0x7c9ad600 _RTL_CRITICAL_SECTION

   +0x020 FastPebLockRoutine : 0x7c931000 Void



 - du : dump unicode string

 

 > du 00401000   // 401000 유니코드



 - dd : dump


 > dd 0046c6b0 0046c6c4

0046c6b0  00000000 00000000 00000000 00000000

0046c6c0  00000000 00000000



 - s  : search (중요!)


 0:014> s -a 00e70000 10071000 cmd

00ea8251  63 6d 64 00 00 00 00 2e-65 78 65 00 00 00 00 2e  cmd.....exe.....

01f17152  63 6d 64 0d 0a 00 41 63-63 65 70 74 3a 20 61 70  cmd...Accept: ap

024f2d74  63 6d 64 2e 65 78 65 00-0d f0 ad ba 0d f0 ad ba  cmd.exe.........

02520d3c  63 6d 64 2e 65 78 65 00-00 00 00 00 31 00 00 00  cmd.exe.....1...

02742d74  63 6d 64 2e 65 78 65 00-0d f0 ad ba 0d f0 ad ba  cmd.exe.



  - !heap :  heap 메모리


 0:014> !heap -stat

_HEAP 00dc0000

     Segments            00000001

         Reserved  bytes 00010000

         Committed bytes 00007000

     VirtAllocBlocks     00000001

         VirtAlloc bytes 00dc0050

_HEAP 003b0000

     Segments            00000002

         Reserved  bytes 00110000

         Committed bytes 00072000

     VirtAllocBlocks     00000000





그동안 손 놓고 있던 시스템 쪽을 다시 열심히 하기로 마음먹었다. 까먹지 않게 하나씩 다시 정리해야지.


다 까먹었다....  : ( 





# Windbg 기본 #


- 장점 : MS지원(symbol 등) / 강력한 커널디버깅 / 익숙해질시 편함

- 단점 : UI 가 불편함 = 단축키 익숙해지면 편함. 하지만 자꾸 까먹음




1) .help - 도움말 / .hh(F1도움말)




2) 디버깅


  - .attach : attach

  - .restart : 어플 재시작

  - .detach : detach


  - ctrl+break : 명령 콘솔(중지)

  - g : 실행


  - lm : list module   

0:014> lm

start    end        module name

00380000 00389000   Normaliz   (deferred)             

00400000 004be000   image00400000   (deferred)             

00ba0000 00ca3000   comctl32_ba0000   (deferred)             

00e70000 00f0f000   MSRMfilter01   (deferred)        


  - !dmi : 모듈 상세정보


0:014> !lmi dnsapi

Loaded Module Info: [dnsapi] 

         Module: DNSAPI

   Base Address: 76ed0000

     Image Name: C:\WINDOWS\system32\DNSAPI.dll

   Machine Type: 332 (I386)

     Time Stamp: 485bec2a Sat Jun 21 02:43:06 2008

           Size: 27000

       CheckSum: 24da0 


 -  .tlist : tasklist

 

 - !peb : process's environment block


 - !teb : thread's environment block


 - ~ : thread list


 - bl : list breakpoints

 

 - bc * : clear all br


 - be # : enable bp #

 

 - bd # : disable bp #


 - bp [addr] : set br at addr


 - ba [r|w|e] [size:1|2|4] addr : 특정 메모리 읽기/쓰기/실행 시 stop 

   ex) ba r4 7c9311dd



3) 팁


  - 코드찾기

   > s [시작주소] l [범위] [코드] [코드] [코드]


 ex) 

0:014> s 01c20000 l 9000 58 5b c3

01ee7f03  58 5b c3 21 4e 90 a0 b4-6e 34 c3 b5 3e 65 c4 02  X[.!N...n4..>e..

0:014> u 01ee7f03




+ Recent posts