Linux下编译WebKit和JSC

Safari和Chrome的内核都是webkit,无论是打算自己开发个浏览器还是在程序里集成完整的HTML解析功能,webkit都是为数不多的选择。特别是webkit分支中的jsc,可以命令行下解释执行javascript,真是想想就让人亢奋的玩物。编译环境选择的是Ubuntu 15.04 x86_64,由于玩心太重,所以首先考虑把jsc编译出来。

下载代码

有大概三种途径弄到代码,git,svn和直接下载tar.xz,我只试了后面两种

svn checkout https://svn.webkit.org/repository/webkit/trunk webkit

或者从webkitgtk直接下载tar.xz

准备编译环境

执行webkit/Tools/gtk/install-dependencies可以安装大部分缺失的库代码,另外再手动补下刀

sudo apt-get install libgstreamer*

其余的库要是还缺,apt-cache search+apt-get install 缺啥补啥吧

编译

如果下载的tar.xz的话,参考linuxfromscratch进入webkit目录执行以下代码,编译好的jsc位于./build/bin/jsc

sed -e 's/“/"/' -e 's/”/"/' 
    -i Source/WebCore/xml/XMLViewer.{css,js} &&

mkdir -vp build &&
cd        build &&

cmake -DCMAKE_BUILD_TYPE=Release 
 -DCMAKE_INSTALL_PREFIX=/usr 
 -DCMAKE_SKIP_RPATH=ON 
 -DENABLE_GEOLOCATION=OFF 
 -DPORT=GTK 
 -DLIB_INSTALL_DIR=/usr/lib 
 -DUSE_LIBHYPHEN=OFF 
 -DSHARED_CORE=OFF 
 -DCMAKE_C_COMPILER=/home/jack/afl/afl-gcc 
 -DCMAKE_CXX_COMPILER=/home/jack/afl/afl-g++ 
 -DBUILD_SHARED_LIBS=OFF 
 -DENABLE_MINIBROWSER=ON 
 -Wno-dev .. &&
make -j10

如果是svn得到的代码,进入webkit目录后执行编译脚本得到./WebKitBuild/Release/bin/jsc

./Tools/Scripts/build-jsc --gtk --makeargs="-j10"

WordPress自动安装本地升级包

自从安装了WordPress,升级工作一直不太顺利。本地搭建的LAMP环境测试没问题,但虚拟空间真刀真枪的时候就每每提示自动升级失败。试过手动升级,小心地覆盖各个文件夹时候心揪的稀碎啊,特别是WordPress不解风情地老推送升级包过来。

社区讨论最多升级失败都归咎于文件夹权限没设置好。但我这个调来调去发现就是虚拟主机访问WordPress资源站点,耽搁的太久,超时带来的副作用。

所以自己走VPN下载升级包是在所难免了。不过比起手动升级,把升级包下载到站点目录,然后让WordPress自动升级还是能省不少心,也就是自动安装本地升级包。唯一要做的就是稍微hack一下升级代码。以升级WordPress 4.4为例,修改代码示例:

// .wp-adminincludesclass-wp-upgrader.php

public function download_package( $package ) {
  ...
  //d:\www\root\xxxwordpress-4.4-zh_CN.zip是升级包传到虚拟主机后的绝对路径
  if ($package == "https://downloads.wordpress.org/release/zh_CN/wordpress-4.4.zip")
    $download_file = "d:\wwwroot\xxx\wordpress-4.4-zh_CN.zip";
  else
    $download_file = download_url($package);
  ...
}

武侠梦

大学起的生活很快让看电视节目成了再没真正拾起的旧习。可有闲有幻想的中学时代里那些金庸剧中的刀光剑影,仍时不时闪回在生活的很多瞬间。

下班路上,会兴奋地抓起着雪,攥成球后飞掷向敌方头目(路牌和栏杆);跆拳道课后的回家途中,要是遇到一段人少的小巷,会情不自禁地突展手臂企图用六脉神剑的光影照亮前方;又或是赶上大风天肆虐,有多少次幻想着用自己的掌风与之相抵;出门晚了的话,走的一快,深呼吸间就开始尝试施展凌波微步,哦,施展不成功~基本身法等级不够,还没做拿武学图谱的任务呢。

2000年算得上是网游元年,《石器时代》、《传奇》等屈指可数的网络游戏招揽了绝大部分有志投身互联网娱乐行业的莘莘学子们。我那会儿就用《金庸群侠传online》(网金)杀过时间。初一下学期开始,玩了快两年,什么点卡、道具、外挂、代练能买的都买了。最后半年,第一次听到其他玩家劝说:“如胶似漆,莫做固陋井底之蛙;回头是岸,迈向精彩大千世间”时开始萌生了去意(这句话放在当年其实一点不突兀,很多玩家多少有点古诗文言文的情节。客观原因是古风古气的游戏作派,而且金庸老前辈是那会儿中学生心目中深厚文字功底的代表,主观原因是经常为了考试题目背对联、背古文)。直到有一次琢磨着要汇款1000给代练公司的时候,突然虎躯一震,意识到大势已去,网金的故事已经跑偏了,该撤了。得益于那段传奇经历,今天对更为粘人的页游手游都不太感冒。

如果说网金提供了在虚拟世界做大侠的平台,游戏外挂就称得上是现实世界中的让你梦想成真的绝世武功。那会儿的神行太保、金庸通天、网金游侠,网金也疯狂等外挂都能让你手中虚拟人物在地图上快速飞跃,自动练习伐木、采矿等铸造神兵利器的技能,自动遇敌战斗获取学点,自动跟掌门学习武功,跨越游戏的限制任意驰骋。耗时乏味的练级时间全部可以让外挂代劳。外挂能通过挂钩游戏进程后强制执行特定代码片段跳过部分耗时操作,或按照游戏协议发送网络封包给服务器,直接完成目标任务。外挂开发者对于游戏代码的逆向分析几乎达到了完备的状态,每个NPC的编号、武功、属性依赖、任务条件、地图、技能的练习方法、哪些游戏规则可以适当逾越、哪些就要严格遵守才能不被封号以及游戏和服务器的通信的协议等。虽然那会儿也想自己写一个或者绕过验证免费用人家的外挂,但实在知之甚浅、无从下手。

十五年过去了,网金居然仍在线运营,外挂们也是一路相伴。就在初冬的一个下午,我闪身回眸间,又望见了尘封十五年的往事。加上最近脚扭伤了,没法去上跆拳道课,就重温了一下网金。这次打算好好折腾一下外挂,跨越时空为那个小小少年圆个梦。


网金游侠是一个仍然活跃的外挂,最近一次更新是在2015-7-28。除了能够快速飞跃、自动战斗、自动练习技能等一系列必不可少的元素,它甚至支持玩家通过自行编写lua脚本的方式扩展外挂的功能,完成自定义任务。构成网金游侠的主要文件包括GMK3.0.exe的外挂主控程序,IME32.dll的内挂代码以及很多包含NPC、物品、地图相关编号的文档。

购买了外挂的玩家运行GMK3.0.exe,填写自己的游戏账号,利用外挂启动游戏的主程序时,外挂会将内挂代码IME32.dll远程注入到游戏内存。IME32.dll载入后会修改游戏相关代码,挂钩特定函数。玩家正式登陆自己的游戏账号后,IME32.dll会根据登陆后的账号判断玩家是否注册了外挂,如果是已经注册的玩家,外挂会在游戏中创建一个辅助窗口供外挂参数调整。如果没有注册,就不会有界面弹出来让玩家使用外挂控制游戏。


最初的绕过思路是打算彻底摸清账号的验证方法,然后进行代码patch或者修改发送的验证包或者伪造服务器返回验证通过的数据包。GMK3.0和IME32.dll的外在表现形式不同,但和外挂服务器通信验证的协议是一样的。通过带有StrongOD的插件的OD避开检测,挂钩GMK的send和recv函数是可以看到验证数据的真身的。发送验证和服务器回复都是单次通信。想来即便不能解析其含义,用合法包替换也肯定能搞定。后来试了几次,发现即便相同包重放,返回结果都不一样,看来至少是加入了秒级别的时间戳,不分析算法是没法伪造了。本以为时至今日已经可以轻易干掉外挂的验证了。残酷事实是,逆向分析的能力确实相比那会儿的零有了提升,但外挂的保护方式也在生根发芽,直到今天我仍然不具备正面进攻的本事。

正面突破时最大的困难还是无法还原Themida虚拟机指令,所以外挂验证账号的数据包过程无从知晓。

虽然绕过验证是完美的解决方案,但想使用外挂功能也并不是就这么一条路。外挂启动游戏后除了注入了IME32.dll,还顺带注入了lua.dll用于执行自定义的lua脚本。外挂允许lua脚本可以调用的API多达150个,如:

void CallBoss(int nBoss)
功 能:呼叫商人
参 数:nBoss 商人编号
返回值:
描 述: 需在商人附近
--------------------------------------------------------------
void XiaoDian(int nMaster,int nKunfu,int nPoint)
功 能:消点
参 数:nMaster:师傅,nKunfu:武功,nPoint:所消学点
返回值:
描 述: 
--------------------------------------------------------------
void Buy(int nItem,int nPrice,int nCount)功 能:购买物品
参 数:nItem:物品编号 ,nPrice:物品价格 ,nCount:购买个数
返回值:
描 述:需先呼叫NPC,并正确填写购买价格
--------------------------------------------------------------
int BatConfig(int npc,int count,int delay,int grid,int speed,int timeout)
功 能:配置战斗信息
参 数:
返回值:
描 述: 
--------------------------------------------------------------
void BatInit()
功 能:初始化战场
参 数:
返回值:
描 述: 
--------------------------------------------------------------
void Log(char *str) 
功 能:输出文字
参 数:字符串
返回值:
描 述: 输出文字信息
--------------------------------------------------------------
void MoveTo(int map,int x,int y) 
功 能:移动到其他地图
参 数:map:目标地图,x:坐标y:坐标
返回值:直到移动完成
描 述: 角色移动到(其他图中)指定的坐标X,Y

自己写一段lua调用这些API就能利用外挂自动化完成很多操作,比如下面的代码就能自动练武功:

local XDEvt = 2; -- 消点事件
local XDMap = 1343; -- 消点地点
local XDX = 234;
local XDY = 278;
local XDMaster = 39778; -- 师傅ID
local XDKunfuID = 8002; -- 武功ID
function DoXiaoDian()
  if RoleXD() < 100 then
    return;
  end 
  if RoleXD() <= 10 then
    Log("Point is 10,n");
  return; 
  end
  if RoleJing() <= 10 then
    MoveTo(66,555,555)
  DoReset(3);  
  end 
  if RoleMap() ~= XDMap then
    MoveTo(XDMap,XDX,XDY); -- 移动到目标点
  end 
  EventID(XDEvt); -- 呼叫师傅事件
  Delay(1000);
  VerifyCode(); -- 效验码
  Delay(1000);
  XiaoDian(XDMaster,XDKunfuID,1); -- #8001:基本刀法100
  Delay(2000);
end
DoXiaoDian();

通过强制修改内存的方法做了一些初步的尝试,lua的接口无需外挂验证账号也是可以照常使用的。而且lua.dll并没有使用Themida进行虚拟机级别的保护,稍加分析可以发现这就是lua源码直接编译的结果,只不过版本号被删掉了。遂从lua的官网把所有版本的bin都下载下来,然后和外挂使用的lua.dll进行文件大小、输出函数、代码片段的比对,基本可以把范围缩小为VS2010编译的lua 5.2。下载了5.2的源码后,自行编译一个lua.dll进行替换,外挂运行正常。

IME32.dll中只有和账户验证相关的敏感代码进行了虚拟机保护,直接dump内存得到的bin中,即便没有修复地址,用IDA分析时仍然可以找到初始化lua库以及注册自定义API的过程:

int sub_774FA140()
{
  int i; // [sp+0h] [bp-4h]@1
  for ( i = 0; (&off_775CA7E8)[8 * i]; ++i ) // 循环注册lua脚本使用的API
    sub_77515540(0x2F57100, (int)(&off_775CA7E8)[8 * i], (int)*(&off_775CA7EC + 2 * i));
  return sub_774F6F50(sub_774D9950);
}

char __thiscall sub_77515540(int this, int a2, int a3)
{
  int v3; // ST0C_4@1
  v3 = this;
  // 注册自定义的API供lua调用
  lua_pushcclosure(*(_DWORD *)(this + 8), a3, 0); 
  lua_setglobal(*(_DWORD *)(v3 + 8), a2);
  return 1;
}

其中off_775CA7E8就是API的列表,每个API的名称和入口函数地址都清晰可见

CODE:775CA7E8 off_775CA7E8    dd offset aMainpath     ; "MainPath"
CODE:775CA7EC off_775CA7EC    dd offset sub_774F6FA0  ;
CODE:775CA7F0                 dd offset aIsrun        ; "IsRun"
CODE:775CA7F4                 dd offset sub_774F6F70
CODE:775CA7F8                 dd offset aMainloop     ; "MainLoop"
CODE:775CA7FC                 dd offset sub_774F6FC0
CODE:775CA800                 dd offset aDelay_0      ; "Delay"
CODE:775CA804                 dd offset sub_774F7030
CODE:775CA808                 dd offset aWaitforevent ; "WaitForEvent"
CODE:775CA80C                 dd offset sub_774F6FE0
CODE:775CA810                 dd offset aLog_1        ; "Log"
CODE:775CA814                 dd offset sub_774F7090
CODE:775CA818                 dd offset aSetpkstate   ; "SetPKState"
CODE:775CA81C                 dd offset sub_774F9670
CODE:775CA820                 dd offset aBatconfig    ; "BatConfig"
CODE:775CA824                 dd offset sub_774F96B0
CODE:775CA828                 dd offset aBatinit      ; "BatInit"
CODE:775CA82C                 dd offset sub_774F9910
CODE:775CA830                 dd offset aBatdircall   ; "BatDirCall"
CODE:775CA834                 dd offset sub_774F9990
CODE:775CA838                 dd offset aBatsetid     ; "BatSetID"
CODE:775CA83C                 dd offset sub_774F9830
CODE:775CA840                 dd offset aBatsetkunfu  ; "BatSetKunfu"
...

如果打算分析外挂的工作原理,这些API入口函数是很好的切入点。为节省体力,写个wrapper就能够方便的调用这些接口使用外挂功能就好。

调用lua相关函数全程都要维持一个实例指针lua_State *L,只有使用外挂初始化后的lua_State指针才能调用那些诱人的内建API。好在lua库创建都要通过luaL_openlibs (lua_State *L)完成,挂钩它就能得到这个实例指针。既然有了lua源代码,想动手脚真是手到擒来。

在lua源码中添加以下代码片段:

lua_State *Handle_L;
LUALIB_API lua_State *luaL_getL() 
{
  return Handle_L;
}

LUALIB_API void luaL_setL(lua_State *L) 
{
  Handle_L = L;
}

LUALIB_API void runCMD(char *luafile) 
{
  lua_State *L = luaL_getL();
  luaL_dofile(L, luafile);
}

LUALIB_API void luaL_openlibs (lua_State *L) {
  const luaL_Reg *lib;
  luaL_setL(L); //记录实例指针
...

这样通过调用runCMD()就能随时让外挂执行我们自行编写的lua脚本,并且lua脚本也可以使用外挂注册的API函数,尽享外挂的功能。另外再写一个图形界面的程序,根据自己预定的功能增加一些按钮,按钮事件仅仅是把每个功能用lua脚本完成,然后写入文件custom.lua。再通过CreateRemoteThread的方式调用游戏内存中的runCMD(“custom.lua”),就能避开验证使用外挂的功能了。

Linux下运行Flash独立播放器

Adobe很早就停止了对Linux下Flash的功能更新,和最新的Flash 18相比,Linux下的Flash仍然停留在11.2。

即便如此,Flash 11.2日常播放个简单的动画还是绰绰有余的,点击下载Flash独立播放器

直接运行绝对报错,形式类似:

(flashplayer:23126): GLib-GObject-WARNING **: instance with invalid (NULL) class pointer
(flashplayer:23126): GLib-GObject-CRITICAL **: g_signal_handlers_disconnect_matched: assertion 'G_TYPE_CHECK_INSTANCE (instance)' failed

官方也没详细说明,后来在64位的ubuntu 14上折腾一阵,发现安装以下的依赖库就OK了

sudo apt-get install 
  libglib2.0-0:i386 
  libxt6:i386 
  libxcursor1:i386 
  libnss3:i386 
  libgtk2.0-0:i386 
  libcurl3:i386 
  libxcursor1:i386

复用IDA的分析结果

IDA Pro分析二进制程序时可以加载PDB,因而可以很容易查看符号信息。

利用这个原理,如果已知二进制应用加载了开源库,可通过自行编译开源库得到PDB,再经由IDA载入目标二进制程序,获得符号信息,辅助逆向分析。如果IDA加载PDB文件出错就换一个版本再试,我就出现6.6、6.8都加载不成功的PDB,6.5反而可以。

现在尝试让Flash复用Tamarin的符号:

  1. 用VS编译后,得到avm.pdb
  2. IDA分析avm.exe过后再载入PDB
  3. 使用IDB2SIG插件生成PAT文件
  4. sigmake -nflash_symbol avm.pat flash.sig
  5. 这一步会得到EXC格式的文件,进去删掉第一行注释后重新执行上述命令就得到了flash.sig
  6. IDA分析Flash过后,载入flash.sig就能看到符号信息了

编译选项很有讲究,如果Flash是VS2008编译的,那生成Tamarin时也应该用VS2008。比如,Win 8.1的Flash是VS2012编译的,或者开启了CFG选项的话,那Tamarin就同样方式编译,这样的sig才会有更高的匹配率。

绕过域组策略限制向U盘写入数据

朋友公司的热心网管通过让大家更为别扭的使用电脑寻找存在感,所以设定域组策略限制了她们用u盘备份资料。话说,真想搞点小动作、扮演个商业间谍的员工,用网盘邮件什么的发送多实时啊,至于上u盘吗。至于域管理,也是大小儿就听说,就没见过真身。此番游玩后发觉这一功能是企业集团这辈子必须依赖的,至于Linux/OSX,这点不服不行。

为了简化原理,以简单的形式来说,就是一台 Windows 2008 和一台 Windows 7:

  • Windows 2008 上面开启了活动目录(AD),也就是域管理。
  • 同时 Windows 2008 上面建立一个账户
  • 这台 Win7 配置了接入域,通俗的说就是登陆验证需连接 Win 2008完成,域名解析也需要借助服务器,可能连ip分配都如此。

一个简单的域就组建好了。由于Win7的账户验证在Win2008完成,这个账户的权限和允许的操作都是受服务器定义的。一旦Win2008设置了组策略管理的规则,连接他的Win7也得受之管辖。不过网管配置时候疏忽了一点,人家Win 7虽然登陆了域,但也可以切换用户,然后登陆本地账户。

这点是他可以通过高级的配置手段残忍阻止的。

虽说由于域管理的缓存机制,本地账户也受到域组策略的限制,但本地账户可是本地管理员级别的。想删改系统文件,装个驱动,格式化个磁盘都没人拦得住啊。至于写U盘被阻止的规则,那只是资源管理器,命令行什么的正常调用AP的程序才遵守。既然我是本地管理员,你组策略又没限制我装驱动,那我从驱动级别去直接调用内核API访问U盘,网管就管不着了。

可这驱动自己写不免有点折腾了,兼容性也不好讲。干脆,用现成的VirtualBox。VirtualBox里面创建一个Guest的Win7,也是可以访问U盘的,因为VirtualBox会在Host系统,也就是原Win7装一个驱动,而这个驱动就完成了底层接管USB设备的任务。既然是驱动级别的接管,你组策略就奈何不了。然后Guest的Win7再访问U盘,实则通过这个底层的驱动完成,效果看起来和组策略失效了一样,随意写。

最无奈,网管过滤了百度网盘,QQ离线发送等网络通讯,煞费苦心哎。最后只能通过新加坡一个VPS主机做中专才把VirtualBox传过去。

其实真逼急了,狠招也有的是,拿到笔记本,我U盘启动个Kali Linux,连Windows都不进,你组策略个溜溜球啊,我爱拷啥拷啥。或者你改了本地管理员口令,我U盘KonBoot启动绕过密码进去,然后爱怎么改怎么改。唯一的问题是,本儿不是我用,选择一个持久、麻瓜、易操作的方案才是最优解。

后记,对比发现,利用本地管理员权限直接改注册表似乎就可以对抗域组策略。对比前后注册表的变化,一般是safe descriptor 之类的字段,有个两三个,全删掉就可以了。

网络安全日CTF

4.29是首都网络安全日,除安全厂商参展炫技外,还有近70支来路各异的队伍前来参加为期一天的CTF攻防赛事。比赛分为上午和下午两个场次,各3个小时,中间不到一小时的休息时间。上午的比赛内容为线上答题,题目种类包含 Misc,Pwn,Web,Crypto,Reverse五个类别。相比下午的攻防夺旗赛,上午的题目和我平日研究的内容更贴近,因而投入了更多的精力。

上午的比赛,每一道题目的答案都以flag形式出现,通常是一个可以明辨含义的单词或其MD5散列,提交到在线答题系统即可得分,各个队伍的总分实时更新显示在题目侧边。赛程只有三个小时,遗憾的是很多复杂的题目连上手分析的机会都没有,庆幸的是有限时间减轻了精神压力,否则再多几个小时的头脑风暴可能就体力不支了。

Crypto

顾名思义,这类题目主要考察密码分析学的知识。和其他类别一样,每道题目都是自成一篇微小说,描绘了一个历史人物的经典剪辑或是谍战风云的早木皆兵。不难看出题目背后的用心雕琢,力图少几分枯燥,多些娱乐。比如第一道题目即是凯撒大帝经典的移位密码,出题者选择了13作为加密秘钥,刚好是26的一半,意味着利用加密算法穷举到秘钥后不用再绞尽脑汁算解密秘钥,而是再次加密就得到明文。

还有一道题目讲述了一个谍战中通过音乐传递情报的故事,并附上乐谱,要求从中还原明文,如图所示:

比赛时并没能参透,匆匆浏览只注意了数字。赛后后发现歌词原来也是提示,充满了浪漫的小趣味,用八进制格式化音符再转成asc码就是答案

ILoveSecurityVeryMuch

Pwn

这类题目没有太多变数,一般通过溢出漏洞让目标程序做些预期外的事儿。通常先给出Linux下跑的程序,只要分析清楚,写好利用代码,然后向指定服务器发包获得权限就能拿到flag文件。

第一题是在比赛中做出来的题目,还比较简单,只要用超长数据覆盖掉指定变量就能让程序流程改变输出flag信息了。

第二题就困难了一大截,程序运行后包含三次scanf输入,前两次输入都有strlen验证输入长度(可以使用0x00填充绕过长度限制),第三次的输入的内容虽然没有长度验证,但会被预定字串I_Need_BMW异或变换。如果变换后的字串和The_Pursuit_of_Happiness相同则会跳转到该字串起始位置开始执行。

虽然程序sub_804859C函数的溢出点很容易发现,且编译时关闭了ASLR和DEP,加之利用三次输入可以进行栈溢出覆盖返回值,但问题是整个程序空间都定位不到jmp esp类的指令用以跳向shellcode。考虑到题目特意包含了第三次输入内容经变换就可被跳转执行,溢出的思路也开阔了不少。

利用过程要触发两次溢出,第一次溢出覆盖ebp为I_Need_BMW等常量所在内存地址附近(不开ASLR时这个地址固定),然后返回值覆盖为sub_804859C起始位置sub esp, 168h的位置,如图所示:

溢出使得程序随后跳转到sub esp, 168h位置,保证ebp指向常量内存空间

这样第一轮溢出后程序会再执行一轮三次scanf,通过前两次输入中注入大量0x00触发第二次溢出,清空秘钥I_Need_BMW和比对明文The_Pursuit_of_Happiness,使得注入的shellcode不会因为异或有过大改动,只要提前反变换shellcode就可以使得变换后仍然不丢失语义。这样第二轮执行后,末尾会跳转到第三次输入的内容作为代码执行。

Reverse

这部分题目比较灵活,除对二进制程序的逆向分析外,还包含很多结合近年热门的解固件的问题。其中一道题目就是给了路由器的固件,要求分析出当中的后门反向连接的地址信息。虽然看过使用binwalk解包的案例分析,但真正拿到固件包时仍心有余而力不足。第一重解出来一堆zlib和squashfs,和心理预期的文件系统样式不符就停滞不前了。这部分题目要么是简单到分析过后就没有印象,要不就是思路上走不通只好放下。

下午的攻防实战环节还有一道比较难的逆向题目,需要分析一个放大镜后门程序的登录口令。一旦输入正确的口令,程序会弹出资源管理器,这样就能绕过远程桌面认证输入直接渗透进入目标主机了

首先这个看似轻巧的后门包含多种反调试陷门,由于平时分析漏洞多用Immunity Debugger,缺少反反调试的插件,只好用IDA纯静态查看。其次,程序内的函数调用逻辑十分抽象,关键算法都是数十个虚函数的嵌套迭代,用静态分析的方法十分吃亏。赛后求助同事老唐,花了一天时间才了解了背后的蹊跷。

它极可能使用易语言完成了开发,因而程序逻辑不像C语言一样明晰。此外程序中还包含一个陷阱,如果通过URLDecode可以很容易解出来一个口令HXB{YAO- YAO- QIE- KE- NAO},点击登入甚至会弹出添加系统管理员账户的界面,并且IDA上下文内包含net user /add等字样,迷惑程度异常高。

最后用包含反反调试插件的OllyDbg跟踪到了真正的加密函数入口,输入的信息被加密后需要和另一个URLEncode的字串相同才可以。加密算法形似RC4,但部分结构被篡改,使用RC4算法并不能解出来真实口令。一个取巧的办法是在加密入口强制修改内存,将输入数据修改为URLEncode字串,查看加密出口时的内存就能发现真实口令HXB{HAHA- HEHE- HEIHEI- HUOHUO- XIXI}赫然出现了。

Misc

杂项题目就很丰富了,可能综合运用到其他题目的技术,并且包含很多有趣的彩蛋。其中一道题目是叙述了一个谍影重重的案件,高潮迭起的故事结尾,间谍U盘镜像落入囊中。镜像解开后,里面包含了很多映射故事中点滴线索的文档

其中systemzx.exe是关键的程序,执行后会释放机密的flag文件,但提示身份验证失败,flag又被删除。题目本意肯定是希望逆向systemzx.exe后找到应该输入的信息,让解压后身份审核通过,flag文件得以保留。

但实际操作中发现再次执行,由于解压缩的覆盖操作需要用户确认,解压执行流程被暂停,flag文件还未被删除,拷出来后执行就得到了隐藏的机密文件。这种彩蛋式的解法让人有种捡了钱的幻觉。

纵观上午的赛事,题目的设计真是用尽了心思,即保证三个小时各队都能有所收获,也有一定难度可以拉开差距,甚至赛后留给大家意犹未尽的味道。

 

Nexus刷机你卡了吗

做实验需要低版本Android系统,按照一般思路:从Google官方下载了4.1的镜像,在fastboot下从5.0.2执行flash-all.sh脚本。但重启后,Nexus 7就一直卡在开机后的Xlogo处。

解决的方法是,解压zip包,手动调整刷机脚本,删掉userdata的刷入:

fastboot erase boot
fastboot erase cache
fastboot erase recovery
fastboot erase system
fastboot erase userdata
fastboot flash bootloader bootloader-grouper-3.41.img
fastboot reboot-bootloader sleep 10
fastboot flash boot image-nakasi-jzo54k/boot.img
fastboot flash system image-nakasi-jzo54k/system.img

iMovie发布优酷高清视频

曾经限于电脑摄像头的羸弱配置,发视频也不在意分辨率。Air的720p摄像头贯彻了苹果一贯的精益求精,光线充足时候画面纯净,色彩逼真,用它要还不顾忌清晰度就说不过去了。

优酷为了拉开会员和平民身份差别,除了标清还相继推出高清、超清、1080p。即便我制作视频保证严格品控,但iMovie导出来的最佳质量文件依旧只在落得个优酷标清的下场。不过相比两年前,这事儿解决起来容易很多。首先,优酷已经有明确的政策文件可查:

1. 以下这些视频平均码率>=1Mbps时为高清,>=1.5Mbps时为超清,>=3.5Mbps时为1080p:

H.264/AVC(Advance Video Coding)/AVCHD/X264 通常使用MP4,MKV文件格式, 也有的使用FLV格式

RV40/RealVideo 9, 通常使用 RMVB文件格式

WMV3/WVC1/WMVA/VC-1/Windows Media Video 9, 通常使用WMV文件格式

2. 以下这些视频平均码率>=2Mbps时为高清,>=3Mbps时为超清,>=5Mbps时为1080p:

MPEG-4 Visual/Xvid/Divx, 通常使用AVI,MP4文件格式

3. 以下这些视频平均码率>=5Mbps时为高清,>=7.5Mbps时为超清,>=8Mbps时为1080p:

MPEG-2, 通常使用MPEG/MPG/VOB文件格式

显然,iMovie推崇的mov格式未列其中,任你再大也不被优酷当做高清。如果退而求其次,iMovie导出的倒数第二个选项的所谓高清,实则是H264,恰符合要求。但如果是手机里面导出来的视频,最好还是用格式工厂调一下码率。以下就是调节后的成品:

二进制格式的AndroidManifest修改

APK重打包后,由于签名不吻合,在包含原程序的手机上是不能覆盖安装的,需要修改包名(packagename)

对于比较简单的APK可以在apktool编译时直接修改AndroidManifest.xml的源文件实现包名篡改,但类似微信这种复杂的程序,想穷尽修改相关的包名就差强人意了。特别是一些包名可能动态生成或在so文件中使用。

行之有效的方法是直接修改重打包APK中包含的二进制格式的AndroidManifest.xml文件。若欲篡改的包名和原始包名长度相同或比原始包名要短时,修改的方法都很容易操作:

  • 解压缩APK
  • 修改包名所在字串,字符串以{长度}{UTF8}{NULL}{NULL}方式存储
  • 重新压缩APK,重新签名即可

但如果有特殊需求,一定要将包名修改为一个比原包名长的名称时(需要修改多个长度字段),必须清楚地理解AndroidManifest.xml的文件格式。虽然格式未公开,但通过一定的逆向分析,已经有比较详细的格式文档供参考了,详见链接

实验时写了一个Python脚本,用于AndroidManifest.xml中包名的替换,代码详见链接

PS:虽然文档中没有提及,但字符数据整体大小需要4字节对齐,我写的代码已经包含了对齐操作