本帖最后由 零度x 于 2014-1-16 11:42 编辑
盛大69棋牌游戏木马分析.rar
(823.61 KB, 下载次数: 76)
首先我们先来看一下这个游戏的加载方式,这个游戏是用ADOBE的AIR技术开发的
可以看到,游戏安装完成后核心是这3个文件,其中DeskPokerLoad.exe是主程序,运行后会调用游戏目录下的69game\AdobeAIR\Versions\1.0\Adobe AIR.dll,这个DLL会加载DeskPokerLoader.swf这个文件,这个文件会加载DeskPokerLoader.swf这个SWF文件,这个SWF只是个加载器,我们来看下反编译代码
上面标记箭头那个就是加载完成的回调函数,我们跟进去看一下 可以看到,这里运用了一个AS3的第三方库hurlant 里的DES算法去解密密钥是
,官网在这里
这里解密完了的数据在_loc_2里面,我们可以自己写代码等它解密完后把它保存一下 这里我们使用FLASHDEVELOP,当然这个软件只是个IDE,你要自己去下SDK和FLASHC CS 版本无所谓,一定要建AIR工程,不然不能使用文件保存功能,这里是是完整的代码 [Actionscript3] 纯文本查看 复制代码 package
{
import flash.display.Sprite;
import com.hurlant.crypto.symmetric.*;
import flash.events.*;
import flash.net.*;
import flash.utils.*;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
/**
*...
*@author 004
*/
public class Main extends Sprite
{
public static var swf_name:String ="DeskPoker.swf";
private var key:String ="activetuts";
private var urlload:URLLoader;
//=============================写入函数
public static function writeAppFile(fileName:String,datas:ByteArray):void{
var file:File = newFile(File.applicationDirectory.resolvePath(fileName).nativePath);
var fs:FileStream = new FileStream();
fs.open(file,FileMode.WRITE);
fs.position = 0;
fs.writeBytes(datas);
fs.close();
}
public function Main():void
{
this.urlload = new URLLoader();
this.urlload.dataFormat =URLLoaderDataFormat.BINARY;
this.urlload.addEventListener(Event.COMPLETE,this.loadLocalSWFComplete);
var _loc_1:* = newURLRequest(swf_name);
_loc_1.cacheResponse = false;
_loc_1.useCache = false;
this.urlload.load(_loc_1);
} //==========================加载完成回调 [Actionscript3] 纯文本查看 复制代码 private functionloadLocalSWFComplete(event:Event) : void[/align] {
var _loc_2:* = this.urlload.data;
var _loc_3:* = new ByteArray();
_loc_3.writeUTF(this.key);
var _loc_4:* = new DESKey(_loc_3);
var _loc_5:* = _loc_2.length &~127;
var _loc_6:* = 0;
while (_loc_6 < _loc_5)
{
_loc_4.decrypt(_loc_2, _loc_6);
_loc_6 = _loc_6 + 128;
}
this.urlload.removeEventListener(Event.COMPLETE, this.loadLocalSWFComplete);
this.urlload.close();
writeAppFile("C:\\Decode.swf",_loc_2);
}
}
} //======================================= 我们把DeskPoker.swf拷贝到和编译好的解码SWF下的BIN同一个目录,然后对这个函数下断点单步看看 可以看到这里循环文件的大小进行相对解密,然后我们就直接把解密后的主SWF抠出来了 //==================== 讲完解密后我们在来看看这个程序的登录方式 和所有程序一样嘛,我们来找按钮事件可以看到舞台里有个这个和登录界面一样
我们记住这个loginform然后在代码里去找 然后我们对这个类名查找所有引用得到这个 然后我们再对this.lf查找引用得到这句,这个是给按钮增加一个单击事件,我们来到onLogin这个函数 我们略掉一些代码看核心的 这里可以看到,取回用户名和密码还有验证码之后广播了一个login_tocom_enter消息,我们跟到消息处理函数 可以看到这里调用HTTP发送了一个tc_ask_mibao,tc_ask_mibao定义为 我们对程序抓包看一下,这是第一个包 [C++] 纯文本查看 复制代码 POST/G69LoginAPI/wechat/getlogintcard.jsp HTTP/1.1..Referer:app:/DeskPokerLoader.swf/[[DYNAMIC]]/1..Accept: text/xml, application/xml,application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, text/css, image/png,image/jpeg, image/gif;q=0.8, application/x-shockwave-flash, video/mp4;q=0.9,flv-application/octet-stream;q=0.8, video/x-flv;q=0.7, audio/mp4,application/futuresplash, */*;q=0.5..x-flash-version: 11,1,102,58..Content-Type:application/x-www-form-urlencoded..Content-Length: 19..Accept-Encoding:gzip,deflate..User-Agent: Mozilla/5.0 (Windows; U; zh-CN) AppleWebKit/533.19.4(KHTML, like Gecko) AdobeAIR/3.1..Host: login.game69.com:8080..Connection:Keep-Alive....param=sfz009910&t=0 //============ 然后它的HTTP发送类注册了一个回调函数用来接收返回的包 这里大概是根据返回包的xyh来进行操作 比如这里返回的是 {"res":1,"xyh":20006} 查找下getMibaoRe这个消息 这里res=1 这个就是第二个包 好了,类似于这样,节省篇幅,我们直接跳到密码那里 可以看到发送密码的方式是这样,把密码机上jym这里是从服务器返回的一串数字,类似于这样"jym":"958184970",比如我密码是12345就是12345958184970,然后传给MD5进行加密后传到服务器,这里大概原理讲得差不多了,我们来看一下木马 这里比原版多了一个DLL文件,可以看到,这里的2个SWF文件都成一样了,其实就是木马作者把原版SWF解密出来后把原来的加载SWF替换成主SWF了,这里木马盗取密码的方式是这样的,修改原版的SWF文件,在发送密码+JYM组成的MD5的时候改成这样了
右边那个是木马的修改版,可以看到直接返回了,我们再来看看那个DLL文件
可以看到它HOOK了3个函数,我们先来看看send函数,首先判断方法 这里我们直接看登录页面
这里就提取数组了,我们看看原版的包是怎样的 [C++] 纯文本查看 复制代码 POST/G69LoginAPI/login.jsp HTTP/1.1..Referer:app:/DeskPokerLoader.swf/[[DYNAMIC]]/1..Accept: text/xml, application/xml,application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, text/css, image/png,image/jpeg, image/gif;q=0.8, application/x-shockwave-flash, video/mp4;q=0.9,flv-application/octet-stream;q=0.8, video/x-flv;q=0.7, audio/mp4,application/futuresplash, */*;q=0.5..x-flash-version: 11,1,102,58..Content-Type:application/x-www-form-urlencoded..Content-Length: 114..Accept-Encoding:gzip,deflate..User-Agent: Mozilla/5.0 (Windows; U; zh-CN) AppleWebKit/533.19.4(KHTML, like Gecko) AdobeAIR/3.1..Host: login.game69.com:8080..Connection:Keep-Alive..Cookie:JSESSIONID=7349A2008A40E3D2D3E41AF5CED014E1....type=1&pw=ce5d8df64486b34bfa0bb75ca16c429f&yzm=%E8%AF%B7%E8%BE%93%E5%85%A5%E9%AA%8C%E8%AF%81%E7%A0%81&un=sfz009910 但被木马修改版的就成这样了 [C++] 纯文本查看 复制代码 type=1&pw=12345958184970=%E8%AF%B7%E8%BE%93%E5%85%A5%E9%AA%8C%E8%AF%81%E7%A0%81&un=sfz009910 //========= 这里木马扣除密码后,再根据原版密码和那个JYM加密成MD5装到包里发送给服务器还会在本地保存一份 然后就会给服务器发消息了,调用这个函数 格式化是这样的 这样吧,我们动态调试一下,首先对HOOK的SEND下个断点,再对格式化这个函数下个断点,可以看到这种,这里那个扫描8T1L的好像是计算机名密码和用户是出来了, 再F9一下 可以看到多了一串32位的MD5,这串MD5是怎么来的呢,是它自己生成的 [C++] 纯文本查看 复制代码 typedefstruct {
ULONG i[2];
ULONG buf[4];
unsigned char in[64];
unsigned char digest[16];
} MD5_CTX;
typedef void(CALLBACK* MD5Init_Tpye)(MD5_CTX* context);
typedef void(CALLBACK* MD5Update_Tpye)(MD5_CTX* context, unsigned char* input, unsigned int inlen);
typedef void(CALLBACK* MD5Final_Tpye)(MD5_CTX* context);
int main(intargc, char* argv[])
{
HINSTANCE hDLL =LoadLibrary("advapi32.dll");
MD5Init_Tpye MD5Init;
MD5Update_Tpye MD5Update;
MD5Final_Tpye MD5Final;
MD5Init =(MD5Init_Tpye)GetProcAddress(hDLL, "MD5Init");
MD5Update =(MD5Update_Tpye)GetProcAddress(hDLL, "MD5Update");
MD5Final =(MD5Final_Tpye)GetProcAddress(hDLL, "MD5Final");
MD5_CTX md5_context;
MD5Init(&md5_context);
unsigned char src[100];
unsigned length = strlen(argv[1]);
memcpy(src, argv[1], length);
MD5Update(&md5_context, src, length);
MD5Final(&md5_context);
char dest[100] = { 0 };
char *p = dest;
for(int i = 0; i < 16; ++i)
{
sprintf(p,"%02x",md5_context.digest);
p += 2;
}
cout << dest << endl;
return 0;
} //============ 用的是这个方法,那么生成这串MD5的是什么东西呢,这里第一次其实是空的没啥用,要知道是干嘛用的我们还要首先说一下,棋牌游戏都有个绑定机器的功能,我们让它进入游戏后,看一下
有个绑定按钮,我们按一下可以看到马上就断下了 可以看到这个MD5变了,这究竟是为什么呢?给我捐款我就告诉你 - -,抱怨一下,好了,我们想要知道为什么肯定就要去找源码来看看, 期间步骤我就不说了,直接来到关键点,发送绑定消息后服务器会返回一个一个code什么什么,可以看到这里把返回的这个CODE保存了, 看一下登录的时候有个 这里的意思就是获取MAC加上什么东西然后加密成MD5发送,可是this.getmac()这个函数是个空函数,那么完全就依靠后来的readfile函数了, 等于读取的就是之前服务器返回的那串,这下知道木马为什么截取这个MD5了吧, 这里我们玩玩谢幕游戏,根据DLL里的什么OutputDebugStringA("yushaowoshimima");可以社工下,其实这些无聊可以不看了 查下它服务器的IP 然后我们扫描一下 但我这个是内网,具体成功没我也不知道没去试了,好了这个样本我就不上传,以防被别人拿去用。 |