Added SEH Viewer to GUI
This commit is contained in:
parent
121b884b7d
commit
fe18bd2a68
|
@ -1200,6 +1200,11 @@ BRIDGE_IMPEXP void GuiUpdateCallStack()
|
|||
_gui_sendmessage(GUI_UPDATE_CALLSTACK, 0, 0);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP void GuiUpdateSEHChain()
|
||||
{
|
||||
_gui_sendmessage(GUI_UPDATE_SEHCHAIN, 0, 0);
|
||||
}
|
||||
|
||||
BRIDGE_IMPEXP void GuiLoadSourceFile(const char* path, int line)
|
||||
{
|
||||
_gui_sendmessage(GUI_LOAD_SOURCE_FILE, (void*)path, (void*)line);
|
||||
|
@ -1289,4 +1294,4 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|||
{
|
||||
hInst = hinstDLL;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -787,6 +787,7 @@ typedef enum
|
|||
GUI_REPAINT_TABLE_VIEW, // param1=unused, param2=unused
|
||||
GUI_UPDATE_PATCHES, // param1=unused, param2=unused
|
||||
GUI_UPDATE_CALLSTACK, // param1=unused, param2=unused
|
||||
GUI_UPDATE_SEHCHAIN, // param1=unused, param2=unused
|
||||
GUI_SYMBOL_REFRESH_CURRENT, // param1=unused, param2=unused
|
||||
GUI_UPDATE_MEMORY_VIEW, // param1=unused, param2=unused
|
||||
GUI_REF_INITIALIZE, // param1=const char* name, param2=unused
|
||||
|
@ -902,6 +903,7 @@ BRIDGE_IMPEXP void GuiUpdateSideBar();
|
|||
BRIDGE_IMPEXP void GuiRepaintTableView();
|
||||
BRIDGE_IMPEXP void GuiUpdatePatches();
|
||||
BRIDGE_IMPEXP void GuiUpdateCallStack();
|
||||
BRIDGE_IMPEXP void GuiUpdateSEHChain();
|
||||
BRIDGE_IMPEXP void GuiLoadSourceFile(const char* path, int line);
|
||||
BRIDGE_IMPEXP void GuiMenuSetIcon(int hMenu, const ICONDATA* icon);
|
||||
BRIDGE_IMPEXP void GuiMenuSetEntryIcon(int hEntry, const ICONDATA* icon);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "stackinfo.h"
|
||||
#include "symbolinfo.h"
|
||||
#include "module.h"
|
||||
#include "exhandlerinfo.h"
|
||||
|
||||
static DBGFUNCTIONS _dbgfunctions;
|
||||
|
||||
|
@ -90,6 +91,22 @@ static void _getcallstack(DBGCALLSTACK* callstack)
|
|||
stackgetcallstack(GetContextDataEx(hActiveThread, UE_CSP), (CALLSTACK*)callstack);
|
||||
}
|
||||
|
||||
static void _getsehchain(DBGSEHCHAIN* sehchain)
|
||||
{
|
||||
std::vector<duint> SEHList;
|
||||
ExHandlerGetSEH(SEHList);
|
||||
sehchain->total = SEHList.size();
|
||||
if(sehchain->total > 0)
|
||||
{
|
||||
sehchain->records = (DBGSEHRECORD*)BridgeAlloc(sehchain->total * sizeof(DBGSEHRECORD));
|
||||
for(size_t i = 0; i < sehchain->total; i++)
|
||||
{
|
||||
sehchain->records[i].addr = SEHList[i];
|
||||
MemRead(SEHList[i] + 4, &sehchain->records[i].handler, sizeof(duint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool _getjitauto(bool* jit_auto)
|
||||
{
|
||||
return dbggetjitauto(jit_auto, notfound, NULL, NULL);
|
||||
|
@ -207,6 +224,7 @@ void dbgfunctionsinit()
|
|||
_dbgfunctions.DisasmFast = disasmfast;
|
||||
_dbgfunctions.MemUpdateMap = _memupdatemap;
|
||||
_dbgfunctions.GetCallStack = _getcallstack;
|
||||
_dbgfunctions.GetSEHChain = _getsehchain;
|
||||
_dbgfunctions.SymbolDownloadAllSymbols = SymDownloadAllSymbols;
|
||||
_dbgfunctions.GetJit = _getjit;
|
||||
_dbgfunctions.GetJitAuto = _getjitauto;
|
||||
|
|
|
@ -27,6 +27,18 @@ typedef struct
|
|||
DBGCALLSTACKENTRY* entries;
|
||||
} DBGCALLSTACK;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
duint addr;
|
||||
duint handler;
|
||||
} DBGSEHRECORD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
size_t total;
|
||||
DBGSEHRECORD* records;
|
||||
} DBGSEHCHAIN;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD dwProcessId;
|
||||
|
@ -52,6 +64,7 @@ typedef int (*MODPATHFROMNAME)(const char* modname, char* path, int size);
|
|||
typedef bool (*DISASMFAST)(const unsigned char* data, duint addr, BASIC_INSTRUCTION_INFO* basicinfo);
|
||||
typedef void (*MEMUPDATEMAP)();
|
||||
typedef void (*GETCALLSTACK)(DBGCALLSTACK* callstack);
|
||||
typedef void (*GETSEHCHAIN)(DBGSEHCHAIN* sehchain);
|
||||
typedef void (*SYMBOLDOWNLOADALLSYMBOLS)(const char* szSymbolStore);
|
||||
typedef bool (*GETJIT)(char* jit, bool x64);
|
||||
typedef bool (*GETJITAUTO)(bool* jitauto);
|
||||
|
@ -91,6 +104,7 @@ typedef struct DBGFUNCTIONS_
|
|||
DISASMFAST DisasmFast;
|
||||
MEMUPDATEMAP MemUpdateMap;
|
||||
GETCALLSTACK GetCallStack;
|
||||
GETSEHCHAIN GetSEHChain;
|
||||
SYMBOLDOWNLOADALLSYMBOLS SymbolDownloadAllSymbols;
|
||||
GETJITAUTO GetJitAuto;
|
||||
GETJIT GetJit;
|
||||
|
|
|
@ -213,6 +213,12 @@ DWORD WINAPI updateCallStackThread(void* ptr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DWORD WINAPI updateSEHChainThread(void* ptr)
|
||||
{
|
||||
GuiUpdateSEHChain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebugUpdateGui(duint disasm_addr, bool stack)
|
||||
{
|
||||
duint cip = GetContextDataEx(hActiveThread, UE_CIP);
|
||||
|
@ -235,6 +241,7 @@ void DebugUpdateGui(duint disasm_addr, bool stack)
|
|||
{
|
||||
cacheCsp = csp;
|
||||
CloseHandle(CreateThread(0, 0, updateCallStackThread, 0, 0, 0));
|
||||
CloseHandle(CreateThread(0, 0, updateSEHChainThread, 0, 0, 0));
|
||||
}
|
||||
char modname[MAX_MODULE_SIZE] = "";
|
||||
char modtext[MAX_MODULE_SIZE * 2] = "";
|
||||
|
@ -1802,4 +1809,4 @@ DWORD WINAPI threadAttachLoop(void* lpParameter)
|
|||
{
|
||||
debugLoopFunction(lpParameter, true);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,18 +54,23 @@ bool ExHandlerGetInfo(EX_HANDLER_TYPE Type, EX_HANDLER_INFO* Info)
|
|||
|
||||
bool ExHandlerGetSEH(std::vector<duint> & Entries)
|
||||
{
|
||||
// TODO: 64-bit
|
||||
#ifdef _WIN64
|
||||
return false; // TODO: 64-bit
|
||||
#endif
|
||||
static duint nextSEH = 0;
|
||||
NT_TIB tib;
|
||||
if(ThreadGetTib((duint)GetTEBLocation(hActiveThread), &tib))
|
||||
{
|
||||
EXCEPTION_REGISTRATION_RECORD sehr;
|
||||
duint addr_ExRegRecord = (duint)tib.ExceptionList;
|
||||
while(addr_ExRegRecord != 0xFFFFFFFF)
|
||||
int MAX_DEPTH = 1000;
|
||||
while(addr_ExRegRecord != 0xFFFFFFFF && MAX_DEPTH)
|
||||
{
|
||||
Entries.push_back(addr_ExRegRecord);
|
||||
MemRead(addr_ExRegRecord , &sehr, sizeof(EXCEPTION_REGISTRATION_RECORD));
|
||||
if(!MemRead(addr_ExRegRecord , &sehr, sizeof(EXCEPTION_REGISTRATION_RECORD)))
|
||||
break;
|
||||
addr_ExRegRecord = (duint)sehr.Next;
|
||||
MAX_DEPTH--;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -16,16 +16,18 @@
|
|||
bool stackcommentget(duint addr, STACK_COMMENT* comment)
|
||||
{
|
||||
std::vector<duint> SEHList;
|
||||
ExHandlerGetSEH(SEHList);
|
||||
std::vector<duint>::iterator iter = std::find(SEHList.begin(), SEHList.end(), addr);
|
||||
if(iter != SEHList.end())
|
||||
if(ExHandlerGetSEH(SEHList))
|
||||
{
|
||||
if(iter + 1 != SEHList.end())
|
||||
sprintf_s(comment->comment, "Pointer to SEH_Record[%d]", iter - SEHList.begin() + 1);
|
||||
else
|
||||
sprintf_s(comment->comment, "End of SEH Chain");
|
||||
strcpy_s(comment->color, "#AE81FF");
|
||||
return true;
|
||||
std::vector<duint>::iterator iter = std::find(SEHList.begin(), SEHList.end(), addr);
|
||||
if(iter != SEHList.end())
|
||||
{
|
||||
if(iter + 1 != SEHList.end())
|
||||
sprintf_s(comment->comment, "Pointer to SEH_Record[%d]", iter - SEHList.begin() + 1);
|
||||
else
|
||||
sprintf_s(comment->comment, "End of SEH Chain");
|
||||
strcpy_s(comment->color, "#AE81FF");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
duint data = 0;
|
||||
memset(comment, 0, sizeof(STACK_COMMENT));
|
||||
|
|
|
@ -424,6 +424,10 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
|
|||
emit updateCallStack();
|
||||
break;
|
||||
|
||||
case GUI_UPDATE_SEHCHAIN:
|
||||
emit updateSEHChain();
|
||||
break;
|
||||
|
||||
case GUI_SYMBOL_REFRESH_CURRENT:
|
||||
emit symbolRefreshCurrent();
|
||||
break;
|
||||
|
|
|
@ -100,6 +100,7 @@ signals:
|
|||
void repaintTableView();
|
||||
void updatePatches();
|
||||
void updateCallStack();
|
||||
void updateSEHChain();
|
||||
void symbolRefreshCurrent();
|
||||
void loadSourceFile(const QString path, int line, int selection);
|
||||
void setIconMenuEntry(int hEntry, QIcon icon);
|
||||
|
|
|
@ -110,6 +110,11 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||
mCallStackView->setWindowIcon(QIcon(":/icons/images/callstack.png"));
|
||||
connect(mCallStackView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
|
||||
// SEH Chain view
|
||||
mSEHChainView = new SEHChainView();
|
||||
mSEHChainView->setWindowTitle("SEH Chain");
|
||||
mSEHChainView->setWindowIcon(QIcon(":/icons/images/seh-chain.png"));
|
||||
connect(mSEHChainView, SIGNAL(showCpu()), this, SLOT(displayCpuWidget()));
|
||||
// Script view
|
||||
mScriptView = new ScriptView();
|
||||
mScriptView->setWindowTitle("Script");
|
||||
|
@ -158,6 +163,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||
mWidgetList.push_back(mBreakpointsView);
|
||||
mWidgetList.push_back(mMemMapView);
|
||||
mWidgetList.push_back(mCallStackView);
|
||||
mWidgetList.push_back(mSEHChainView);
|
||||
mWidgetList.push_back(mScriptView);
|
||||
mWidgetList.push_back(mSymbolView);
|
||||
mWidgetList.push_back(mSourceViewManager);
|
||||
|
@ -227,6 +233,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||
connect(ui->actionFunctions, SIGNAL(triggered()), this, SLOT(displayFunctions()));
|
||||
connect(ui->actionCheckUpdates, SIGNAL(triggered()), this, SLOT(checkUpdates()));
|
||||
connect(ui->actionCallStack, SIGNAL(triggered()), this, SLOT(displayCallstack()));
|
||||
connect(ui->actionSEHChain, SIGNAL(triggered()), this, SLOT(displaySEHChain()));
|
||||
connect(ui->actionDonate, SIGNAL(triggered()), this, SLOT(donate()));
|
||||
connect(ui->actionReportBug, SIGNAL(triggered()), this, SLOT(reportBug()));
|
||||
connect(ui->actionAttach, SIGNAL(triggered()), this, SLOT(displayAttach()));
|
||||
|
@ -1073,6 +1080,11 @@ void MainWindow::displayCallstack()
|
|||
showQWidgetTab(mCallStackView);
|
||||
}
|
||||
|
||||
void MainWindow::displaySEHChain()
|
||||
{
|
||||
showQWidgetTab(mSEHChainView);
|
||||
}
|
||||
|
||||
void MainWindow::donate()
|
||||
{
|
||||
QMessageBox msg(QMessageBox::Information, "Donate", "All the money will go to x64dbg development.");
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "CPUWidget.h"
|
||||
#include "MemoryMapView.h"
|
||||
#include "CallStackView.h"
|
||||
#include "SEHChainView.h"
|
||||
#include "LogView.h"
|
||||
#include "SymbolView.h"
|
||||
#include "BreakpointsView.h"
|
||||
|
@ -102,6 +103,7 @@ public slots:
|
|||
void displayFunctions();
|
||||
void checkUpdates();
|
||||
void displayCallstack();
|
||||
void displaySEHChain();
|
||||
void setGlobalShortcut(QAction* action, const QKeySequence & key);
|
||||
void refreshShortcuts();
|
||||
void openShortcuts();
|
||||
|
@ -129,6 +131,7 @@ private:
|
|||
CPUWidget* mCpuWidget;
|
||||
MemoryMapView* mMemMapView;
|
||||
CallStackView* mCallStackView;
|
||||
SEHChainView* mSEHChainView;
|
||||
LogView* mLogView;
|
||||
SymbolView* mSymbolView;
|
||||
SourceViewerManager* mSourceViewManager;
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
<addaction name="actionBreakpoints"/>
|
||||
<addaction name="actionMemoryMap"/>
|
||||
<addaction name="actionCallStack"/>
|
||||
<addaction name="actionSEHChain"/>
|
||||
<addaction name="actionScript"/>
|
||||
<addaction name="actionSymbolInfo"/>
|
||||
<addaction name="actionSource"/>
|
||||
|
@ -157,6 +158,7 @@
|
|||
<addaction name="actionBreakpoints"/>
|
||||
<addaction name="actionMemoryMap"/>
|
||||
<addaction name="actionCallStack"/>
|
||||
<addaction name="actionSEHChain"/>
|
||||
<addaction name="actionScript"/>
|
||||
<addaction name="actionSymbolInfo"/>
|
||||
<addaction name="actionSource"/>
|
||||
|
@ -697,6 +699,15 @@
|
|||
<string>FAQ</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSEHChain">
|
||||
<property name="icon">
|
||||
<iconset resource="../../resource.qrc">
|
||||
<normaloff>:/icons/images/seh-chain.png</normaloff>:/icons/images/seh-chain.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SEH Chain</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
#include "SEHChainView.h"
|
||||
#include "Bridge.h"
|
||||
|
||||
SEHChainView::SEHChainView(StdTable* parent) : StdTable(parent)
|
||||
{
|
||||
int charWidth = getCharWidth();
|
||||
|
||||
addColumnAt(8 + charWidth * sizeof(dsint) * 2, "Address", true); //address in the stack
|
||||
addColumnAt(8 + charWidth * 18, "Exception Handler", true); // Exception Handler
|
||||
addColumnAt(8 + charWidth * 50, "Module/Label", false);
|
||||
addColumnAt(charWidth * 10, "Comment", false);
|
||||
connect(Bridge::getBridge(), SIGNAL(updateSEHChain()), this, SLOT(updateSEHChain()));
|
||||
connect(this, SIGNAL(contextMenuSignal(QPoint)), this, SLOT(contextMenuSlot(QPoint)));
|
||||
connect(this, SIGNAL(doubleClickedSignal()), this, SLOT(doubleClickedSlot()));
|
||||
setupContextMenu();
|
||||
}
|
||||
|
||||
void SEHChainView::setupContextMenu()
|
||||
{
|
||||
mFollowAddress = new QAction("Follow &Address", this);
|
||||
connect(mFollowAddress, SIGNAL(triggered()), this, SLOT(followAddress()));
|
||||
mFollowHandler = new QAction("Follow Handler", this);
|
||||
mFollowHandler->setShortcutContext(Qt::WidgetShortcut);
|
||||
mFollowHandler->setShortcut(QKeySequence("enter"));
|
||||
connect(mFollowHandler, SIGNAL(triggered()), this, SLOT(followHandler()));
|
||||
connect(this, SIGNAL(enterPressedSignal()), this, SLOT(followHandler()));
|
||||
}
|
||||
|
||||
void SEHChainView::updateSEHChain()
|
||||
{
|
||||
DBGSEHCHAIN sehchain;
|
||||
memset(&sehchain, 0, sizeof(DBGSEHCHAIN));
|
||||
DbgFunctions()->GetSEHChain(&sehchain);
|
||||
setRowCount(sehchain.total);
|
||||
for(size_t i = 0; i < sehchain.total; i++)
|
||||
{
|
||||
QString cellText = QString("%1").arg(sehchain.records[i].addr, sizeof(duint) * 2, 16, QChar('0')).toUpper();
|
||||
setCellContent(i, 0, cellText);
|
||||
cellText = QString("%1").arg(sehchain.records[i].handler, sizeof(duint) * 2, 16, QChar('0')).toUpper();
|
||||
setCellContent(i, 1, cellText);
|
||||
|
||||
char label[MAX_LABEL_SIZE] = "";
|
||||
char module[MAX_MODULE_SIZE] = "";
|
||||
DbgGetModuleAt(sehchain.records[i].handler, module);
|
||||
QString label_text;
|
||||
if(DbgGetLabelAt(sehchain.records[i].handler, SEG_DEFAULT, label))
|
||||
label_text = "<" + QString(module) + "." + QString(label) + ">";
|
||||
else
|
||||
label_text = QString(module);
|
||||
setCellContent(i, 2, label_text);
|
||||
char comment[MAX_COMMENT_SIZE] = "";
|
||||
if(DbgGetCommentAt(sehchain.records[i].handler, comment))
|
||||
{
|
||||
if(comment[0] == '\1') //automatic comment
|
||||
setCellContent(i, 3, QString(comment + 1));
|
||||
else
|
||||
setCellContent(i, 3, comment);
|
||||
}
|
||||
}
|
||||
if(sehchain.total)
|
||||
BridgeFree(sehchain.records);
|
||||
reloadData();
|
||||
}
|
||||
|
||||
void SEHChainView::contextMenuSlot(const QPoint pos)
|
||||
{
|
||||
if(!DbgIsDebugging())
|
||||
return;
|
||||
QMenu* wMenu = new QMenu(this); //create context menu
|
||||
wMenu->addAction(mFollowAddress);
|
||||
wMenu->addAction(mFollowHandler);
|
||||
QMenu wCopyMenu("&Copy", this);
|
||||
setupCopyMenu(&wCopyMenu);
|
||||
if(wCopyMenu.actions().length())
|
||||
{
|
||||
wMenu->addSeparator();
|
||||
wMenu->addMenu(&wCopyMenu);
|
||||
}
|
||||
wMenu->exec(mapToGlobal(pos)); //execute context menu
|
||||
}
|
||||
|
||||
void SEHChainView::doubleClickedSlot()
|
||||
{
|
||||
followHandler();
|
||||
}
|
||||
|
||||
void SEHChainView::followAddress()
|
||||
{
|
||||
QString addrText = getCellContent(getInitialSelection(), 0);
|
||||
DbgCmdExecDirect(QString("sdump " + addrText).toUtf8().constData());
|
||||
emit showCpu();
|
||||
}
|
||||
|
||||
void SEHChainView::followHandler()
|
||||
{
|
||||
QString addrText = getCellContent(getInitialSelection(), 1);
|
||||
DbgCmdExecDirect(QString("disasm " + addrText).toUtf8().constData());
|
||||
emit showCpu();
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef SEHCHAINVIEW_H
|
||||
#define SEHCHAINVIEW_H
|
||||
|
||||
#include "StdTable.h"
|
||||
|
||||
class SEHChainView : public StdTable
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SEHChainView(StdTable* parent = 0);
|
||||
void setupContextMenu();
|
||||
|
||||
signals:
|
||||
void showCpu();
|
||||
|
||||
protected slots:
|
||||
void updateSEHChain();
|
||||
void contextMenuSlot(const QPoint pos);
|
||||
void doubleClickedSlot();
|
||||
void followAddress();
|
||||
void followHandler();
|
||||
|
||||
private:
|
||||
QAction* mFollowAddress;
|
||||
QAction* mFollowHandler;
|
||||
};
|
||||
|
||||
#endif // SEHCHAINVIEW_H
|
Binary file not shown.
After Width: | Height: | Size: 707 B |
|
@ -74,5 +74,6 @@
|
|||
<file>images/processor32.png</file>
|
||||
<file>images/processor64.png</file>
|
||||
<file>images/processor-cpu.png</file>
|
||||
<file>images/seh-chain.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -142,7 +142,8 @@ SOURCES += \
|
|||
Src/Gui/CPUMultiDump.cpp \
|
||||
Src/Gui/AssembleDialog.cpp \
|
||||
Src/ThirdPartyLibs/float128/float128.cpp \
|
||||
Src/Utils/StringUtil.cpp
|
||||
Src/Utils/StringUtil.cpp \
|
||||
Src/Gui/SEHChainView.cpp
|
||||
|
||||
|
||||
HEADERS += \
|
||||
|
@ -225,7 +226,8 @@ HEADERS += \
|
|||
Src/Utils/QActionLambda.h \
|
||||
Src/Gui/CPUMultiDump.h \
|
||||
Src/Gui/AssembleDialog.h \
|
||||
Src/ThirdPartyLibs/float128/float128.h
|
||||
Src/ThirdPartyLibs/float128/float128.h \
|
||||
Src/Gui/SEHChainView.h
|
||||
|
||||
FORMS += \
|
||||
Src/Gui/MainWindow.ui \
|
||||
|
|
Loading…
Reference in New Issue