使用ChakraCore来Fuzz NScript
NScript是JScript的子集,前面的文章(http://www.nul.pw/2017/06/12/237.html)我介绍了使用NScript的方式,也提到了语法混乱,写的让人头疼的Mozilla Funfuzz的最终精简版本(http://www.nul.pw/2017/06/12/235.html),以及使用ChakraCore运行Funfuzz的壮观景象。
因为NScript事实上是一个非常精简的子集,以至于不能够支持Funfuzz那一大堆雄伟的语法,如下图:
事实上,先不说NScript,就连微软引以为豪的ChakraCore要支持起来都十分吃力,但是好在Funfuzz虽然代码写得奔放,但是最终生成的测试用例却格外的“人性化”。
转载请标明来源: http://nul.pw/
本文作者:blast
不过基本上看,NScript还是表现的和JScript很相似的:
代码测试
input:function p(){log(myVar)}; function q(){var myVar=2; p(); log(myVar);}; var myVar=1; log(myVar); q();
evaluation result: 1
evaluation result: 1
evaluation result: 2
为了能够取到Funfuzz的运行结果(即生成的Fuzz语句),而不修改Funfuzz的代码,(那一堆变量我实在是不敢修改啊)。我要选择更容易下手的——ChakraCore的代码。ChakraCore的print实现了类似console.log的功能,而我们可以在FunFuzz中print一下,来记录代码。
而只要我们知道print的代码,我们就可以在print时,将代码抛给NScript,这样,我们就可以同时Fuzz ChakraCore和NScript了。我知道你在想什么,wsprintf系列并不合适,因为不止print,ChakraCore的很多代码都依附类似的函数输出数据,所以我们要找到更精确的print。
print是一个很常见的名词,因此搜索并不管用,我们会搜出一大堆带print的函数。而ChakraCore也不支持阻塞的函数,比如alert之类的,这样就让我们的调试显得难以入手。不过还好,Math类下有一大堆名字独特的函数,比如反正切函数atan。Math.atan,我们搜索atan(之后,可以发现Js::Math::Atan的位置,给Atan的入口下断点。如果你不想多看一堆转换,最好是下一个参数的那个Atan。我们的代码是print(Math.atan(4))。运行后,断点停止在Atan处:
> ChakraCore.dll!Js::Math::Atan(double x) Line 263 C++
ChakraCore.dll!Js::Math::Atan(Js::RecyclableObject * function, Js::CallInfo callInfo, ...) Line 246 C++
ChakraCore.dll!Js::LocalCallFunction(Js::RecyclableObject * function, void * (Js::RecyclableObject *, Js::CallInfo, ...) * entryPoint, Js::Arguments args, bool doStackProbe) Line 1313 C++
ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void * (Js::RecyclableObject *, Js::CallInfo, ...) * entryPoint, Js::Arguments args) Line 1329 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, const Js::AuxArray<unsigned int> * spreadIndices) Line 3902 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfileCallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, unsigned short profileId, unsigned int inlineCacheIndex, const Js::AuxArray<unsigned int> * spreadIndices) Line 3927 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, unsigned int flags) Line 456 C++
ChakraCore.dll!Js::InterpreterStackFrame::ProcessProfiled() Line 86 C++
ChakraCore.dll!Js::InterpreterStackFrame::Process() Line 3452 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterHelper(Js::ScriptFunction * function, Js::ArgumentReader args, void * returnAddress, void * addressOfReturnAddress, const bool isAsmJs) Line 2039 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterThunk(Js::JavascriptCallStackLayout * layout) Line 1776 C++
我们的目标当然不是Atan,而是外面的print,返回后跟踪,可以看到ChakraCore取出Next Op,并在这里:
> ChakraCore.dll!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, const Js::AuxArray<unsigned int> * spreadIndices) Line 3888 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfileCallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, unsigned short profileId, unsigned int inlineCacheIndex, const Js::AuxArray<unsigned int> * spreadIndices) Line 3927 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, unsigned int flags) Line 456 C++
ChakraCore.dll!Js::InterpreterStackFrame::ProcessProfiled() Line 86 C++
ChakraCore.dll!Js::InterpreterStackFrame::Process() Line 3452 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterHelper(Js::ScriptFunction * function, Js::ArgumentReader args, void * returnAddress, void * addressOfReturnAddress, const bool isAsmJs) Line 2039 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterThunk(Js::JavascriptCallStackLayout * layout) Line 1776 C++
[External Code]
可以看到调用:
JavascriptFunction::CallFunction<true>(function, function->GetEntryPoint(), args);
这里的function就是最终实现print的函数。可以看到ChakraCore在这里使用了JSRT来扩展(参考我翻译的文章:http://tem.pw/?x=entry:entry170620-222216)实现了print,无论怎样,我们找到了print的位置:
- function 0x0210c540 {signature=0x00000000 callbackState=0x00000000 nativeMethod=0x00251bc0 {ch.exe!WScriptJsrt::EchoCallback(void *, bool, void * *, unsigned short, void *)} ...} Js::RecyclableObject *
- [Js::JavascriptExternalFunction] {signature=0x00000000 callbackState=0x00000000 nativeMethod=0x00251bc0 {ch.exe!WScriptJsrt::EchoCallback(void *, bool, void * *, unsigned short, void *)} ...} Js::JavascriptExternalFunction
+ Js::RuntimeFunction {functionNameId=0x0210a720 } Js::RuntimeFunction
signature 0x00000000 void *
callbackState 0x00000000 void *
nativeMethod 0x00251bc0 {ch.exe!WScriptJsrt::EchoCallback(void *, bool, void * *, unsigned short, void *)} void * (Js::RecyclableObject *, Js::CallInfo, void * *) *
+ wrappedMethod 0x00251bc0 {ch.exe!WScriptJsrt::EchoCallback(void *, bool, void * *, unsigned short, void *)} {signature=...} Js::JavascriptExternalFunction *
stdCallNativeMethod 0x00251bc0 {ch.exe!WScriptJsrt::EchoCallback(void *, bool, void * *, unsigned short, void *)} void * (void *, bool, void * *, unsigned short, void *) *
initMethod 0x00000000 HRESULT (void *) *
oneBit 1 unsigned int
typeSlots 0 unsigned int
hasAccessors 0 unsigned int
unused 0 unsigned int
prototypeTypeId -1 int
flags 0 unsigned __int64
+ FinalizableObject {...} FinalizableObject
- type 0x020f2c80 {typeId=TypeIds_Function (26) flags=TypeFlagMask_None (0 '\0') javascriptLibrary=0x02110000 {...} ...} Js::Type *
typeId TypeIds_Function (26) Js::TypeId
flags TypeFlagMask_None (0 '\0') TypeFlagMask
+ javascriptLibrary 0x02110000 {cacheForCopyOnAccessArraySegments=0x02122000 {cache=0x02122000 {0x00000000 <NULL>, 0x00000000 <NULL>, ...} ...} ...} Js::JavascriptLibrary *
- prototype 0x020f2540 {constructorCache=0x10a921b0 {ChakraCore.dll!Js::ConstructorCache Js::ConstructorCache::DefaultInstance} {...} ...} Js::RecyclableObject *
- [Js::JavascriptFunction] {constructorCache=0x10a921b0 {ChakraCore.dll!Js::ConstructorCache Js::ConstructorCache::DefaultInstance} {...} ...} Js::JavascriptFunction
+ Js::DynamicObject {auxSlots=0x00000000 {???} objectArray=0x00000000 <NULL> arrayFlags=None (0) ...} Js::DynamicObject
+ constructorCache 0x10a921b0 {ChakraCore.dll!Js::ConstructorCache Js::ConstructorCache::DefaultInstance} {guard={value=...} ...} Js::ConstructorCache *
+ functionInfo 0x10a93dd8 {ChakraCore.dll!Js::FunctionInfo Js::JavascriptFunction::EntryInfo::PrototypeEntryPoint} {...} Js::FunctionInfo *
+ FinalizableObject {...} FinalizableObject
+ type 0x020f2520 {typeId=TypeIds_Function (26) flags=TypeFlagMask_None (0 '\0') javascriptLibrary=0x02110000 {...} ...} Js::Type *
entryPoint 0x0fe56270 {ChakraCore.dll!Js::JavascriptExternalFunction::StdCallExternalFunctionThunk(Js::RecyclableObject *, Js::CallInfo, ...)} void * (Js::RecyclableObject *, Js::CallInfo, ...) *
+ propertyCache 0x00000000 <NULL> Js::TypePropertyCache *
为WScriptJsrt::EchoCallback下断点,运行过去,栈如下:
> ch.exe!WScriptJsrt::EchoCallback(void * callee, bool isConstructCall, void * * arguments, unsigned short argumentCount, void * callbackState) Line 105 C++
ChakraCore.dll!Js::JavascriptExternalFunction::StdCallExternalFunctionThunk(Js::RecyclableObject * function, Js::CallInfo callInfo, ...) Line 275 C++
ChakraCore.dll!Js::LocalCallFunction(Js::RecyclableObject * function, void * (Js::RecyclableObject *, Js::CallInfo, ...) * entryPoint, Js::Arguments args, bool doStackProbe) Line 1313 C++
ChakraCore.dll!Js::JavascriptFunction::CallFunction<1>(Js::RecyclableObject * function, void * (Js::RecyclableObject *, Js::CallInfo, ...) * entryPoint, Js::Arguments args) Line 1329 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_CallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, const Js::AuxArray<unsigned int> * spreadIndices) Line 3891 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfileCallCommon<Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, Js::RecyclableObject * function, unsigned int flags, unsigned short profileId, unsigned int inlineCacheIndex, const Js::AuxArray<unsigned int> * spreadIndices) Line 3927 C++
ChakraCore.dll!Js::InterpreterStackFrame::OP_ProfiledCallIWithICIndex<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > >(const Js::OpLayoutDynamicProfile<Js::OpLayoutT_CallIWithICIndex<Js::LayoutSizePolicy<0> > > * playout, unsigned int flags) Line 456 C++
ChakraCore.dll!Js::InterpreterStackFrame::ProcessProfiled() Line 86 C++
ChakraCore.dll!Js::InterpreterStackFrame::Process() Line 3452 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterHelper(Js::ScriptFunction * function, Js::ArgumentReader args, void * returnAddress, void * addressOfReturnAddress, const bool isAsmJs) Line 2039 C++
ChakraCore.dll!Js::InterpreterStackFrame::InterpreterThunk(Js::JavascriptCallStackLayout * layout) Line 1776 C++
[External Code]
对AutoString str进行检视:
- str {length=18 data=0x00486730 "1.3258176636680325" data_wide=0x00000000 <NULL> ...} AutoString
length 18 unsigned int
+ data 0x00486730 "1.3258176636680325" char *
+ data_wide 0x00000000 <NULL> wchar_t *
errorCode JsNoError (0) _JsErrorCode
dontFree false bool
值1.3258176636680325确实就是Atan(4)的结果。
所以,我们只要修改这个函数,用它来和NScript进行通信,就可以达到我们想要的效果。