안드로이드 해킹 실습 (2)

By | 2013년 11월 22일

이 문서는 어디까지나 스터디의 목적으로 작성되었습니다. 악용하였을때 받는 불이익 및 피해는 책임지지 않습니다.

1부에서 살펴보았던 소스 기반으로 실제 통신 할 수 있는 외부 프로그램을 구성해보자.

실제 사용되는 모듈은 base64, md5, http, json같이 간단한 상황이니 쉽게 테스트 할 수 있는 스크립트 언어로 작성하는게 편할것이다.

해서 난 php스크립트를 이용해서 작성했음. 멀로 해도 상관은 없..

먼저 간단한 암복호화와 http통신을 위한 함수이다.

<?php
$xor_table = array(
        1, 0x62, 0x36, 0x6a, 0x6a, 0x38, 0x75, 15, 0x4c, 0x40, 0x4c, 0x6a, 0x3d, 0x3f, 0x36, 0x57, 
        0x23, 0x44, 0x2f, 0x71, 1, 0x6d, 0x4c, 110, 0x61, 0x57, 1, 0x62, 0x65, 0x6d, 6, 0x7c, 
        0x6a, 0x61, 0x65, 0x5b, 0x63, 0x2b, 0x4d, 0x24, 0x67, 0x49, 0x24, 11, 0x1a, 0x3e, 0x2a, 0x39, 
        0x43, 100, 0x24, 0x49, 0x4a, 0x6f, 0x5f, 60, 0x11, 0x2f, 0x3b, 0x7b, 0x5c, 0x23, 0x53, 0x4f, 
        0x1a, 0x24, 0x24, 4, 10, 0x63, 120, 0x73, 0x4c, 0x56, 100, 0x40, 13, 0x6f, 0x7f, 0x7b, 
        0x70, 0x39, 0x7b, 15, 0x4f, 0x13, 0x13, 0x27, 0x6a, 2, 0x27, 0x1f, 0x29, 0x56, 0x27, 11, 
        0x5b, 0x1c, 0x31, 0x60
);

// 암호화
function encode($param) {
    global $xor_table;
    $encode = $param;
    for ($i=0; $i<strlen($param); $i++) {
        $key = $xor_table[$i%count($xor_table)];
        $encode[$i] = chr($key^ord($param[$i]));
    }
    return md5($param).base64_encode($encode);
}

// 복호화
function decode($encode) {
    global $xor_table;
    $decode = base64_decode($encode);
    for ($i=0; $i<strlen($decode); $i++) {
        $key = $xor_table[$i%count($xor_table)];
        $decode[$i] = chr($key^ord($decode[$i]));
    }
    return $decode;
}

// http통신
function push($host, $path, $post) {
    $header = array();
    $header[] = "POST ".$path." HTTP/1.1";
    $header[] = "Content-Type: application/x-www-form-urlencoded";
    $header[] = "User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; SHV-E210S Build/JZO54K)";
    $header[] = "Host: monster-server.netmarble.net";
    $header[] = "Connection: Keep-Alive";
    $header[] = "Accept-Encoding: gzip";
    $header[] = "Content-Length: ".strlen($post);

    $out = join("rn", $header)."rnrn";

    if($post) $out .= $post."rnrn";
    $body = "";
    $head = "";
    $fp = fsockopen($host, 80, $errno, $errstr, 30);
    usleep(50);
    if($fp) {
        socket_set_timeout($fp, 30);
        fwrite($fp, $out);
        $is_body = false;
        $is_chunk = false;
        $chunk_size = 0;
        $body_size = 0;
        while(!feof($fp)) {
            $buffer = fgets($fp, 128);
            if ($is_body) {
                if ($is_chunked) {
                    if ($chunk_size) {
                        $body .= $buffer;
                        $current_size += strlen($buffer);
                        if ($current_size >= $chunk_size) {
                            $chunk_size = hexdec($buffer);
                            $current_size = 0;
                            next;
                        }
                    }
                } else {
                    $body .= $buffer;
                }
            } else {
                if($buffer=="rn") {
                    $is_body = 1;
                    $current_size = 0;
                    if ($is_chunked) {
                        $chunk_size = hexdec($buffer);
                    }
                } else {
                    $is_chunked = (strpos($head, "chunked")!==false);
                    $head .= $buffer;
                }
            }
        }
        fclose($fp);
    } else {
        echo "error";
    }

    return array($head, $body);
}

// json문자열에서 값추출
function jsonval($json, $key) {
    $pos1 = strpos($json, ""$key":");
    $val = false;
    if ($pos1!==false) {
        $pos1 += strlen(""$key":");
        $pos2 = strpos($json, ",", $pos1);
        $val = substr($json, $pos1, $pos2-$pos1);
        if ($val[0]=='"') {
            $val = substr($val, 1, strlen($val)-2);
        }
    }
    return $val;
}
?>

encode, decode는 원래 소스의 암복호함수를 그대로 포팅한거고 push는 http post요청과 응답을 받기위한 함수이다. 쓸데없이 긴데 php의 http api를 써도 무방하다. 여타 스크립트도 어지간하면 http client기능은 구현되어있으니 그냥 편할걸로 쓰면되겠다.

jsonval도 단순하게 json문자열에서 값을 가져오는 함수이다. 역시 json api를 써서 가져와도 상관없겠다.

이 함수들을 이용하여 간단하게 로그인 요청을 하고 응답값을 받는 코드를 작성해보자.

<?php
//login
$path = "/SR/login";
$param = "userid=884567053453453&img_url=http://th-p0.talk.kakao.co.kr/th/talkp/23f23f23f/weff223f2/f23f32f323f.jpg&ver=1.25&platform=Android&cookie=527c2196_7f3036e69a2ce3ce4c33b1681d33061c_8d2401853135d705ab5c63cc7668be28";
$post = "epp=".urlencode(encode($param));

list($head, $body) = push("xxxxx-server.xxxxxxxx.net", $path, $post);
echo $head;
$output = decode($body);
echo $output;
?>

당연히 $param 안에 들어간값은 다들 다를것이다. 저 $post에 들어가는 값이 1부에서 request값이다. url decoding 해서 decode했었으니 여기선 거꾸로 encode후 url encoding.

unity3d 소스에서 요롷코름 암호화해서 서버로 보내는것이겠다. $output이 정상적으로 출력되면 일단 로그인은 된것이다.

$param의 변수값을 일일히 분석하지 않아도 변수명만 봐도 대충 짐작이 가능할 것이다.

userid는 아이디겠고 (카톡게임이니 카톡아이디겠다..) img_url은 직접 브라우저에 띄워보면 알겠지만 카톡이미지다. ver는 빌드버전. platform은 Android이다.

cookie라는게 조금 복잡한데.. 이것은 추후에 자세히 설펴보도록 하자.

—————————참고용 node.js 소스추가

var util = require('util');
var querystring = require('querystring');
var crypto = require('crypto');
var http = require('http');

var xor_table = [
    1, 0x62, 0x36, 0x6a, 0x6a, 0x38, 0x75, 15, 0x4c, 0x40, 0x4c, 0x6a, 0x3d, 0x3f, 0x36, 0x57, 
    0x23, 0x44, 0x2f, 0x71, 1, 0x6d, 0x4c, 110, 0x61, 0x57, 1, 0x62, 0x65, 0x6d, 6, 0x7c, 
    0x6a, 0x61, 0x65, 0x5b, 0x63, 0x2b, 0x4d, 0x24, 0x67, 0x49, 0x24, 11, 0x1a, 0x3e, 0x2a, 0x39, 
    0x43, 100, 0x24, 0x49, 0x4a, 0x6f, 0x5f, 60, 0x11, 0x2f, 0x3b, 0x7b, 0x5c, 0x23, 0x53, 0x4f, 
    0x1a, 0x24, 0x24, 4, 10, 0x63, 120, 0x73, 0x4c, 0x56, 100, 0x40, 13, 0x6f, 0x7f, 0x7b, 
    0x70, 0x39, 0x7b, 15, 0x4f, 0x13, 0x13, 0x27, 0x6a, 2, 0x27, 0x1f, 0x29, 0x56, 0x27, 11, 
    0x5b, 0x1c, 0x31, 0x60
];

function base64_encode(param) {
    var b = new Buffer(param);
    return b.toString('base64');
}

function base64_decode(param) {
    var b = new Buffer(param, 'base64');
    return b.toString();
}

function md5(param) {
    var md5hash = crypto.createHash('md5');
    md5hash.update(param);
    return md5hash.digest('hex');
}

function encode(param) {
    var buf = new Buffer(param.length);
    for (var i=0; i<param.length; i++) {
        key = xor_table[i % xor_table.length];
        buf[i] = param.charCodeAt(i);
        buf[i] = key ^ buf[i];
    }
    return md5(param)+base64_encode(buf.toString());
}

function decode(param) {
    var decode = base64_decode(param);
    var buf = new Buffer(decode.length);
    for (var i=0; i<decode.length; i++) {
        key = xor_table[i % xor_table.length];
        buf[i] = decode.charCodeAt(i);
        buf[i] = key ^ buf[i];
    }
    return buf.toString();
}

function server_push(host, path, post, callback) {
    var options = {
        hostname: host,
        port: 80,
        path: path,
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': post.length,
            'User-Agent': 'Dalvik/1.6.0 (Linux; U; Android 4.1.2; SHV-E210S Build/JZO54K)',
            'Host': 'monster-server.netmarble.net',
            'Connection': 'Keep-Alive',
            'Accept-Encoding': 'gzip'
        }
    };

    var data = '';
    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function() {
            callback(false, data);
        });
    });

    req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
        callback(true, e.message);
    });

    req.write(post);
    req.end();
}

var srlib = {
    debug: false,
    server: function(action, param, callback) {
        var path = '/SR/' + action;
        var query = querystring.stringify(param);
        var post = "epp="+encodeURIComponent(encode(query));
        if (srlib.debug) console.log(query);
        server_push("xxxxx-server.xxxxxxxx.net", path, post, function(error, result) {
            if (error) {
                callback(false, {error:result});
                return;
            }

            var retval = '';
            var retobj = {};
            try {
                retval = decode(result);
                retobj = JSON.parse(retval);
            } catch (e) {
                console.log('server-push-exception');
                console.log(e.toString());
                console.log(result);
                callback(false, {err:-2, reason:'script-exception'});
                return;
            }
            if (!retobj) {
                callback(false, {err:-2, reason:'connect-error'});
                return;
            }
            if (retobj.err) {
                callback(false, retobj);
                return;
            }

            callback(true, retobj);
        });
    },
    login: function(param, callback) {
        srlib.server('login', param, callback);
    },
    startgame: function(param, callback) {
        srlib.server('startgame', param, callback);
    },
    gameover: function(param, callback) {
        srlib.server('gameover', param, callback);
    },
    getdropitem: function(param, callback) {
        srlib.server('getdropitem', param, callback);
    },
    getdungeoncleartime: function(param, callback) {
        srlib.server('getdungeoncleartime', param, callback);
    },
};

module.exports = srlib;

8 thoughts on “안드로이드 해킹 실습 (2)

  1. AGKeiz

    p…php군요..

    웹언어는 문외한인지라 여기서부턴 이런게 있구나 정도로 해석밖에 안되겠네요ㅠ.. 따라해보는게 재미있는데..

    이번 포스트도 잘보고갑니다~!

    Reply
    1. cook Post author

      사실 어떤 언어로 해도 상관없어요~ 단지.. c에서는 http 통신을 구현하는게 조금 귀찮(?)을 수 있겠지요

      예제처럼 직접 소켓열어서 구현해도되고.. 좀더 쉽게 윈도우모듈인 wininet을 써서 구현해도되고요~

      Reply
  2. AGKeiz

    여기서 서버에 신호를 주고 응답값을 받는다 하셨는데

    이 응답값이 의미하는게 세션아이디인가요?

    Reply
    1. cook Post author

      응답값은 1부에서 RESPONSE라고 지칭한 부분이에요.

      이건 http 통신에 대해서 조금 찾아보시면 쉽게 이해하실거에요~

      Reply
  3. AGKeiz

    php소스를 해석해보고 합쳐다가 제 개인서버에 업로드 해서 구동했으나 실패.

    혹시나 싶어 base64_decode.js, base64_encode.js md5.js json.js 등을 구해서 같이넣어도실패,,

    에구.. 웹쪽은 이해하는정도로 만족해야할거 같습니다ㅎㅎ;

    내일 wininet에 대해 찾아보고 공부해보고 오겠습니다ㅎ

    Reply
  4. lovevirus

    부탁이 있습니다. 미국에서 출시된 안드로이용 온라인 게임인데, 아이템 해킹을 해서 숫자를 늘리고 싶습니다. 그런데, 워낙 초보라, 도와주실 순 없으신가해서요.

    Reply
  5. tjruddhks

    제가 안드로이드 해킹에대해 관심이 좀 많은데…..

    카톡으로 도와주실수 있으시나요? 자바랑 그런거 모르는데

    귀찬으셔도 도와주셨으면 합니다.

    카톡아이디:01024426994

    아니면 카톡 아이디 주시면 제가 친추 걸겠습니다.

    Reply
  6. jjangser09

    안드로이드해킹에 대해 알어보고 알어보다 여기까지 오게되었습니다
    좀 도와주세요 부탁드려요
    카톡 아이디 jjangser09 입니다
    제발도와주세요 부탁드려요

    Reply

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다